changeset 10189:4e7249591251

[gaim-migrate @ 11304] this would appear to be jabber buddy icon support no, this isn't a standard yet, but those nice folks over at apple decided to put jabber into iChat, and this is how they do things committer: Tailor Script <tailor@pidgin.im>
author Nathan Walp <nwalp@pidgin.im>
date Mon, 15 Nov 2004 06:15:53 +0000
parents 5709800d1dee
children e67ecadcbe0f
files src/protocols/jabber/buddy.c src/protocols/jabber/buddy.h src/protocols/jabber/jabber.c src/protocols/jabber/jabber.h src/protocols/jabber/presence.c
diffstat 5 files changed, 179 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/jabber/buddy.c	Mon Nov 15 03:53:56 2004 +0000
+++ b/src/protocols/jabber/buddy.c	Mon Nov 15 06:15:53 2004 +0000
@@ -32,6 +32,7 @@
 #include "jabber.h"
 #include "iq.h"
 #include "presence.h"
+#include "sha.h"
 
 
 void jabber_buddy_free(JabberBuddy *jb)
@@ -360,16 +361,51 @@
 	JabberIq *iq;
 	JabberStream *js = gc->proto_data;
 	xmlnode *vc_node;
+	const char *avatar_file = NULL;
 
+	if(js->avatar_hash)
+		g_free(js->avatar_hash);
+	js->avatar_hash = NULL;
 
 	/*
 	 * Send only if there's actually any *information* to send
 	 */
 	vc_node = xmlnode_from_str(info, -1);
+	avatar_file = gaim_account_get_buddy_icon(gc->account);
+
+	if(!vc_node && avatar_file) {
+		vc_node = xmlnode_new("vCard");
+	}
 
 	if(vc_node) {
 		if (vc_node->name &&
-				!g_ascii_strncasecmp(vc_node->name, "vcard", 5)) {
+				!g_ascii_strncasecmp(vc_node->name, "vCard", 5)) {
+			GError *error = NULL;
+			char *avatar_data;
+			gsize avatar_len;
+
+			if(avatar_file && g_file_get_contents(avatar_file, &avatar_data, &avatar_len, &error)) {
+				xmlnode *photo;
+				char *enc;
+				int i;
+				unsigned char hashval[20];
+				char *p, hash[41];
+
+				photo = xmlnode_new_child(vc_node, "PHOTO");
+				enc = gaim_base64_encode(avatar_data, avatar_len);
+				shaBlock((unsigned char *)avatar_data, avatar_len, hashval);
+				p = hash;
+				for(i=0; i<20; i++, p+=2)
+					snprintf(p, 3, "%02x", hashval[i]);
+				js->avatar_hash = g_strdup(hash);
+
+				xmlnode_insert_data(photo, enc, -1);
+				g_free(enc);
+				g_free(avatar_data);
+			} else {
+				g_error_free(error);
+			}
+
 			iq = jabber_iq_new(js, JABBER_IQ_SET);
 			xmlnode_insert_child(iq->node, vc_node);
 			jabber_iq_send(iq);
@@ -379,6 +415,18 @@
 	}
 }
 
+void jabber_set_buddy_icon(GaimConnection *gc, const char *iconfile)
+{
+	GaimPresence *gpresence;
+	GaimStatus *status;
+
+	jabber_set_info(gc, gaim_account_get_user_info(gc->account));
+
+	gpresence = gaim_account_get_presence(gc->account);
+	status = gaim_presence_get_active_status(gpresence);
+	jabber_presence_send(gc, status);
+}
+
 /*
  * This is the callback from the "ok clicked" for "set vCard"
  *
@@ -540,6 +588,7 @@
 	char *text;
 	xmlnode *vcard;
 	GaimBuddy *b;
+	GSList *imgids = NULL;
 
 	if(!from)
 		return;
@@ -589,9 +638,9 @@
 	}
 
 	g_free(resource_name);
-	g_free(bare_jid);
 
-	if((vcard = xmlnode_get_child(packet, "vCard"))) {
+	if((vcard = xmlnode_get_child(packet, "vCard")) ||
+			(vcard = xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) {
 		xmlnode *child;
 		for(child = vcard->child; child; child = child->next)
 		{
@@ -750,23 +799,29 @@
 						_("Description"), text);
 			} else if(!strcmp(child->name, "PHOTO") ||
 					!strcmp(child->name, "LOGO")) {
-				if((child2 = xmlnode_get_child(child, "BINVAL"))) {
-					char *data, *text2;
-					int size, imgid;
-					if((text2 = xmlnode_get_data(child2))) {
-						gaim_base64_decode(text2, &data, &size);
+				int size, i;
+				unsigned char hashval[20];
+				char *data, *p, hash[41];
+				gboolean photo = (strcmp(child->name, "PHOTO") == 0);
+
+				gaim_base64_decode(text, &data, &size);
 
-						imgid = gaim_imgstore_add(data, size, "logo.png");
-						g_string_append_printf(info_text,
-								"<b>%s:</b> <img id='%d'><br/>",
-								strcmp(child->name, "PHOTO")  == 0 ?
-								_("Photo") : _("Logo"),
-								imgid);
+				imgids = g_slist_prepend(imgids, GINT_TO_POINTER(gaim_imgstore_add(data, size, "logo.png")));
+				g_string_append_printf(info_text,
+						"<b>%s:</b> <img id='%d'><br/>",
+						photo ? _("Photo") : _("Logo"),
+						GPOINTER_TO_INT(imgids->data));
 
-						g_free(data);
-						g_free(text2);
-					}
-				}
+				gaim_buddy_icons_set_for_user(js->gc->account, bare_jid,
+						data, size);
+
+				shaBlock((unsigned char *)data, size, hashval);
+				p = hash;
+				for(i=0; i<20; i++, p+=2)
+					snprintf(p, 3, "%02x", hashval[i]);
+				gaim_blist_node_set_string((GaimBlistNode*)b, "avatar_hash", hash);
+
+				g_free(data);
 			}
 			g_free(text);
 		}
@@ -779,20 +834,24 @@
 	gaim_notify_userinfo(js->gc, from, title, _("Jabber Profile"),
 			NULL, text, NULL, NULL);
 
+	while(imgids) {
+		gaim_imgstore_unref(GPOINTER_TO_INT(imgids->data));
+		imgids = g_slist_delete_link(imgids, imgids);
+	}
 	g_free(title);
 	g_string_free(info_text, TRUE);
 	g_free(text);
+	g_free(bare_jid);
 }
 
-void jabber_buddy_get_info(GaimConnection *gc, const char *who)
+static void jabber_buddy_get_info_for_jid(JabberStream *js, const char *full_jid)
 {
-	JabberStream *js = gc->proto_data;
 	JabberIq *iq;
 	xmlnode *vcard;
 
 	iq = jabber_iq_new(js, JABBER_IQ_GET);
 
-	xmlnode_set_attrib(iq->node, "to", who);
+	xmlnode_set_attrib(iq->node, "to", full_jid);
 	vcard = xmlnode_new_child(iq->node, "vCard");
 	xmlnode_set_attrib(vcard, "xmlns", "vcard-temp");
 
@@ -801,6 +860,17 @@
 	jabber_iq_send(iq);
 }
 
+void jabber_buddy_get_info(GaimConnection *gc, const char *who)
+{
+	JabberStream *js = gc->proto_data;
+	char *bare_jid = jabber_get_bare_jid(who);
+
+	if(bare_jid) {
+		jabber_buddy_get_info_for_jid(js, bare_jid);
+		g_free(bare_jid);
+	}
+}
+
 void jabber_buddy_get_info_chat(GaimConnection *gc, int id,
 		const char *resource)
 {
@@ -812,7 +882,7 @@
 		return;
 
 	full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, resource);
-	jabber_buddy_get_info(gc, full_jid);
+	jabber_buddy_get_info_for_jid(js, full_jid);
 	g_free(full_jid);
 }
 
--- a/src/protocols/jabber/buddy.h	Mon Nov 15 03:53:56 2004 +0000
+++ b/src/protocols/jabber/buddy.h	Mon Nov 15 06:15:53 2004 +0000
@@ -81,6 +81,7 @@
 
 void jabber_set_info(GaimConnection *gc, const char *info);
 void jabber_setup_set_info(GaimPluginAction *action);
+void jabber_set_buddy_icon(GaimConnection *gc, const char *iconfile);
 
 const char *jabber_buddy_state_get_name(JabberBuddyState state);
 const char *jabber_buddy_state_get_status_id(JabberBuddyState state);
--- a/src/protocols/jabber/jabber.c	Mon Nov 15 03:53:56 2004 +0000
+++ b/src/protocols/jabber/jabber.c	Mon Nov 15 06:15:53 2004 +0000
@@ -771,6 +771,8 @@
 		g_free(js->stream_id);
 	if(js->user)
 		jabber_id_free(js->user);
+	if(js->avatar_hash)
+		g_free(js->avatar_hash);
 	g_free(js);
 }
 
@@ -1511,7 +1513,7 @@
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME,
 	NULL,							/* user_splits */
 	NULL,							/* protocol_options */
-	NO_BUDDY_ICONS,					/* icon_spec */
+	{"jpeg,gif,png", 0, 0, 96, 96, GAIM_ICON_SCALE_DISPLAY}, /* icon_spec */
 	jabber_list_icon,				/* list_icon */
 	jabber_list_emblems,			/* list_emblems */
 	jabber_status_text,				/* status_text */
@@ -1556,7 +1558,7 @@
 	NULL,							/* buddy_free */
 	jabber_convo_closed,			/* convo_closed */
 	jabber_normalize,				/* normalize */
-	NULL,							/* set_buddy_icon */
+	jabber_set_buddy_icon,			/* set_buddy_icon */
 	NULL,							/* remove_group */
 	jabber_chat_buddy_real_name,	/* get_cb_real_name */
 	jabber_chat_set_topic,			/* set_chat_topic */
--- a/src/protocols/jabber/jabber.h	Mon Nov 15 03:53:56 2004 +0000
+++ b/src/protocols/jabber/jabber.h	Mon Nov 15 06:15:53 2004 +0000
@@ -95,6 +95,8 @@
 	GaimSslConnection *gsc;
 
 	gboolean registration;
+
+	char *avatar_hash;
 } JabberStream;
 
 void jabber_process_packet(JabberStream *js, xmlnode *packet);
--- a/src/protocols/jabber/presence.c	Mon Nov 15 03:53:56 2004 +0000
+++ b/src/protocols/jabber/presence.c	Mon Nov 15 06:15:53 2004 +0000
@@ -32,6 +32,7 @@
 #include "presence.h"
 #include "iq.h"
 #include "jutil.h"
+#include "sha.h"
 #include "xmlnode.h"
 
 
@@ -84,7 +85,7 @@
 void jabber_presence_send(GaimConnection *gc, GaimStatus *status)
 {
 	JabberStream *js = gc->proto_data;
-	xmlnode *presence;
+	xmlnode *presence, *x, *photo;
 	char *stripped = NULL;
 	const char *msg;
 	JabberBuddyState state;
@@ -99,6 +100,14 @@
 	g_free(stripped);
 
 	jabber_send(js, presence);
+
+	if(js->avatar_hash) {
+		x = xmlnode_new_child(presence, "x");
+		xmlnode_set_attrib(x, "xmlns", "vcard-temp:x:update");
+		photo = xmlnode_new_child(x, "photo");
+		xmlnode_insert_data(photo, js->avatar_hash, -1);
+	}
+
 	g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence);
 	xmlnode_free(presence);
 
@@ -165,6 +174,40 @@
 	g_free(jap);
 }
 
+static void jabber_vcard_parse_avatar(JabberStream *js, xmlnode *packet, gpointer blah)
+{
+	GaimBuddy *b = NULL;
+	xmlnode *vcard, *photo;
+	char *text, *data;
+	int size;
+	const char *from = xmlnode_get_attrib(packet, "from");
+
+	if(!from)
+		return;
+
+	if((vcard = xmlnode_get_child(packet, "vCard")) ||
+			(vcard = xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) {
+		if((photo = xmlnode_get_child(vcard, "PHOTO"))) {
+			if((text = xmlnode_get_data(photo))) {
+				gaim_base64_decode(text, &data, &size);
+
+				gaim_buddy_icons_set_for_user(js->gc->account, from, data, size);
+				if((b = gaim_find_buddy(js->gc->account, from))) {
+					unsigned char hashval[20];
+					char hash[41], *p;
+					int i;
+
+					shaBlock((unsigned char *)data, size, hashval);
+					p = hash;
+					for(i=0; i<20; i++, p+=2)
+						snprintf(p, 3, "%02x", hashval[i]);
+					gaim_blist_node_set_string((GaimBlistNode*)b, "avatar_hash", hash);
+				}
+			}
+		}
+	}
+}
+
 void jabber_presence_parse(JabberStream *js, xmlnode *packet)
 {
 	const char *from = xmlnode_get_attrib(packet, "from");
@@ -180,11 +223,12 @@
 	JabberBuddyResource *jbr = NULL, *found_jbr = NULL;
 	GaimConvChatBuddyFlags flags = GAIM_CBFLAGS_NONE;
 	gboolean delayed = FALSE;
-	GaimBuddy *b;
+	GaimBuddy *b = NULL;
 	char *buddy_name;
 	JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN;
 	xmlnode *y;
 	gboolean muc = FALSE;
+	char *avatar_hash = NULL;
 
 
 	if(!(jb = jabber_buddy_find(js, from, TRUE)))
@@ -209,7 +253,7 @@
 		jap->gc = js->gc;
 		jap->who = g_strdup(from);
 
-		gaim_request_action(js->gc, NULL, msg, NULL, GAIM_DEFAULT_ACTION_NONE, 
+		gaim_request_action(js->gc, NULL, msg, NULL, GAIM_DEFAULT_ACTION_NONE,
 				jap, 2,
 				_("Authorize"), G_CALLBACK(authorize_add_cb),
 				_("Deny"), G_CALLBACK(deny_add_cb));
@@ -282,6 +326,13 @@
 							flags |= GAIM_CBFLAGS_VOICE;
 					}
 				}
+			} else if(xmlns && !strcmp(xmlns, "vcard-temp:x:update")) {
+				xmlnode *photo = xmlnode_get_child(y, "photo");
+				if(photo) {
+					if(avatar_hash)
+						g_free(avatar_hash);
+					avatar_hash = xmlnode_get_data(photo);
+				}
 			}
 		}
 	}
@@ -308,6 +359,8 @@
 			jabber_id_free(jid);
 			g_free(status);
 			g_free(room_jid);
+			if(avatar_hash)
+				g_free(avatar_hash);
 			return;
 		}
 
@@ -323,6 +376,8 @@
 				jabber_id_free(jid);
 				g_free(status);
 				g_free(room_jid);
+				if(avatar_hash)
+					g_free(avatar_hash);
 				return;
 			}
 
@@ -404,6 +459,8 @@
 					"got unexpected presence from %s, ignoring\n", from);
 			jabber_id_free(jid);
 			g_free(status);
+			if(avatar_hash)
+				g_free(avatar_hash);
 			return;
 		}
 
@@ -411,11 +468,29 @@
 				jid->node ? "@" : "", jid->domain);
 		if((b = gaim_find_buddy(js->gc->account, buddy_name)) == NULL) {
 			jabber_id_free(jid);
+			if(avatar_hash)
+				g_free(avatar_hash);
 			g_free(buddy_name);
 			g_free(status);
 			return;
 		}
 
+		if(avatar_hash) {
+			const char *avatar_hash2 = gaim_blist_node_get_string((GaimBlistNode*)b, "avatar_hash");
+			if(!avatar_hash2 || strcmp(avatar_hash, avatar_hash2)) {
+				JabberIq *iq;
+				xmlnode *vcard;
+
+				iq = jabber_iq_new(js, JABBER_IQ_GET);
+				xmlnode_set_attrib(iq->node, "to", buddy_name);
+				vcard = xmlnode_new_child(iq->node, "vCard");
+				xmlnode_set_attrib(vcard, "xmlns", "vcard-temp");
+
+				jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL);
+				jabber_iq_send(iq);
+			}
+		}
+
 		if(state == JABBER_BUDDY_STATE_ERROR ||
 				(type && (!strcmp(type, "unavailable") ||
 						  !strcmp(type, "unsubscribed")))) {
@@ -441,6 +516,8 @@
 	}
 	g_free(status);
 	jabber_id_free(jid);
+	if(avatar_hash)
+		g_free(avatar_hash);
 }
 
 void jabber_presence_subscription_set(JabberStream *js, const char *who, const char *type)