From 6301b2e490b7d8602f88ac897826b19c4b8ee7ec Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Mon, 16 Jun 2008 13:52:45 +0000 Subject: Removing short-names for features and calculating own caps hash. --- libpurple/protocols/jabber/caps.c | 108 +++++++++++++++++++++++++++++---- libpurple/protocols/jabber/caps.h | 8 +++ libpurple/protocols/jabber/disco.c | 111 ++++++++++------------------------ libpurple/protocols/jabber/jabber.c | 59 +++++++++++++++--- libpurple/protocols/jabber/jabber.h | 23 +++++-- libpurple/protocols/jabber/libxmpp.c | 10 +-- libpurple/protocols/jabber/message.c | 2 +- libpurple/protocols/jabber/message.h | 2 +- libpurple/protocols/jabber/pep.c | 6 +- libpurple/protocols/jabber/pep.h | 10 ++- libpurple/protocols/jabber/presence.c | 10 +-- libpurple/protocols/jabber/usermood.c | 4 +- libpurple/protocols/jabber/usernick.c | 4 +- libpurple/protocols/jabber/usertune.c | 4 +- 14 files changed, 231 insertions(+), 130 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 6fc4c78365..b04f2d198b 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -21,7 +21,9 @@ #include "internal.h" +#include #include "caps.h" +#include "cipher.h" #include #include "internal.h" #include "util.h" @@ -30,6 +32,7 @@ #define JABBER_CAPS_FILENAME "xmpp-caps.xml" static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */ +static gchar *caps_hash = NULL; typedef struct _JabberCapsKey { char *node; @@ -111,6 +114,7 @@ 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); jabber_caps_load(); + jabber_caps_calculate_hash(); } static void jabber_caps_load(void) { @@ -499,7 +503,6 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, const char *ver, const char *ext, jabber_caps_get_info_cb cb, gpointer user_data) { JabberCapsValue *client; JabberCapsKey *key = g_new0(JabberCapsKey, 1); - char *originalext = g_strdup(ext); jabber_caps_cbplususerdata *userdata = g_new0(jabber_caps_cbplususerdata, 1); userdata->cb = cb; userdata->user_data = user_data; @@ -507,17 +510,6 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c userdata->node = g_strdup(node); userdata->ver = g_strdup(ver); - if(originalext) { - int i; - gchar **splat = g_strsplit(originalext, " ", 0); - for(i =0; splat[i]; i++) { - userdata->ext = g_list_append(userdata->ext, splat[i]); - ++userdata->extOutstanding; - } - g_free(splat); - } - g_free(originalext); - key->node = (char *)node; key->ver = (char *)ver; @@ -570,3 +562,95 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c } } +static gint jabber_caps_jabber_identity_compare(gconstpointer a, gconstpointer b) { + const JabberIdentity *ac; + const JabberIdentity *bc; + gint cat_cmp; + + ac = a; + bc = b; + + if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) { + return strcmp(ac->type, bc->type); + } else { + return cat_cmp; + } +} + +static gint jabber_caps_jabber_feature_compare(gconstpointer a, gconstpointer b) { + const JabberFeature *ac; + const JabberFeature *bc; + + ac = a; + bc = b; + + return strcmp(ac->namespace, bc->namespace); +} + + +void jabber_caps_calculate_hash() { + gchar *verification = 0; + gchar *free_verification; + gchar *identity_string, *feature_string; + GList *identities, *features; + PurpleCipherContext *context; + guint8 checksum[20]; + gsize checksum_size = 20; + + /* sort identities */ + jabber_identities = g_list_sort(jabber_identities, jabber_caps_jabber_identity_compare); + + /* concat identities to the verification string */ + for(identities = jabber_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); + if(free_verification) g_free(free_verification); + } + + /* sort features */ + jabber_features = g_list_sort(jabber_features, jabber_caps_jabber_feature_compare); + + /* concat features to the verification string */ + for(features = jabber_features; features; features = features->next) { + JabberFeature *feat = (JabberFeature*)features->data; + feature_string = g_strdup_printf("%s<", feat->namespace); + free_verification = verification; + if(verification == 0) g_strdup(feature_string); + else verification = g_strconcat(verification, feature_string, NULL); + g_free(feature_string); + if(free_verification) g_free(free_verification); + } + printf("\n%s", verification); + + + /* generate SHA-1 hash */ + context = purple_cipher_context_new_by_name("sha1", NULL); + if (context == NULL) { + purple_debug_error("jabber", "Could not find sha1 cipher\n"); + return; + } + purple_cipher_context_append(context, verification, strlen(verification)); + + if (!purple_cipher_context_digest(context, strlen(verification), checksum, &checksum_size)) { + purple_debug_error("util", "Failed to get SHA-1 digest.\n"); + } + purple_cipher_context_destroy(context); + + /* apply Base64 on hash */ + + g_free(verification); + verification = purple_base64_encode(checksum, checksum_size); // for 2.0 compability + printf("\n%s", verification); + + if (caps_hash != 0) g_free(caps_hash); + caps_hash = verification; +} + +const gchar* jabber_caps_get_hash() { + return caps_hash; +} + diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index 07cf0baaaf..d61aca1a65 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -46,4 +46,12 @@ void jabber_caps_init(void); void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, const char *ver, const char *ext, jabber_caps_get_info_cb cb, gpointer user_data); void jabber_caps_free_clientinfo(JabberCapsClientInfo *clientinfo); + +void jabber_caps_calculate_hash(); + +/** Get the current caps hash. + * @ret hash +**/ +const gchar* jabber_caps_get_hash(); + #endif /* _PURPLE_JABBER_CAPS_H_ */ diff --git a/libpurple/protocols/jabber/disco.c b/libpurple/protocols/jabber/disco.c index 7cfbcd6f2c..861e3212b4 100644 --- a/libpurple/protocols/jabber/disco.c +++ b/libpurple/protocols/jabber/disco.c @@ -79,7 +79,6 @@ jabber_disco_bytestream_server_cb(JabberStream *js, xmlnode *packet, gpointer da void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) { const char *from = xmlnode_get_attrib(packet, "from"); const char *type = xmlnode_get_attrib(packet, "type"); - if(!from || !type) return; @@ -89,6 +88,10 @@ void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) { xmlnode *in_query; const char *node = NULL; + const char *node_uri = NULL; + + // create custom caps node URI + node_uri = g_strconcat(CAPS0115_NODE, "#", jabber_caps_get_hash(), NULL); if((in_query = xmlnode_get_child(packet, "query"))) { node = xmlnode_get_attrib(in_query, "node"); @@ -106,90 +109,38 @@ void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) { if(node) xmlnode_set_attrib(query, "node", node); - if(!node || !strcmp(node, CAPS0115_NODE "#" VERSION)) { - identity = xmlnode_new_child(query, "identity"); - xmlnode_set_attrib(identity, "category", "client"); - xmlnode_set_attrib(identity, "type", "pc"); /* XXX: bot, console, - * handheld, pc, phone, - * web */ - xmlnode_set_attrib(identity, "name", PACKAGE); - - SUPPORT_FEATURE("jabber:iq:last") - SUPPORT_FEATURE("jabber:iq:oob") - SUPPORT_FEATURE("jabber:iq:time") - SUPPORT_FEATURE("xmpp:urn:time") - SUPPORT_FEATURE("jabber:iq:version") - SUPPORT_FEATURE("jabber:x:conference") - SUPPORT_FEATURE("http://jabber.org/protocol/bytestreams") - SUPPORT_FEATURE("http://jabber.org/protocol/disco#info") - SUPPORT_FEATURE("http://jabber.org/protocol/disco#items") -#if 0 - SUPPORT_FEATURE("http://jabber.org/protocol/ibb") -#endif - SUPPORT_FEATURE("http://jabber.org/protocol/muc") - SUPPORT_FEATURE("http://jabber.org/protocol/muc#user") - SUPPORT_FEATURE("http://jabber.org/protocol/si") - SUPPORT_FEATURE("http://jabber.org/protocol/si/profile/file-transfer") - SUPPORT_FEATURE("http://jabber.org/protocol/xhtml-im") - SUPPORT_FEATURE("urn:xmpp:ping") - SUPPORT_FEATURE("http://www.xmpp.org/extensions/xep-0199.html#ns") - - if(!node) { /* non-caps disco#info, add all enabled extensions */ - GList *features; - for(features = jabber_features; features; features = features->next) { - JabberFeature *feat = (JabberFeature*)features->data; - if(feat->is_enabled == NULL || feat->is_enabled(js, feat->shortname, feat->namespace) == TRUE) - SUPPORT_FEATURE(feat->namespace); - } + + if(!node || !strcmp(node, node_uri)) { + GList *identities; + for(identities = jabber_identities; identities; identities = identities->next) { + JabberIdentity *ident = (JabberIdentity*)identities->data; + 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); + } + GList *features; + 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) { + feature = xmlnode_new_child(query, "feature"); + xmlnode_set_attrib(feature, "var", feat->namespace); + } } } else { - const char *ext = NULL; - unsigned pos; - unsigned nodelen = strlen(node); - unsigned capslen = strlen(CAPS0115_NODE); - /* do a basic plausability check */ - if(nodelen > capslen+1) { - /* verify that the string is CAPS0115# and get the pointer to the ext part */ - for(pos = 0; pos < capslen+1; ++pos) { - if(pos == capslen) { - if(node[pos] == '#') - ext = &node[pos+1]; - else - break; - } else if(node[pos] != CAPS0115_NODE[pos]) - break; - } + xmlnode *error, *inf; - if(ext != NULL) { - /* look for that ext */ - GList *features; - for(features = jabber_features; features; features = features->next) { - JabberFeature *feat = (JabberFeature*)features->data; - if(!strcmp(feat->shortname, ext)) { - SUPPORT_FEATURE(feat->namespace); - break; - } - } - if(features == NULL) - ext = NULL; - } - } + /* XXX: gross hack, implement jabber_iq_set_type or something */ + xmlnode_set_attrib(iq->node, "type", "error"); + iq->type = JABBER_IQ_ERROR; - if(ext == NULL) { - xmlnode *error, *inf; - - /* XXX: gross hack, implement jabber_iq_set_type or something */ - xmlnode_set_attrib(iq->node, "type", "error"); - iq->type = JABBER_IQ_ERROR; - - error = xmlnode_new_child(query, "error"); - xmlnode_set_attrib(error, "code", "404"); - xmlnode_set_attrib(error, "type", "cancel"); - inf = xmlnode_new_child(error, "item-not-found"); - xmlnode_set_namespace(inf, "urn:ietf:params:xml:ns:xmpp-stanzas"); - } + error = xmlnode_new_child(query, "error"); + xmlnode_set_attrib(error, "code", "404"); + xmlnode_set_attrib(error, "type", "cancel"); + inf = xmlnode_new_child(error, "item-not-found"); + xmlnode_set_namespace(inf, "urn:ietf:params:xml:ns:xmpp-stanzas"); } - + g_free(node_uri); jabber_iq_send(iq); } else if(!strcmp(type, "result")) { xmlnode *query = xmlnode_get_child(packet, "query"); diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index 35bf536e50..11b853ec0e 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -60,7 +60,9 @@ #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5) static PurplePlugin *my_protocol = NULL; + GList *jabber_features = NULL; +GList *jabber_identities = NULL; static void jabber_unregister_account_cb(JabberStream *js); @@ -1394,31 +1396,27 @@ void jabber_idle_set(PurpleConnection *gc, int idle) js->idle = idle ? time(NULL) - idle : idle; } -void jabber_add_feature(const char *shortname, const char *namespace, JabberFeatureEnabled cb) { +void jabber_add_feature(const char *namespace, JabberFeatureEnabled cb) { JabberFeature *feat; - g_return_if_fail(shortname != NULL); g_return_if_fail(namespace != NULL); feat = g_new0(JabberFeature,1); - feat->shortname = g_strdup(shortname); feat->namespace = g_strdup(namespace); feat->is_enabled = cb; /* try to remove just in case it already exists in the list */ - jabber_remove_feature(shortname); + jabber_remove_feature(namespace); jabber_features = g_list_append(jabber_features, feat); } -void jabber_remove_feature(const char *shortname) { +void jabber_remove_feature(const char *namespace) { GList *feature; for(feature = jabber_features; feature; feature = feature->next) { JabberFeature *feat = (JabberFeature*)feature->data; - if(!strcmp(feat->shortname, shortname)) { - g_free(feat->shortname); + if(!strcmp(feat->namespace, namespace)) { g_free(feat->namespace); - g_free(feature->data); jabber_features = g_list_delete_link(jabber_features, feature); break; @@ -1426,6 +1424,27 @@ void jabber_remove_feature(const char *shortname) { } } +void jabber_add_identity(const gchar *category, const gchar *type, const gchar *name) { + GList *identity; + JabberIdentity *ident; + /* both required according to XEP-0030 */ + g_return_if_fail(category != NULL); + g_return_if_fail(type != NULL); + + for(identity = jabber_identities; identity; identity = identity->next) { + JabberIdentity *ident = (JabberIdentity*)identity->data; + if(!strcmp(ident->category, category)) { + if (!strcmp(ident->type, type)) return; + } + } + + ident = g_new0(JabberIdentity, 1); + ident->category = g_strdup(category); + ident->type = g_strdup(type); + ident->name = g_strdup(name); + jabber_identities = g_list_append(jabber_identities, ident); +} + const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b) { return "jabber"; @@ -2429,5 +2448,27 @@ void jabber_register_commands(void) void jabber_init_plugin(PurplePlugin *plugin) { - my_protocol = plugin; + my_protocol = plugin; + + jabber_add_identity("client", "pc", PACKAGE); + + // initialize jabber_features list + jabber_add_feature("jabber:iq:last", 0); + jabber_add_feature("jabber:iq:oob", 0); + jabber_add_feature("jabber:iq:time", 0); + jabber_add_feature("xmpp:urn:time", 0); + jabber_add_feature("jabber:iq:version", 0); + jabber_add_feature("jabber:x:conference", 0); + jabber_add_feature("http://jabber.org/protocol/bytestreams", 0); + jabber_add_feature("http://jabber.org/protocol/disco#info", 0); + jabber_add_feature("http://jabber.org/protocol/disco#items", 0); +#if 0 + jabber_add_feature("http://jabber.org/protocol/ibb", 0); +#endif + jabber_add_feature("http://jabber.org/protocol/muc", 0); + jabber_add_feature("http://jabber.org/protocol/muc#user", 0); + jabber_add_feature("http://jabber.org/protocol/si", 0); + jabber_add_feature("http://jabber.org/protocol/si/profile/file-transfer", 0); + jabber_add_feature("http://jabber.org/protocol/xhtml-im", 0); + jabber_add_feature("urn:xmpp:ping", 0); } diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h index 81cc8e0d03..56663cbbec 100644 --- a/libpurple/protocols/jabber/jabber.h +++ b/libpurple/protocols/jabber/jabber.h @@ -203,15 +203,21 @@ struct _JabberStream int keepalive_timeout; }; -typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *shortname, const gchar *namespace); +typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *namespace); typedef struct _JabberFeature { - gchar *shortname; gchar *namespace; JabberFeatureEnabled *is_enabled; } JabberFeature; +typedef struct _JabberIdentity +{ + gchar *category; + gchar *type; + gchar *name; +} JabberIdentity; + typedef struct _JabberBytestreamsStreamhost { char *jid; char *host; @@ -221,6 +227,7 @@ typedef struct _JabberBytestreamsStreamhost { /* what kind of additional features as returned from disco#info are supported? */ extern GList *jabber_features; +extern GList *jabber_identities; void jabber_process_packet(JabberStream *js, xmlnode **packet); void jabber_send(JabberStream *js, xmlnode *data); @@ -242,8 +249,16 @@ char *jabber_get_next_id(JabberStream *js); */ char *jabber_parse_error(JabberStream *js, xmlnode *packet, PurpleConnectionError *reason); -void jabber_add_feature(const gchar *shortname, const gchar *namespace, JabberFeatureEnabled cb); /* cb may be NULL */ -void jabber_remove_feature(const gchar *shortname); +void jabber_add_feature(const gchar *namespace, JabberFeatureEnabled cb); /* cb may be NULL */ +void jabber_remove_feature(const gchar *namespace); + +/** 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. + */ +void jabber_add_identity(const gchar *category, const gchar *type, const gchar *name); /** PRPL functions */ const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b); diff --git a/libpurple/protocols/jabber/libxmpp.c b/libpurple/protocols/jabber/libxmpp.c index 0d719a6ca0..6b1350da50 100644 --- a/libpurple/protocols/jabber/libxmpp.c +++ b/libpurple/protocols/jabber/libxmpp.c @@ -263,11 +263,13 @@ init_plugin(PurplePlugin *plugin) jabber_tune_init(); jabber_caps_init(); - jabber_add_feature("avatarmeta", AVATARNAMESPACEMETA, jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_add_feature("avatardata", AVATARNAMESPACEDATA, jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_add_feature("buzz", "http://www.xmpp.org/extensions/xep-0224.html#ns", jabber_buzz_isenabled); + #warning implement adding and retrieving own features via IPC API + + jabber_add_feature(AVATARNAMESPACEMETA, jabber_pep_namespace_only_when_pep_enabled_cb); + jabber_add_feature(AVATARNAMESPACEDATA, jabber_pep_namespace_only_when_pep_enabled_cb); + jabber_add_feature("http://www.xmpp.org/extensions/xep-0224.html#ns", jabber_buzz_isenabled); - jabber_pep_register_handler("avatar", AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata); + jabber_pep_register_handler(AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata); } diff --git a/libpurple/protocols/jabber/message.c b/libpurple/protocols/jabber/message.c index 944cbda1cd..b75a8daa4e 100644 --- a/libpurple/protocols/jabber/message.c +++ b/libpurple/protocols/jabber/message.c @@ -758,7 +758,7 @@ void jabber_message_conv_closed(JabberStream *js, const char *who) jabber_message_free(jm); } -gboolean jabber_buzz_isenabled(JabberStream *js, const gchar *shortname, const gchar *namespace) { +gboolean jabber_buzz_isenabled(JabberStream *js, const gchar *namespace) { return js->allowBuzz; } diff --git a/libpurple/protocols/jabber/message.h b/libpurple/protocols/jabber/message.h index 56c89819b1..d859f94980 100644 --- a/libpurple/protocols/jabber/message.h +++ b/libpurple/protocols/jabber/message.h @@ -78,6 +78,6 @@ int jabber_message_send_chat(PurpleConnection *gc, int id, const char *message, unsigned int jabber_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state); void jabber_message_conv_closed(JabberStream *js, const char *who); -gboolean jabber_buzz_isenabled(JabberStream *js, const gchar *shortname, const gchar *namespace); +gboolean jabber_buzz_isenabled(JabberStream *js, const gchar *namespace); #endif /* _PURPLE_JABBER_MESSAGE_H_ */ diff --git a/libpurple/protocols/jabber/pep.c b/libpurple/protocols/jabber/pep.c index e7d849b184..9b13c66b7a 100644 --- a/libpurple/protocols/jabber/pep.c +++ b/libpurple/protocols/jabber/pep.c @@ -45,9 +45,9 @@ void jabber_pep_init_actions(GList **m) { jabber_nick_init_action(m); } -void jabber_pep_register_handler(const char *shortname, const char *xmlns, JabberPEPHandler handlerfunc) { +void jabber_pep_register_handler(const char *xmlns, JabberPEPHandler handlerfunc) { gchar *notifyns = g_strdup_printf("%s+notify", xmlns); - jabber_add_feature(shortname, notifyns, NULL); /* receiving PEPs is always supported */ + jabber_add_feature(notifyns, NULL); /* receiving PEPs is always supported */ g_free(notifyns); g_hash_table_replace(pep_handlers, g_strdup(xmlns), handlerfunc); } @@ -85,7 +85,7 @@ void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, jabber_iq_send(iq); } -gboolean jabber_pep_namespace_only_when_pep_enabled_cb(JabberStream *js, const gchar *shortname, const gchar *namespace) { +gboolean jabber_pep_namespace_only_when_pep_enabled_cb(JabberStream *js, const gchar *namespace) { return js->pep; } diff --git a/libpurple/protocols/jabber/pep.h b/libpurple/protocols/jabber/pep.h index dd43f80c6a..0fd76bdc74 100644 --- a/libpurple/protocols/jabber/pep.h +++ b/libpurple/protocols/jabber/pep.h @@ -42,11 +42,10 @@ typedef void (JabberPEPHandler)(JabberStream *js, const char *from, xmlnode *ite * Registers a callback for PEP events. Also automatically announces this receiving capability via disco#info. * Don't forget to use jabber_add_feature when supporting the sending of PEP events of this type. * - * @parameter shortname A short name for this feature for XEP-0115. It has no semantic meaning, it just has to be unique. - * @parameter xmlns The namespace for this event + * @parameter xmlns The namespace for this event * @parameter handlerfunc The callback to be used when receiving an event with this namespace */ -void jabber_pep_register_handler(const char *shortname, const char *xmlns, JabberPEPHandler handlerfunc); +void jabber_pep_register_handler(const char *xmlns, JabberPEPHandler handlerfunc); /* * Request a specific item from another PEP node. @@ -64,13 +63,12 @@ void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, /* * Default callback that can be used for namespaces which should only be enabled when PEP is supported * - * @parameter js The JabberStream struct for this connection - * @parameter shortname The namespace's shortname (for caps), ignored. + * @parameter js The JabberStream struct for this connection * @parameter namespace The namespace that's queried, ignored. * * @returns TRUE when PEP is enabled, FALSE otherwise */ -gboolean jabber_pep_namespace_only_when_pep_enabled_cb(JabberStream *js, const gchar *shortname, const gchar *namespace); +gboolean jabber_pep_namespace_only_when_pep_enabled_cb(JabberStream *js, const gchar *namespace); void jabber_handle_event(JabberMessage *jm); diff --git a/libpurple/protocols/jabber/presence.c b/libpurple/protocols/jabber/presence.c index 53bc34969c..4ff891ee04 100644 --- a/libpurple/protocols/jabber/presence.c +++ b/libpurple/protocols/jabber/presence.c @@ -264,8 +264,10 @@ xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, con c = xmlnode_new_child(presence, "c"); xmlnode_set_namespace(c, "http://jabber.org/protocol/caps"); xmlnode_set_attrib(c, "node", CAPS0115_NODE); - xmlnode_set_attrib(c, "ver", VERSION); - + xmlnode_set_attrib(c, "hash", "sha-1"); + xmlnode_set_attrib(c, "ver", jabber_caps_get_hash()); + +#if 0 if(js != NULL) { /* add the extensions */ char extlist[1024]; @@ -277,7 +279,7 @@ xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, con JabberFeature *feat = (JabberFeature*)feature->data; unsigned featlen; - if(feat->is_enabled != NULL && feat->is_enabled(js, feat->shortname, feat->namespace) == FALSE) + if(feat->is_enabled != NULL && feat->is_enabled(js, feat->namespace) == FALSE) continue; /* skip this feature */ featlen = strlen(feat->shortname); @@ -297,7 +299,7 @@ xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, con if(remaining < 1023) xmlnode_set_attrib(c, "ext", extlist); } - +#endif return presence; } diff --git a/libpurple/protocols/jabber/usermood.c b/libpurple/protocols/jabber/usermood.c index 3ff09d0402..9c3cfa47a0 100644 --- a/libpurple/protocols/jabber/usermood.c +++ b/libpurple/protocols/jabber/usermood.c @@ -141,8 +141,8 @@ static void jabber_mood_cb(JabberStream *js, const char *from, xmlnode *items) { } void jabber_mood_init(void) { - jabber_add_feature("mood", "http://jabber.org/protocol/mood", jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_pep_register_handler("moodn", "http://jabber.org/protocol/mood", jabber_mood_cb); + jabber_add_feature("http://jabber.org/protocol/mood", jabber_pep_namespace_only_when_pep_enabled_cb); + jabber_pep_register_handler("http://jabber.org/protocol/mood", jabber_mood_cb); } static void do_mood_set_from_fields(PurpleConnection *gc, PurpleRequestFields *fields) { diff --git a/libpurple/protocols/jabber/usernick.c b/libpurple/protocols/jabber/usernick.c index 618d493aed..e1503684b7 100644 --- a/libpurple/protocols/jabber/usernick.c +++ b/libpurple/protocols/jabber/usernick.c @@ -92,8 +92,8 @@ static void do_nick_set_nick(PurplePluginAction *action) { } void jabber_nick_init(void) { - jabber_add_feature("nick", "http://jabber.org/protocol/nick", jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_pep_register_handler("nickn", "http://jabber.org/protocol/nick", jabber_nick_cb); + jabber_add_feature("http://jabber.org/protocol/nick", jabber_pep_namespace_only_when_pep_enabled_cb); + jabber_pep_register_handler("http://jabber.org/protocol/nick", jabber_nick_cb); } void jabber_nick_init_action(GList **m) { diff --git a/libpurple/protocols/jabber/usertune.c b/libpurple/protocols/jabber/usertune.c index 92d06d3168..e18674b649 100644 --- a/libpurple/protocols/jabber/usertune.c +++ b/libpurple/protocols/jabber/usertune.c @@ -109,8 +109,8 @@ static void jabber_tune_cb(JabberStream *js, const char *from, xmlnode *items) { } void jabber_tune_init(void) { - jabber_add_feature("tune", "http://jabber.org/protocol/tune", jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_pep_register_handler("tunen", "http://jabber.org/protocol/tune", jabber_tune_cb); + jabber_add_feature("http://jabber.org/protocol/tune", jabber_pep_namespace_only_when_pep_enabled_cb); + jabber_pep_register_handler("http://jabber.org/protocol/tune", jabber_tune_cb); } void jabber_tune_set(PurpleConnection *gc, const PurpleJabberTuneInfo *tuneinfo) { -- cgit v1.2.1 From 8ed0c80163ad0cd705653ae4a43a0c56c3cceeb4 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 22 Jun 2008 17:25:23 +0000 Subject: * changed name of jabber_caps_calculate_hash() to jabber_caps_calculate_own_hash() * added function which parses a query node to internal caps structure --- libpurple/protocols/jabber/caps.c | 55 +++++++++++++++++++++++++++++++---- libpurple/protocols/jabber/caps.h | 23 +++++++++++++-- libpurple/protocols/jabber/disco.c | 2 +- libpurple/protocols/jabber/presence.c | 2 +- 4 files changed, 73 insertions(+), 9 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index b04f2d198b..d8258bbe35 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -114,7 +114,7 @@ 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); jabber_caps_load(); - jabber_caps_calculate_hash(); + jabber_caps_calculate_own_hash(); } static void jabber_caps_load(void) { @@ -448,6 +448,7 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer /* TODO: Better error checking! */ if (query) { + JabberCapsValue *value = g_new0(JabberCapsValue, 1); JabberCapsKey *key = g_new0(JabberCapsKey, 1); @@ -512,12 +513,12 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c key->node = (char *)node; key->ver = (char *)ver; - + client = g_hash_table_lookup(capstable, key); g_free(key); - if(!client) { +// if(!client) { 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 *nodever = g_strdup_printf("%s#%s", node, ver); @@ -527,6 +528,7 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c jabber_iq_set_callback(iq,jabber_caps_client_iqcb,userdata); jabber_iq_send(iq); +#if 0 } else { GList *iter; /* fetch unknown exts only */ @@ -560,6 +562,7 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c /* maybe we have all data available anyways? This is the ideal case where no network traffic is necessary */ jabber_caps_get_info_check_completion(userdata); } +#endif } static gint jabber_caps_jabber_identity_compare(gconstpointer a, gconstpointer b) { @@ -587,8 +590,50 @@ static gint jabber_caps_jabber_feature_compare(gconstpointer a, gconstpointer b) return strcmp(ac->namespace, bc->namespace); } +JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) { + xmlnode *child; + + if (!query) return 0; + if (strcmp(query->xmlns,"http://jabber.org/protocol/disco#info")) return 0; + + JabberCapsClientInfo *info = g_new0(JabberCapsClientInfo, 1); + + for(child = query->child; child; child = child->next) { + if (!strcmp(child->name,"identity")) { + /* parse 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); + + info->identities = g_list_append(info->identities, id); + } else if (!strcmp(child->name, "feature")) { + /* parse feature */ + const char *var = xmlnode_get_attrib(child, "var"); + if(!var) + continue; + info->features = g_list_append(info->features, g_strdup(var)); + } else if (!strcmp(child->name, "x")) { + if (!strcmp(child->xmlns, "jabber:x:data")) { + /* x-data form */ + xmlnode *dataform = xmlnode_copy(child); + info->forms = g_list_append(info->forms, dataform); + } + } + } + return info; +} + +gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info) { + if (!info) return 0; + +} -void jabber_caps_calculate_hash() { +void jabber_caps_calculate_own_hash() { gchar *verification = 0; gchar *free_verification; gchar *identity_string, *feature_string; @@ -650,7 +695,7 @@ void jabber_caps_calculate_hash() { caps_hash = verification; } -const gchar* jabber_caps_get_hash() { +const gchar* jabber_caps_get_own_hash() { return caps_hash; } diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index d61aca1a65..7ca042455b 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -32,11 +32,13 @@ typedef struct _JabberCapsIdentity { char *category; char *type; char *name; + char *lang; } JabberCapsIdentity; struct _JabberCapsClientInfo { GList *identities; /* JabberCapsIdentity */ GList *features; /* char * */ + GList *forms; /* xmlnode * */ }; typedef void (*jabber_caps_get_info_cb)(JabberCapsClientInfo *info, gpointer user_data); @@ -46,12 +48,29 @@ void jabber_caps_init(void); void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, const char *ver, const char *ext, 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 + * XEP-0115 Version 1.5. + * + * @param info A JabberCapsClientInfo pointer. + * @return The base64 encoded SHA-1 hash; needs to be freed if not needed + * any furthermore. + */ +gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info); -void jabber_caps_calculate_hash(); +void jabber_caps_calculate_own_hash(); /** Get the current caps hash. * @ret hash **/ -const gchar* jabber_caps_get_hash(); +const gchar* jabber_caps_get_own_hash(); #endif /* _PURPLE_JABBER_CAPS_H_ */ diff --git a/libpurple/protocols/jabber/disco.c b/libpurple/protocols/jabber/disco.c index 861e3212b4..7197e60de4 100644 --- a/libpurple/protocols/jabber/disco.c +++ b/libpurple/protocols/jabber/disco.c @@ -91,7 +91,7 @@ void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) { const char *node_uri = NULL; // create custom caps node URI - node_uri = g_strconcat(CAPS0115_NODE, "#", jabber_caps_get_hash(), NULL); + node_uri = g_strconcat(CAPS0115_NODE, "#", jabber_caps_get_own_hash(), NULL); if((in_query = xmlnode_get_child(packet, "query"))) { node = xmlnode_get_attrib(in_query, "node"); diff --git a/libpurple/protocols/jabber/presence.c b/libpurple/protocols/jabber/presence.c index 4ff891ee04..4211af35be 100644 --- a/libpurple/protocols/jabber/presence.c +++ b/libpurple/protocols/jabber/presence.c @@ -265,7 +265,7 @@ xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, con xmlnode_set_namespace(c, "http://jabber.org/protocol/caps"); xmlnode_set_attrib(c, "node", CAPS0115_NODE); xmlnode_set_attrib(c, "hash", "sha-1"); - xmlnode_set_attrib(c, "ver", jabber_caps_get_hash()); + xmlnode_set_attrib(c, "ver", jabber_caps_get_own_hash()); #if 0 if(js != NULL) { -- cgit v1.2.1 From b26f8b454c6110dc2a804262fb99ef5a72f1c1cc Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 22 Jun 2008 18:30:12 +0000 Subject: * jabber_caps_jabber_identity_compare() compares xml:lang too, now. --- libpurple/protocols/jabber/caps.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index d8258bbe35..45d729cc00 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -569,12 +569,17 @@ static gint jabber_caps_jabber_identity_compare(gconstpointer a, gconstpointer b const JabberIdentity *ac; const JabberIdentity *bc; gint cat_cmp; + gint typ_cmp; ac = a; bc = b; if ((cat_cmp = strcmp(ac->category, bc->category)) == 0) { - return strcmp(ac->type, bc->type); + if ((typ_cmp = strcmp(ac->type, bc->type)) == 0) { + return strcmp(ac->lang, bc->lang); + } else { + return typ_cmp; + } } else { return cat_cmp; } -- cgit v1.2.1 From 2f36c1d50283bc2283ed574e8645dd58ba8afe93 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 22 Jun 2008 18:59:33 +0000 Subject: * defined JabberCapsIdentity as struct _JabberIdentity * added jabber_caps_jabber_xdata_compare() which compares two x-data forms as required by XEP-0115 --- libpurple/protocols/jabber/caps.c | 40 +++++++++++++++++++++++++++++++++++++ libpurple/protocols/jabber/caps.h | 7 +------ libpurple/protocols/jabber/jabber.h | 1 + 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 45d729cc00..5ed5447a29 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -595,6 +595,42 @@ static gint jabber_caps_jabber_feature_compare(gconstpointer a, gconstpointer b) return strcmp(ac->namespace, bc->namespace); } +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 gint jabber_caps_jabber_xdata_compare(gconstpointer a, gconstpointer b) { + const xmlnode *ac; + const xmlnode *bc; + xmlnode *aformtypefield; + xmlnode *bformtypefield; + char *aformtype; + char *bformtype; + int result; + + ac = a; + bc = b; + + aformtypefield = xmlnode_get_child(ac, "field"); + while (aformtypefield && strcmp(xmlnode_get_attrib(aformtypefield, "var"), "FORM_TYPE")) aformtypefield = xmlnode_get_next_twin(aformtypefield); + aformtype = xmlnode_get_data(aformtypefield); + + bformtypefield = xmlnode_get_child(bc, "field"); + while (bformtypefield && strcmp(xmlnode_get_attrib(bformtypefield, "var"), "FORM_TYPE")) bformtypefield = xmlnode_get_next_twin(bformtypefield); + bformtype = xmlnode_get_data(bformtypefield); + + result = strcmp(aformtype, bformtype); + g_free(aformtype); + g_free(bformtype); + return result; +} + JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) { xmlnode *child; @@ -636,6 +672,10 @@ JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) { gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info) { if (!info) return 0; + /* 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->forms = g_list_sort(info->forms, jabber_caps_jabber_xdata_compare); } void jabber_caps_calculate_own_hash() { diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index 7ca042455b..1b62309d0b 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -28,12 +28,7 @@ typedef struct _JabberCapsClientInfo JabberCapsClientInfo; /* Implementation of XEP-0115 */ -typedef struct _JabberCapsIdentity { - char *category; - char *type; - char *name; - char *lang; -} JabberCapsIdentity; +typedef struct _JabberIdentity JabberCapsIdentity; struct _JabberCapsClientInfo { GList *identities; /* JabberCapsIdentity */ diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h index 56663cbbec..4455d2385f 100644 --- a/libpurple/protocols/jabber/jabber.h +++ b/libpurple/protocols/jabber/jabber.h @@ -216,6 +216,7 @@ typedef struct _JabberIdentity gchar *category; gchar *type; gchar *name; + gchar *lang; } JabberIdentity; typedef struct _JabberBytestreamsStreamhost { -- cgit v1.2.1 From cfdb05f1080849d3bb1ea90ed4e6114cb5b29da8 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 29 Jun 2008 14:32:33 +0000 Subject: * implemented jabber_caps_calcualte_hash() which calculates the SHA-1 hash for someones disco response according to XEP-0115 --- libpurple/protocols/jabber/caps.c | 145 ++++++++++++++++++++++++++++++++++---- libpurple/protocols/jabber/caps.h | 5 ++ 2 files changed, 138 insertions(+), 12 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 5ed5447a29..3e9046ed53 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -383,7 +383,7 @@ static void jabber_caps_ext_iqcb(JabberStream *js, xmlnode *packet, gpointer dat --userdata->extOutstanding; /* TODO: Better error handling */ - + printf("\n\tjabber_caps_ext_iqcb for %s", xmlnode_get_attrib(packet, "from")); if(node && query) { const char *key; JabberCapsValue *client; @@ -446,7 +446,7 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer jabber_caps_cbplususerdata *userdata = data; /* TODO: Better error checking! */ - + printf("\n\tjabber_caps_client_iqcb for %s", xmlnode_get_attrib(packet, "from")); if (query) { JabberCapsValue *value = g_new0(JabberCapsValue, 1); @@ -605,6 +605,14 @@ static gint jabber_caps_string_compare(gconstpointer a, gconstpointer b) { return strcmp(ac, bc); } +gchar *jabber_caps_get_formtype(const xmlnode *x) { + xmlnode *formtypefield; + formtypefield = xmlnode_get_child(x, "field"); + while (formtypefield && strcmp(xmlnode_get_attrib(formtypefield, "var"), "FORM_TYPE")) formtypefield = xmlnode_get_next_twin(formtypefield); + formtypefield = xmlnode_get_child(formtypefield, "value"); + return xmlnode_get_data(formtypefield);; +} + static gint jabber_caps_jabber_xdata_compare(gconstpointer a, gconstpointer b) { const xmlnode *ac; const xmlnode *bc; @@ -617,13 +625,10 @@ static gint jabber_caps_jabber_xdata_compare(gconstpointer a, gconstpointer b) { ac = a; bc = b; - aformtypefield = xmlnode_get_child(ac, "field"); - while (aformtypefield && strcmp(xmlnode_get_attrib(aformtypefield, "var"), "FORM_TYPE")) aformtypefield = xmlnode_get_next_twin(aformtypefield); - aformtype = xmlnode_get_data(aformtypefield); + + aformtype = jabber_caps_get_formtype(aformtypefield); - bformtypefield = xmlnode_get_child(bc, "field"); - while (bformtypefield && strcmp(xmlnode_get_attrib(bformtypefield, "var"), "FORM_TYPE")) bformtypefield = xmlnode_get_next_twin(bformtypefield); - bformtype = xmlnode_get_data(bformtypefield); + bformtype = jabber_caps_get_formtype(bformtypefield); result = strcmp(aformtype, bformtype); g_free(aformtype); @@ -669,13 +674,132 @@ JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query) { return info; } +static gint jabber_caps_xdata_field_compare(gconstpointer a, gconstpointer b) { + const JabberDataFormField *ac; + const JabberDataFormField *bc; + + ac = a; + bc = b; + + return strcmp(ac->var, bc->var); +} + +GList *jabber_caps_xdata_get_fields(xmlnode *x) { + GList *fields = 0; + 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); + xdatafield->var = g_strdup(xmlnode_get_attrib(field, "var")); + for(value = xmlnode_get_child(field, "value"); value != 0; 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); + fields = g_list_append(fields, xdatafield); + } + fields = g_list_sort(fields, jabber_caps_xdata_field_compare); + return fields; +} + +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; +} + gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info) { + GList *identities; + GList *features; + GList *xdata; + gchar *verification = 0; + gchar *feature_string; + gchar *free_verification; + gchar *identity_string; + PurpleCipherContext *context; + guint8 checksum[20]; + gsize checksum_size = 20; + if (!info) return 0; /* 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->forms = g_list_sort(info->forms, jabber_caps_jabber_xdata_compare); + + /* 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); + } + + /* 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); + } + + /* concat x-data forms to the verification string */ + for(xdata = info->forms; xdata; xdata = xdata->next) { + gchar *formtype = 0; + GList *fields; + /* 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) { + GList *value; + JabberDataFormField *field = (JabberDataFormField*)fields; + if(strcmp(field->var, "FORM_TYPE")) { + /* Append the value of the "var" attribute, followed by the '<' character. */ + 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) { + verification = jabber_caps_verification_append(verification, value->data); + } + } + for(value = field->values; value != 0; value = value->next) { + g_free(value->data); + } + g_free(field->var); + g_list_free(field->values); + } + g_list_free(fields); + } + + /* generate SHA-1 hash */ + context = purple_cipher_context_new_by_name("sha1", NULL); + if (context == NULL) { + purple_debug_error("jabber", "Could not find sha1 cipher\n"); + return 0; + } + purple_cipher_context_append(context, verification, strlen(verification)); + + if (!purple_cipher_context_digest(context, strlen(verification), checksum, &checksum_size)) { + purple_debug_error("util", "Failed to get SHA-1 digest.\n"); + } + purple_cipher_context_destroy(context); + + /* apply Base64 on hash */ + + g_free(verification); + verification = purple_base64_encode(checksum, checksum_size); + printf("\n%s", verification); + + if (caps_hash != 0) g_free(caps_hash); + return verification; } void jabber_caps_calculate_own_hash() { @@ -713,9 +837,7 @@ void jabber_caps_calculate_own_hash() { else verification = g_strconcat(verification, feature_string, NULL); g_free(feature_string); if(free_verification) g_free(free_verification); - } - printf("\n%s", verification); - + } /* generate SHA-1 hash */ context = purple_cipher_context_new_by_name("sha1", NULL); @@ -734,7 +856,6 @@ void jabber_caps_calculate_own_hash() { g_free(verification); verification = purple_base64_encode(checksum, checksum_size); // for 2.0 compability - printf("\n%s", verification); if (caps_hash != 0) g_free(caps_hash); caps_hash = verification; diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index 1b62309d0b..7e2abb0593 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -36,6 +36,11 @@ struct _JabberCapsClientInfo { GList *forms; /* xmlnode * */ }; +typedef struct _JabberDataFormField { + gchar *var; + GList *values; +} JabberDataFormField; + typedef void (*jabber_caps_get_info_cb)(JabberCapsClientInfo *info, gpointer user_data); void jabber_caps_init(void); -- cgit v1.2.1 From 599c95f907ac5fc2cf7e578878757640dba75815 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 1 Jul 2008 23:16:15 +0000 Subject: * pass hash type to the caps handling functions * fix small bug in generation of caps hash * removed a bit debugging output --- libpurple/protocols/jabber/caps.c | 20 ++++++++++++++------ libpurple/protocols/jabber/caps.h | 5 ++++- libpurple/protocols/jabber/presence.c | 7 ++++--- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 3e9046ed53..120d2c7ca9 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -351,6 +351,7 @@ typedef struct _jabber_caps_cbplususerdata { char *node; char *ver; GList *ext; + char *hash; unsigned extOutstanding; } jabber_caps_cbplususerdata; @@ -446,8 +447,15 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer jabber_caps_cbplususerdata *userdata = data; /* TODO: Better error checking! */ - printf("\n\tjabber_caps_client_iqcb for %s", xmlnode_get_attrib(packet, "from")); if (query) { + // check hash + JabberCapsClientInfo *info = jabber_caps_parse_client_info(query); + gchar *sha_hash = jabber_caps_calcualte_hash(info); + + #warning INSERT HASH CHECKING CODE HERE! ONLY ADD TO CACHE IF HASH IS THE SAME. + + g_free(info); + g_free(sha_hash); JabberCapsValue *value = g_new0(JabberCapsValue, 1); JabberCapsKey *key = g_new0(JabberCapsKey, 1); @@ -501,7 +509,7 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer jabber_caps_get_info_check_completion(userdata); } -void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, const char *ver, const char *ext, jabber_caps_get_info_cb cb, gpointer user_data) { +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; JabberCapsKey *key = g_new0(JabberCapsKey, 1); jabber_caps_cbplususerdata *userdata = g_new0(jabber_caps_cbplususerdata, 1); @@ -510,6 +518,7 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c userdata->who = g_strdup(who); userdata->node = g_strdup(node); userdata->ver = g_strdup(ver); + userdata->hash = g_strdup(hash); key->node = (char *)node; key->ver = (char *)ver; @@ -745,7 +754,7 @@ gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info) { /* concat features to the verification string */ for(features = info->features; features; features = features->next) { - feature_string = g_strdup_printf("%s<", (gchar*)features->data); + feature_string = g_strdup_printf("%s", (gchar*)features->data); verification = jabber_caps_verification_append(verification, feature_string); g_free(feature_string); } @@ -761,7 +770,7 @@ gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info) { for(fields = jabber_caps_xdata_get_fields((xmlnode*)(xdata->data)); fields != 0; fields = fields->next) { GList *value; - JabberDataFormField *field = (JabberDataFormField*)fields; + JabberDataFormField *field = (JabberDataFormField*)fields->data; if(strcmp(field->var, "FORM_TYPE")) { /* Append the value of the "var" attribute, followed by the '<' character. */ verification = jabber_caps_verification_append(verification, field->var); @@ -778,7 +787,7 @@ gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info) { } g_list_free(fields); } - + /* generate SHA-1 hash */ context = purple_cipher_context_new_by_name("sha1", NULL); if (context == NULL) { @@ -796,7 +805,6 @@ gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info) { g_free(verification); verification = purple_base64_encode(checksum, checksum_size); - printf("\n%s", verification); if (caps_hash != 0) g_free(caps_hash); return verification; diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index 7e2abb0593..bf66a769a6 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -45,7 +45,10 @@ typedef void (*jabber_caps_get_info_cb)(JabberCapsClientInfo *info, gpointer use void jabber_caps_init(void); -void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, const char *ver, const char *ext, jabber_caps_get_info_cb cb, gpointer user_data); +/** + * 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); /** diff --git a/libpurple/protocols/jabber/presence.c b/libpurple/protocols/jabber/presence.c index 4211af35be..43077315ac 100644 --- a/libpurple/protocols/jabber/presence.c +++ b/libpurple/protocols/jabber/presence.c @@ -750,16 +750,17 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet) jbr = jabber_buddy_track_resource(jb, jid->resource, priority, state, status); if(caps) { + /* handle XEP-0115 */ const char *node = xmlnode_get_attrib(caps,"node"); const char *ver = xmlnode_get_attrib(caps,"ver"); - const char *ext = xmlnode_get_attrib(caps,"ext"); + const char *hash = xmlnode_get_attrib(caps,"hash"); - if(node && ver) { + if(node && ver && hash) { JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1); userdata->js = js; userdata->jb = jb; userdata->from = g_strdup(from); - jabber_caps_get_info(js, from, node, ver, ext, jabber_presence_set_capabilities, userdata); + jabber_caps_get_info(js, from, node, ver, hash, jabber_presence_set_capabilities, userdata); } } } -- cgit v1.2.1 From d444924e08e7a06bc25a5d8ff4327607b6a42f1f Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Wed, 2 Jul 2008 21:27:51 +0000 Subject: * changing caps node namespace. According to version 1.5 of XEP-0115 it should be a real URL to client's homepage. --- libpurple/protocols/jabber/jabber.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h index 4455d2385f..94cf6b4aa4 100644 --- a/libpurple/protocols/jabber/jabber.h +++ b/libpurple/protocols/jabber/jabber.h @@ -64,7 +64,7 @@ typedef struct _JabberStream JabberStream; #include #endif -#define CAPS0115_NODE "http://pidgin.im/caps" +#define CAPS0115_NODE "http://pidgin.im/" /* Index into attention_types list */ #define JABBER_BUZZ 0 -- cgit v1.2.1 From 4c8e6b1bdf8057adf7a836859561242a3c5247e2 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 3 Jul 2008 22:30:44 +0000 Subject: * support for calculating both hashes, sha-1 and md5 * preparing merge of two data structres; JabberCapsClientInfo and new one --- libpurple/protocols/jabber/caps.c | 48 +++++++++++++++++++++++++++++---------- libpurple/protocols/jabber/caps.h | 3 ++- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 120d2c7ca9..73ab0fea44 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -42,6 +42,7 @@ typedef struct _JabberCapsKey { typedef struct _JabberCapsValueExt { GList *identities; /* JabberCapsIdentity */ GList *features; /* char * */ + GList *xdatas; /* xmlnode * */ } JabberCapsValueExt; typedef struct _JabberCapsValue { @@ -61,7 +62,7 @@ static guint jabber_caps_hash(gconstpointer key) { static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) { const JabberCapsKey *name1 = v1; const JabberCapsKey *name2 = v2; - + return strcmp(name1->node,name2->node) == 0 && strcmp(name1->ver,name2->ver) == 0; } @@ -447,15 +448,35 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer jabber_caps_cbplususerdata *userdata = data; /* 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 *sha_hash = jabber_caps_calcualte_hash(info); + gchar *hash = 0; + if (!strcmp(userdata->hash, "sha-1")) { + hash = jabber_caps_calcualte_hash(info, "sha1"); + } else if (!strcmp(userdata->hash, "md5")) { + hash = jabber_caps_calcualte_hash(info, "md5"); + } else { + // clean up + return; + } - #warning INSERT HASH CHECKING CODE HERE! ONLY ADD TO CACHE IF HASH IS THE SAME. + 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(info); - g_free(sha_hash); + g_free(hash); JabberCapsValue *value = g_new0(JabberCapsValue, 1); JabberCapsKey *key = g_new0(JabberCapsKey, 1); @@ -477,7 +498,9 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer const char *category = xmlnode_get_attrib(child, "category"); const char *type = xmlnode_get_attrib(child, "type"); const char *name = xmlnode_get_attrib(child, "name"); - + + if (category == 0 || type == 0 || name == 0) printf("\nMISSING"); + JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); id->category = g_strdup(category); id->type = g_strdup(type); @@ -486,8 +509,9 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer value->identities = g_list_append(value->identities,id); } } - g_hash_table_replace(capstable, key, value); - jabber_caps_store(); + + //g_hash_table_replace(capstable, key, value); + //jabber_caps_store(); } /* fetch all exts */ @@ -722,7 +746,7 @@ gchar *jabber_caps_verification_append(gchar *verification_string, gchar *string return verification; } -gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info) { +gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info, const char *hash) { GList *identities; GList *features; GList *xdata; @@ -788,16 +812,16 @@ gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info) { g_list_free(fields); } - /* generate SHA-1 hash */ - context = purple_cipher_context_new_by_name("sha1", NULL); + /* generate hash */ + context = purple_cipher_context_new_by_name(hash, NULL); if (context == NULL) { - purple_debug_error("jabber", "Could not find sha1 cipher\n"); + //purple_debug_error("jabber", "Could not find cipher\n"); return 0; } purple_cipher_context_append(context, verification, strlen(verification)); if (!purple_cipher_context_digest(context, strlen(verification), checksum, &checksum_size)) { - purple_debug_error("util", "Failed to get SHA-1 digest.\n"); + //purple_debug_error("util", "Failed to get digest.\n"); } purple_cipher_context_destroy(context); diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index bf66a769a6..993dc317ed 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -64,10 +64,11 @@ JabberCapsClientInfo *jabber_caps_parse_client_info(xmlnode *query); * XEP-0115 Version 1.5. * * @param info A JabberCapsClientInfo pointer. + * @param hash Hash cipher to be used. Either sha-1 or md5. * @return The base64 encoded SHA-1 hash; needs to be freed if not needed * any furthermore. */ -gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info); +gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info, const char *hash); void jabber_caps_calculate_own_hash(); -- cgit v1.2.1 From 4cdd628f737d81ec5e0fb7a9ec8b26ab8e26bcd1 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 4 Jul 2008 19:11:09 +0000 Subject: * changed storage format for cache to mirror all needed features for XEP-0115v1.5 * exlcuded all shortnames (ext-attribute) realed functions; mostly storage/loading functions. Will be removed completely later. --- libpurple/protocols/jabber/caps.c | 88 ++++++++++++++++----------------------- libpurple/protocols/jabber/caps.h | 2 + 2 files changed, 39 insertions(+), 51 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 73ab0fea44..31d1b0a5c9 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -37,19 +37,17 @@ static gchar *caps_hash = NULL; typedef struct _JabberCapsKey { char *node; char *ver; + char *hash; } JabberCapsKey; -typedef struct _JabberCapsValueExt { - GList *identities; /* JabberCapsIdentity */ - GList *features; /* char * */ - GList *xdatas; /* xmlnode * */ -} JabberCapsValueExt; - +#if 0 typedef struct _JabberCapsValue { GList *identities; /* JabberCapsIdentity */ GList *features; /* char * */ GHashTable *ext; /* char * -> JabberCapsValueExt */ } JabberCapsValue; +#endif +typedef struct _JabberCapsClientInfo JabberCapsValue; static guint jabber_caps_hash(gconstpointer key) { const JabberCapsKey *name = key; @@ -70,6 +68,7 @@ static void jabber_caps_destroy_key(gpointer key) { JabberCapsKey *keystruct = key; g_free(keystruct->node); g_free(keystruct->ver); + g_free(keystruct->hash); g_free(keystruct); } @@ -88,7 +87,12 @@ static void jabber_caps_destroy_value(gpointer value) { g_free(valuestruct->features->data); valuestruct->features = g_list_delete_link(valuestruct->features,valuestruct->features); } - g_hash_table_destroy(valuestruct->ext); + + while(valuestruct->forms) { + g_free(valuestruct->forms->data); + valuestruct->forms = g_list_delete_link(valuestruct->forms,valuestruct->forms); + } + //g_hash_table_destroy(valuestruct->ext); g_free(valuestruct); } @@ -139,7 +143,7 @@ static void jabber_caps_load(void) { xmlnode *child; key->node = g_strdup(xmlnode_get_attrib(client,"node")); key->ver = g_strdup(xmlnode_get_attrib(client,"ver")); - value->ext = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_caps_ext_destroy_value); + key->hash = g_strdup(xmlnode_get_attrib(client,"hash")); for(child = client->child; child; child = child->next) { if(child->type != XMLNODE_TYPE_TAG) continue; @@ -159,36 +163,8 @@ static void jabber_caps_load(void) { id->name = g_strdup(name); value->identities = g_list_append(value->identities,id); - } else if(!strcmp(child->name,"ext")) { - const char *identifier = xmlnode_get_attrib(child, "identifier"); - if(identifier) { - xmlnode *extchild; - - JabberCapsValueExt *extvalue = g_new0(JabberCapsValueExt, 1); - - for(extchild = child->child; extchild; extchild = extchild->next) { - if(extchild->type != XMLNODE_TYPE_TAG) - continue; - if(!strcmp(extchild->name,"feature")) { - const char *var = xmlnode_get_attrib(extchild, "var"); - if(!var) - continue; - extvalue->features = g_list_append(extvalue->features,g_strdup(var)); - } else if(!strcmp(extchild->name,"identity")) { - const char *category = xmlnode_get_attrib(extchild, "category"); - const char *type = xmlnode_get_attrib(extchild, "type"); - const char *name = xmlnode_get_attrib(extchild, "name"); - - JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); - id->category = g_strdup(category); - id->type = g_strdup(type); - id->name = g_strdup(name); - - extvalue->identities = g_list_append(extvalue->identities,id); - } - } - g_hash_table_replace(value->ext, g_strdup(identifier), extvalue); - } + } else if(!strcmp(child->name,"x")) { + value->forms = g_list_append(value->forms, xmlnode_copy(child)); } } g_hash_table_replace(capstable, key, value); @@ -197,6 +173,7 @@ static void jabber_caps_load(void) { xmlnode_free(capsdata); } +#if 0 static void jabber_caps_store_ext(gpointer key, gpointer value, gpointer user_data) { const char *extname = key; JabberCapsValueExt *props = value; @@ -221,6 +198,7 @@ static void jabber_caps_store_ext(gpointer key, gpointer value, gpointer user_da xmlnode_set_attrib(feature, "var", feat); } } +#endif static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user_data) { JabberCapsKey *clientinfo = key; @@ -231,7 +209,7 @@ static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user xmlnode_set_attrib(client,"node",clientinfo->node); 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; xmlnode *identity = xmlnode_new_child(client, "identity"); @@ -247,7 +225,10 @@ static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user xmlnode_set_attrib(feature, "var", feat); } - g_hash_table_foreach(props->ext,jabber_caps_store_ext,client); + for(iter = props->forms; iter; iter = g_list_next(iter)) { + xmlnode *xdata = iter->data; + xmlnode_insert_child(client, xdata); + } } static void jabber_caps_store(void) { @@ -295,7 +276,7 @@ static JabberCapsClientInfo *jabber_caps_collect_info(const char *node, const ch result->features = g_list_append(result->features,newfeat); } - +#if 0 for(iter = ext; iter; iter = g_list_next(iter)) { const char *extname = iter->data; JabberCapsValueExt *extinfo = g_hash_table_lookup(caps->ext,extname); @@ -319,6 +300,7 @@ static JabberCapsClientInfo *jabber_caps_collect_info(const char *node, const ch } } } +#endif return result; } @@ -351,7 +333,6 @@ typedef struct _jabber_caps_cbplususerdata { char *who; char *node; char *ver; - GList *ext; char *hash; unsigned extOutstanding; } jabber_caps_cbplususerdata; @@ -361,6 +342,7 @@ typedef struct jabber_ext_userdata { char *node; } jabber_ext_userdata; +#if 0 static void jabber_caps_get_info_check_completion(jabber_caps_cbplususerdata *userdata) { if(userdata->extOutstanding == 0) { userdata->cb(jabber_caps_collect_info(userdata->node, userdata->ver, userdata->ext), userdata->user_data); @@ -374,6 +356,7 @@ static void jabber_caps_get_info_check_completion(jabber_caps_cbplususerdata *us g_free(userdata); } } +#endif static void jabber_caps_ext_iqcb(JabberStream *js, xmlnode *packet, gpointer data) { /* collect data and fetch all exts */ @@ -429,7 +412,7 @@ static void jabber_caps_ext_iqcb(JabberStream *js, xmlnode *packet, gpointer dat value->identities = g_list_append(value->identities,id); } } - g_hash_table_replace(client->ext, g_strdup(key), value); + //g_hash_table_replace(client->ext, g_strdup(key), value); jabber_caps_store(); } @@ -475,17 +458,18 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer return; } - g_free(info); g_free(hash); JabberCapsValue *value = g_new0(JabberCapsValue, 1); JabberCapsKey *key = g_new0(JabberCapsKey, 1); - +#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); + + for(child = query->child; child; child = child->next) { if(child->type != XMLNODE_TYPE_TAG) continue; @@ -499,21 +483,22 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer const char *type = xmlnode_get_attrib(child, "type"); const char *name = xmlnode_get_attrib(child, "name"); - if (category == 0 || type == 0 || name == 0) printf("\nMISSING"); - JabberCapsIdentity *id = g_new0(JabberCapsIdentity, 1); id->category = g_strdup(category); id->type = g_strdup(type); id->name = g_strdup(name); 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(); + g_hash_table_replace(capstable, key, value); + jabber_caps_store(); } +#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"); @@ -531,6 +516,7 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer } jabber_caps_get_info_check_completion(userdata); +#endif } 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) { diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index 993dc317ed..fff0d03951 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -36,6 +36,8 @@ struct _JabberCapsClientInfo { GList *forms; /* xmlnode * */ }; +typedef struct _JabberCapsClientInfo JabberCapsValueExt; + typedef struct _JabberDataFormField { gchar *var; GList *values; -- cgit v1.2.1 From 71400a89717e1147219513a246d859bd276abfef Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sat, 5 Jul 2008 09:22:59 +0000 Subject: * correctly calculating own features and factor in is_enabled() of features --- libpurple/protocols/jabber/caps.c | 85 +++++++++++++---------------------- libpurple/protocols/jabber/libxmpp.c | 3 +- libpurple/protocols/jabber/presence.c | 3 ++ 3 files changed, 34 insertions(+), 57 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 31d1b0a5c9..a3f1dfb43d 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -444,7 +444,7 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer // clean up return; } - + printf("\n\tfrom: %s", xmlnode_get_attrib(packet, "from")); printf("\n\tnode: %s", xmlnode_get_attrib(query, "node")); printf("\n\tcalculated key: %s", hash); @@ -532,12 +532,13 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c key->node = (char *)node; key->ver = (char *)ver; + key->hash = (char*)hash; client = g_hash_table_lookup(capstable, key); g_free(key); -// if(!client) { + if(!client) { 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 *nodever = g_strdup_printf("%s#%s", node, ver); @@ -547,6 +548,7 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c jabber_iq_set_callback(iq,jabber_caps_client_iqcb,userdata); jabber_iq_send(iq); + } #if 0 } else { GList *iter; @@ -646,7 +648,6 @@ static gint jabber_caps_jabber_xdata_compare(gconstpointer a, gconstpointer b) { aformtype = jabber_caps_get_formtype(aformtypefield); - bformtype = jabber_caps_get_formtype(bformtypefield); result = strcmp(aformtype, bformtype); @@ -797,7 +798,7 @@ gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info, const char *hash) } g_list_free(fields); } - + /* generate hash */ context = purple_cipher_context_new_by_name(hash, NULL); if (context == NULL) { @@ -816,67 +817,41 @@ gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info, const char *hash) g_free(verification); verification = purple_base64_encode(checksum, checksum_size); - if (caps_hash != 0) g_free(caps_hash); return verification; } -void jabber_caps_calculate_own_hash() { - gchar *verification = 0; - gchar *free_verification; - gchar *identity_string, *feature_string; - GList *identities, *features; - PurpleCipherContext *context; - guint8 checksum[20]; - gsize checksum_size = 20; +void jabber_caps_calculate_own_hash(JabberStream *js) { + JabberCapsClientInfo *info; + GList *iter = 0; + GList *features = 0; /* sort identities */ - jabber_identities = g_list_sort(jabber_identities, jabber_caps_jabber_identity_compare); - - /* concat identities to the verification string */ - for(identities = jabber_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); - if(free_verification) g_free(free_verification); + if (jabber_identities == 0 && jabber_features == 0) return; + if (jabber_identities) { + for (iter = jabber_identities; iter; iter = iter->next) { + JabberIdentity *ident = iter->data; + } } /* sort features */ - jabber_features = g_list_sort(jabber_features, jabber_caps_jabber_feature_compare); - - /* concat features to the verification string */ - for(features = jabber_features; features; features = features->next) { - JabberFeature *feat = (JabberFeature*)features->data; - feature_string = g_strdup_printf("%s<", feat->namespace); - free_verification = verification; - if(verification == 0) g_strdup(feature_string); - else verification = g_strconcat(verification, feature_string, NULL); - g_free(feature_string); - if(free_verification) g_free(free_verification); - } - - /* generate SHA-1 hash */ - context = purple_cipher_context_new_by_name("sha1", NULL); - if (context == NULL) { - purple_debug_error("jabber", "Could not find sha1 cipher\n"); - return; - } - purple_cipher_context_append(context, verification, strlen(verification)); - - if (!purple_cipher_context_digest(context, strlen(verification), checksum, &checksum_size)) { - purple_debug_error("util", "Failed to get SHA-1 digest.\n"); + 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) { + features = g_list_append(features, feat->namespace); + } + } } - purple_cipher_context_destroy(context); - - /* apply Base64 on hash */ - - g_free(verification); - verification = purple_base64_encode(checksum, checksum_size); // for 2.0 compability + + info = g_new0(JabberCapsClientInfo, 1); + info->features = features; + info->identities = jabber_identities; + info->forms = 0; - if (caps_hash != 0) g_free(caps_hash); - caps_hash = verification; + if (caps_hash) g_free(caps_hash); + caps_hash = jabber_caps_calcualte_hash(info, "sha1"); + g_free(info); + g_list_free(features); } const gchar* jabber_caps_get_own_hash() { diff --git a/libpurple/protocols/jabber/libxmpp.c b/libpurple/protocols/jabber/libxmpp.c index 6b1350da50..b4940b6572 100644 --- a/libpurple/protocols/jabber/libxmpp.c +++ b/libpurple/protocols/jabber/libxmpp.c @@ -259,9 +259,8 @@ init_plugin(PurplePlugin *plugin) jabber_iq_init(); jabber_pep_init(); - - jabber_tune_init(); jabber_caps_init(); + jabber_tune_init(); #warning implement adding and retrieving own features via IPC API diff --git a/libpurple/protocols/jabber/presence.c b/libpurple/protocols/jabber/presence.c index 43077315ac..60e8212376 100644 --- a/libpurple/protocols/jabber/presence.c +++ b/libpurple/protocols/jabber/presence.c @@ -261,6 +261,9 @@ xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, con } /* JEP-0115 */ + /* calculate hash */ + jabber_caps_calculate_own_hash(js); + /* create xml */ c = xmlnode_new_child(presence, "c"); xmlnode_set_namespace(c, "http://jabber.org/protocol/caps"); xmlnode_set_attrib(c, "node", CAPS0115_NODE); -- cgit v1.2.1 From 4f721ff1e4058e053e8d8ff0a68eda82d2d3d6b2 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 6 Jul 2008 18:51:26 +0000 Subject: * fixing a bug in jabber_caps_compare() * preparing contact capabilities lookup functionality --- libpurple/protocols/jabber/caps.c | 16 ++++------------ libpurple/protocols/jabber/caps.h | 8 ++++++++ libpurple/protocols/jabber/jabber.c | 17 +++++++++++++++++ libpurple/protocols/jabber/jabber.h | 2 ++ 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index a3f1dfb43d..e4ffbd028b 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -34,12 +34,6 @@ static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */ static gchar *caps_hash = NULL; -typedef struct _JabberCapsKey { - char *node; - char *ver; - char *hash; -} JabberCapsKey; - #if 0 typedef struct _JabberCapsValue { GList *identities; /* JabberCapsIdentity */ @@ -53,18 +47,18 @@ static guint jabber_caps_hash(gconstpointer key) { const JabberCapsKey *name = key; guint nodehash = g_str_hash(name->node); guint verhash = g_str_hash(name->ver); - - return nodehash ^ verhash; + guint hashhash = g_str_hash(name->hash); + return nodehash ^ verhash ^ hashhash; } static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) { const JabberCapsKey *name1 = v1; const JabberCapsKey *name2 = v2; - return strcmp(name1->node,name2->node) == 0 && strcmp(name1->ver,name2->ver) == 0; + return strcmp(name1->node,name2->node) == 0 && strcmp(name1->ver,name2->ver) == 0 && strcmp(name1->hash,name2->hash) == 0; } -static void jabber_caps_destroy_key(gpointer key) { +void jabber_caps_destroy_key(gpointer key) { JabberCapsKey *keystruct = key; g_free(keystruct->node); g_free(keystruct->ver); @@ -412,8 +406,6 @@ static void jabber_caps_ext_iqcb(JabberStream *js, xmlnode *packet, gpointer dat value->identities = g_list_append(value->identities,id); } } - //g_hash_table_replace(client->ext, g_strdup(key), value); - jabber_caps_store(); } diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index fff0d03951..07fea7b3c7 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -43,10 +43,18 @@ typedef struct _JabberDataFormField { GList *values; } JabberDataFormField; +typedef struct _JabberCapsKey { + char *node; + char *ver; + char *hash; +} JabberCapsKey; + typedef void (*jabber_caps_get_info_cb)(JabberCapsClientInfo *info, gpointer user_data); void jabber_caps_init(void); +void jabber_caps_destroy_key(gpointer value); + /** * Main entity capabilites function to get the capabilities of a contact. */ diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index 11b853ec0e..df4ec76dcd 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -39,6 +39,7 @@ #include "version.h" #include "xmlnode.h" +#include "caps.h" #include "auth.h" #include "buddy.h" #include "chat.h" @@ -2445,6 +2446,20 @@ void jabber_register_commands(void) _("buzz: Buzz a user to get their attention"), NULL); } +static void +jabber_client_info_destroy_key(gpointer key) { + gchar *s = key; + g_free(s); +} + +static gboolean +jabber_client_info_compare(gconstpointer v1, gconstpointer v2) { + const gchar *name1 = v1; + const gchar *name2 = v2; + + return strcmp(name1,name2) == 0; +} + void jabber_init_plugin(PurplePlugin *plugin) { @@ -2471,4 +2486,6 @@ jabber_init_plugin(PurplePlugin *plugin) jabber_add_feature("http://jabber.org/protocol/si/profile/file-transfer", 0); 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, jabber_client_info_compare, jabber_client_info_destroy_key, jabber_caps_destroy_key); } diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h index 94cf6b4aa4..88cf0d6faa 100644 --- a/libpurple/protocols/jabber/jabber.h +++ b/libpurple/protocols/jabber/jabber.h @@ -230,6 +230,8 @@ typedef struct _JabberBytestreamsStreamhost { extern GList *jabber_features; extern GList *jabber_identities; +extern GHashTable *jabber_contact_info; /* char * -> JabberCapsKey */ + void jabber_process_packet(JabberStream *js, xmlnode **packet); void jabber_send(JabberStream *js, xmlnode *data); void jabber_send_raw(JabberStream *js, const char *data, int len); -- cgit v1.2.1 From 8f61e4d464bf35fb35a9f8a54920601d46419eed Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 6 Jul 2008 21:09:50 +0000 Subject: * adding IPC function 'contact_has_feature' * keep track of the contacts' resources' caps hashes --- libpurple/protocols/jabber/caps.c | 6 ++++-- libpurple/protocols/jabber/caps.h | 1 + libpurple/protocols/jabber/jabber.c | 39 ++++++++++++++++++++++++------------- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index e4ffbd028b..a54a4773f0 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -31,7 +31,7 @@ #define JABBER_CAPS_FILENAME "xmpp-caps.xml" -static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */ +GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */ static gchar *caps_hash = NULL; #if 0 @@ -528,7 +528,9 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c client = g_hash_table_lookup(capstable, key); - g_free(key); + //g_free(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"); diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index 07fea7b3c7..f8b0792f12 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -27,6 +27,7 @@ typedef struct _JabberCapsClientInfo JabberCapsClientInfo; #include "jabber.h" /* Implementation of XEP-0115 */ +extern GHashTable *capstable; typedef struct _JabberIdentity JabberCapsIdentity; diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index df4ec76dcd..50d752dfa6 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -65,6 +65,8 @@ static PurplePlugin *my_protocol = NULL; GList *jabber_features = NULL; GList *jabber_identities = NULL; +GHashTable *jabber_contact_info = NULL; + static void jabber_unregister_account_cb(JabberStream *js); static void jabber_stream_init(JabberStream *js) @@ -2446,18 +2448,20 @@ void jabber_register_commands(void) _("buzz: Buzz a user to get their attention"), NULL); } -static void -jabber_client_info_destroy_key(gpointer key) { - gchar *s = key; - g_free(s); -} - -static gboolean -jabber_client_info_compare(gconstpointer v1, gconstpointer v2) { - const gchar *name1 = v1; - const gchar *name2 = v2; +/* IPC fucntions*/ +static gboolean +jabber_ipc_contact_has_feature(gchar *fulljid, gchar *feature) +{ + JabberCapsKey *caps_info = NULL; + JabberCapsValueExt *capabilities = NULL; - return strcmp(name1,name2) == 0; + 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(capabilities->features, feature) == NULL) return FALSE ; + return TRUE; } void @@ -2467,7 +2471,7 @@ jabber_init_plugin(PurplePlugin *plugin) jabber_add_identity("client", "pc", PACKAGE); - // initialize jabber_features list + /* initialize jabber_features list */ jabber_add_feature("jabber:iq:last", 0); jabber_add_feature("jabber:iq:oob", 0); jabber_add_feature("jabber:iq:time", 0); @@ -2487,5 +2491,12 @@ jabber_init_plugin(PurplePlugin *plugin) 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, jabber_client_info_compare, jabber_client_info_destroy_key, jabber_caps_destroy_key); -} + 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_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_STRING)); +} \ No newline at end of file -- cgit v1.2.1 From 85ffbb0d8c09e21d379800c90ea1478f126cb411 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 6 Jul 2008 21:20:35 +0000 Subject: * adding documentation for IPC function contact_has_feature --- libpurple/protocols/jabber/jabber.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index 50d752dfa6..170dbcf49e 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -2449,6 +2449,15 @@ void jabber_register_commands(void) } /* IPC fucntions*/ + +/* + * 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. + * + * @return TRUE if supports feature; else FALSE. + */ static gboolean jabber_ipc_contact_has_feature(gchar *fulljid, gchar *feature) { -- cgit v1.2.1 From 3953acbc0d09e992ec356ae251af562322217c9c Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Mon, 7 Jul 2008 18:28:05 +0000 Subject: * removing several warnings * more checking for errors in jabber_caps_client_iqcb() * preparing another IPC function --- libpurple/protocols/jabber/caps.c | 41 +++++++++++++++---------------------- libpurple/protocols/jabber/jabber.c | 8 ++++++++ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index a54a4773f0..0d9a279839 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -351,7 +351,7 @@ static void jabber_caps_get_info_check_completion(jabber_caps_cbplususerdata *us } } #endif - +#if 0 static void jabber_caps_ext_iqcb(JabberStream *js, xmlnode *packet, gpointer data) { /* collect data and fetch all exts */ xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#info"); @@ -413,13 +413,12 @@ static void jabber_caps_ext_iqcb(JabberStream *js, xmlnode *packet, gpointer dat g_free(extuserdata); jabber_caps_get_info_check_completion(userdata); } - +#endif static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer data) { /* collect data and fetch all exts */ xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#info"); xmlnode *child; - GList *iter; jabber_caps_cbplususerdata *userdata = data; /* TODO: Better error checking! */ @@ -449,7 +448,7 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer printf("\n! ! ! invalid hash ! ! !"); return; } - + g_free(hash); JabberCapsValue *value = g_new0(JabberCapsValue, 1); @@ -461,6 +460,13 @@ static void jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer 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) @@ -528,10 +534,9 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c client = g_hash_table_lookup(capstable, key); - //g_free(key); - g_hash_table_replace(jabber_contact_info, g_strdup(who), key); - + g_free(key); + if(!client) { 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"); @@ -629,17 +634,11 @@ gchar *jabber_caps_get_formtype(const xmlnode *x) { } static gint jabber_caps_jabber_xdata_compare(gconstpointer a, gconstpointer b) { - const xmlnode *ac; - const xmlnode *bc; - xmlnode *aformtypefield; - xmlnode *bformtypefield; + const xmlnode *aformtypefield = a; + const xmlnode *bformtypefield = b; char *aformtype; char *bformtype; int result; - - ac = a; - bc = b; - aformtype = jabber_caps_get_formtype(aformtypefield); bformtype = jabber_caps_get_formtype(bformtypefield); @@ -698,7 +697,7 @@ static gint jabber_caps_xdata_field_compare(gconstpointer a, gconstpointer b) { return strcmp(ac->var, bc->var); } -GList *jabber_caps_xdata_get_fields(xmlnode *x) { +GList *jabber_caps_xdata_get_fields(const xmlnode *x) { GList *fields = 0; xmlnode *field; xmlnode *value; @@ -799,7 +798,7 @@ gchar *jabber_caps_calcualte_hash(JabberCapsClientInfo *info, const char *hash) //purple_debug_error("jabber", "Could not find cipher\n"); return 0; } - purple_cipher_context_append(context, verification, strlen(verification)); + 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"); @@ -819,14 +818,8 @@ void jabber_caps_calculate_own_hash(JabberStream *js) { GList *iter = 0; GList *features = 0; - /* sort identities */ if (jabber_identities == 0 && jabber_features == 0) return; - if (jabber_identities) { - for (iter = jabber_identities; iter; iter = iter->next) { - JabberIdentity *ident = iter->data; - } - } - + /* sort features */ if (jabber_features) { for (iter = jabber_features; iter; iter = iter->next) { diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index 170dbcf49e..f34e4a85fd 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -2473,6 +2473,14 @@ jabber_ipc_contact_has_feature(gchar *fulljid, gchar *feature) return TRUE; } +static void +jabber_ipc_add_feature(gchar *feature) +{ + if (feature == 0) return; + + jabber_add_feature(feature, 0); +} + void jabber_init_plugin(PurplePlugin *plugin) { -- cgit v1.2.1 From cd290badc9393ee6f561640022589e95e4244521 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 8 Jul 2008 15:05:45 +0000 Subject: * IPC work * removed g_free which may've resulted in a bug --- libpurple/protocols/jabber/caps.c | 6 +++--- libpurple/protocols/jabber/jabber.c | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 0d9a279839..1633ef6965 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -227,11 +227,12 @@ static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user static void jabber_caps_store(void) { char *str; + int length = 0; xmlnode *root = xmlnode_new("capabilities"); g_hash_table_foreach(capstable, jabber_caps_store_client, root); - str = xmlnode_to_formatted_str(root, NULL); + str = xmlnode_to_formatted_str(root, &length); xmlnode_free(root); - purple_util_write_data_to_file(JABBER_CAPS_FILENAME, str, -1); + purple_util_write_data_to_file(JABBER_CAPS_FILENAME, str, length); g_free(str); } @@ -535,7 +536,6 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c client = g_hash_table_lookup(capstable, key); g_hash_table_replace(jabber_contact_info, g_strdup(who), key); - g_free(key); if(!client) { JabberIq *iq = jabber_iq_new_query(js,JABBER_IQ_GET,"http://jabber.org/protocol/disco#info"); diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index f34e4a85fd..41c932726f 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -2477,8 +2477,9 @@ static void jabber_ipc_add_feature(gchar *feature) { if (feature == 0) return; - jabber_add_feature(feature, 0); + + // send presence with new caps info for all connected accounts } void @@ -2516,4 +2517,8 @@ jabber_init_plugin(PurplePlugin *plugin) purple_value_new(PURPLE_TYPE_BOOLEAN), 2, purple_value_new(PURPLE_TYPE_STRING), purple_value_new(PURPLE_TYPE_STRING)); -} \ No newline at end of file + purple_plugin_ipc_register(plugin, "add_feature", PURPLE_CALLBACK(jabber_ipc_add_feature), + purple_marshal_VOID__POINTER, + NULL, 1, + purple_value_new(PURPLE_TYPE_STRING)); +} -- cgit v1.2.1 From 6fd731b0a9bee7e11f314819c11f9d741a2aeb1f Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 15 Jul 2008 15:38:15 +0000 Subject: * implemented jabber_caps_broadcast_change() and finished jabber_ipc_add_feature() IPC function --- libpurple/protocols/jabber/caps.c | 13 +++++++++++++ libpurple/protocols/jabber/caps.h | 5 +++++ libpurple/protocols/jabber/jabber.c | 1 + 3 files changed, 19 insertions(+) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 1633ef6965..7f6515b409 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -845,3 +845,16 @@ const gchar* jabber_caps_get_own_hash() { return caps_hash; } +void jabber_caps_broadcast_change() { + GList *active_accounts = purple_accounts_get_all_active(); + for (active_accounts = purple_accounts_get_all_active(); active_accounts; active_accounts = active_accounts->next) { + PurpleAccount *account = active_accounts->data; + if (!strcmp(account->protocol_id, "jabber")) { + PurpleConnection *conn = account->gc; + JabberStream *js = conn->proto_data; + xmlnode *presence = jabber_presence_create_js(js, JABBER_BUDDY_STATE_UNKNOWN, 0, 0); + jabber_send(js, presence); + } + } +} + diff --git a/libpurple/protocols/jabber/caps.h b/libpurple/protocols/jabber/caps.h index f8b0792f12..2fb61d3583 100644 --- a/libpurple/protocols/jabber/caps.h +++ b/libpurple/protocols/jabber/caps.h @@ -88,4 +88,9 @@ void jabber_caps_calculate_own_hash(); **/ const gchar* jabber_caps_get_own_hash(); +/** + * + */ +void jabber_caps_broadcast_change(); + #endif /* _PURPLE_JABBER_CAPS_H_ */ diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index 41c932726f..06849893d6 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -2480,6 +2480,7 @@ jabber_ipc_add_feature(gchar *feature) jabber_add_feature(feature, 0); // send presence with new caps info for all connected accounts + jabber_caps_broadcast_change(); } void -- cgit v1.2.1 From 5168ea8ff5b4a08cd20b4a676f86e02c0a2c426a Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 15 Jul 2008 21:26:51 +0000 Subject: * fixing memory related bug; forgotton to duplicate some data which otherwise may be deleted by other parts of the program --- libpurple/protocols/jabber/caps.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 7f6515b409..bd40ac76e8 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -201,27 +201,27 @@ static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user xmlnode *client = xmlnode_new_child(root,"client"); GList *iter; - xmlnode_set_attrib(client,"node",clientinfo->node); - xmlnode_set_attrib(client,"ver",clientinfo->ver); - xmlnode_set_attrib(client,"hash",clientinfo->hash); + xmlnode_set_attrib(client,"node",g_strdup(clientinfo->node)); + xmlnode_set_attrib(client,"ver",g_strdup(clientinfo->ver)); + xmlnode_set_attrib(client,"hash",g_strdup(clientinfo->hash)); for(iter = props->identities; iter; iter = g_list_next(iter)) { JabberCapsIdentity *id = iter->data; xmlnode *identity = xmlnode_new_child(client, "identity"); - xmlnode_set_attrib(identity, "category", id->category); - xmlnode_set_attrib(identity, "type", id->type); + xmlnode_set_attrib(identity, "category", g_strdup(id->category)); + xmlnode_set_attrib(identity, "type", g_strdup(id->type)); if (id->name) - xmlnode_set_attrib(identity, "name", id->name); + xmlnode_set_attrib(identity, "name", g_strdup(id->name)); } for(iter = props->features; iter; iter = g_list_next(iter)) { const char *feat = iter->data; xmlnode *feature = xmlnode_new_child(client, "feature"); - xmlnode_set_attrib(feature, "var", feat); + xmlnode_set_attrib(feature, "var", g_strdup(feat)); } for(iter = props->forms; iter; iter = g_list_next(iter)) { xmlnode *xdata = iter->data; - xmlnode_insert_child(client, xdata); + xmlnode_insert_child(client, xmlnode_copy(xdata)); } } @@ -529,9 +529,9 @@ void jabber_caps_get_info(JabberStream *js, const char *who, const char *node, c userdata->ver = g_strdup(ver); userdata->hash = g_strdup(hash); - key->node = (char *)node; - key->ver = (char *)ver; - key->hash = (char*)hash; + key->node = g_strdup(node); + key->ver = g_strdup(ver); + key->hash = g_strdup(hash); client = g_hash_table_lookup(capstable, key); -- cgit v1.2.1 From 70482b2bc0568a4e439027b9b5959b37fca03efb Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 15 Jul 2008 22:16:26 +0000 Subject: * removing some g_strdup()s; I meant it too good --- libpurple/protocols/jabber/caps.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index bd40ac76e8..8d2cf3ba63 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -201,22 +201,22 @@ static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user xmlnode *client = xmlnode_new_child(root,"client"); GList *iter; - xmlnode_set_attrib(client,"node",g_strdup(clientinfo->node)); - xmlnode_set_attrib(client,"ver",g_strdup(clientinfo->ver)); - xmlnode_set_attrib(client,"hash",g_strdup(clientinfo->hash)); + xmlnode_set_attrib(client,"node", clientinfo->node); + 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; xmlnode *identity = xmlnode_new_child(client, "identity"); - xmlnode_set_attrib(identity, "category", g_strdup(id->category)); - xmlnode_set_attrib(identity, "type", g_strdup(id->type)); + xmlnode_set_attrib(identity, "category", id->category); + xmlnode_set_attrib(identity, "type", id->type); if (id->name) - xmlnode_set_attrib(identity, "name", g_strdup(id->name)); + xmlnode_set_attrib(identity, "name", id->name); } for(iter = props->features; iter; iter = g_list_next(iter)) { const char *feat = iter->data; xmlnode *feature = xmlnode_new_child(client, "feature"); - xmlnode_set_attrib(feature, "var", g_strdup(feat)); + xmlnode_set_attrib(feature, "var", feat); } for(iter = props->forms; iter; iter = g_list_next(iter)) { -- cgit v1.2.1 From 17818a093416ebab2ded1c5c251d4a54e333f278 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 1 Aug 2008 12:34:33 +0000 Subject: * some code style adjustments in caps.c * some DNS API changes to prepare TXT record resolving support --- libpurple/dnssrv.c | 340 ++++++++++++++++++++++++++++-------- libpurple/dnssrv.h | 25 ++- libpurple/protocols/jabber/caps.c | 8 +- libpurple/protocols/jabber/jabber.c | 2 +- 4 files changed, 294 insertions(+), 81 deletions(-) diff --git a/libpurple/dnssrv.c b/libpurple/dnssrv.c index d57b20f40a..1168b25fc3 100644 --- a/libpurple/dnssrv.c +++ b/libpurple/dnssrv.c @@ -33,12 +33,18 @@ #ifndef T_SRV #define T_SRV 33 #endif +#ifndef T_TXT +#define T_TXT 16 +#endif #else #include /* Missing from the mingw headers */ #ifndef DNS_TYPE_SRV # define DNS_TYPE_SRV 33 #endif +#ifndef DNS_TYPE_TXT +# define DNS_TYPE_TXT 16 +#endif #endif #include "dnssrv.h" @@ -60,9 +66,11 @@ static void (WINAPI *MyDnsRecordListFree) (PDNS_RECORD pRecordList, #endif struct _PurpleSrvQueryData { - PurpleSrvCallback cb; + PurpleSrvCallback srv_cb; + PurpleTxtCallback txt_cb; gpointer extradata; guint handle; + int type; #ifdef _WIN32 GThread *resolver; char *query; @@ -74,6 +82,11 @@ struct _PurpleSrvQueryData { #endif }; +typedef struct _PurpleSrvInternalQuery { + int type; + char query[256]; +} PurpleSrvInternalQuery; + static gint responsecompare(gconstpointer ar, gconstpointer br) { @@ -99,6 +112,7 @@ resolve(int in, int out) { GList *ret = NULL; PurpleSrvResponse *srvres; + PurpleTxtResponse *txtres; queryans answer; int size; int qdcount; @@ -107,19 +121,19 @@ resolve(int in, int out) guchar *cp; gchar name[256]; guint16 type, dlen, pref, weight, port; - gchar query[256]; + PurpleSrvInternalQuery query; #ifdef HAVE_SIGNAL_H purple_restore_default_signal_handlers(); #endif - if (read(in, query, 256) <= 0) { + if (read(in, &query, sizeof(query)) <= 0) { close(out); close(in); _exit(0); } - size = res_query( query, C_IN, T_SRV, (u_char*)&answer, sizeof( answer)); + size = res_query( query.query, C_IN, query.type, (u_char*)&answer, sizeof( answer)); qdcount = ntohs(answer.hdr.qdcount); ancount = ntohs(answer.hdr.ancount); @@ -148,7 +162,7 @@ resolve(int in, int out) GETSHORT(dlen,cp); - if (type == T_SRV) { + if (query.type == T_SRV) { GETSHORT(pref,cp); GETSHORT(weight,cp); @@ -168,6 +182,14 @@ resolve(int in, int out) srvres->weight = weight; ret = g_list_insert_sorted(ret, srvres, responsecompare); + } else if (query.type == T_TXT) { + size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); + if(size < 0 ) + goto end; + txtres = g_new0(PurpleTxtResponse, 1); + strcpy(txtres->content, name); + + ret = g_list_append(ret, txtres); } else { cp += dlen; } @@ -175,10 +197,12 @@ resolve(int in, int out) end: size = g_list_length(ret); + write(out, &(query.type), sizeof(query.type)); write(out, &size, sizeof(int)); while (ret != NULL) { - write(out, ret->data, sizeof(PurpleSrvResponse)); + if (query.type == T_SRV) write(out, ret->data, sizeof(PurpleSrvResponse)); + if (query.type == T_TXT) write(out, ret->data, sizeof(PurpleTxtResponse)); g_free(ret->data); ret = g_list_remove(ret, ret->data); } @@ -193,39 +217,67 @@ static void resolved(gpointer data, gint source, PurpleInputCondition cond) { int size; + int type; PurpleSrvQueryData *query_data = (PurpleSrvQueryData*)data; - PurpleSrvResponse *res; - PurpleSrvResponse *tmp; int i; - PurpleSrvCallback cb = query_data->cb; int status; - - if (read(source, &size, sizeof(int)) == sizeof(int)) - { - ssize_t red; - purple_debug_info("dnssrv","found %d SRV entries\n", size); - tmp = res = g_new0(PurpleSrvResponse, size); - for (i = 0; i < size; i++) { - red = read(source, tmp++, sizeof(PurpleSrvResponse)); - if (red != sizeof(PurpleSrvResponse)) { - purple_debug_error("dnssrv","unable to read srv " - "response: %s\n", g_strerror(errno)); + + if (read(source, &type, sizeof(type)) == sizeof(type)) { + purple_debug_info("dnssrv","type: %d\n", type); + if (type == T_SRV) { + PurpleSrvResponse *res; + PurpleSrvResponse *tmp; + PurpleSrvCallback cb = query_data->srv_cb; + if (read(source, &size, sizeof(int)) == sizeof(int)) { + ssize_t red; + purple_debug_info("dnssrv","found %d SRV entries\n", size); + tmp = res = g_new0(PurpleSrvResponse, size); + for (i = 0; i < size; i++) { + red = read(source, tmp++, sizeof(PurpleSrvResponse)); + if (red != sizeof(PurpleSrvResponse)) { + purple_debug_error("dnssrv","unable to read srv " + "response: %s\n", g_strerror(errno)); + size = 0; + g_free(res); + res = NULL; + } + } + } else { + purple_debug_info("dnssrv","found 0 SRV entries; errno is %i\n", errno); + size = 0; + res = NULL; + } + cb(res, size, query_data->extradata); + } else if (type == T_TXT) { + PurpleTxtResponse *res; + PurpleTxtResponse *tmp; + PurpleTxtCallback cb = query_data->txt_cb; + if (read(source, &size, sizeof(int)) == sizeof(int)) { + ssize_t red; + purple_debug_info("dnssrv","found %d TXT entries\n", size); + tmp = res = g_new0(PurpleTxtResponse, size); + for (i = 0; i < size; i++) { + red = read(source, tmp++, sizeof(PurpleTxtResponse)); + if (red != sizeof(PurpleTxtResponse)) { + purple_debug_error("dnssrv","unable to read txt " + "response: %s\n", g_strerror(errno)); + size = 0; + g_free(res); + res = NULL; + } + } + } else { + purple_debug_info("dnssrv","found 0 SRV entries; errno is %i\n", errno); size = 0; - g_free(res); res = NULL; } + cb(res, size, query_data->extradata); + } else { + purple_debug_info("dnssrv","type unknown of DNS result entry; errno is %i\n", errno); } } - else - { - purple_debug_info("dnssrv","found 0 SRV entries; errno is %i\n", errno); - size = 0; - res = NULL; - } - cb(res, size, query_data->extradata); waitpid(query_data->pid, &status, 0); - purple_srv_cancel(query_data); } @@ -237,33 +289,56 @@ static gboolean res_main_thread_cb(gpointer data) { PurpleSrvResponse *srvres = NULL; + PurpleTxtResponse *txtres = NULL; int size = 0; PurpleSrvQueryData *query_data = data; - if(query_data->error_message != NULL) purple_debug_error("dnssrv", query_data->error_message); else { - PurpleSrvResponse *srvres_tmp = NULL; - GSList *lst = query_data->results; - - size = g_slist_length(lst); - - if(query_data->cb && size > 0) - srvres_tmp = srvres = g_new0(PurpleSrvResponse, size); - while (lst) { - if(query_data->cb) - memcpy(srvres_tmp++, lst->data, sizeof(PurpleSrvResponse)); - g_free(lst->data); - lst = g_slist_remove(lst, lst->data); - } + if (query_data->type == T_SRV) { + PurpleSrvResponse *srvres_tmp = NULL; + GSList *lst = query_data->results; + + size = g_slist_length(lst); + + if(query_data->srv_cb && size > 0) + srvres_tmp = srvres = g_new0(PurpleSrvResponse, size); + while (lst) { + if(query_data->cb) + memcpy(srvres_tmp++, lst->data, sizeof(PurpleSrvResponse)); + g_free(lst->data); + lst = g_slist_remove(lst, lst->data); + } - query_data->results = NULL; + query_data->results = NULL; - purple_debug_info("dnssrv", "found %d SRV entries\n", size); - } + purple_debug_info("dnssrv", "found %d SRV entries\n", size); + + if(query_data->srv_cb) query_data->srv_cb(srvres, size, query_data->extradata); + } else if (query_data->type == T_TXT) { + PurpleTxtResponse *txtres_tmp = NULL; + GSList *lst = query_data->results; + + size = g_slist_length(lst); + + if(query_data->txt_cb && size > 0) + txtres_tmp = txtres = g_new0(PurpleTxtResponse, size); + while (lst) { + if(query_data->txt_cb) + memcpy(txtres_tmp++, lst->data, sizeof(PurpleTxtResponse)); + g_free(lst->data); + lst = g_slist_remove(lst, lst->data); + } - if(query_data->cb) - query_data->cb(srvres, size, query_data->extradata); + query_data->results = NULL; + + purple_debug_info("dnssrv", "found %d TXT entries\n", size); + + if(query_data->txt_cb) query_data->txt_cb(txtres, size, query_data->extradata); + } else { + purple_debug_error("dnssrv", "unknown query type"); + } + } query_data->resolver = NULL; query_data->handle = 0; @@ -277,40 +352,50 @@ static gpointer res_thread(gpointer data) { PDNS_RECORD dr = NULL; - int type = DNS_TYPE_SRV; + int type; DNS_STATUS ds; PurpleSrvQueryData *query_data = data; - + type = query_data->type; ds = MyDnsQuery_UTF8(query_data->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL); if (ds != ERROR_SUCCESS) { gchar *msg = g_win32_error_message(ds); - query_data->error_message = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); + if (type == T_SRV) { + query_data->error_message = g_strdup_printf("Couldn't look up SRV record. %s (%lu).\n", msg, ds); + } else if (type == T_TXT) { + query_data->error_message = g_strdup_printf("Couldn't look up TXT record. %s (%lu).\n", msg, ds); + } g_free(msg); } else { - PDNS_RECORD dr_tmp; - GSList *lst = NULL; - DNS_SRV_DATA *srv_data; - PurpleSrvResponse *srvres; - - for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { - /* Discard any incorrect entries. I'm not sure if this is necessary */ - if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { - continue; + if (type == T_SRV) { + PDNS_RECORD dr_tmp; + GSList *lst = NULL; + DNS_SRV_DATA *srv_data; + PurpleSrvResponse *srvres; + + for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { + /* Discard any incorrect entries. I'm not sure if this is necessary */ + if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { + continue; + } + + srv_data = &dr_tmp->Data.SRV; + srvres = g_new0(PurpleSrvResponse, 1); + strncpy(srvres->hostname, srv_data->pNameTarget, 255); + srvres->hostname[255] = '\0'; + srvres->pref = srv_data->wPriority; + srvres->port = srv_data->wPort; + srvres->weight = srv_data->wWeight; + + lst = g_slist_insert_sorted(lst, srvres, responsecompare); } - srv_data = &dr_tmp->Data.SRV; - srvres = g_new0(PurpleSrvResponse, 1); - strncpy(srvres->hostname, srv_data->pNameTarget, 255); - srvres->hostname[255] = '\0'; - srvres->pref = srv_data->wPriority; - srvres->port = srv_data->wPort; - srvres->weight = srv_data->wWeight; - - lst = g_slist_insert_sorted(lst, srvres, responsecompare); + MyDnsRecordListFree(dr, DnsFreeRecordList); + query_data->results = lst; + } else if (type == T_TXT) { + #error IMPLEMENTATION MISSING + } else { + } - - MyDnsRecordListFree(dr, DnsFreeRecordList); - query_data->results = lst; } /* back to main thread */ @@ -328,6 +413,7 @@ purple_srv_resolve(const char *protocol, const char *transport, const char *doma { char *query; PurpleSrvQueryData *query_data; + PurpleSrvInternalQuery internal_query; #ifndef _WIN32 int in[2], out[2]; int pid; @@ -369,11 +455,16 @@ purple_srv_resolve(const char *protocol, const char *transport, const char *doma close(out[1]); close(in[0]); - if (write(in[1], query, strlen(query)+1) < 0) + internal_query.type = T_SRV; + strncpy(internal_query.query, query, 255); + + if (write(in[1], &internal_query, sizeof(internal_query)) < 0) purple_debug_error("dnssrv", "Could not write to SRV resolver\n"); + query_data = g_new0(PurpleSrvQueryData, 1); - query_data->cb = cb; + query_data->type = T_SRV; + query_data->srv_cb = cb; query_data->extradata = extradata; query_data->pid = pid; query_data->fd_out = out[0]; @@ -392,7 +483,100 @@ purple_srv_resolve(const char *protocol, const char *transport, const char *doma } query_data = g_new0(PurpleSrvQueryData, 1); - query_data->cb = cb; + query_data->srv_cb = cb; + query_data->query = query; + query_data->extradata = extradata; + + if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree) + query_data->error_message = g_strdup("System missing DNS API (Requires W2K+)\n"); + else { + query_data->resolver = g_thread_create(res_thread, query_data, FALSE, &err); + if (query_data->resolver == NULL) { + query_data->error_message = g_strdup_printf("SRV thread create failure: %s\n", (err && err->message) ? err->message : ""); + g_error_free(err); + } + } + + /* The query isn't going to happen, so finish the SRV lookup now. + * Asynchronously call the callback since stuff may not expect + * the callback to be called before this returns */ + if (query_data->error_message != NULL) + query_data->handle = purple_timeout_add(0, res_main_thread_cb, query_data); + + return query_data; +#endif +} + +PurpleSrvQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata) +{ + char *query; + PurpleSrvQueryData *query_data; +#ifndef _WIN32 + int in[2], out[2]; + int pid; +#else + GError* err = NULL; + static gboolean initialized = FALSE; +#endif + + query = g_strdup_printf("%s.%s", owner, domain); + purple_debug_info("dnssrv","querying TXT record for %s\n", query); + +#ifndef _WIN32 + if(pipe(in) || pipe(out)) { + purple_debug_error("dnssrv", "Could not create pipe\n"); + g_free(query); + cb(NULL, 0, extradata); + return NULL; + } + + pid = fork(); + if (pid == -1) { + purple_debug_error("dnssrv", "Could not create process!\n"); + cb(NULL, 0, extradata); + g_free(query); + return NULL; + } + + /* Child */ + if (pid == 0) + { + g_free(query); + + close(out[0]); + close(in[1]); + resolve(in[0], out[1]); + /* resolve() does not return */ + } + + close(out[1]); + close(in[0]); + + if (write(in[1], query, strlen(query)+1) < 0) + purple_debug_error("dnssrv", "Could not write to TXT resolver\n"); + + query_data = g_new0(PurpleSrvQueryData, 1); + query_data->type = T_TXT; + query_data->txt_cb = cb; + query_data->extradata = extradata; + query_data->pid = pid; + query_data->fd_out = out[0]; + query_data->fd_in = in[1]; + query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data); + + g_free(query); + + return query_data; +#else + if (!initialized) { + MyDnsQuery_UTF8 = (void*) wpurple_find_and_loadproc("dnsapi.dll", "DnsQuery_UTF8"); + MyDnsRecordListFree = (void*) wpurple_find_and_loadproc( + "dnsapi.dll", "DnsRecordListFree"); + initialized = TRUE; + } + + query_data = g_new0(PurpleSrvQueryData, 1); + query_data->txt_cb = cb; query_data->query = query; query_data->extradata = extradata; @@ -440,3 +624,9 @@ purple_srv_cancel(PurpleSrvQueryData *query_data) #endif g_free(query_data); } + +void +purple_txt_cancel(PurpleSrvQueryData *query_data) +{ + purple_srv_cancel(query_data); +} diff --git a/libpurple/dnssrv.h b/libpurple/dnssrv.h index 4e17ae068c..4d452021bd 100644 --- a/libpurple/dnssrv.h +++ b/libpurple/dnssrv.h @@ -28,8 +28,9 @@ extern "C" { #endif -typedef struct _PurpleSrvResponse PurpleSrvResponse; typedef struct _PurpleSrvQueryData PurpleSrvQueryData; +typedef struct _PurpleSrvResponse PurpleSrvResponse; +typedef struct _PurpleTxtResponse PurpleTxtResponse; struct _PurpleSrvResponse { char hostname[256]; @@ -38,7 +39,12 @@ struct _PurpleSrvResponse { int pref; }; +struct _PurpleTxtResponse { + char content[256]; +}; + typedef void (*PurpleSrvCallback)(PurpleSrvResponse *resp, int results, gpointer data); +typedef void (*PurpleTxtCallback)(PurpleTxtResponse *resp, int results, gpointer data); /** * Queries an SRV record. @@ -58,6 +64,23 @@ PurpleSrvQueryData *purple_srv_resolve(const char *protocol, const char *transpo */ void purple_srv_cancel(PurpleSrvQueryData *query_data); +/** + * Queries an TXT record. + * + * @param owner Name of the protocol (e.g. "_xmppconnect") + * @param domain Domain name to query (e.g. "blubb.com") + * @param cb A callback which will be called with the results + * @param extradata Extra data to be passed to the callback + */ +PurpleSrvQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata); + +/** + * Cancel an TXT DNS query. + * + * @param query_data The request to cancel. + */ +void purple_txt_cancel(PurpleSrvQueryData *query_data); + #ifdef __cplusplus } #endif diff --git a/libpurple/protocols/jabber/caps.c b/libpurple/protocols/jabber/caps.c index 8d2cf3ba63..adf805fc33 100644 --- a/libpurple/protocols/jabber/caps.c +++ b/libpurple/protocols/jabber/caps.c @@ -198,12 +198,12 @@ static void jabber_caps_store_client(gpointer key, gpointer value, gpointer user JabberCapsKey *clientinfo = key; JabberCapsValue *props = value; xmlnode *root = user_data; - xmlnode *client = xmlnode_new_child(root,"client"); + xmlnode *client = xmlnode_new_child(root, "client"); GList *iter; - xmlnode_set_attrib(client,"node", clientinfo->node); - xmlnode_set_attrib(client,"ver", clientinfo->ver); - xmlnode_set_attrib(client,"hash", clientinfo->hash); + xmlnode_set_attrib(client, "node", clientinfo->node); + 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; xmlnode *identity = xmlnode_new_child(client, "identity"); diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index 06849893d6..0b013f7097 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -683,7 +683,7 @@ jabber_login(PurpleAccount *account) /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll * invoke the magic of SRV lookups, to figure out host and port */ if(!js->gsc) { - if(connect_server[0]) { + if(connect_server[0]) { jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222)); } else { js->srv_query_data = purple_srv_resolve("xmpp-client", -- cgit v1.2.1 From f0d158050b9fd3d6f2f37b7e3fa8611f63d24c38 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 1 Aug 2008 22:37:51 +0000 Subject: * fixing some bugs, finishing support for TXT queries --- libpurple/dnssrv.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libpurple/dnssrv.c b/libpurple/dnssrv.c index 1168b25fc3..2ce2bca02c 100644 --- a/libpurple/dnssrv.c +++ b/libpurple/dnssrv.c @@ -198,7 +198,7 @@ resolve(int in, int out) end: size = g_list_length(ret); write(out, &(query.type), sizeof(query.type)); - write(out, &size, sizeof(int)); + write(out, &size, sizeof(size)); while (ret != NULL) { if (query.type == T_SRV) write(out, ret->data, sizeof(PurpleSrvResponse)); @@ -223,7 +223,6 @@ resolved(gpointer data, gint source, PurpleInputCondition cond) int status; if (read(source, &type, sizeof(type)) == sizeof(type)) { - purple_debug_info("dnssrv","type: %d\n", type); if (type == T_SRV) { PurpleSrvResponse *res; PurpleSrvResponse *tmp; @@ -511,6 +510,7 @@ PurpleSrvQueryData *purple_txt_resolve(const char *owner, const char *domain, Pu { char *query; PurpleSrvQueryData *query_data; + PurpleSrvInternalQuery internal_query; #ifndef _WIN32 int in[2], out[2]; int pid; @@ -552,7 +552,10 @@ PurpleSrvQueryData *purple_txt_resolve(const char *owner, const char *domain, Pu close(out[1]); close(in[0]); - if (write(in[1], query, strlen(query)+1) < 0) + internal_query.type = T_TXT; + strncpy(internal_query.query, query, 255); + + if (write(in[1], &internal_query, sizeof(internal_query)) < 0) purple_debug_error("dnssrv", "Could not write to TXT resolver\n"); query_data = g_new0(PurpleSrvQueryData, 1); -- cgit v1.2.1 From 1c5940f7f47980b5b5fa9f61af14f358ed0c43e2 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sat, 2 Aug 2008 16:43:04 +0000 Subject: * some further bugfixing, BIND's libresolv does so much for the programmer, just happy it exists --- libpurple/dnssrv.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/libpurple/dnssrv.c b/libpurple/dnssrv.c index 2ce2bca02c..8db47d9477 100644 --- a/libpurple/dnssrv.c +++ b/libpurple/dnssrv.c @@ -122,6 +122,7 @@ resolve(int in, int out) gchar name[256]; guint16 type, dlen, pref, weight, port; PurpleSrvInternalQuery query; + int n; #ifdef HAVE_SIGNAL_H purple_restore_default_signal_handlers(); @@ -134,10 +135,9 @@ resolve(int in, int out) } size = res_query( query.query, C_IN, query.type, (u_char*)&answer, sizeof( answer)); - + qdcount = ntohs(answer.hdr.qdcount); ancount = ntohs(answer.hdr.ancount); - cp = (guchar*)&answer + sizeof(HEADER); end = (guchar*)&answer + size; @@ -152,16 +152,13 @@ resolve(int in, int out) size = dn_expand((unsigned char*)&answer, end, cp, name, 256); if(size < 0) goto end; - cp += size; - GETSHORT(type,cp); /* skip ttl and class since we already know it */ cp += 6; GETSHORT(dlen,cp); - if (query.type == T_SRV) { GETSHORT(pref,cp); @@ -183,13 +180,10 @@ resolve(int in, int out) ret = g_list_insert_sorted(ret, srvres, responsecompare); } else if (query.type == T_TXT) { - size = dn_expand( (unsigned char*)&answer, end, cp, name, 256); - if(size < 0 ) - goto end; txtres = g_new0(PurpleTxtResponse, 1); - strcpy(txtres->content, name); - + strncpy(txtres->content, ++cp, dlen-1); ret = g_list_append(ret, txtres); + cp += dlen - 1; } else { cp += dlen; } -- cgit v1.2.1 From ccfdc3961a05e755b77610612ddfaa40dd867475 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 3 Aug 2008 13:48:59 +0000 Subject: * looking for alternative connection methods if direct connection fails * introducing bosh files --- libpurple/protocols/jabber/bosh.c | 39 +++++++++++++++++++++++++++++++++++++ libpurple/protocols/jabber/bosh.h | 34 ++++++++++++++++++++++++++++++++ libpurple/protocols/jabber/jabber.c | 27 +++++++++++++++++++------ 3 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 libpurple/protocols/jabber/bosh.c create mode 100644 libpurple/protocols/jabber/bosh.h diff --git a/libpurple/protocols/jabber/bosh.c b/libpurple/protocols/jabber/bosh.c new file mode 100644 index 0000000000..c45f088170 --- /dev/null +++ b/libpurple/protocols/jabber/bosh.c @@ -0,0 +1,39 @@ +/* + * purple - Jabber Protocol Plugin + * + * Copyright (C) 2008, Tobias Markmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ +#include "internal.h" +#include "cipher.h" +#include "debug.h" +#include "imgstore.h" +#include "prpl.h" +#include "notify.h" +#include "request.h" +#include "util.h" +#include "xmlnode.h" + +#include "buddy.h" +#include "chat.h" +#include "jabber.h" +#include "iq.h" +#include "presence.h" +#include "xdata.h" +#include "pep.h" +#include "adhoccommands.h" + diff --git a/libpurple/protocols/jabber/bosh.h b/libpurple/protocols/jabber/bosh.h new file mode 100644 index 0000000000..f217c03c8c --- /dev/null +++ b/libpurple/protocols/jabber/bosh.h @@ -0,0 +1,34 @@ +/** + * @file bosh.h Buddy handlers + * + * purple + * + * Copyright (C) 2008, Tobias Markmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#ifndef _PURPLE_JABBER_BOSH_H_ +#define _PURPLE_JABBER_BOSH_H_ + +typedef struct { + char *url; + gboolean pipelining; +} PurpleBOSHConnection; + +typedef struct { + +} PurpleHTTPRequest; + +#endif /* _PURPLE_JABBER_BOSH_H_ */ diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index 0b013f7097..91f94e1053 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -521,6 +521,25 @@ jabber_login_callback_ssl(gpointer data, PurpleSslConnection *gsc, jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); } +static void +txt_resolved_cb(PurpleTxtResponse *resp, int results, gpointer data) +{ + PurpleConnection *gc = data; + JabberStream *js = gc->proto_data; + int n; + + if (results > 0) { + gchar *tmp; + tmp = g_strdup_printf(_("Could not find alternative XMPP connection methods after failing to connect directly.\n")); + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); + g_free(tmp); + } + + for (n = 0; n < results; n++) { + purple_debug_info("dnssrv","TXT RDATA: %s\n", resp[n].content); + } +} static void jabber_login_callback(gpointer data, gint source, const gchar *error) @@ -529,12 +548,8 @@ jabber_login_callback(gpointer data, gint source, const gchar *error) JabberStream *js = gc->proto_data; if (source < 0) { - gchar *tmp; - tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), - error); - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); + purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); + purple_txt_resolve("_xmppconnect", js->user->domain, txt_resolved_cb, gc); return; } -- cgit v1.2.1 From 2b336d6db0ab067fdb74be096e270984bc79c81d Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 3 Aug 2008 22:21:49 +0000 Subject: * small bugfix in jabber_ipc_add_feature() --- libpurple/protocols/jabber/jabber.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c index 91f94e1053..2b72863798 100644 --- a/libpurple/protocols/jabber/jabber.c +++ b/libpurple/protocols/jabber/jabber.c @@ -528,7 +528,7 @@ txt_resolved_cb(PurpleTxtResponse *resp, int results, gpointer data) JabberStream *js = gc->proto_data; int n; - if (results > 0) { + if (results == 0) { gchar *tmp; tmp = g_strdup_printf(_("Could not find alternative XMPP connection methods after failing to connect directly.\n")); purple_connection_error_reason (gc, @@ -537,7 +537,15 @@ txt_resolved_cb(PurpleTxtResponse *resp, int results, gpointer data) } for (n = 0; n < results; n++) { - purple_debug_info("dnssrv","TXT RDATA: %s\n", resp[n].content); + gchar **token; + token = g_strsplit(resp[n].content, "=", 2); + if (!strcmp(token[0], "_xmpp-client-xbosh")) { + purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]); + js->bosh.url = g_strdup(token[1]); + g_strfreev(token); + break; + } + g_strfreev(token); } } @@ -2484,7 +2492,7 @@ jabber_ipc_contact_has_feature(gchar *fulljid, gchar *feature) if (!caps_info) return FALSE; capabilities = g_hash_table_lookup(capstable, caps_info); - if (g_list_find(capabilities->features, feature) == NULL) return FALSE ; + if (g_list_find_custom(capabilities->features, feature, strcmp) == NULL) return FALSE ; return TRUE; } -- cgit v1.2.1 From 36f0a60a2686be979945631e56dc753efa499227 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 3 Aug 2008 22:50:13 +0000 Subject: * some refactoring --- libpurple/protocols/jabber/bosh.c | 10 +++++++++ libpurple/protocols/jabber/bosh.h | 47 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/libpurple/protocols/jabber/bosh.c b/libpurple/protocols/jabber/bosh.c index c45f088170..544bce4a8f 100644 --- a/libpurple/protocols/jabber/bosh.c +++ b/libpurple/protocols/jabber/bosh.c @@ -37,3 +37,13 @@ #include "pep.h" #include "adhoccommands.h" +PurpleHTTPHeaderField* jabber_bosh_http_header_field(const char *name, *const char *value) { + PurpleHTTPHeaderField *tmp = g_new0(PurpleHTTPHeaderField, 0); + tmp->name = g_strdup(name); + tmp->value = g_strdup(value); + return tmp; +} + +void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn) { + +} \ No newline at end of file diff --git a/libpurple/protocols/jabber/bosh.h b/libpurple/protocols/jabber/bosh.h index f217c03c8c..34c0697c46 100644 --- a/libpurple/protocols/jabber/bosh.h +++ b/libpurple/protocols/jabber/bosh.h @@ -22,13 +22,54 @@ #ifndef _PURPLE_JABBER_BOSH_H_ #define _PURPLE_JABBER_BOSH_H_ +#include + +typedef struct _PurpleHTTPRequest PurpleHTTPRequest; +typedef struct _PurpleHTTPResponse PurpleHTTPResponse; +typedef struct _PurpleHTTPHeaderField PurpleHTTPHeaderField; + +typedef void (*PurpleHTTPRequestCallback)(PurpleHTTPRequest *req, PurpleHTTPResponse *res, void *userdata); + +typedef struct { + int fd; + PurpleConnection *conn; + GQueue *requests; + void *userdata; +} PurpleHTTPConnection; + typedef struct { char *url; gboolean pipelining; + PurpleHTTPConnection *conn_a; + PurpleHTTPConnection *conn_b; } PurpleBOSHConnection; -typedef struct { - -} PurpleHTTPRequest; +struct _PurpleHTTPRequest { + PurpleHTTPRequestCallback cb; + char *method; + char *url; + GList *header; + char *data; + void *userdata; +}; + +struct _PurpleHTTPResponse { + int status; + GList *header; + char *data; +}; + +struct _PurpleHTTPHeaderField { + char *name; + char *value; +}; + +PurpleHTTPHeaderField *jabber_bosh_http_header_field(const char *name, const char *value); + +void jabber_bosh_http_connection_connect(PurpleHTTPConnection *conn); +void jabber_bosh_http_send_request(PurpleHTTPConnection *conn, PurpleHTTPRequest *req); +void jabber_bosh_http_connection_clean(PurpleHTTPConnection *conn); +void jabber_bosh_http_request_init(PurpleHTTPRequest *req, const char *method, const char *url, PurpleHTTPRequestCallback cb, void *userdata); +void jabber_bosh_http_request_clean(PurpleHTTPRequest *req); #endif /* _PURPLE_JABBER_BOSH_H_ */ -- cgit v1.2.1 From 0d3dffa2bb987e05de39d993fa93d414e5eef8b2 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 3 Aug 2008 22:58:10 +0000 Subject: * missing commit --- libpurple/protocols/jabber/jabber.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h index 88cf0d6faa..d75cd88073 100644 --- a/libpurple/protocols/jabber/jabber.h +++ b/libpurple/protocols/jabber/jabber.h @@ -59,6 +59,7 @@ typedef struct _JabberStream JabberStream; #include "jutil.h" #include "xmlnode.h" #include "buddy.h" +#include "bosh.h" #ifdef HAVE_CYRUS_SASL #include @@ -201,6 +202,9 @@ struct _JabberStream /* A purple timeout tag for the keepalive */ int keepalive_timeout; + + /* BOSH stuff*/ + PurpleBOSHConnection bosh; }; typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *namespace); -- cgit v1.2.1