# HG changeset patch # User Paul Aurich # Date 1227412360 0 # Node ID a259d2711416f89128f08afebf93ed5437641ef9 # Parent 6adbaf3d25e3ea54cc01017774c4ab74df1d01d6# Parent 805aadbb1a85aa31bfbfe886baa19450cb35003c merge of '0da3644551b2442b2db4cf9d828865d167df8072' and 'efc71f836438eb97ec8d2c8e0874326b6fc64774' diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/buddy.c --- a/libpurple/protocols/jabber/buddy.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/buddy.c Sun Nov 23 03:52:40 2008 +0000 @@ -176,9 +176,8 @@ g_free(cmd); jbr->commands = g_list_delete_link(jbr->commands, jbr->commands); } - - jabber_caps_free_clientinfo(jbr->caps); + /* jbr->caps is owned by the caps code */ g_free(jbr->name); g_free(jbr->status); g_free(jbr->thread_id); diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/buddy.h --- a/libpurple/protocols/jabber/buddy.h Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/buddy.h Sun Nov 23 03:52:40 2008 +0000 @@ -81,7 +81,7 @@ char *name; char *os; } client; - JabberCapsClientInfo *caps; + const JabberCapsClientInfo *caps; GList *commands; } JabberBuddyResource; diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/caps.c --- a/libpurple/protocols/jabber/caps.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/caps.c Sun Nov 23 03:52:40 2008 +0000 @@ -21,17 +21,35 @@ #include "internal.h" -#include +#include "debug.h" #include "caps.h" #include "cipher.h" -#include #include "iq.h" #include "presence.h" #include "util.h" #define JABBER_CAPS_FILENAME "xmpp-caps.xml" -GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */ +typedef struct _JabberDataFormField { + gchar *var; + GList *values; +} JabberDataFormField; + +typedef struct _JabberCapsKey { + char *node; + char *ver; + char *hash; +} JabberCapsKey; + +static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsClientInfo */ + +/** + * Processes a query-node and returns a JabberCapsClientInfo object with all relevant info. + * + * @param query A query object. + * @return A JabberCapsClientInfo object. + */ +static JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query); #if 0 typedef struct _JabberCapsValue { @@ -40,7 +58,6 @@ GHashTable *ext; /* char * -> JabberCapsValueExt */ } JabberCapsValue; #endif -typedef struct _JabberCapsClientInfo JabberCapsValue; static guint jabber_caps_hash(gconstpointer key) { const JabberCapsKey *name = key; @@ -54,7 +71,9 @@ const JabberCapsKey *name1 = v1; const JabberCapsKey *name2 = v2; - return strcmp(name1->node,name2->node) == 0 && strcmp(name1->ver,name2->ver) == 0 && strcmp(name1->hash,name2->hash) == 0; + return strcmp(name1->node, name2->node) == 0 && + strcmp(name1->ver, name2->ver) == 0 && + strcmp(name1->hash, name2->hash) == 0; } void jabber_caps_destroy_key(gpointer key) { @@ -65,28 +84,34 @@ g_free(keystruct); } -static void jabber_caps_destroy_value(gpointer value) { - JabberCapsValue *valuestruct = value; - while(valuestruct->identities) { - JabberCapsIdentity *id = valuestruct->identities->data; +static void +jabber_caps_client_info_destroy(gpointer data) { + JabberCapsClientInfo *info = data; + while(info->identities) { + JabberIdentity *id = info->identities->data; g_free(id->category); g_free(id->type); g_free(id->name); + g_free(id->lang); g_free(id); - - valuestruct->identities = g_list_delete_link(valuestruct->identities,valuestruct->identities); + info->identities = g_list_delete_link(info->identities, info->identities); } - while(valuestruct->features) { - g_free(valuestruct->features->data); - valuestruct->features = g_list_delete_link(valuestruct->features,valuestruct->features); + + while(info->features) { + g_free(info->features->data); + info->features = g_list_delete_link(info->features, info->features); } - while(valuestruct->forms) { - g_free(valuestruct->forms->data); - valuestruct->forms = g_list_delete_link(valuestruct->forms,valuestruct->forms); + while(info->forms) { + g_free(info->forms->data); + info->forms = g_list_delete_link(info->forms, info->forms); } - //g_hash_table_destroy(valuestruct->ext); - g_free(valuestruct); + +#if 0 + g_hash_table_destroy(valuestruct->ext); +#endif + + g_free(info); } #if 0 @@ -111,11 +136,18 @@ static void jabber_caps_load(void); -void jabber_caps_init(void) { - capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, jabber_caps_destroy_key, jabber_caps_destroy_value); +void jabber_caps_init(void) +{ + capstable = g_hash_table_new_full(jabber_caps_hash, jabber_caps_compare, jabber_caps_destroy_key, jabber_caps_client_info_destroy); jabber_caps_load(); } +void jabber_caps_uninit(void) +{ + g_hash_table_destroy(capstable); + capstable = NULL; +} + static void jabber_caps_load(void) { xmlnode *capsdata = purple_util_read_xml_from_file(JABBER_CAPS_FILENAME, "XMPP capabilities cache"); xmlnode *client; @@ -133,7 +165,7 @@ continue; if(!strcmp(client->name, "client")) { JabberCapsKey *key = g_new0(JabberCapsKey, 1); - JabberCapsValue *value = g_new0(JabberCapsValue, 1); + JabberCapsClientInfo *value = g_new0(JabberCapsClientInfo, 1); xmlnode *child; key->node = g_strdup(xmlnode_get_attrib(client,"node")); key->ver = g_strdup(xmlnode_get_attrib(client,"ver")); @@ -150,11 +182,17 @@ const char *category = xmlnode_get_attrib(child, "category"); const char *type = xmlnode_get_attrib(child, "type"); const char *name = xmlnode_get_attrib(child, "name"); - - JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); + const char *lang = xmlnode_get_attrib(child, "lang"); + JabberIdentity *id; + + if (!category || !type) + continue; + + id = g_new0(JabberIdentity, 1); id->category = g_strdup(category); id->type = g_strdup(type); id->name = g_strdup(name); + id->lang = g_strdup(lang); value->identities = g_list_append(value->identities,id); } else if(!strcmp(child->name,"x")) { @@ -196,7 +234,7 @@ static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) { JabberCapsKey *clientinfo = key; - JabberCapsValue *props = value; + JabberCapsClientInfo *props = value; xmlnode *root = user_data; xmlnode *client = xmlnode_new_child(root, "client"); GList *iter; @@ -205,12 +243,14 @@ xmlnode_set_attrib(client, "ver", clientinfo->ver); xmlnode_set_attrib(client, "hash", clientinfo->hash); for(iter = props->identities; iter; iter = g_list_next(iter)) { - JabberCapsIdentity *id = iter->data; + JabberIdentity *id = iter->data; xmlnode *identity = xmlnode_new_child(client, "identity"); xmlnode_set_attrib(identity, "category", id->category); xmlnode_set_attrib(identity, "type", id->type); if (id->name) xmlnode_set_attrib(identity, "name", id->name); + if (id->lang) + xmlnode_set_attrib(identity, "lang", id->lang); } for(iter = props->features; iter; iter = g_list_next(iter)) { @@ -301,28 +341,6 @@ } #endif -void jabber_caps_free_clientinfo(JabberCapsClientInfo *clientinfo) { - if(!clientinfo) - return; - while(clientinfo->identities) { - JabberCapsIdentity *id = clientinfo->identities->data; - g_free(id->category); - g_free(id->type); - g_free(id->name); - g_free(id); - - clientinfo->identities = g_list_delete_link(clientinfo->identities,clientinfo->identities); - } - while(clientinfo->features) { - char *feat = clientinfo->features->data; - g_free(feat); - - clientinfo->features = g_list_delete_link(clientinfo->features,clientinfo->features); - } - - g_free(clientinfo); -} - typedef struct _jabber_caps_cbplususerdata { jabber_caps_get_info_cb cb; gpointer user_data; @@ -331,13 +349,17 @@ char *node; char *ver; char *hash; +#if 0 unsigned extOutstanding; +#endif } jabber_caps_cbplususerdata; +#if 0 typedef struct jabber_ext_userdata { jabber_caps_cbplususerdata *userdata; char *node; } jabber_ext_userdata; +#endif #if 0 static void jabber_caps_get_info_check_completion(jabber_caps_cbplususerdata *userdata) { @@ -418,122 +440,99 @@ } #endif -static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer data) { - /* collect data and fetch all exts */ +static void +jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer data) +{ xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#info"); jabber_caps_cbplususerdata *userdata = data; + JabberCapsClientInfo *info, *value; + gchar *hash; + const char *type = xmlnode_get_attrib(packet, "type"); + JabberCapsKey key; - /* TODO: Better error checking! */ - if (!strcmp(xmlnode_get_attrib(packet, "type"), "error"))return; - if (query) { - // check hash - JabberCapsClientInfo *info = jabber_caps_parse_client_info(query); - gchar *hash = 0; - JabberCapsValue *value; - JabberCapsKey *key; - xmlnode *child; + if (!query || !strcmp(type, "error")) { + userdata->cb(NULL, userdata->user_data); + + g_free(userdata->who); + g_free(userdata->node); + g_free(userdata->ver); + g_free(userdata->hash); + g_free(userdata); + return; + } - if (!strcmp(userdata->hash, "sha-1")) { - hash = jabber_caps_calculate_hash(info, "sha1"); - } else if (!strcmp(userdata->hash, "md5")) { - hash = jabber_caps_calculate_hash(info, "md5"); - } else { - // clean up - return; - } + /* check hash */ + info = jabber_caps_parse_client_info(query); + + if (!strcmp(userdata->hash, "sha-1")) { + hash = jabber_caps_calculate_hash(info, "sha1"); + } else if (!strcmp(userdata->hash, "md5")) { + hash = jabber_caps_calculate_hash(info, "md5"); + } else { + purple_debug_warning("jabber", "unknown caps hash algorithm: %s\n", userdata->hash); - printf("\n\tfrom: %s", xmlnode_get_attrib(packet, "from")); - printf("\n\tnode: %s", xmlnode_get_attrib(query, "node")); - printf("\n\tcalculated key: %s", hash); - printf("\n\thash: %s", userdata->hash); - printf("\n"); - - if (strcmp(hash, userdata->ver)) { - g_free(info); - g_free(hash); - printf("\n! ! ! invalid hash ! ! !"); - return; - } - - g_free(hash); - - value = g_new0(JabberCapsValue, 1); - key = g_new0(JabberCapsKey, 1); + userdata->cb(NULL, userdata->user_data); + + jabber_caps_client_info_destroy(info); + g_free(userdata->who); + g_free(userdata->node); + g_free(userdata->ver); + g_free(userdata->hash); + g_free(userdata); + return; + } -#if 0 - value->ext = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_caps_ext_destroy_value); -#endif - key->node = g_strdup(userdata->node); - key->ver = g_strdup(userdata->ver); - key->hash = g_strdup(userdata->hash); - - /* check whether it's stil not in the table */ - if (g_hash_table_lookup(capstable, key)) { - jabber_caps_destroy_key(key); - g_free(value); - return; - } - - - for(child = query->child; child; child = child->next) { - if(child->type != XMLNODE_TYPE_TAG) - continue; - if(!strcmp(child->name,"feature")) { - const char *var = xmlnode_get_attrib(child, "var"); - if(!var) - continue; - value->features = g_list_append(value->features, g_strdup(var)); - } else if(!strcmp(child->name,"identity")) { - const char *category = xmlnode_get_attrib(child, "category"); - const char *type = xmlnode_get_attrib(child, "type"); - const char *name = xmlnode_get_attrib(child, "name"); - - JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); - id->category = g_strdup(category); - id->type = g_strdup(type); - id->name = g_strdup(name); + if (!hash || strcmp(hash, userdata->ver)) { + purple_debug_warning("jabber", "caps hash from %s did not match\n", xmlnode_get_attrib(packet, "from")); + userdata->cb(NULL, userdata->user_data); + + jabber_caps_client_info_destroy(info); + g_free(userdata->who); + g_free(userdata->node); + g_free(userdata->ver); + g_free(userdata->hash); + g_free(userdata); + g_free(hash); + return; + } + + key.node = userdata->node; + key.ver = userdata->ver; + key.hash = userdata->hash; - value->identities = g_list_append(value->identities,id); - } else if(!strcmp(child->name, "x")) { - value->forms = g_list_append(value->forms, xmlnode_copy(child)); - } - } - - g_hash_table_replace(capstable, key, value); - jabber_caps_store(); - + /* check whether it's not in the table */ + if ((value = g_hash_table_lookup(capstable, &key))) { + JabberCapsClientInfo *tmp = info; + info = value; + jabber_caps_client_info_destroy(tmp); + } else { + JabberCapsKey *n_key = g_new(JabberCapsKey, 1); + n_key->node = userdata->node; + n_key->ver = userdata->ver; + n_key->hash = userdata->hash; + userdata->node = userdata->ver = userdata->hash = NULL; -#if 0 - /* fetch all exts */ - for(iter = userdata->ext; iter; iter = g_list_next(iter)) { - JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, - "http://jabber.org/protocol/disco#info"); - xmlnode *query = xmlnode_get_child_with_namespace(iq->node, - "query", "http://jabber.org/protocol/disco#info"); - char *node = g_strdup_printf("%s#%s", userdata->node, (const char*)iter->data); - jabber_ext_userdata *ext_data = g_new0(jabber_ext_userdata, 1); - ext_data->node = node; - ext_data->userdata = userdata; + g_hash_table_insert(capstable, n_key, info); + jabber_caps_store(); + } + + userdata->cb(info, userdata->user_data); - xmlnode_set_attrib(query, "node", node); - xmlnode_set_attrib(iq->node, "to", userdata->who); - - jabber_iq_set_callback(iq, jabber_caps_ext_iqcb, ext_data); - jabber_iq_send(iq); - } - - } else - /* Don't wait for the ext discoveries; they aren't going to happen */ - userdata->extOutstanding = 0; - - jabber_caps_get_info_check_completion(userdata); -#endif - } + /* capstable will free info */ + g_free(userdata->who); + g_free(userdata->node); + g_free(userdata->ver); + g_free(userdata->hash); + g_free(userdata); + g_free(hash); } -void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, const char *ver, const char *hash, jabber_caps_get_info_cb cb, gpointer user_data) { - JabberCapsValue *client; +void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, + const char *ver, const char *hash, jabber_caps_get_info_cb cb, + gpointer user_data) +{ + JabberCapsClientInfo *client; JabberCapsKey *key = g_new0(JabberCapsKey, 1); jabber_caps_cbplususerdata *userdata = g_new0(jabber_caps_cbplususerdata, 1); userdata->cb = cb; @@ -548,8 +547,6 @@ key->hash = g_strdup(hash); client = g_hash_table_lookup(capstable, key); - - g_hash_table_replace(jabber_contact_info, g_strdup(who), key); if(!client) { JabberIq *iq = jabber_iq_new_query(js,JABBER_IQ_GET,"http://jabber.org/protocol/disco#info"); @@ -607,10 +604,18 @@ ac = a; bc = b; - + if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) { if ((typ_cmp = strcmp(ac->type, bc->type)) == 0) { - return strcmp(ac->lang, bc->lang); + if (!ac->lang && !bc->lang) { + return 0; + } else if (ac->lang && !bc->lang) { + return 1; + } else if (!ac->lang && bc->lang) { + return -1; + } else { + return strcmp(ac->lang, bc->lang); + } } else { return typ_cmp; } @@ -631,16 +636,6 @@ } #endif -static gint jabber_caps_string_compare(gconstpointer a, gconstpointer b) { - const gchar *ac; - const gchar *bc; - - ac = a; - bc = b; - - return strcmp(ac, bc); -} - static gchar *jabber_caps_get_formtype(const xmlnode *x) { xmlnode *formtypefield; formtypefield = xmlnode_get_child(x, "field"); @@ -665,12 +660,13 @@ return result; } -JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) { +static JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) +{ xmlnode *child; JabberCapsClientInfo *info; - if (!query) return 0; - if (strcmp(query->xmlns,"http://jabber.org/protocol/disco#info")) return 0; + if (!query || strcmp(query->xmlns, "http://jabber.org/protocol/disco#info")) + return 0; info = g_new0(JabberCapsClientInfo, 1); @@ -680,12 +676,18 @@ const char *category = xmlnode_get_attrib(child, "category"); const char *type = xmlnode_get_attrib(child, "type"); const char *name = xmlnode_get_attrib(child, "name"); + const char *lang = xmlnode_get_attrib(child, "lang"); + JabberIdentity *id; - JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); + if (!category || !type) + continue; + + id = g_new0(JabberIdentity, 1); id->category = g_strdup(category); id->type = g_strdup(type); id->name = g_strdup(name); - + id->lang = g_strdup(lang); + info->identities = g_list_append(info->identities, id); } else if (!strcmp(child->name, "feature")) { /* parse feature */ @@ -694,7 +696,7 @@ continue; info->features = g_list_append(info->features, g_strdup(var)); } else if (!strcmp(child->name, "x")) { - if (!strcmp(child->xmlns, "jabber:x:data")) { + if (child->xmlns && !strcmp(child->xmlns, "jabber:x:data")) { /* x-data form */ xmlnode *dataform = xmlnode_copy(child); info->forms = g_list_append(info->forms, dataform); @@ -704,158 +706,154 @@ return info; } -static gint jabber_caps_xdata_field_compare(gconstpointer a, gconstpointer b) { - const JabberDataFormField *ac; - const JabberDataFormField *bc; - - ac = a; - bc = b; - +static gint jabber_caps_xdata_field_compare(gconstpointer a, gconstpointer b) +{ + const JabberDataFormField *ac = a; + const JabberDataFormField *bc = b; + return strcmp(ac->var, bc->var); } -static GList *jabber_caps_xdata_get_fields(const xmlnode *x) { - GList *fields = 0; +static GList* jabber_caps_xdata_get_fields(const xmlnode *x) +{ + GList *fields = NULL; xmlnode *field; - xmlnode *value; - JabberDataFormField *xdatafield; - - if(!x) return 0; - - for(field = xmlnode_get_child(x, "field"); field != 0; field = xmlnode_get_next_twin(field)) { - xdatafield = g_new0(JabberDataFormField, 1); + + if (!x) + return NULL; + + for (field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { + xmlnode *value; + JabberDataFormField *xdatafield = g_new0(JabberDataFormField, 1); xdatafield->var = g_strdup(xmlnode_get_attrib(field, "var")); - for(value = xmlnode_get_child(field, "value"); value != 0; value = xmlnode_get_next_twin(value)) { + + for (value = xmlnode_get_child(field, "value"); value; value = xmlnode_get_next_twin(value)) { gchar *val = xmlnode_get_data(value); xdatafield->values = g_list_append(xdatafield->values, val); } - xdatafield->values = g_list_sort(xdatafield->values, jabber_caps_string_compare); + + xdatafield->values = g_list_sort(xdatafield->values, (GCompareFunc)strcmp); fields = g_list_append(fields, xdatafield); - } + } + fields = g_list_sort(fields, jabber_caps_xdata_field_compare); return fields; } -static gchar *jabber_caps_verification_append(gchar *verification_string, gchar *string) { - gchar *verification; - verification = g_strconcat(verification_string, string, "<", NULL); - g_free(verification_string); - return verification; +static GString* +jabber_caps_verification_append(GString *verification, const gchar *string) +{ + verification = g_string_append(verification, string); + return g_string_append_c(verification, '<'); } -gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash) { - GList *identities; - GList *features; - GList *xdata; - gchar *verification = 0; - gchar *feature_string; - gchar *free_verification; - gchar *identity_string; +gchar *jabber_caps_calculate_hash(JabberCapsClientInfo *info, const char *hash) +{ + GList *node; + GString *verification; PurpleCipherContext *context; guint8 checksum[20]; gsize checksum_size = 20; - - if (!info) return 0; - + + if (!info || !(context = purple_cipher_context_new_by_name(hash, NULL))) + return NULL; + /* sort identities, features and x-data forms */ info->identities = g_list_sort(info->identities, jabber_caps_jabber_identity_compare); - info->features = g_list_sort(info->features, jabber_caps_string_compare); + info->features = g_list_sort(info->features, (GCompareFunc)strcmp); info->forms = g_list_sort(info->forms, jabber_caps_jabber_xdata_compare); - + + verification = g_string_new(""); + /* concat identities to the verification string */ - for(identities = info->identities; identities; identities = identities->next) { - JabberIdentity *ident = (JabberIdentity*)identities->data; - identity_string = g_strdup_printf("%s/%s//%s<", ident->category, ident->type, ident->name); - free_verification = verification; - if(verification == 0) verification = g_strdup(identity_string); - else verification = g_strconcat(verification, identity_string, NULL); - g_free(identity_string); - g_free(free_verification); + for (node = info->identities; node; node = node->next) { + JabberIdentity *id = (JabberIdentity*)node->data; + + g_string_append_printf(verification, "%s/%s/%s/%s<", id->category, + id->type, id->lang ? id->lang : "", id->name); } - + /* concat features to the verification string */ - for(features = info->features; features; features = features->next) { - feature_string = g_strdup_printf("%s", (gchar*)features->data); - verification = jabber_caps_verification_append(verification, feature_string); - g_free(feature_string); + for (node = info->features; node; node = node->next) { + verification = jabber_caps_verification_append(verification, node->data); } - + /* concat x-data forms to the verification string */ - for(xdata = info->forms; xdata; xdata = xdata->next) { - gchar *formtype = 0; - GList *fields; + for(node = info->forms; node; node = node->next) { + xmlnode *data = (xmlnode *)node->data; + gchar *formtype = jabber_caps_get_formtype(data); + GList *fields = jabber_caps_xdata_get_fields(data); + /* append FORM_TYPE's field value to the verification string */ - formtype = jabber_caps_get_formtype((xmlnode*)xdata->data); verification = jabber_caps_verification_append(verification, formtype); g_free(formtype); - - for(fields = jabber_caps_xdata_get_fields((xmlnode*)(xdata->data)); fields != 0; fields = fields->next) { + + while (fields) { GList *value; JabberDataFormField *field = (JabberDataFormField*)fields->data; - if(strcmp(field->var, "FORM_TYPE")) { - /* Append the value of the "var" attribute, followed by the '<' character. */ + + if (strcmp(field->var, "FORM_TYPE")) { + /* Append the "var" attribute */ verification = jabber_caps_verification_append(verification, field->var); - /* For each element, append the XML character data, followed by the '<' character. */ - for(value = field->values; value != 0; value = value->next) { + /* Append element's cdata */ + for(value = field->values; value; value = value->next) { verification = jabber_caps_verification_append(verification, value->data); + g_free(value->data); } } - for(value = field->values; value != 0; value = value->next) { - g_free(value->data); - } + g_free(field->var); g_list_free(field->values); + + fields = g_list_delete_link(fields, fields); } - g_list_free(fields); - } - - /* generate hash */ - context = purple_cipher_context_new_by_name(hash, NULL); - if (context == NULL) { - //purple_debug_error("jabber", "Could not find cipher\n"); - return 0; } - purple_cipher_context_append(context, (guchar*)verification, strlen(verification)); - - if (!purple_cipher_context_digest(context, strlen(verification), checksum, &checksum_size)) { - //purple_debug_error("util", "Failed to get digest.\n"); + + /* generate hash */ + purple_cipher_context_append(context, (guchar*)verification->str, verification->len); + + if (!purple_cipher_context_digest(context, verification->len, checksum, &checksum_size)) { + /* purple_debug_error("util", "Failed to get digest.\n"); */ + g_string_free(verification, TRUE); + purple_cipher_context_destroy(context); + return NULL; } + + g_string_free(verification, TRUE); purple_cipher_context_destroy(context); - - /* apply Base64 on hash */ - - g_free(verification); - verification = purple_base64_encode(checksum, checksum_size); - - return verification; + + return purple_base64_encode(checksum, checksum_size); } void jabber_caps_calculate_own_hash(JabberStream *js) { - JabberCapsClientInfo *info; + JabberCapsClientInfo info; GList *iter = 0; GList *features = 0; - if (jabber_identities == 0 && jabber_features == 0) return; + if (!jabber_identities && !jabber_features) { + /* This really shouldn't ever happen */ + purple_debug_warning("jabber", "No features or identities, cannot calculate own caps hash.\n"); + g_free(js->caps_hash); + js->caps_hash = NULL; + return; + } - /* sort features */ + /* build the currently-supported list of features */ if (jabber_features) { for (iter = jabber_features; iter; iter = iter->next) { JabberFeature *feat = iter->data; - if(feat->is_enabled == NULL || feat->is_enabled(js, feat->namespace) == TRUE) { + if(!feat->is_enabled || feat->is_enabled(js, feat->namespace)) { features = g_list_append(features, feat->namespace); } } } - info = g_new0(JabberCapsClientInfo, 1); - info->features = features; - info->identities = jabber_identities; - info->forms = 0; - - if (js->caps_hash) - g_free(js->caps_hash); - js->caps_hash = jabber_caps_calculate_hash(info, "sha1"); - g_free(info); + info.features = features; + info.identities = jabber_identities; + info.forms = NULL; + + g_free(js->caps_hash); + js->caps_hash = jabber_caps_calculate_hash(&info, "sha1"); g_list_free(features); } diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/caps.h --- a/libpurple/protocols/jabber/caps.h Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/caps.h Sun Nov 23 03:52:40 2008 +0000 @@ -26,33 +26,22 @@ #include "jabber.h" -/* Implementation of XEP-0115 */ -extern GHashTable *capstable; - -typedef struct _JabberIdentity JabberCapsIdentity; +/* Implementation of XEP-0115 - Entity Capabilities */ struct _JabberCapsClientInfo { - GList *identities; /* JabberCapsIdentity */ + GList *identities; /* JabberIdentity */ GList *features; /* char * */ GList *forms; /* xmlnode * */ }; +#if 0 typedef struct _JabberCapsClientInfo JabberCapsValueExt; - -typedef struct _JabberDataFormField { - gchar *var; - GList *values; -} JabberDataFormField; - -typedef struct _JabberCapsKey { - char *node; - char *ver; - char *hash; -} JabberCapsKey; +#endif typedef void (*jabber_caps_get_info_cb)(JabberCapsClientInfo *info, gpointer user_data); void jabber_caps_init(void); +void jabber_caps_uninit(void); void jabber_caps_destroy_key(gpointer value); @@ -60,15 +49,6 @@ * Main entity capabilites function to get the capabilities of a contact. */ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, const char *ver, const char *hash, jabber_caps_get_info_cb cb, gpointer user_data); -void jabber_caps_free_clientinfo(JabberCapsClientInfo *clientinfo); - -/** - * Processes a query-node and returns a JabberCapsClientInfo object with all relevant info. - * - * @param query A query object. - * @return A JabberCapsClientInfo object. - */ -JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query); /** * Takes a JabberCapsClientInfo pointer and returns the caps hash according to diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/data.c --- a/libpurple/protocols/jabber/data.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/data.c Sun Nov 23 03:52:40 2008 +0000 @@ -244,4 +244,5 @@ g_hash_table_destroy(local_data_by_alt); g_hash_table_destroy(local_data_by_cid); g_hash_table_destroy(remote_data_by_cid); + local_data_by_alt = local_data_by_cid = remote_data_by_cid = NULL; } diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/disco.c --- a/libpurple/protocols/jabber/disco.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/disco.c Sun Nov 23 03:52:40 2008 +0000 @@ -99,7 +99,7 @@ const char *node = NULL; char *node_uri = NULL; - // create custom caps node URI + /* create custom caps node URI */ node_uri = g_strconcat(CAPS0115_NODE, "#", jabber_caps_get_own_hash(js), NULL); if((in_query = xmlnode_get_child(packet, "query"))) { @@ -126,11 +126,14 @@ identity = xmlnode_new_child(query, "identity"); xmlnode_set_attrib(identity, "category", ident->category); xmlnode_set_attrib(identity, "type", ident->type); - if (ident->name != 0) xmlnode_set_attrib(identity, "name", ident->name); + if (ident->lang) + xmlnode_set_attrib(identity, "xml:lang", ident->lang); + if (ident->name) + xmlnode_set_attrib(identity, "name", ident->name); } for(features = jabber_features; features; features = features->next) { JabberFeature *feat = (JabberFeature*)features->data; - if(feat->is_enabled == NULL || feat->is_enabled(js, feat->namespace) == TRUE) { + if (!feat->is_enabled || feat->is_enabled(js, feat->namespace)) { feature = xmlnode_new_child(query, "feature"); xmlnode_set_attrib(feature, "var", feat->namespace); } diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sun Nov 23 03:52:40 2008 +0000 @@ -67,8 +67,6 @@ GList *jabber_features = NULL; GList *jabber_identities = NULL; -GHashTable *jabber_contact_info = NULL; - static void jabber_unregister_account_cb(JabberStream *js); static void try_srv_connect(JabberStream *js); @@ -1520,7 +1518,17 @@ } } -void jabber_add_identity(const gchar *category, const gchar *type, const gchar *name) { +static void jabber_features_destroy(void) +{ + while (jabber_features) { + JabberFeature *feature = jabber_features->data; + g_free(feature->namespace); + g_free(feature); + jabber_features = g_list_remove_link(jabber_features, jabber_features); + } +} + +void jabber_add_identity(const gchar *category, const gchar *type, const gchar *lang, const gchar *name) { GList *identity; JabberIdentity *ident; /* both required according to XEP-0030 */ @@ -1529,18 +1537,34 @@ for(identity = jabber_identities; identity; identity = identity->next) { JabberIdentity *ident = (JabberIdentity*)identity->data; - if(!strcmp(ident->category, category)) { - if (!strcmp(ident->type, type)) return; + if (!strcmp(ident->category, category) && + !strcmp(ident->type, type) && + ((!ident->lang && !lang) || (ident->lang && lang && !strcmp(ident->lang, lang)))) { + return; } } - + ident = g_new0(JabberIdentity, 1); ident->category = g_strdup(category); ident->type = g_strdup(type); + ident->lang = g_strdup(lang); ident->name = g_strdup(name); jabber_identities = g_list_append(jabber_identities, ident); } +static void jabber_identities_destroy(void) +{ + while (jabber_identities) { + JabberIdentity *id = jabber_identities->data; + g_free(id->category); + g_free(id->type); + g_free(id->lang); + g_free(id->name); + g_free(id); + jabber_identities = g_list_remove_link(jabber_identities, jabber_identities); + } +} + const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b) { return "jabber"; @@ -2541,38 +2565,57 @@ _("buzz: Buzz a user to get their attention"), NULL); } -/* IPC functions*/ - -/* - * IPC function for checking wheather a client at a full JID supports a certain feature. - * - * @param fulljid The full JID of the client. - * @param featrure The feature's namespace. - * +/* IPC functions */ + +/** + * IPC function for determining if a contact supports a certain feature. + * + * @param account The PurpleAccount + * @param jid The full JID of the contact. + * @param feature The feature's namespace. + * * @return TRUE if supports feature; else FALSE. */ static gboolean -jabber_ipc_contact_has_feature(gchar *fulljid, gchar *feature) +jabber_ipc_contact_has_feature(PurpleAccount *account, const gchar *jid, + const gchar *feature) { - JabberCapsKey *caps_info = NULL; - JabberCapsValueExt *capabilities = NULL; - - caps_info = g_hash_table_lookup(jabber_contact_info, fulljid); - - if (!caps_info) return FALSE; - capabilities = g_hash_table_lookup(capstable, caps_info); - - if (g_list_find_custom(capabilities->features, feature, (GCompareFunc)strcmp) == NULL) return FALSE ; - return TRUE; + PurpleConnection *gc = purple_account_get_connection(account); + JabberStream *js; + JabberBuddy *jb; + JabberBuddyResource *jbr; + gchar *resource; + + if (!purple_account_is_connected(account)) + return FALSE; + js = gc->proto_data; + + resource = jabber_get_resource(jid); + if (!(resource = jabber_get_resource(jid)) || + !(jb = jabber_buddy_find(js, jid, FALSE)) || + !(jbr = jabber_buddy_find_resource(jb, resource))) { + g_free(resource); + return FALSE; + } + + g_free(resource); + + if (!jbr->caps) { + /* TODO: fetch them? */ + return FALSE; + } + + return NULL != g_list_find_custom(jbr->caps->features, feature, (GCompareFunc)strcmp); } static void -jabber_ipc_add_feature(gchar *feature) +jabber_ipc_add_feature(const gchar *feature) { - if (feature == 0) return; + if (!feature) + return; jabber_add_feature(feature, 0); - - // send presence with new caps info for all connected accounts + + /* send presence with new caps info for all connected accounts */ jabber_caps_broadcast_change(); } @@ -2581,7 +2624,7 @@ { my_protocol = plugin; - jabber_add_identity("client", "pc", PACKAGE); + jabber_add_identity("client", "pc", NULL, PACKAGE); /* initialize jabber_features list */ jabber_add_feature("jabber:iq:last", 0); @@ -2603,12 +2646,11 @@ jabber_add_feature("http://jabber.org/protocol/xhtml-im", 0); jabber_add_feature("urn:xmpp:ping", 0); - jabber_contact_info = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_caps_destroy_key); - /* IPC functions */ purple_plugin_ipc_register(plugin, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature), - purple_marshal_BOOLEAN__POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 2, + purple_marshal_BOOLEAN__POINTER_POINTER_POINTER, + purple_value_new(PURPLE_TYPE_BOOLEAN), 3, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), purple_value_new(PURPLE_TYPE_STRING), purple_value_new(PURPLE_TYPE_STRING)); purple_plugin_ipc_register(plugin, "add_feature", PURPLE_CALLBACK(jabber_ipc_add_feature), @@ -2616,3 +2658,10 @@ NULL, 1, purple_value_new(PURPLE_TYPE_STRING)); } + +void +jabber_uninit_plugin(void) +{ + jabber_features_destroy(); + jabber_identities_destroy(); +} diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.h Sun Nov 23 03:52:40 2008 +0000 @@ -279,8 +279,6 @@ extern GList *jabber_features; extern GList *jabber_identities; -extern GHashTable *jabber_contact_info; /* char * -> JabberCapsKey */ - void jabber_stream_features_parse(JabberStream *js, xmlnode *packet); void jabber_process_packet(JabberStream *js, xmlnode **packet); void jabber_send(JabberStream *js, xmlnode *data); @@ -308,10 +306,11 @@ /** Adds an identitiy to this jabber library instance. For list of valid values vistit the * webiste of the XMPP Registrar ( http://www.xmpp.org/registrar/disco-categories.html#client ). * @param category the category of the identity. - * @param type the type of the identity. - * @param name the name of the identity. + * @param type the type of the identity. + * @param language the language localization of the name. Can be NULL. + * @param name the name of the identity. */ -void jabber_add_identity(const gchar *category, const gchar *type, const gchar *name); +void jabber_add_identity(const gchar *category, const gchar *type, const gchar *lang, const gchar *name); /** PRPL functions */ const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b); @@ -334,6 +333,8 @@ int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len); GList *jabber_actions(PurplePlugin *plugin, gpointer context); void jabber_register_commands(void); + void jabber_init_plugin(PurplePlugin *plugin); +void jabber_uninit_plugin(void); #endif /* _PURPLE_JABBER_H_ */ diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/libxmpp.c --- a/libpurple/protocols/jabber/libxmpp.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Sun Nov 23 03:52:40 2008 +0000 @@ -148,9 +148,17 @@ purple_signal_unregister(plugin, "jabber-sending-xmlnode"); purple_signal_unregister(plugin, "jabber-sending-text"); - + + /* reverse order of init_plugin */ jabber_data_uninit(); - + /* PEP things should be uninit via jabber_pep_uninit, not here */ + jabber_pep_uninit(); + jabber_caps_uninit(); + jabber_iq_uninit(); + + /* Stay on target...stay on target... Almost there... */ + jabber_uninit_plugin(); + return TRUE; } @@ -272,11 +280,12 @@ #endif #endif jabber_register_commands(); - + + /* reverse order of unload_plugin */ jabber_iq_init(); + jabber_caps_init(); + /* PEP things should be init via jabber_pep_init, not here */ jabber_pep_init(); - jabber_caps_init(); - jabber_tune_init(); jabber_data_init(); #warning implement adding and retrieving own features via IPC API diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/parser.c --- a/libpurple/protocols/jabber/parser.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/parser.c Sun Nov 23 03:52:40 2008 +0000 @@ -114,7 +114,8 @@ xmlnode *packet = js->current; js->current = NULL; jabber_process_packet(js, &packet); - xmlnode_free(packet); + if (packet != NULL) + xmlnode_free(packet); } } diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/pep.c --- a/libpurple/protocols/jabber/pep.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/pep.c Sun Nov 23 03:52:40 2008 +0000 @@ -26,6 +26,7 @@ #include #include "usermood.h" #include "usernick.h" +#include "usertune.h" static GHashTable *pep_handlers = NULL; @@ -35,10 +36,17 @@ /* register PEP handlers */ jabber_mood_init(); + jabber_tune_init(); jabber_nick_init(); } } +void jabber_pep_uninit(void) { + /* any PEP handlers that need to clean things up go here */ + g_hash_table_destroy(pep_handlers); + pep_handlers = NULL; +} + void jabber_pep_init_actions(GList **m) { /* register the PEP-specific actions */ jabber_mood_init_action(m); diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/pep.h --- a/libpurple/protocols/jabber/pep.h Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/pep.h Sun Nov 23 03:52:40 2008 +0000 @@ -27,6 +27,7 @@ #include "buddy.h" void jabber_pep_init(void); +void jabber_pep_uninit(void); void jabber_pep_init_actions(GList **m); diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/jabber/presence.c --- a/libpurple/protocols/jabber/presence.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/jabber/presence.c Sun Nov 23 03:52:40 2008 +0000 @@ -382,38 +382,31 @@ char *from; } JabberPresenceCapabilities; -static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) { - JabberPresenceCapabilities *userdata = user_data; - JabberID *jid; +static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, JabberPresenceCapabilities *userdata) +{ JabberBuddyResource *jbr; - GList *iter; + char *resource = g_utf8_strrchr(userdata->from, -1, '/'); + resource += 1; - jid = jabber_id_new(userdata->from); - jbr = jabber_buddy_find_resource(userdata->jb, jid->resource); - jabber_id_free(jid); - - if(!jbr) { + jbr = jabber_buddy_find_resource(userdata->jb, resource); + if (!jbr) { g_free(userdata->from); g_free(userdata); return; } - if(jbr->caps) - jabber_caps_free_clientinfo(jbr->caps); + /* old value in jbr->caps is owned by caps code */ jbr->caps = info; if (info) { - for(iter = info->features; iter; iter = g_list_next(iter)) { - if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) { - JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); - xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items"); - xmlnode_set_attrib(iq->node, "to", userdata->from); - xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); - - jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL); - jabber_iq_send(iq); - break; - } + GList *node = g_list_find_custom(info->features, "http://jabber.org/protocol/commands", (GCompareFunc)strcmp); + if (node) { + JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); + xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", "http://jabber.org/protocol/disco#items"); + xmlnode_set_attrib(iq->node, "to", userdata->from); + xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); + jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL); + jabber_iq_send(iq); } } @@ -468,7 +461,7 @@ if (buddy) { jb = jabber_buddy_find(js, from, TRUE); - if ((jb->subscription & JABBER_SUB_TO)) + if ((jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING))) onlist = TRUE; } @@ -767,7 +760,9 @@ userdata->js = js; userdata->jb = jb; userdata->from = g_strdup(from); - jabber_caps_get_info(js, from, node, ver, hash, jabber_presence_set_capabilities, userdata); + jabber_caps_get_info(js, from, node, ver, hash, + (jabber_caps_get_info_cb)jabber_presence_set_capabilities, + userdata); } } } diff -r 6adbaf3d25e3 -r a259d2711416 libpurple/protocols/sametime/sametime.c --- a/libpurple/protocols/sametime/sametime.c Sun Nov 23 03:52:33 2008 +0000 +++ b/libpurple/protocols/sametime/sametime.c Sun Nov 23 03:52:40 2008 +0000 @@ -1445,7 +1445,7 @@ MW_PLUGIN_DEFAULT_HOST); if(purple_account_get_bool(account, MW_KEY_FORCE, FALSE) || - (! strcmp(current_host, host)) || + !host || (! strcmp(current_host, host)) || (purple_proxy_connect(NULL, account, host, port, connect_cb, pd) == NULL)) { /* if we're configured to force logins, or if we're being