| 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 |
| 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 * |
| 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 } |