Mercurial > pidgin.yaz
comparison libpurple/protocols/jabber/buddy.c @ 26817:3912f55a1633
propagate from branch 'im.pidgin.pidgin' (head fbb4fe5da444943eecc76bdcd6c8ba967790b6c8)
to branch 'im.pidgin.cpw.darkrain42.xmpp.bosh' (head 601bc627c9430320848361f0ed81c6c4c6ee53e0)
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Tue, 28 Apr 2009 18:43:57 +0000 |
parents | 538b4175fd24 80437c891f92 |
children | 6028712210ca 98ff7c538e48 |
comparison
equal
deleted
inserted
replaced
26743:de9816c970fe | 26817:3912f55a1633 |
---|---|
30 #include "buddy.h" | 30 #include "buddy.h" |
31 #include "chat.h" | 31 #include "chat.h" |
32 #include "jabber.h" | 32 #include "jabber.h" |
33 #include "iq.h" | 33 #include "iq.h" |
34 #include "presence.h" | 34 #include "presence.h" |
35 #include "useravatar.h" | |
35 #include "xdata.h" | 36 #include "xdata.h" |
36 #include "pep.h" | 37 #include "pep.h" |
37 #include "adhoccommands.h" | 38 #include "adhoccommands.h" |
38 | |
39 #define MAX_HTTP_BUDDYICON_BYTES (200 * 1024) | |
40 | 39 |
41 typedef struct { | 40 typedef struct { |
42 long idle_seconds; | 41 long idle_seconds; |
43 } JabberBuddyInfoResource; | 42 } JabberBuddyInfoResource; |
44 | 43 |
96 if(!jb) | 95 if(!jb) |
97 return NULL; | 96 return NULL; |
98 | 97 |
99 for(l = jb->resources; l; l = l->next) | 98 for(l = jb->resources; l; l = l->next) |
100 { | 99 { |
101 if(!jbr && !resource) { | 100 JabberBuddyResource *tmp = (JabberBuddyResource *) l->data; |
102 jbr = l->data; | 101 if (!jbr && !resource) { |
103 } else if(!resource) { | 102 jbr = tmp; |
104 if(((JabberBuddyResource *)l->data)->priority > jbr->priority) | 103 } else if (!resource) { |
105 jbr = l->data; | 104 if (tmp->priority > jbr->priority) |
106 else if(((JabberBuddyResource *)l->data)->priority == jbr->priority) { | 105 jbr = tmp; |
106 else if (tmp->priority == jbr->priority) { | |
107 /* Determine if this resource is more available than the one we've currently chosen */ | 107 /* Determine if this resource is more available than the one we've currently chosen */ |
108 switch(((JabberBuddyResource *)l->data)->state) { | 108 switch(tmp->state) { |
109 case JABBER_BUDDY_STATE_ONLINE: | 109 case JABBER_BUDDY_STATE_ONLINE: |
110 case JABBER_BUDDY_STATE_CHAT: | 110 case JABBER_BUDDY_STATE_CHAT: |
111 /* This resource is online/chatty. Prefer to one which isn't either. */ | 111 /* This resource is online/chatty. Prefer to one which isn't either. */ |
112 if ((jbr->state != JABBER_BUDDY_STATE_ONLINE) && (jbr->state != JABBER_BUDDY_STATE_CHAT)) | 112 if (((jbr->state != JABBER_BUDDY_STATE_ONLINE) && (jbr->state != JABBER_BUDDY_STATE_CHAT)) |
113 jbr = l->data; | 113 || (jbr->idle && !tmp->idle) |
114 || (jbr->idle && tmp->idle && tmp->idle > jbr->idle)) | |
115 jbr = tmp; | |
114 break; | 116 break; |
115 case JABBER_BUDDY_STATE_AWAY: | 117 case JABBER_BUDDY_STATE_AWAY: |
116 case JABBER_BUDDY_STATE_DND: | 118 case JABBER_BUDDY_STATE_DND: |
117 /* This resource is away/dnd. Prefer to one which is extended away, unavailable, or unknown. */ | 119 /* This resource is away/dnd. Prefer to one which is extended away, unavailable, or unknown. */ |
118 if ((jbr->state == JABBER_BUDDY_STATE_XA) || (jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || | 120 if (((jbr->state == JABBER_BUDDY_STATE_XA) || (jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || |
119 (jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) | 121 (jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) |
120 jbr = l->data; | 122 || (jbr->idle && !tmp->idle) |
123 || (jbr->idle && tmp->idle && tmp->idle > jbr->idle)) | |
124 jbr = tmp; | |
121 break; | 125 break; |
122 case JABBER_BUDDY_STATE_XA: | 126 case JABBER_BUDDY_STATE_XA: |
123 /* This resource is extended away. That's better than unavailable or unknown. */ | 127 /* This resource is extended away. That's better than unavailable or unknown. */ |
124 if ((jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || (jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) | 128 if ((jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || (jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) |
125 jbr = l->data; | 129 jbr = tmp; |
126 break; | 130 break; |
127 case JABBER_BUDDY_STATE_UNAVAILABLE: | 131 case JABBER_BUDDY_STATE_UNAVAILABLE: |
128 /* This resource is unavailable. That's better than unknown. */ | 132 /* This resource is unavailable. That's better than unknown. */ |
129 if ((jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) | 133 if ((jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) |
130 jbr = l->data; | 134 jbr = tmp; |
131 break; | 135 break; |
132 case JABBER_BUDDY_STATE_UNKNOWN: | 136 case JABBER_BUDDY_STATE_UNKNOWN: |
133 case JABBER_BUDDY_STATE_ERROR: | 137 case JABBER_BUDDY_STATE_ERROR: |
134 /* These are never preferable. */ | 138 /* These are never preferable. */ |
135 break; | 139 break; |
136 } | 140 } |
137 } | 141 } |
138 } else if(((JabberBuddyResource *)l->data)->name) { | 142 } else if(tmp->name) { |
139 if(!strcmp(((JabberBuddyResource *)l->data)->name, resource)) { | 143 if(!strcmp(tmp->name, resource)) { |
140 jbr = l->data; | 144 jbr = tmp; |
141 break; | 145 break; |
142 } | 146 } |
143 } | 147 } |
144 } | 148 } |
145 | 149 |
200 | 204 |
201 if(!jbr) | 205 if(!jbr) |
202 return; | 206 return; |
203 | 207 |
204 jabber_buddy_resource_free(jbr); | 208 jabber_buddy_resource_free(jbr); |
205 } | |
206 | |
207 const char *jabber_buddy_get_status_msg(JabberBuddy *jb) | |
208 { | |
209 JabberBuddyResource *jbr; | |
210 | |
211 if(!jb) | |
212 return NULL; | |
213 | |
214 jbr = jabber_buddy_find_resource(jb, NULL); | |
215 | |
216 if(!jbr) | |
217 return NULL; | |
218 | |
219 return jbr->status; | |
220 } | 209 } |
221 | 210 |
222 /******* | 211 /******* |
223 * This is the old vCard stuff taken from the old prpl. vCards, by definition | 212 * This is the old vCard stuff taken from the old prpl. vCards, by definition |
224 * are a temporary thing until jabber can get its act together and come up | 213 * are a temporary thing until jabber can get its act together and come up |
485 | 474 |
486 if (vc_node != NULL) { | 475 if (vc_node != NULL) { |
487 iq = jabber_iq_new(js, JABBER_IQ_SET); | 476 iq = jabber_iq_new(js, JABBER_IQ_SET); |
488 xmlnode_insert_child(iq->node, vc_node); | 477 xmlnode_insert_child(iq->node, vc_node); |
489 jabber_iq_send(iq); | 478 jabber_iq_send(iq); |
479 | |
480 /* Send presence to update vcard-temp:x:update */ | |
481 jabber_presence_send(js, FALSE); | |
490 } | 482 } |
491 } | 483 } |
492 | 484 |
493 void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) | 485 void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) |
494 { | 486 { |
495 if(((JabberStream*)purple_connection_get_protocol_data(gc))->pep) { | 487 PurpleAccount *account = purple_connection_get_account(gc); |
496 /* XEP-0084: User Avatars */ | 488 |
497 if(img) { | 489 /* Publish the avatar as specified in XEP-0084 */ |
498 /* | 490 jabber_avatar_set(gc->proto_data, img); |
499 * TODO: This is pretty gross. The Jabber PRPL really shouldn't | 491 /* Set the image in our vCard */ |
500 * do voodoo to try to determine the image type, height | 492 jabber_set_info(gc, purple_account_get_user_info(account)); |
501 * and width. | 493 |
502 */ | 494 /* TODO: Fake image to ourselves, since a number of servers do not echo |
503 /* A PNG header, including the IHDR, but nothing else */ | 495 * back our presence to us. To do this without uselessly copying the data |
504 const struct { | 496 * of the image, we need purple_buddy_icons_set_for_user_image (i.e. takes |
505 guchar signature[8]; /* must be hex 89 50 4E 47 0D 0A 1A 0A */ | 497 * an existing icon/stored image). */ |
506 struct { | |
507 guint32 length; /* must be 0x0d */ | |
508 guchar type[4]; /* must be 'I' 'H' 'D' 'R' */ | |
509 guint32 width; | |
510 guint32 height; | |
511 guchar bitdepth; | |
512 guchar colortype; | |
513 guchar compression; | |
514 guchar filter; | |
515 guchar interlace; | |
516 } ihdr; | |
517 } *png = purple_imgstore_get_data(img); /* ATTN: this is in network byte order! */ | |
518 | |
519 /* check if the data is a valid png file (well, at least to some extend) */ | |
520 if(png->signature[0] == 0x89 && | |
521 png->signature[1] == 0x50 && | |
522 png->signature[2] == 0x4e && | |
523 png->signature[3] == 0x47 && | |
524 png->signature[4] == 0x0d && | |
525 png->signature[5] == 0x0a && | |
526 png->signature[6] == 0x1a && | |
527 png->signature[7] == 0x0a && | |
528 ntohl(png->ihdr.length) == 0x0d && | |
529 png->ihdr.type[0] == 'I' && | |
530 png->ihdr.type[1] == 'H' && | |
531 png->ihdr.type[2] == 'D' && | |
532 png->ihdr.type[3] == 'R') { | |
533 /* parse PNG header to get the size of the image (yes, this is required) */ | |
534 guint32 width = ntohl(png->ihdr.width); | |
535 guint32 height = ntohl(png->ihdr.height); | |
536 xmlnode *publish, *item, *data, *metadata, *info; | |
537 char *lengthstring, *widthstring, *heightstring; | |
538 | |
539 /* compute the sha1 hash */ | |
540 char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), purple_imgstore_get_size(img)); | |
541 char *base64avatar; | |
542 | |
543 publish = xmlnode_new("publish"); | |
544 xmlnode_set_attrib(publish,"node",AVATARNAMESPACEDATA); | |
545 | |
546 item = xmlnode_new_child(publish, "item"); | |
547 xmlnode_set_attrib(item, "id", hash); | |
548 | |
549 data = xmlnode_new_child(item, "data"); | |
550 xmlnode_set_namespace(data,AVATARNAMESPACEDATA); | |
551 | |
552 base64avatar = purple_base64_encode(purple_imgstore_get_data(img), purple_imgstore_get_size(img)); | |
553 xmlnode_insert_data(data,base64avatar,-1); | |
554 g_free(base64avatar); | |
555 | |
556 /* publish the avatar itself */ | |
557 jabber_pep_publish((JabberStream*)purple_connection_get_protocol_data(gc), publish); | |
558 | |
559 /* next step: publish the metadata */ | |
560 publish = xmlnode_new("publish"); | |
561 xmlnode_set_attrib(publish,"node",AVATARNAMESPACEMETA); | |
562 | |
563 item = xmlnode_new_child(publish, "item"); | |
564 xmlnode_set_attrib(item, "id", hash); | |
565 | |
566 metadata = xmlnode_new_child(item, "metadata"); | |
567 xmlnode_set_namespace(metadata,AVATARNAMESPACEMETA); | |
568 | |
569 info = xmlnode_new_child(metadata, "info"); | |
570 xmlnode_set_attrib(info, "id", hash); | |
571 xmlnode_set_attrib(info, "type", "image/png"); | |
572 lengthstring = g_strdup_printf("%u", (unsigned)purple_imgstore_get_size(img)); | |
573 xmlnode_set_attrib(info, "bytes", lengthstring); | |
574 g_free(lengthstring); | |
575 widthstring = g_strdup_printf("%u", width); | |
576 xmlnode_set_attrib(info, "width", widthstring); | |
577 g_free(widthstring); | |
578 heightstring = g_strdup_printf("%u", height); | |
579 xmlnode_set_attrib(info, "height", heightstring); | |
580 g_free(heightstring); | |
581 | |
582 /* publish the metadata */ | |
583 jabber_pep_publish((JabberStream*)purple_connection_get_protocol_data(gc), publish); | |
584 | |
585 g_free(hash); | |
586 } else { | |
587 purple_debug_error("jabber", "jabber_set_buddy_icon received non-png data"); | |
588 } | |
589 } else { | |
590 /* remove the metadata */ | |
591 xmlnode *metadata, *item; | |
592 xmlnode *publish = xmlnode_new("publish"); | |
593 xmlnode_set_attrib(publish,"node",AVATARNAMESPACEMETA); | |
594 | |
595 item = xmlnode_new_child(publish, "item"); | |
596 | |
597 metadata = xmlnode_new_child(item, "metadata"); | |
598 xmlnode_set_namespace(metadata,AVATARNAMESPACEMETA); | |
599 | |
600 xmlnode_new_child(metadata, "stop"); | |
601 | |
602 /* publish the metadata */ | |
603 jabber_pep_publish((JabberStream*)gc->proto_data, publish); | |
604 } | |
605 } | |
606 | |
607 /* vCard avatars do not have an image type requirement so update our | |
608 * vCard avatar regardless of image type for those poor older clients | |
609 */ | |
610 jabber_set_info(gc, purple_account_get_user_info(gc->account)); | |
611 | |
612 jabber_presence_send(gc->proto_data, FALSE); | |
613 } | 498 } |
614 | 499 |
615 /* | 500 /* |
616 * This is the callback from the "ok clicked" for "set vCard" | 501 * This is the callback from the "ok clicked" for "set vCard" |
617 * | 502 * |
1182 | 1067 |
1183 static void jabber_vcard_save_mine(JabberStream *js, const char *from, | 1068 static void jabber_vcard_save_mine(JabberStream *js, const char *from, |
1184 JabberIqType type, const char *id, | 1069 JabberIqType type, const char *id, |
1185 xmlnode *packet, gpointer data) | 1070 xmlnode *packet, gpointer data) |
1186 { | 1071 { |
1187 xmlnode *vcard; | 1072 xmlnode *vcard, *photo, *binval; |
1188 char *txt; | 1073 char *txt, *vcard_hash = NULL; |
1189 PurpleStoredImage *img; | |
1190 | 1074 |
1191 if (type == JABBER_IQ_ERROR) { | 1075 if (type == JABBER_IQ_ERROR) { |
1192 purple_debug_warning("jabber", "Server returned error while retrieving vCard"); | 1076 purple_debug_warning("jabber", "Server returned error while retrieving vCard"); |
1193 return; | 1077 return; |
1194 } | 1078 } |
1204 /* if we have no vCard, then lets not overwrite what we might have locally */ | 1088 /* if we have no vCard, then lets not overwrite what we might have locally */ |
1205 } | 1089 } |
1206 | 1090 |
1207 js->vcard_fetched = TRUE; | 1091 js->vcard_fetched = TRUE; |
1208 | 1092 |
1209 if(NULL != (img = purple_buddy_icons_find_account_icon(js->gc->account))) { | 1093 if (vcard && (photo = xmlnode_get_child(vcard, "PHOTO")) && |
1210 jabber_set_buddy_icon(js->gc, img); | 1094 (binval = xmlnode_get_child(photo, "BINVAL"))) { |
1211 purple_imgstore_unref(img); | 1095 gsize size; |
1212 } | 1096 char *bintext = xmlnode_get_data(binval); |
1097 guchar *data = purple_base64_decode(bintext, &size); | |
1098 g_free(bintext); | |
1099 | |
1100 if (data) { | |
1101 vcard_hash = jabber_calculate_data_sha1sum(data, size); | |
1102 g_free(data); | |
1103 } | |
1104 } | |
1105 | |
1106 /* Republish our vcard if the photo is different than the server's */ | |
1107 if (!purple_strequal(vcard_hash, js->initial_avatar_hash)) { | |
1108 PurpleAccount *account = purple_connection_get_account(js->gc); | |
1109 jabber_set_info(js->gc, purple_account_get_user_info(account)); | |
1110 } else if (js->initial_avatar_hash) { | |
1111 /* Our photo is in the vcard, so advertise vcard-temp updates */ | |
1112 js->avatar_hash = g_strdup(js->initial_avatar_hash); | |
1113 } | |
1114 | |
1115 g_free(vcard_hash); | |
1213 } | 1116 } |
1214 | 1117 |
1215 void jabber_vcard_fetch_mine(JabberStream *js) | 1118 void jabber_vcard_fetch_mine(JabberStream *js) |
1216 { | 1119 { |
1217 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); | 1120 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); |
1455 g_free(bare_jid); | 1358 g_free(bare_jid); |
1456 | 1359 |
1457 jabber_buddy_info_show_if_ready(jbi); | 1360 jabber_buddy_info_show_if_ready(jbi); |
1458 } | 1361 } |
1459 | 1362 |
1460 typedef struct _JabberBuddyAvatarUpdateURLInfo { | |
1461 JabberStream *js; | |
1462 char *from; | |
1463 char *id; | |
1464 } JabberBuddyAvatarUpdateURLInfo; | |
1465 | |
1466 static void do_buddy_avatar_update_fromurl(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) { | |
1467 JabberBuddyAvatarUpdateURLInfo *info = user_data; | |
1468 if(!url_text) { | |
1469 purple_debug(PURPLE_DEBUG_ERROR, "jabber", | |
1470 "do_buddy_avatar_update_fromurl got error \"%s\"", error_message); | |
1471 return; | |
1472 } | |
1473 | |
1474 purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), info->from, (void*)url_text, len, info->id); | |
1475 g_free(info->from); | |
1476 g_free(info->id); | |
1477 g_free(info); | |
1478 } | |
1479 | |
1480 static void do_buddy_avatar_update_data(JabberStream *js, const char *from, xmlnode *items) { | |
1481 xmlnode *item, *data; | |
1482 const char *checksum; | |
1483 char *b64data; | |
1484 void *img; | |
1485 size_t size; | |
1486 if(!items) | |
1487 return; | |
1488 | |
1489 item = xmlnode_get_child(items, "item"); | |
1490 if(!item) | |
1491 return; | |
1492 | |
1493 data = xmlnode_get_child_with_namespace(item,"data",AVATARNAMESPACEDATA); | |
1494 if(!data) | |
1495 return; | |
1496 | |
1497 checksum = xmlnode_get_attrib(item,"id"); | |
1498 if(!checksum) | |
1499 return; | |
1500 | |
1501 b64data = xmlnode_get_data(data); | |
1502 if(!b64data) | |
1503 return; | |
1504 | |
1505 img = purple_base64_decode(b64data, &size); | |
1506 if(!img) { | |
1507 g_free(b64data); | |
1508 return; | |
1509 } | |
1510 | |
1511 purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum); | |
1512 g_free(b64data); | |
1513 } | |
1514 | |
1515 void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items) { | |
1516 PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(js->gc), from); | |
1517 const char *checksum; | |
1518 xmlnode *item, *metadata; | |
1519 if(!buddy) | |
1520 return; | |
1521 | |
1522 checksum = purple_buddy_icons_get_checksum_for_user(buddy); | |
1523 item = xmlnode_get_child(items,"item"); | |
1524 metadata = xmlnode_get_child_with_namespace(item, "metadata", AVATARNAMESPACEMETA); | |
1525 if(!metadata) | |
1526 return; | |
1527 /* check if we have received a stop */ | |
1528 if(xmlnode_get_child(metadata, "stop")) { | |
1529 purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); | |
1530 } else { | |
1531 xmlnode *info, *goodinfo = NULL; | |
1532 gboolean has_children = FALSE; | |
1533 | |
1534 /* iterate over all info nodes to get one we can use */ | |
1535 for(info = metadata->child; info; info = info->next) { | |
1536 if(info->type == XMLNODE_TYPE_TAG) | |
1537 has_children = TRUE; | |
1538 if(info->type == XMLNODE_TYPE_TAG && !strcmp(info->name,"info")) { | |
1539 const char *type = xmlnode_get_attrib(info,"type"); | |
1540 const char *id = xmlnode_get_attrib(info,"id"); | |
1541 | |
1542 if(checksum && id && !strcmp(id, checksum)) { | |
1543 /* we already have that avatar, so we don't have to do anything */ | |
1544 goodinfo = NULL; | |
1545 break; | |
1546 } | |
1547 /* We'll only pick the png one for now. It's a very nice image format anyways. */ | |
1548 if(type && id && !goodinfo && !strcmp(type, "image/png")) | |
1549 goodinfo = info; | |
1550 } | |
1551 } | |
1552 if(has_children == FALSE) { | |
1553 purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); | |
1554 } else if(goodinfo) { | |
1555 const char *url = xmlnode_get_attrib(goodinfo, "url"); | |
1556 const char *id = xmlnode_get_attrib(goodinfo,"id"); | |
1557 | |
1558 /* the avatar might either be stored in a pep node, or on a HTTP/HTTPS URL */ | |
1559 if(!url) | |
1560 jabber_pep_request_item(js, from, AVATARNAMESPACEDATA, id, do_buddy_avatar_update_data); | |
1561 else { | |
1562 PurpleUtilFetchUrlData *url_data; | |
1563 JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1); | |
1564 info->js = js; | |
1565 | |
1566 url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE, | |
1567 MAX_HTTP_BUDDYICON_BYTES, | |
1568 do_buddy_avatar_update_fromurl, info); | |
1569 if (url_data) { | |
1570 info->from = g_strdup(from); | |
1571 info->id = g_strdup(id); | |
1572 js->url_datas = g_slist_prepend(js->url_datas, url_data); | |
1573 } else | |
1574 g_free(info); | |
1575 | |
1576 } | |
1577 } | |
1578 } | |
1579 } | |
1580 | |
1581 static void jabber_buddy_info_resource_free(gpointer data) | 1363 static void jabber_buddy_info_resource_free(gpointer data) |
1582 { | 1364 { |
1583 JabberBuddyInfoResource *jbri = data; | 1365 JabberBuddyInfoResource *jbri = data; |
1584 g_free(jbri); | 1366 g_free(jbri); |
1585 } | 1367 } |
1648 if((query = xmlnode_get_child(packet, "query"))) { | 1430 if((query = xmlnode_get_child(packet, "query"))) { |
1649 seconds = xmlnode_get_attrib(query, "seconds"); | 1431 seconds = xmlnode_get_attrib(query, "seconds"); |
1650 if(seconds) { | 1432 if(seconds) { |
1651 char *end = NULL; | 1433 char *end = NULL; |
1652 long sec = strtol(seconds, &end, 10); | 1434 long sec = strtol(seconds, &end, 10); |
1653 if(end != seconds) { | 1435 JabberBuddy *jb = NULL; |
1436 char *resource = NULL; | |
1437 char *buddy_name = NULL; | |
1438 JabberBuddyResource *jbr = NULL; | |
1439 | |
1440 if(end != seconds) { | |
1654 JabberBuddyInfoResource *jbir = g_hash_table_lookup(jbi->resources, resource_name); | 1441 JabberBuddyInfoResource *jbir = g_hash_table_lookup(jbi->resources, resource_name); |
1655 if(jbir) { | 1442 if(jbir) { |
1656 jbir->idle_seconds = sec; | 1443 jbir->idle_seconds = sec; |
1657 } | 1444 } |
1658 } | 1445 } |
1446 /* Update the idle time of the buddy resource, if we got it. | |
1447 This will correct the value when a server doesn't mark | |
1448 delayed presence and we got the presence when signing on */ | |
1449 jb = jabber_buddy_find(js, from, FALSE); | |
1450 if (jb) { | |
1451 resource = jabber_get_resource(from); | |
1452 buddy_name = jabber_get_bare_jid(from); | |
1453 /* if the resource already has an idle time set, we | |
1454 must have gotten it originally from a presence. In | |
1455 this case we update it. Otherwise don't update it, to | |
1456 avoid setting an idle and not getting informed about | |
1457 the resource getting unidle */ | |
1458 if (resource && buddy_name) { | |
1459 jbr = jabber_buddy_find_resource(jb, resource); | |
1460 | |
1461 if (jbr->idle) { | |
1462 if (sec) { | |
1463 jbr->idle = time(NULL) - sec; | |
1464 } else { | |
1465 jbr->idle = 0; | |
1466 } | |
1467 | |
1468 if (jbr == | |
1469 jabber_buddy_find_resource(jb, NULL)) { | |
1470 purple_prpl_got_user_idle(js->gc->account, | |
1471 buddy_name, jbr->idle, jbr->idle); | |
1472 } | |
1473 } | |
1474 } | |
1475 g_free(resource); | |
1476 g_free(buddy_name); | |
1477 } | |
1659 } | 1478 } |
1660 } | 1479 } |
1661 } | 1480 } |
1662 g_free(resource_name); | 1481 g_free(resource_name); |
1663 } | 1482 } |