changeset 25815:5dd25c58b65e

Migrate the XMPP User Avatar (XEP-0084) code to its own file I also slightly rearranged the ordering of jabber_avatar_set to make the error cases slightly more apparent.
author Paul Aurich <paul@darkrain42.org>
date Wed, 21 Jan 2009 17:55:09 +0000
parents 7f9b8351a6b4
children b68ac693ae2d
files libpurple/protocols/jabber/Makefile.am libpurple/protocols/jabber/buddy.c libpurple/protocols/jabber/buddy.h libpurple/protocols/jabber/libxmpp.c libpurple/protocols/jabber/pep.c libpurple/protocols/jabber/useravatar.c libpurple/protocols/jabber/useravatar.h
diffstat 7 files changed, 330 insertions(+), 240 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/Makefile.am	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/Makefile.am	Wed Jan 21 17:55:09 2009 +0000
@@ -45,6 +45,8 @@
 			  adhoccommands.h \
 			  pep.c \
 			  pep.h \
+			  useravatar.c \
+			  useravatar.h \
 			  usermood.c \
 			  usermood.h \
 			  usernick.c \
--- a/libpurple/protocols/jabber/buddy.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Wed Jan 21 17:55:09 2009 +0000
@@ -32,12 +32,11 @@
 #include "jabber.h"
 #include "iq.h"
 #include "presence.h"
+#include "useravatar.h"
 #include "xdata.h"
 #include "pep.h"
 #include "adhoccommands.h"
 
-#define MAX_HTTP_BUDDYICON_BYTES (200 * 1024)
-
 typedef struct {
 	long idle_seconds;
 } JabberBuddyInfoResource;
@@ -496,123 +495,13 @@
 	PurplePresence *gpresence;
 	PurpleStatus *status;
 	
-	if(((JabberStream*)gc->proto_data)->pep) {
-		/* XEP-0084: User Avatars */
-		if(img) {
-			/*
-			 * TODO: This is pretty gross.  The Jabber PRPL really shouldn't
-			 *       do voodoo to try to determine the image type, height
-			 *       and width.
-			 */
-			/* A PNG header, including the IHDR, but nothing else */
-			const struct {
-				guchar signature[8]; /* must be hex 89 50 4E 47 0D 0A 1A 0A */
-				struct {
-					guint32 length; /* must be 0x0d */
-					guchar type[4]; /* must be 'I' 'H' 'D' 'R' */
-					guint32 width;
-					guint32 height;
-					guchar bitdepth;
-					guchar colortype;
-					guchar compression;
-					guchar filter;
-					guchar interlace;
-				} ihdr;
-			} *png = purple_imgstore_get_data(img); /* ATTN: this is in network byte order! */
-
-			/* check if the data is a valid png file (well, at least to some extend) */
-			if(png->signature[0] == 0x89 &&
-			   png->signature[1] == 0x50 &&
-			   png->signature[2] == 0x4e &&
-			   png->signature[3] == 0x47 &&
-			   png->signature[4] == 0x0d &&
-			   png->signature[5] == 0x0a &&
-			   png->signature[6] == 0x1a &&
-			   png->signature[7] == 0x0a &&
-			   ntohl(png->ihdr.length) == 0x0d &&
-			   png->ihdr.type[0] == 'I' &&
-			   png->ihdr.type[1] == 'H' &&
-			   png->ihdr.type[2] == 'D' &&
-			   png->ihdr.type[3] == 'R') {
-				/* parse PNG header to get the size of the image (yes, this is required) */
-				guint32 width = ntohl(png->ihdr.width);
-				guint32 height = ntohl(png->ihdr.height);
-				xmlnode *publish, *item, *data, *metadata, *info;
-				char *lengthstring, *widthstring, *heightstring;
-				
-				/* compute the sha1 hash */
-				char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
-				char *base64avatar;
-				
-				publish = xmlnode_new("publish");
-				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,AVATARNAMESPACEDATA);
-				
-				base64avatar = purple_base64_encode(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
-				xmlnode_insert_data(data,base64avatar,-1);
-				g_free(base64avatar);
-				
-				/* publish the avatar itself */
-				jabber_pep_publish((JabberStream*)gc->proto_data, publish);
-				
-				/* next step: publish the metadata */
-				publish = xmlnode_new("publish");
-				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,AVATARNAMESPACEMETA);
-				
-				info = xmlnode_new_child(metadata, "info");
-				xmlnode_set_attrib(info, "id", hash);
-				xmlnode_set_attrib(info, "type", "image/png");
-				lengthstring = g_strdup_printf("%u", (unsigned)purple_imgstore_get_size(img));
-				xmlnode_set_attrib(info, "bytes", lengthstring);
-				g_free(lengthstring);
-				widthstring = g_strdup_printf("%u", width);
-				xmlnode_set_attrib(info, "width", widthstring);
-				g_free(widthstring);
-				heightstring = g_strdup_printf("%u", height);
-				xmlnode_set_attrib(info, "height", heightstring);
-				g_free(heightstring);
-				
-				/* publish the metadata */
-				jabber_pep_publish((JabberStream*)gc->proto_data, publish);
-				
-				g_free(hash);
-			} else {
-				purple_debug_error("jabber", "jabber_set_buddy_icon received non-png data");
-			}
-		} else {
-			/* remove the metadata */
-			xmlnode *metadata, *item;
-			xmlnode *publish = xmlnode_new("publish");
-			xmlnode_set_attrib(publish,"node",AVATARNAMESPACEMETA);
-
-			item = xmlnode_new_child(publish, "item");
-
-			metadata = xmlnode_new_child(item, "metadata");
-			xmlnode_set_namespace(metadata,AVATARNAMESPACEMETA);
-
-			xmlnode_new_child(metadata, "stop");
-
-			/* publish the metadata */
-			jabber_pep_publish((JabberStream*)gc->proto_data, publish);
-		}
-	}
-
+	jabber_avatar_set(gc->proto_data, img);
 	/* vCard avatars do not have an image type requirement so update our
 	 * vCard avatar regardless of image type for those poor older clients
 	 */
 	jabber_set_info(gc, purple_account_get_user_info(gc->account));
 
+	/* TODO: Call this in jabber_set_info() if avatar changed? */
 	gpresence = purple_account_get_presence(gc->account);
 	status = purple_presence_get_active_status(gpresence);
 	jabber_presence_send(gc->account, status);
@@ -1427,127 +1316,6 @@
 	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 *items) {
-	xmlnode *item, *data;
-	const char *checksum;
-	char *b64data;
-	void *img;
-	size_t size;
-	if(!items)
-		return;
-	
-	item = xmlnode_get_child(items, "item");
-	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) {
-		g_free(b64data);
-		return;
-	}
-	
-	purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum);
-	g_free(b64data);
-}
-
-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;
-		gboolean has_children = FALSE;
-
-		/* 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)
-				has_children = TRUE;
-			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(has_children == FALSE) {
-			purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL);
-		} else 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 {
-				PurpleUtilFetchUrlData *url_data;
-				JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1);
-				info->js = js;
-
-				url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE,
-										  MAX_HTTP_BUDDYICON_BYTES,
-										  do_buddy_avatar_update_fromurl, info);
-				if (url_data) {
-					info->from = g_strdup(from);
-					info->id = g_strdup(id);
-					js->url_datas = g_slist_prepend(js->url_datas, url_data);
-				} else
-					g_free(info);
-
-			}
-		}
-	}
-}
-
 static void jabber_buddy_info_resource_free(gpointer data)
 {
 	JabberBuddyInfoResource *jbri = data;
--- a/libpurple/protocols/jabber/buddy.h	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.h	Wed Jan 21 17:55:09 2009 +0000
@@ -102,7 +102,6 @@
 void jabber_set_info(PurpleConnection *gc, const char *info);
 void jabber_setup_set_info(PurplePluginAction *action);
 void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
-void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items);
 
 const char *jabber_buddy_state_get_name(JabberBuddyState state);
 const char *jabber_buddy_state_get_status_id(JabberBuddyState state);
--- a/libpurple/protocols/jabber/libxmpp.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Wed Jan 21 17:55:09 2009 +0000
@@ -281,14 +281,10 @@
 	
 	jabber_data_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);
 	jabber_add_feature("bob", XEP_0231_NAMESPACE,
 					   jabber_custom_smileys_isenabled);
-
-	jabber_pep_register_handler("avatar", AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata);
 }
 
 
--- a/libpurple/protocols/jabber/pep.c	Tue Jan 20 19:38:53 2009 +0000
+++ b/libpurple/protocols/jabber/pep.c	Wed Jan 21 17:55:09 2009 +0000
@@ -24,6 +24,7 @@
 #include "pep.h"
 #include "iq.h"
 #include <string.h>
+#include "useravatar.h"
 #include "usermood.h"
 #include "usernick.h"
 
@@ -34,6 +35,7 @@
 		pep_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
 		
 		/* register PEP handlers */
+		jabber_avatar_init();
 		jabber_mood_init();
 		jabber_nick_init();
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/useravatar.c	Wed Jan 21 17:55:09 2009 +0000
@@ -0,0 +1,289 @@
+/*
+ * purple - Jabber Protocol Plugin
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA	 02111-1307	 USA
+ *
+ */
+
+#include "internal.h"
+
+#include "useravatar.h"
+#include "pep.h"
+#include "debug.h"
+
+#define MAX_HTTP_BUDDYICON_BYTES (200 * 1024)
+
+static void update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items);
+
+void jabber_avatar_init(void)
+{
+	jabber_add_feature("avatarmeta", AVATARNAMESPACEMETA,
+	                   jabber_pep_namespace_only_when_pep_enabled_cb);
+	jabber_add_feature("avatardata", AVATARNAMESPACEMETA,
+	                   jabber_pep_namespace_only_when_pep_enabled_cb);
+
+	jabber_pep_register_handler("avatar", AVATARNAMESPACEMETA,
+	                            update_buddy_metadata);
+}
+
+void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img)
+{
+	xmlnode *publish, *metadata, *item;
+
+	if (!js->pep)
+		return;
+
+	if (!img) {
+		/* remove the metadata */
+		publish = xmlnode_new("publish");
+		xmlnode_set_attrib(publish, "node", AVATARNAMESPACEMETA);
+
+		item = xmlnode_new_child(publish, "item");
+		metadata = xmlnode_new_child(item, "metadata");
+		xmlnode_set_namespace(metadata, AVATARNAMESPACEMETA);
+
+		xmlnode_new_child(metadata, "stop");
+		/* publish */
+		jabber_pep_publish(js, publish);
+	} else {
+		/*
+		 * TODO: This is pretty gross.  The Jabber PRPL really shouldn't
+		 *       do voodoo to try to determine the image type, height
+		 *       and width.
+		 */
+		/* A PNG header, including the IHDR, but nothing else */
+		const struct {
+			guchar signature[8]; /* must be hex 89 50 4E 47 0D 0A 1A 0A */
+			struct {
+				guint32 length; /* must be 0x0d */
+				guchar type[4]; /* must be 'I' 'H' 'D' 'R' */
+				guint32 width;
+				guint32 height;
+				guchar bitdepth;
+				guchar colortype;
+				guchar compression;
+				guchar filter;
+				guchar interlace;
+			} ihdr;
+		} *png = purple_imgstore_get_data(img); /* ATTN: this is in network byte order! */
+
+		/* check if the data is a valid png file (well, at least to some extend) */
+		if(png->signature[0] == 0x89 &&
+		   png->signature[1] == 0x50 &&
+		   png->signature[2] == 0x4e &&
+		   png->signature[3] == 0x47 &&
+		   png->signature[4] == 0x0d &&
+		   png->signature[5] == 0x0a &&
+		   png->signature[6] == 0x1a &&
+		   png->signature[7] == 0x0a &&
+		   ntohl(png->ihdr.length) == 0x0d &&
+		   png->ihdr.type[0] == 'I' &&
+		   png->ihdr.type[1] == 'H' &&
+		   png->ihdr.type[2] == 'D' &&
+		   png->ihdr.type[3] == 'R') {
+			/* parse PNG header to get the size of the image (yes, this is required) */
+			guint32 width = ntohl(png->ihdr.width);
+			guint32 height = ntohl(png->ihdr.height);
+			xmlnode *data, *info;
+			char *lengthstring, *widthstring, *heightstring;
+
+			/* compute the sha1 hash */
+			char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
+			char *base64avatar;
+
+			publish = xmlnode_new("publish");
+			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, AVATARNAMESPACEDATA);
+
+			base64avatar = purple_base64_encode(purple_imgstore_get_data(img),
+			                                    purple_imgstore_get_size(img));
+			xmlnode_insert_data(data,base64avatar,-1);
+			g_free(base64avatar);
+
+			/* publish the avatar itself */
+			jabber_pep_publish(js, publish);
+
+			/* next step: publish the metadata */
+			publish = xmlnode_new("publish");
+			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, AVATARNAMESPACEMETA);
+
+			lengthstring = g_strdup_printf("%u", (unsigned)purple_imgstore_get_size(img));
+			widthstring = g_strdup_printf("%u", width);
+			heightstring = g_strdup_printf("%u", height);
+
+			info = xmlnode_new_child(metadata, "info");
+			xmlnode_set_attrib(info, "id", hash);
+			xmlnode_set_attrib(info, "type", "image/png");
+			xmlnode_set_attrib(info, "bytes", lengthstring);
+			xmlnode_set_attrib(info, "width", widthstring);
+			xmlnode_set_attrib(info, "height", heightstring);
+			g_free(lengthstring);
+			g_free(widthstring);
+			g_free(heightstring);
+
+			/* publish the metadata */
+			jabber_pep_publish(js, publish);
+
+			g_free(hash);
+		} else {
+			purple_debug_error("jabber", "Cannot set PEP avatar to non-PNG data\n");
+		}
+	}
+}
+
+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 *items)
+{
+	xmlnode *item, *data;
+	const char *checksum;
+	char *b64data;
+	void *img;
+	size_t size;
+	if(!items)
+		return;
+	
+	item = xmlnode_get_child(items, "item");
+	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) {
+		g_free(b64data);
+		return;
+	}
+	
+	purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum);
+	g_free(b64data);
+}
+
+static void
+update_buddy_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;
+		gboolean has_children = FALSE;
+
+		/* 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)
+				has_children = TRUE;
+			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(has_children == FALSE) {
+			purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL);
+		} else 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(S) URL */
+			if(!url)
+				jabber_pep_request_item(js, from, AVATARNAMESPACEDATA, id, do_buddy_avatar_update_data);
+			else {
+				PurpleUtilFetchUrlData *url_data;
+				JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1);
+				info->js = js;
+
+				url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE,
+										  MAX_HTTP_BUDDYICON_BYTES,
+										  do_buddy_avatar_update_fromurl, info);
+				if (url_data) {
+					info->from = g_strdup(from);
+					info->id = g_strdup(id);
+					js->url_datas = g_slist_prepend(js->url_datas, url_data);
+				} else
+					g_free(info);
+
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/useravatar.h	Wed Jan 21 17:55:09 2009 +0000
@@ -0,0 +1,34 @@
+/*
+ * purple - Jabber Protocol Plugin
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA	 02111-1307	 USA
+ *
+ */
+
+#ifndef _PURPLE_JABBER_USERAVATAR_H_
+#define _PURPLE_JABBER_USERAVATAR_H_
+
+#include "jabber.h"
+
+/* Implementation of XEP-0084 */
+
+void jabber_avatar_init(void);
+void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img);
+
+#endif /* _PURPLE_JABBER_USERAVATAR_H_ */