[FEATURE] Send *server* info from prpl-jabber/irc ONLY if public soc.2012.statscollector
authorSanket Agarwal <sanket@soc.pidgin.im>
Sat, 18 Aug 2012 08:30:40 +0530
branchsoc.2012.statscollector
changesetf55e642f3e88 pushlog
parent ebf013265d9b
child ed0fb2431b17
[FEATURE] Send *server* info from prpl-jabber/irc ONLY if public

Sending servers without knowing if they are public, for prpl-jabber/irc
breakdown can be considered breach of secrecy. In order to solve this
problem, the server provides a service on "/trusted" which replies back with
trusted md5's. These can be used to send only the public/trusted bits of
information.

Read on: http://developer.pidgin.im/wiki/GSoC2012/Statscollector#Ensuringservernamesinprpl-jabberircarepublic
libpurple/plugins/statscollector.c
     1.1 --- a/libpurple/plugins/statscollector.c
     1.2 +++ b/libpurple/plugins/statscollector.c
     1.3 @@ -40,9 +40,11 @@
     1.4  
     1.5  /* Timeout */
     1.6  #define RESEND_SEC (24*3600)
     1.7 +#define TRUSTED_CACHE_REFRESH (24*3600)
     1.8  
     1.9  /* Sending URL */
    1.10 -#define SEND_URL "http://stats.pidgin.im:8000/collect/"
    1.11 +#define SEND_URL "http://stats.pidgin.im/collect/"
    1.12 +#define TRUSTED_URL "http://stats.pidgin.im/trusted/"
    1.13  
    1.14  /* Version of XML this plugin supports writing */
    1.15  
    1.16 @@ -65,6 +67,8 @@
    1.17  PurplePlugin *plugin_g;
    1.18  xmlnode *root_stats, *cpuinfo_xml, *ui_info;
    1.19  GHashTable *stats_acc_ht, *stats_plugins_ht, *stats_uis_ht;
    1.20 +GHashTable *trusted_server_cache_ht=NULL;
    1.21 +
    1.22  int save_timer = 0, send_handle = 0, pref_cb_id = -1;
    1.23  
    1.24  /* Types of Operating Systems */
    1.25 @@ -75,6 +79,8 @@
    1.26  static gboolean send_stats();
    1.27  static gboolean plugin_load(PurplePlugin *);
    1.28  static gboolean plugin_unload(PurplePlugin *plugin);
    1.29 +static gboolean refresh_trusted_cache(gpointer data);
    1.30 +static xmlnode *init_stats();
    1.31  
    1.32  static char *
    1.33  epoch_to_str(time_t epoch){
    1.34 @@ -198,6 +204,86 @@
    1.35      save_timer = purple_timeout_add_seconds(5, save_cb, NULL);
    1.36  }
    1.37  
    1.38 +static void
    1.39 +refresh_trusted_cache_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message){
    1.40 +
    1.41 +  /*
    1.42 +   * Check if the header has HTTP/1.1 200 ...
    1.43 +   * I am assuming that the first few characters will always follow
    1.44 +   * the following format:
    1.45 +   * HTTP/1.x xyz reason
    1.46 +   */
    1.47 +
    1.48 +  int code = -1;
    1.49 +  char *header = g_strdup_printf("%s", url_text);
    1.50 +  char *data_loc=NULL;
    1.51 +  const char *hash_id;
    1.52 +  xmlnode *trusted_hash_root, *start;
    1.53 +
    1.54 +  if(!trusted_server_cache_ht){
    1.55 +    trusted_server_cache_ht = g_hash_table_new(g_str_hash, g_str_equal);
    1.56 +  }
    1.57 +
    1.58 +  if(header && strlen(header) >= 14) {
    1.59 +    header += 9;
    1.60 +    header[3] = '\0';
    1.61 +    code = atoi(header);
    1.62 +  }
    1.63 +
    1.64 +  purple_debug_info("STATS", "Code returned: %d\n", code);
    1.65 +  if(code == 200){
    1.66 +    /* Extract the data to be converted to XML => GList */
    1.67 +    data_loc = strstr(url_text, "\r\n\r\n");
    1.68 +    trusted_hash_root = xmlnode_from_str(data_loc, -1);
    1.69 +    if(trusted_hash_root != NULL){
    1.70 +      /* Now load a Hash Table of accepted Hashes, currently they won't
    1.71 +       * contain any data, but this is to keep space for any extra info
    1.72 +       * that the server might give away!
    1.73 +       */
    1.74 +      start = xmlnode_get_child(trusted_hash_root, "hash");
    1.75 +      for(;start;start = xmlnode_get_next_twin(start)){
    1.76 +        hash_id = xmlnode_get_attrib(start, "id");
    1.77 +        g_hash_table_insert(trusted_server_cache_ht, (void *)hash_id, NULL);
    1.78 +      }
    1.79 +      purple_timeout_add_seconds(TRUSTED_CACHE_REFRESH, refresh_trusted_cache, NULL);
    1.80 +      /* Load the stats file into a global variable for any updations */
    1.81 +      root_stats = init_stats();
    1.82 +    }
    1.83 +  } else {
    1.84 +     purple_timeout_add_seconds(10, refresh_trusted_cache, NULL);
    1.85 +  }
    1.86 +}
    1.87 +
    1.88 +static gboolean
    1.89 +refresh_trusted_cache(gpointer data){
    1.90 +
    1.91 +  /* Refresh the stored cache of information about IRC/Jabber
    1.92 +   * servers which are in-effect public using md5 hashes!
    1.93 +   */
    1.94 +
    1.95 +  /* Obtain the list through a webservice */
    1.96 +  gchar *host, *path, *request, *url= TRUSTED_URL;
    1.97 +  gboolean *send_success;
    1.98 +  int port;
    1.99 +
   1.100 +  purple_debug_info("STATS", "requesting trusted ...");
   1.101 +  purple_url_parse(url, &host, &port, &path, NULL, NULL);
   1.102 +  send_success = g_new0(gboolean, 1);
   1.103 +  request = g_strdup_printf(\
   1.104 +          "GET /%s HTTP/1.0\r\n"
   1.105 +          "Connection: keep-alive\r\n"
   1.106 +          "Host: %s:%d\r\n\r\n",
   1.107 +          path, host, port);
   1.108 +  purple_debug_info("STATS", "%s", request);
   1.109 +  purple_util_fetch_url_request(url, TRUE, NULL, FALSE, request, TRUE, refresh_trusted_cache_cb, send_success);
   1.110 +
   1.111 +  g_free(host);
   1.112 +  g_free(path);
   1.113 +  g_free(request);
   1.114 +  return FALSE;
   1.115 +
   1.116 +}
   1.117 +
   1.118  static xmlnode *
   1.119  get_app_32_64(){
   1.120  
   1.121 @@ -708,9 +794,9 @@
   1.122        for (l = g_list_last(user_splits);l != NULL;l = l->prev) {
   1.123  
   1.124            PurpleAccountUserSplit *split = l->data;
   1.125 -          const char *value = NULL;
   1.126 +          const char *value = NULL, *value_md5=NULL;
   1.127            char *c;
   1.128 -          xmlnode *user_split_node;
   1.129 +          xmlnode *user_split_node, *user_split_node_hash;
   1.130  
   1.131            if(purple_account_user_split_get_reverse(split))
   1.132                c = strrchr(username_dup,
   1.133 @@ -725,17 +811,29 @@
   1.134                value = c;
   1.135            }
   1.136  
   1.137 -          if (value == NULL)
   1.138 +          if (value == NULL){
   1.139                value = "";
   1.140 +              value_md5 = NULL;
   1.141 +          } else {
   1.142 +            value_md5 = md5((const guchar *)value);
   1.143 +          }
   1.144  
   1.145 -          user_split_node = xmlnode_new(split->text);
   1.146 -          xmlnode_insert_data(user_split_node, value, -1);
   1.147 +
   1.148 +          /* Check if the Hash is in the trusted_hash_table */
   1.149 +          user_split_node = xmlnode_new(g_strdup_printf("%s", split->text));
   1.150 +          if(g_hash_table_lookup_extended(trusted_server_cache_ht, value_md5, NULL, NULL)){
   1.151 +            xmlnode_insert_data(user_split_node, value, -1);
   1.152 +          }
   1.153            xmlnode_insert_child(acc, user_split_node);
   1.154 +
   1.155 +          /* Normal MD5 node */
   1.156 +          user_split_node_hash = xmlnode_new(g_strdup_printf("%s_hash", split->text));
   1.157 +          xmlnode_insert_data(user_split_node_hash, value_md5, -1);
   1.158 +          xmlnode_insert_child(acc, user_split_node_hash);
   1.159 +
   1.160        }
   1.161 -
   1.162      }
   1.163  
   1.164 -
   1.165      /* Server information if Jabber */
   1.166      if(!g_strcmp0(protocol,"prpl-jabber")){
   1.167  
   1.168 @@ -1161,8 +1259,11 @@
   1.169  
   1.170    else if(check_ask == ALLOW){
   1.171  
   1.172 -    /* Load the stats file into a global variable for any updations */
   1.173 -    root_stats = init_stats();
   1.174 +
   1.175 +    /* Refresh the trusted cache first! */
   1.176 +    refresh_trusted_cache(NULL);
   1.177 +
   1.178 +
   1.179  
   1.180      /* Register the account signals for sign-on */
   1.181      purple_signal_connect(purple_accounts_get_handle(), "account-signed-on",