Mercurial > pidgin
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)