changeset 17853:7754d39d70c5

Added support for setting the avatar via XEP-0084. Receiving other people's avatar is up next.
author Andreas Monitzer <pidgin@monitzer.com>
date Sun, 17 Jun 2007 01:16:55 +0000
parents af833a3204bb
children 5ab3c6bb95b4
files libpurple/protocols/jabber/buddy.c libpurple/protocols/jabber/buddy.h libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/libxmpp.c libpurple/protocols/jabber/pep.c libpurple/protocols/jabber/pep.h libpurple/protocols/jabber/usermood.c
diffstat 7 files changed, 151 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/buddy.c	Fri Jun 15 21:09:22 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Sun Jun 17 01:16:55 2007 +0000
@@ -34,6 +34,7 @@
 #include "iq.h"
 #include "presence.h"
 #include "xdata.h"
+#include "pep.h"
 
 typedef struct {
 	long idle_seconds;
@@ -406,7 +407,7 @@
 		if ((img = purple_buddy_icons_find_account_icon(gc->account))) {
 			gconstpointer avatar_data;
 			gsize avatar_len;
-			xmlnode *photo, *binval;
+			xmlnode *photo, *binval, *type;
 			gchar *enc;
 			int i;
 			unsigned char hashval[20];
@@ -415,6 +416,8 @@
 			avatar_data = purple_imgstore_get_data(img);
 			avatar_len = purple_imgstore_get_size(img);
 			photo = xmlnode_new_child(vc_node, "PHOTO");
+			type = xmlnode_new_child(photo, "TYPE");
+			xmlnode_insert_data(type, "image/png", -1);
 			binval = xmlnode_new_child(photo, "BINVAL");
 			enc = purple_base64_encode(avatar_data, avatar_len);
 
@@ -445,7 +448,128 @@
 {
 	PurplePresence *gpresence;
 	PurpleStatus *status;
+	
+	if(((JabberStream*)gc->proto_data)->pep) {
+		/* XEP-0084: User Avatars */
+		if(img) {
+			/* 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 */
+				PurpleCipherContext *ctx;
+				unsigned char digest[20];
+				char *hash;
+				char *base64avatar;
+				
+				ctx = purple_cipher_context_new_by_name("sha1", NULL);
+				purple_cipher_context_append(ctx, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
+				purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
+				
+				/* convert digest to a string */
+				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");
+				
+				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");
+				
+				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","http://www.xmpp.org/extensions/xep-0084.html#ns-metadata");
+				
+				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");
+				
+				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(lengthstring);
+				
+				/* publish the metadata */
+				jabber_pep_publish((JabberStream*)gc->proto_data, publish);
+				
+				g_free(hash);
+			} else { /* if(img) */
+				/* 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");
+				
+				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_new_child(metadata, "stop");
+				
+				/* publish the metadata */
+				jabber_pep_publish((JabberStream*)gc->proto_data, publish);
+			}
+		} else {
+			purple_debug(PURPLE_DEBUG_ERROR, "jabber",
+						 "jabber_set_buddy_icon received non-png data");
+		}
+	}
+
+	/* even when the image is not png, we can still publish the vCard, since this
+	   one doesn't require a specific image type */
+
+	/* publish vCard for those poor older clients */
 	jabber_set_info(gc, purple_account_get_user_info(gc->account));
 
 	gpresence = purple_account_get_presence(gc->account);
@@ -985,6 +1109,9 @@
 	jabber_buddy_info_show_if_ready(jbi);
 }
 
+void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items) {
+	
+}
 
 static void jabber_buddy_info_resource_free(gpointer data)
 {
--- a/libpurple/protocols/jabber/buddy.h	Fri Jun 15 21:09:22 2007 +0000
+++ b/libpurple/protocols/jabber/buddy.h	Sun Jun 17 01:16:55 2007 +0000
@@ -93,6 +93,7 @@
 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/jabber.c	Fri Jun 15 21:09:22 2007 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sun Jun 17 01:16:55 2007 +0000
@@ -1965,5 +1965,9 @@
 void
 jabber_init_plugin(PurplePlugin *plugin)
 {
-		my_protocol = 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);
 }
--- a/libpurple/protocols/jabber/libxmpp.c	Fri Jun 15 21:09:22 2007 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Sun Jun 17 01:16:55 2007 +0000
@@ -50,7 +50,7 @@
 #endif
 	NULL,							/* user_splits */
 	NULL,							/* protocol_options */
-	{"png,gif,jpeg", 32, 32, 96, 96, 8191, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
+	{"png", 32, 32, 96, 96, 8191, PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
 	jabber_list_icon,				/* list_icon */
 	jabber_list_emblem,			/* list_emblems */
 	jabber_status_text,				/* status_text */
--- a/libpurple/protocols/jabber/pep.c	Fri Jun 15 21:09:22 2007 +0000
+++ b/libpurple/protocols/jabber/pep.c	Sun Jun 17 01:16:55 2007 +0000
@@ -46,6 +46,10 @@
 	g_hash_table_replace(pep_handlers, g_strdup(xmlns), handlerfunc);
 }
 
+gboolean jabber_pep_namespace_only_when_pep_enabled_cb(JabberStream *js, const gchar *shortname, const gchar *namespace) {
+	return js->pep;
+}
+
 void jabber_handle_event(JabberMessage *jm) {
 	/* this may be called even when the own server doesn't support pep! */
 	JabberPEPHandler *jph;
--- a/libpurple/protocols/jabber/pep.h	Fri Jun 15 21:09:22 2007 +0000
+++ b/libpurple/protocols/jabber/pep.h	Sun Jun 17 01:16:55 2007 +0000
@@ -48,6 +48,17 @@
  */
 void jabber_pep_register_handler(const char *shortname, const char *xmlns, JabberPEPHandler handlerfunc);
 
+/*
+ * 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 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);
+
 void jabber_handle_event(JabberMessage *jm);
 
 /*
--- a/libpurple/protocols/jabber/usermood.c	Fri Jun 15 21:09:22 2007 +0000
+++ b/libpurple/protocols/jabber/usermood.c	Sun Jun 17 01:16:55 2007 +0000
@@ -134,12 +134,8 @@
 		g_free(moodtext);
 }
 
-static gboolean is_mood_supported(JabberStream *js, const gchar *shortname, const gchar *namespace) {
-	return js->pep;
-}
-
 void jabber_mood_init(void) {
-	jabber_add_feature("mood", "http://jabber.org/protocol/mood", is_mood_supported);
+	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);
 }