# HG changeset patch # User Andreas Monitzer # Date 1182052279 0 # Node ID 5ab3c6bb95b4f3d439a3988c4406191a415d2aa2 # Parent 7754d39d70c57a41434ff5330d45aac9b91f894e Implemented receiving other people's avatars via XEP-0084. Note that this code now includes a workaround for a non-spec incompatibility for the current ejabberd PEP implementation, and doesn't use the correct namespace due to Psi using the wrong one (outdated?). Works fine though, and the vcard-based approach is retained. diff -r 7754d39d70c5 -r 5ab3c6bb95b4 libpurple/protocols/jabber/buddy.c --- a/libpurple/protocols/jabber/buddy.c Sun Jun 17 01:16:55 2007 +0000 +++ b/libpurple/protocols/jabber/buddy.c Sun Jun 17 03:51:19 2007 +0000 @@ -502,13 +502,13 @@ hash = g_strdup_printf("%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x",digest[0],digest[1],digest[2],digest[3],digest[4],digest[5],digest[6],digest[7],digest[8],digest[9],digest[10],digest[11],digest[12],digest[13],digest[14],digest[15],digest[16],digest[17],digest[18],digest[19]); publish = xmlnode_new("publish"); - xmlnode_set_attrib(publish,"node","http://www.xmpp.org/extensions/xep-0084.html#ns-data"); + xmlnode_set_attrib(publish,"node",AVATARNAMESPACEDATA); item = xmlnode_new_child(publish, "item"); xmlnode_set_attrib(item, "id", hash); data = xmlnode_new_child(item, "data"); - xmlnode_set_namespace(data,"http://www.xmpp.org/extensions/xep-0084.html#ns-data"); + xmlnode_set_namespace(data,AVATARNAMESPACEDATA); base64avatar = purple_base64_encode(purple_imgstore_get_data(img), purple_imgstore_get_size(img)); xmlnode_insert_data(data,base64avatar,-1); @@ -519,13 +519,13 @@ /* next step: publish the metadata */ publish = xmlnode_new("publish"); - xmlnode_set_attrib(publish,"node","http://www.xmpp.org/extensions/xep-0084.html#ns-metadata"); + xmlnode_set_attrib(publish,"node",AVATARNAMESPACEMETA); item = xmlnode_new_child(publish, "item"); xmlnode_set_attrib(item, "id", hash); metadata = xmlnode_new_child(item, "metadata"); - xmlnode_set_namespace(metadata,"http://www.xmpp.org/extensions/xep-0084.html#ns-metadata"); + xmlnode_set_namespace(metadata,AVATARNAMESPACEMETA); info = xmlnode_new_child(metadata, "info"); xmlnode_set_attrib(info, "id", hash); @@ -548,12 +548,12 @@ /* remove the metadata */ xmlnode *metadata, *item; xmlnode *publish = xmlnode_new("publish"); - xmlnode_set_attrib(publish,"node","http://www.xmpp.org/extensions/xep-0084.html#ns-metadata"); + xmlnode_set_attrib(publish,"node",AVATARNAMESPACEMETA); item = xmlnode_new_child(publish, "item"); metadata = xmlnode_new_child(item, "metadata"); - xmlnode_set_namespace(metadata,"http://www.xmpp.org/extensions/xep-0084.html#ns-metadata"); + xmlnode_set_namespace(metadata,AVATARNAMESPACEMETA); xmlnode_new_child(metadata, "stop"); @@ -1109,8 +1109,104 @@ jabber_buddy_info_show_if_ready(jbi); } +typedef struct _JabberBuddyAvatarUpdateURLInfo { + JabberStream *js; + char *from; + char *id; +} JabberBuddyAvatarUpdateURLInfo; + +static void do_buddy_avatar_update_fromurl(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) { + JabberBuddyAvatarUpdateURLInfo *info = user_data; + if(!url_text) { + purple_debug(PURPLE_DEBUG_ERROR, "jabber", + "do_buddy_avatar_update_fromurl got error \"%s\"", error_message); + return; + } + + purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), info->from, (void*)url_text, len, info->id); + g_free(info->from); + g_free(info->id); + g_free(info); +} + +static void do_buddy_avatar_update_data(JabberStream *js, const char *from, xmlnode *item) { + xmlnode *data; + const char *checksum; + char *b64data; + void *img; + size_t size; + if(!item) + return; + + data = xmlnode_get_child_with_namespace(item,"data",AVATARNAMESPACEDATA); + if(!data) + return; + + checksum = xmlnode_get_attrib(item,"id"); + if(!checksum) + return; + + b64data = xmlnode_get_data(data); + if(!b64data) + return; + + img = purple_base64_decode(b64data, &size); + if(!img) + return; + + purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum); +} + void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items) { + PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(js->gc), from); + const char *checksum; + xmlnode *item, *metadata; + if(!buddy) + return; + checksum = purple_buddy_icons_get_checksum_for_user(buddy); + item = xmlnode_get_child(items,"item"); + metadata = xmlnode_get_child_with_namespace(item, "metadata", AVATARNAMESPACEMETA); + if(!metadata) + return; + /* check if we have received a stop */ + if(xmlnode_get_child(metadata, "stop")) { + purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); + } else { + xmlnode *info, *goodinfo = NULL; + + /* iterate over all info nodes to get one we can use */ + for(info = metadata->child; info; info = info->next) { + if(info->type == XMLNODE_TYPE_TAG && !strcmp(info->name,"info")) { + const char *type = xmlnode_get_attrib(info,"type"); + const char *id = xmlnode_get_attrib(info,"id"); + + if(checksum && id && !strcmp(id, checksum)) { + /* we already have that avatar, so we don't have to do anything */ + goodinfo = NULL; + break; + } + /* We'll only pick the png one for now. It's a very nice image format anyways. */ + if(type && id && !goodinfo && !strcmp(type, "image/png")) + goodinfo = info; + } + } + if(goodinfo) { + const char *url = xmlnode_get_attrib(goodinfo,"url"); + const char *id = xmlnode_get_attrib(goodinfo,"id"); + + /* the avatar might either be stored in a pep node, or on a HTTP/HTTPS URL */ + if(!url) + jabber_pep_request_item(js, from, AVATARNAMESPACEDATA, id, do_buddy_avatar_update_data); + else { + JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1); + info->js = js; + info->from = g_strdup(from); + info->id = g_strdup(id); + purple_util_fetch_url(url, TRUE, NULL, TRUE, do_buddy_avatar_update_fromurl, info); + } + } + } } static void jabber_buddy_info_resource_free(gpointer data) diff -r 7754d39d70c5 -r 5ab3c6bb95b4 libpurple/protocols/jabber/buddy.h --- a/libpurple/protocols/jabber/buddy.h Sun Jun 17 01:16:55 2007 +0000 +++ b/libpurple/protocols/jabber/buddy.h Sun Jun 17 03:51:19 2007 +0000 @@ -25,6 +25,15 @@ #include "jabber.h" #include "usermood.h" +/* for some reason, Psi uses the first one instead of the second one as defined in the XEP */ +#ifndef USE_XEP_0084 +#define AVATARNAMESPACEDATA "http://jabber.org/protocol/avatar#data" +#define AVATARNAMESPACEMETA "http://jabber.org/protocol/avatar#metadata" +#else +#define AVATARNAMESPACEDATA "http://www.xmpp.org/extensions/xep-0084.html#ns-data" +#define AVATARNAMESPACEMETA "http://www.xmpp.org/extensions/xep-0084.html#ns-metadata" +#endif + typedef enum { JABBER_BUDDY_STATE_UNKNOWN = -2, JABBER_BUDDY_STATE_ERROR = -1, diff -r 7754d39d70c5 -r 5ab3c6bb95b4 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Sun Jun 17 01:16:55 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sun Jun 17 03:51:19 2007 +0000 @@ -1966,8 +1966,4 @@ jabber_init_plugin(PurplePlugin *plugin) { my_protocol = plugin; - jabber_add_feature("avatarmeta", "http://www.xmpp.org/extensions/xep-0084.html#ns-metadata", jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_add_feature("avatardata", "http://www.xmpp.org/extensions/xep-0084.html#ns-data", jabber_pep_namespace_only_when_pep_enabled_cb); - - jabber_pep_register_handler("avatar", "http://www.xmpp.org/extensions/xep-0084.html#ns-data", jabber_buddy_avatar_update_metadata); } diff -r 7754d39d70c5 -r 5ab3c6bb95b4 libpurple/protocols/jabber/libxmpp.c --- a/libpurple/protocols/jabber/libxmpp.c Sun Jun 17 01:16:55 2007 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Sun Jun 17 03:51:19 2007 +0000 @@ -231,6 +231,11 @@ jabber_iq_init(); jabber_pep_init(); + + jabber_add_feature("avatarnonxepmeta", AVATARNAMESPACEMETA, jabber_pep_namespace_only_when_pep_enabled_cb); + jabber_add_feature("avatarnonxepdata", AVATARNAMESPACEDATA, jabber_pep_namespace_only_when_pep_enabled_cb); + + jabber_pep_register_handler("avatarnonxep", AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata); } diff -r 7754d39d70c5 -r 5ab3c6bb95b4 libpurple/protocols/jabber/pep.c --- a/libpurple/protocols/jabber/pep.c Sun Jun 17 01:16:55 2007 +0000 +++ b/libpurple/protocols/jabber/pep.c Sun Jun 17 03:51:19 2007 +0000 @@ -46,6 +46,46 @@ g_hash_table_replace(pep_handlers, g_strdup(xmlns), handlerfunc); } +static void do_pep_iq_request_item_callback(JabberStream *js, xmlnode *packet, gpointer data) { + const char *from = xmlnode_get_attrib(packet,"from"); + xmlnode *pubsub = xmlnode_get_child_with_namespace(packet,"pubsub","http://jabber.org/protocol/pubsub#event"); + xmlnode *item = NULL; + JabberPEPHandler *cb = data; + + if(pubsub) { + item = xmlnode_get_child(pubsub, "item"); + if(!item) { + /* does not follow the spec, but the ejabberd PEP implementation behaves that way */ + xmlnode *items = xmlnode_get_child(pubsub, "items"); + if(items) + item = xmlnode_get_child(items, "item"); + } + } + + cb(js, from, item); +} + +void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb) { + JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); + xmlnode *pubsub, *items, *item; + + xmlnode_set_attrib(iq->node,"to",to); + pubsub = xmlnode_new_child(iq->node,"pubsub"); + + xmlnode_set_namespace(pubsub,"http://jabber.org/protocol/pubsub"); + + items = xmlnode_new_child(pubsub, "items"); + xmlnode_set_attrib(items,"node",node); + + item = xmlnode_new_child(items, "item"); + if(id) + xmlnode_set_attrib(item, "id", id); + + jabber_iq_set_callback(iq,do_pep_iq_request_item_callback,(gpointer)cb); + + jabber_iq_send(iq); +} + gboolean jabber_pep_namespace_only_when_pep_enabled_cb(JabberStream *js, const gchar *shortname, const gchar *namespace) { return js->pep; } diff -r 7754d39d70c5 -r 5ab3c6bb95b4 libpurple/protocols/jabber/pep.h --- a/libpurple/protocols/jabber/pep.h Sun Jun 17 01:16:55 2007 +0000 +++ b/libpurple/protocols/jabber/pep.h Sun Jun 17 03:51:19 2007 +0000 @@ -49,6 +49,20 @@ void jabber_pep_register_handler(const char *shortname, const char *xmlns, JabberPEPHandler handlerfunc); /* + * Request a specific item from another PEP node. + * + * @parameter js The JabberStream that should be used + * @parameter to The target PEP node + * @parameter node The node name of the item that is requested + * @parameter id The item id of the requested item (may be NULL) + * @parameter cb The callback to be used when this item is received + * + * ATTN: The items parameter of the callback will only point to the single element here! + * Additionally, the items element passed will be NULL if any error occured (like a permission error, node doesn't exist etc.) + */ +void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb); + +/* * 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