comparison libpurple/protocols/jabber/buddy.c @ 17580: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 95affacf6f82
children 5ab3c6bb95b4
comparison
equal deleted inserted replaced
17579:af833a3204bb 17580:7754d39d70c5
32 #include "chat.h" 32 #include "chat.h"
33 #include "jabber.h" 33 #include "jabber.h"
34 #include "iq.h" 34 #include "iq.h"
35 #include "presence.h" 35 #include "presence.h"
36 #include "xdata.h" 36 #include "xdata.h"
37 #include "pep.h"
37 38
38 typedef struct { 39 typedef struct {
39 long idle_seconds; 40 long idle_seconds;
40 } JabberBuddyInfoResource; 41 } JabberBuddyInfoResource;
41 42
404 PurpleStoredImage *img; 405 PurpleStoredImage *img;
405 406
406 if ((img = purple_buddy_icons_find_account_icon(gc->account))) { 407 if ((img = purple_buddy_icons_find_account_icon(gc->account))) {
407 gconstpointer avatar_data; 408 gconstpointer avatar_data;
408 gsize avatar_len; 409 gsize avatar_len;
409 xmlnode *photo, *binval; 410 xmlnode *photo, *binval, *type;
410 gchar *enc; 411 gchar *enc;
411 int i; 412 int i;
412 unsigned char hashval[20]; 413 unsigned char hashval[20];
413 char *p, hash[41]; 414 char *p, hash[41];
414 415
415 avatar_data = purple_imgstore_get_data(img); 416 avatar_data = purple_imgstore_get_data(img);
416 avatar_len = purple_imgstore_get_size(img); 417 avatar_len = purple_imgstore_get_size(img);
417 photo = xmlnode_new_child(vc_node, "PHOTO"); 418 photo = xmlnode_new_child(vc_node, "PHOTO");
419 type = xmlnode_new_child(photo, "TYPE");
420 xmlnode_insert_data(type, "image/png", -1);
418 binval = xmlnode_new_child(photo, "BINVAL"); 421 binval = xmlnode_new_child(photo, "BINVAL");
419 enc = purple_base64_encode(avatar_data, avatar_len); 422 enc = purple_base64_encode(avatar_data, avatar_len);
420 423
421 purple_cipher_digest_region("sha1", avatar_data, 424 purple_cipher_digest_region("sha1", avatar_data,
422 avatar_len, sizeof(hashval), 425 avatar_len, sizeof(hashval),
443 446
444 void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) 447 void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
445 { 448 {
446 PurplePresence *gpresence; 449 PurplePresence *gpresence;
447 PurpleStatus *status; 450 PurpleStatus *status;
448 451
452 if(((JabberStream*)gc->proto_data)->pep) {
453 /* XEP-0084: User Avatars */
454 if(img) {
455 /* A PNG header, including the IHDR, but nothing else */
456 const struct {
457 guchar signature[8]; /* must be hex 89 50 4E 47 0D 0A 1A 0A */
458 struct {
459 guint32 length; /* must be 0x0d */
460 guchar type[4]; /* must be 'I' 'H' 'D' 'R' */
461 guint32 width;
462 guint32 height;
463 guchar bitdepth;
464 guchar colortype;
465 guchar compression;
466 guchar filter;
467 guchar interlace;
468 } ihdr;
469 } *png = purple_imgstore_get_data(img); /* ATTN: this is in network byte order! */
470
471 /* check if the data is a valid png file (well, at least to some extend) */
472 if(png->signature[0] == 0x89 &&
473 png->signature[1] == 0x50 &&
474 png->signature[2] == 0x4e &&
475 png->signature[3] == 0x47 &&
476 png->signature[4] == 0x0d &&
477 png->signature[5] == 0x0a &&
478 png->signature[6] == 0x1a &&
479 png->signature[7] == 0x0a &&
480 ntohl(png->ihdr.length) == 0x0d &&
481 png->ihdr.type[0] == 'I' &&
482 png->ihdr.type[1] == 'H' &&
483 png->ihdr.type[2] == 'D' &&
484 png->ihdr.type[3] == 'R') {
485 /* parse PNG header to get the size of the image (yes, this is required) */
486 guint32 width = ntohl(png->ihdr.width);
487 guint32 height = ntohl(png->ihdr.height);
488 xmlnode *publish, *item, *data, *metadata, *info;
489 char *lengthstring, *widthstring, *heightstring;
490
491 /* compute the sha1 hash */
492 PurpleCipherContext *ctx;
493 unsigned char digest[20];
494 char *hash;
495 char *base64avatar;
496
497 ctx = purple_cipher_context_new_by_name("sha1", NULL);
498 purple_cipher_context_append(ctx, purple_imgstore_get_data(img), purple_imgstore_get_size(img));
499 purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
500
501 /* convert digest to a string */
502 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]);
503
504 publish = xmlnode_new("publish");
505 xmlnode_set_attrib(publish,"node","http://www.xmpp.org/extensions/xep-0084.html#ns-data");
506
507 item = xmlnode_new_child(publish, "item");
508 xmlnode_set_attrib(item, "id", hash);
509
510 data = xmlnode_new_child(item, "data");
511 xmlnode_set_namespace(data,"http://www.xmpp.org/extensions/xep-0084.html#ns-data");
512
513 base64avatar = purple_base64_encode(purple_imgstore_get_data(img), purple_imgstore_get_size(img));
514 xmlnode_insert_data(data,base64avatar,-1);
515 g_free(base64avatar);
516
517 /* publish the avatar itself */
518 jabber_pep_publish((JabberStream*)gc->proto_data, publish);
519
520 /* next step: publish the metadata */
521 publish = xmlnode_new("publish");
522 xmlnode_set_attrib(publish,"node","http://www.xmpp.org/extensions/xep-0084.html#ns-metadata");
523
524 item = xmlnode_new_child(publish, "item");
525 xmlnode_set_attrib(item, "id", hash);
526
527 metadata = xmlnode_new_child(item, "metadata");
528 xmlnode_set_namespace(metadata,"http://www.xmpp.org/extensions/xep-0084.html#ns-metadata");
529
530 info = xmlnode_new_child(metadata, "info");
531 xmlnode_set_attrib(info, "id", hash);
532 xmlnode_set_attrib(info, "type", "image/png");
533 lengthstring = g_strdup_printf("%u", (unsigned)purple_imgstore_get_size(img));
534 xmlnode_set_attrib(info, "bytes", lengthstring);
535 g_free(lengthstring);
536 widthstring = g_strdup_printf("%u", width);
537 xmlnode_set_attrib(info, "width", widthstring);
538 g_free(widthstring);
539 heightstring = g_strdup_printf("%u", height);
540 xmlnode_set_attrib(info, "height", heightstring);
541 g_free(lengthstring);
542
543 /* publish the metadata */
544 jabber_pep_publish((JabberStream*)gc->proto_data, publish);
545
546 g_free(hash);
547 } else { /* if(img) */
548 /* remove the metadata */
549 xmlnode *metadata, *item;
550 xmlnode *publish = xmlnode_new("publish");
551 xmlnode_set_attrib(publish,"node","http://www.xmpp.org/extensions/xep-0084.html#ns-metadata");
552
553 item = xmlnode_new_child(publish, "item");
554
555 metadata = xmlnode_new_child(item, "metadata");
556 xmlnode_set_namespace(metadata,"http://www.xmpp.org/extensions/xep-0084.html#ns-metadata");
557
558 xmlnode_new_child(metadata, "stop");
559
560 /* publish the metadata */
561 jabber_pep_publish((JabberStream*)gc->proto_data, publish);
562 }
563 } else {
564 purple_debug(PURPLE_DEBUG_ERROR, "jabber",
565 "jabber_set_buddy_icon received non-png data");
566 }
567 }
568
569 /* even when the image is not png, we can still publish the vCard, since this
570 one doesn't require a specific image type */
571
572 /* publish vCard for those poor older clients */
449 jabber_set_info(gc, purple_account_get_user_info(gc->account)); 573 jabber_set_info(gc, purple_account_get_user_info(gc->account));
450 574
451 gpresence = purple_account_get_presence(gc->account); 575 gpresence = purple_account_get_presence(gc->account);
452 status = purple_presence_get_active_status(gpresence); 576 status = purple_presence_get_active_status(gpresence);
453 jabber_presence_send(gc->account, status); 577 jabber_presence_send(gc->account, status);
983 g_free(bare_jid); 1107 g_free(bare_jid);
984 1108
985 jabber_buddy_info_show_if_ready(jbi); 1109 jabber_buddy_info_show_if_ready(jbi);
986 } 1110 }
987 1111
1112 void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items) {
1113
1114 }
988 1115
989 static void jabber_buddy_info_resource_free(gpointer data) 1116 static void jabber_buddy_info_resource_free(gpointer data)
990 { 1117 {
991 JabberBuddyInfoResource *jbri = data; 1118 JabberBuddyInfoResource *jbri = data;
992 g_free(jbri); 1119 g_free(jbri);