comparison libpurple/protocols/jabber/useravatar.c @ 25355:ba8f51cb3691

Support XEP-0084 (User Avatar) v1.1 alongside v0.12 Publish avatars to both namespaces and support receiving metadata notifications from either one. The fetching of our own avatars needs to be fixed to fetch the metdata for both namespaces. Closes #7732.
author Paul Aurich <paul@darkrain42.org>
date Mon, 26 Jan 2009 03:52:05 +0000
parents 9195955395b6
children c0a27a44ebd6
comparison
equal deleted inserted replaced
25354:370d8eba2ce0 25355:ba8f51cb3691
31 31
32 static void update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items); 32 static void update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items);
33 33
34 void jabber_avatar_init(void) 34 void jabber_avatar_init(void)
35 { 35 {
36 jabber_add_feature("avatarmeta", AVATARNAMESPACEMETA, 36 jabber_add_feature("avatarmeta", NS_AVATAR_0_12_METADATA,
37 jabber_pep_namespace_only_when_pep_enabled_cb); 37 jabber_pep_namespace_only_when_pep_enabled_cb);
38 jabber_add_feature("avatardata", AVATARNAMESPACEMETA, 38 jabber_add_feature("avatardata", NS_AVATAR_0_12_DATA,
39 jabber_pep_namespace_only_when_pep_enabled_cb); 39 jabber_pep_namespace_only_when_pep_enabled_cb);
40 40 jabber_pep_register_handler("avatar", NS_AVATAR_0_12_METADATA,
41 jabber_pep_register_handler("avatar", AVATARNAMESPACEMETA, 41 update_buddy_metadata);
42
43 jabber_add_feature("urn_avatarmeta", NS_AVATAR_1_1_METADATA,
44 jabber_pep_namespace_only_when_pep_enabled_cb);
45 jabber_add_feature("urn_avatardata", NS_AVATAR_1_1_DATA,
46 jabber_pep_namespace_only_when_pep_enabled_cb);
47
48 jabber_pep_register_handler("urn_avatar", NS_AVATAR_1_1_METADATA,
42 update_buddy_metadata); 49 update_buddy_metadata);
43 } 50 }
44 51
45 void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img) 52 void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img)
46 { 53 {
50 return; 57 return;
51 58
52 if (!img) { 59 if (!img) {
53 /* remove the metadata */ 60 /* remove the metadata */
54 publish = xmlnode_new("publish"); 61 publish = xmlnode_new("publish");
55 xmlnode_set_attrib(publish, "node", AVATARNAMESPACEMETA); 62 xmlnode_set_attrib(publish, "node", NS_AVATAR_0_12_METADATA);
56 63
57 item = xmlnode_new_child(publish, "item"); 64 item = xmlnode_new_child(publish, "item");
58 metadata = xmlnode_new_child(item, "metadata"); 65 metadata = xmlnode_new_child(item, "metadata");
59 xmlnode_set_namespace(metadata, AVATARNAMESPACEMETA); 66 xmlnode_set_namespace(metadata, NS_AVATAR_0_12_METADATA);
60 67
61 xmlnode_new_child(metadata, "stop"); 68 xmlnode_new_child(metadata, "stop");
69 /* publish */
70 jabber_pep_publish(js, publish);
71
72 /* Now for the XEP-0084 v1.1 namespace, where we publish an empty
73 * metadata node instead of a <stop/> element */
74 publish = xmlnode_new("publish");
75 xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA);
76
77 item = xmlnode_new_child(publish, "item");
78 metadata = xmlnode_new_child(item, "metadata");
79 xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA);
80
62 /* publish */ 81 /* publish */
63 jabber_pep_publish(js, publish); 82 jabber_pep_publish(js, publish);
64 } else { 83 } else {
65 /* 84 /*
66 * TODO: This is pretty gross. The Jabber PRPL really shouldn't 85 * TODO: This is pretty gross. The Jabber PRPL really shouldn't
98 png->ihdr.type[2] == 'D' && 117 png->ihdr.type[2] == 'D' &&
99 png->ihdr.type[3] == 'R') { 118 png->ihdr.type[3] == 'R') {
100 /* parse PNG header to get the size of the image (yes, this is required) */ 119 /* parse PNG header to get the size of the image (yes, this is required) */
101 guint32 width = ntohl(png->ihdr.width); 120 guint32 width = ntohl(png->ihdr.width);
102 guint32 height = ntohl(png->ihdr.height); 121 guint32 height = ntohl(png->ihdr.height);
103 xmlnode *data, *info; 122 xmlnode *data, *info, *tmp;
104 char *lengthstring, *widthstring, *heightstring; 123 char *lengthstring, *widthstring, *heightstring;
105 124
106 /* compute the sha1 hash */ 125 /* compute the sha1 hash */
107 char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), purple_imgstore_get_size(img)); 126 char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
108 char *base64avatar; 127 char *base64avatar;
109 128
110 publish = xmlnode_new("publish"); 129 publish = xmlnode_new("publish");
111 xmlnode_set_attrib(publish, "node", AVATARNAMESPACEDATA); 130 xmlnode_set_attrib(publish, "node", NS_AVATAR_0_12_DATA);
112 131
113 item = xmlnode_new_child(publish, "item"); 132 item = xmlnode_new_child(publish, "item");
114 xmlnode_set_attrib(item, "id", hash); 133 xmlnode_set_attrib(item, "id", hash);
115 134
116 data = xmlnode_new_child(item, "data"); 135 data = xmlnode_new_child(item, "data");
117 xmlnode_set_namespace(data, AVATARNAMESPACEDATA); 136 xmlnode_set_namespace(data, NS_AVATAR_0_12_DATA);
118 137
119 base64avatar = purple_base64_encode(purple_imgstore_get_data(img), 138 base64avatar = purple_base64_encode(purple_imgstore_get_data(img),
120 purple_imgstore_get_size(img)); 139 purple_imgstore_get_size(img));
121 xmlnode_insert_data(data,base64avatar,-1); 140 xmlnode_insert_data(data,base64avatar,-1);
122 g_free(base64avatar); 141 g_free(base64avatar);
123 142
124 /* publish the avatar itself */ 143 /* publish the avatar itself */
144 tmp = xmlnode_copy(publish);
125 jabber_pep_publish(js, publish); 145 jabber_pep_publish(js, publish);
126 146
127 /* next step: publish the metadata */ 147 /* publish the avatar to the XEP-0084 v1.1 namespace */
148 publish = tmp;
149 xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_DATA);
150
151 item = xmlnode_get_child(publish, "item");
152 data = xmlnode_get_child(item, "data");
153 xmlnode_set_namespace(data, NS_AVATAR_1_1_DATA);
154
155 /* publish the avatar itself */
156 jabber_pep_publish(js, publish);
157
158 /* next step: publish the metadata to the old namespace */
128 publish = xmlnode_new("publish"); 159 publish = xmlnode_new("publish");
129 xmlnode_set_attrib(publish,"node", AVATARNAMESPACEMETA); 160 xmlnode_set_attrib(publish,"node", NS_AVATAR_0_12_METADATA);
130 161
131 item = xmlnode_new_child(publish, "item"); 162 item = xmlnode_new_child(publish, "item");
132 xmlnode_set_attrib(item, "id", hash); 163 xmlnode_set_attrib(item, "id", hash);
133 164
134 metadata = xmlnode_new_child(item, "metadata"); 165 metadata = xmlnode_new_child(item, "metadata");
135 xmlnode_set_namespace(metadata, AVATARNAMESPACEMETA); 166 xmlnode_set_namespace(metadata, NS_AVATAR_0_12_METADATA);
136 167
137 lengthstring = g_strdup_printf("%u", (unsigned)purple_imgstore_get_size(img)); 168 lengthstring = g_strdup_printf("%" G_GSIZE_FORMAT,
169 purple_imgstore_get_size(img));
138 widthstring = g_strdup_printf("%u", width); 170 widthstring = g_strdup_printf("%u", width);
139 heightstring = g_strdup_printf("%u", height); 171 heightstring = g_strdup_printf("%u", height);
140 172
141 info = xmlnode_new_child(metadata, "info"); 173 info = xmlnode_new_child(metadata, "info");
142 xmlnode_set_attrib(info, "id", hash); 174 xmlnode_set_attrib(info, "id", hash);
147 g_free(lengthstring); 179 g_free(lengthstring);
148 g_free(widthstring); 180 g_free(widthstring);
149 g_free(heightstring); 181 g_free(heightstring);
150 182
151 /* publish the metadata */ 183 /* publish the metadata */
184 tmp = xmlnode_copy(publish);
185 jabber_pep_publish(js, publish);
186
187 /* publish the metadata to the new namespace */
188 publish = tmp;
189 xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA);
190
191 item = xmlnode_get_child(publish, "item");
192 metadata = xmlnode_get_child(item, "metdata");
193 xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA);
194
152 jabber_pep_publish(js, publish); 195 jabber_pep_publish(js, publish);
153 196
154 g_free(hash); 197 g_free(hash);
155 } else { 198 } else {
156 purple_debug_error("jabber", "Cannot set PEP avatar to non-PNG data\n"); 199 purple_debug_error("jabber", "Cannot set PEP avatar to non-PNG data\n");
182 } 225 }
183 226
184 void jabber_avatar_fetch_mine(JabberStream *js) 227 void jabber_avatar_fetch_mine(JabberStream *js)
185 { 228 {
186 char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain); 229 char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
187 jabber_pep_request_item(js, jid, AVATARNAMESPACEMETA, NULL, 230 jabber_pep_request_item(js, jid, NS_AVATAR_0_12_METADATA, NULL,
188 do_got_own_avatar_cb); 231 do_got_own_avatar_cb);
189 g_free(jid); 232 g_free(jid);
190 } 233 }
191 234
192 typedef struct _JabberBuddyAvatarUpdateURLInfo { 235 typedef struct _JabberBuddyAvatarUpdateURLInfo {
218 261
219 static void 262 static void
220 do_buddy_avatar_update_data(JabberStream *js, const char *from, xmlnode *items) 263 do_buddy_avatar_update_data(JabberStream *js, const char *from, xmlnode *items)
221 { 264 {
222 xmlnode *item, *data; 265 xmlnode *item, *data;
223 const char *checksum; 266 const char *checksum, *ns;
224 char *b64data; 267 char *b64data;
225 void *img; 268 void *img;
226 size_t size; 269 size_t size;
227 if(!items) 270 if(!items)
228 return; 271 return;
229 272
230 item = xmlnode_get_child(items, "item"); 273 item = xmlnode_get_child(items, "item");
231 if(!item) 274 if(!item)
232 return; 275 return;
233 276
234 data = xmlnode_get_child_with_namespace(item,"data",AVATARNAMESPACEDATA); 277 data = xmlnode_get_child(item, "data");
235 if(!data) 278 if(!data)
236 return; 279 return;
237 280
281 ns = xmlnode_get_namespace(data);
282 /* Make sure the namespace is one of the two valid possibilities */
283 if (!ns || (strcmp(ns, NS_AVATAR_0_12_DATA) &&
284 strcmp(ns, NS_AVATAR_1_1_DATA)))
285 return;
286
238 checksum = xmlnode_get_attrib(item,"id"); 287 checksum = xmlnode_get_attrib(item,"id");
239 if(!checksum) 288 if(!checksum)
240 return; 289 return;
241 290
242 b64data = xmlnode_get_data(data); 291 b64data = xmlnode_get_data(data);
255 304
256 static void 305 static void
257 update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items) 306 update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items)
258 { 307 {
259 PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(js->gc), from); 308 PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(js->gc), from);
260 const char *checksum; 309 const char *checksum, *ns;
261 xmlnode *item, *metadata; 310 xmlnode *item, *metadata;
262 if(!buddy) 311 if(!buddy)
263 return; 312 return;
264 313
265 checksum = purple_buddy_icons_get_checksum_for_user(buddy); 314 checksum = purple_buddy_icons_get_checksum_for_user(buddy);
266 item = xmlnode_get_child(items,"item"); 315 item = xmlnode_get_child(items,"item");
267 metadata = xmlnode_get_child_with_namespace(item, "metadata", AVATARNAMESPACEMETA); 316 metadata = xmlnode_get_child(item, "metadata");
268 if(!metadata) 317 if(!metadata)
269 return; 318 return;
319
320 ns = xmlnode_get_namespace(metadata);
321 /* Make sure the namespace is one of the two valid possibilities */
322 if (!ns || (strcmp(ns, NS_AVATAR_0_12_METADATA) &&
323 strcmp(ns, NS_AVATAR_1_1_METADATA)))
324 return;
325
270 /* check if we have received a stop */ 326 /* check if we have received a stop */
271 if(xmlnode_get_child(metadata, "stop")) { 327 if(xmlnode_get_child(metadata, "stop")) {
272 purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); 328 purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL);
273 } else { 329 } else {
274 xmlnode *info, *goodinfo = NULL; 330 xmlnode *info, *goodinfo = NULL;
297 } else if(goodinfo) { 353 } else if(goodinfo) {
298 const char *url = xmlnode_get_attrib(goodinfo, "url"); 354 const char *url = xmlnode_get_attrib(goodinfo, "url");
299 const char *id = xmlnode_get_attrib(goodinfo,"id"); 355 const char *id = xmlnode_get_attrib(goodinfo,"id");
300 356
301 /* the avatar might either be stored in a pep node, or on a HTTP(S) URL */ 357 /* the avatar might either be stored in a pep node, or on a HTTP(S) URL */
302 if(!url) 358 if(!url) {
303 jabber_pep_request_item(js, from, AVATARNAMESPACEDATA, id, do_buddy_avatar_update_data); 359 const char *data_ns;
304 else { 360 data_ns = (strcmp(ns, NS_AVATAR_0_12_METADATA) == 0 ?
361 NS_AVATAR_0_12_DATA : NS_AVATAR_1_1_DATA);
362 jabber_pep_request_item(js, from, data_ns, id,
363 do_buddy_avatar_update_data);
364 } else {
305 PurpleUtilFetchUrlData *url_data; 365 PurpleUtilFetchUrlData *url_data;
306 JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1); 366 JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1);
307 info->js = js; 367 info->js = js;
308 368
309 url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE, 369 url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE,