comparison libpurple/protocols/jabber/presence.c @ 19277:f821d4bffb0a

propagate from branch 'im.pidgin.pidgin' (head 37793415aab363acafa9ca7ce5a76c974dbc6c4c) to branch 'im.pidgin.soc.2007.xmpp' (head c9129cb5ffdd67387b55d418b79fd8c2b6665f55)
author Evan Schoenberg <evan.s@dreskin.net>
date Tue, 14 Aug 2007 22:00:24 +0000
parents 1ca6c4b234ab 177552010f1d
children b0733d5d7621
comparison
equal deleted inserted replaced
19276:85324e9d0821 19277:f821d4bffb0a
34 #include "buddy.h" 34 #include "buddy.h"
35 #include "chat.h" 35 #include "chat.h"
36 #include "presence.h" 36 #include "presence.h"
37 #include "iq.h" 37 #include "iq.h"
38 #include "jutil.h" 38 #include "jutil.h"
39 #include "adhoccommands.h"
40
41 #include "usertune.h"
39 42
40 43
41 static void chats_send_presence_foreach(gpointer key, gpointer val, 44 static void chats_send_presence_foreach(gpointer key, gpointer val,
42 gpointer user_data) 45 gpointer user_data)
43 { 46 {
99 int primitive; 102 int primitive;
100 xmlnode *presence, *x, *photo; 103 xmlnode *presence, *x, *photo;
101 char *stripped = NULL; 104 char *stripped = NULL;
102 JabberBuddyState state; 105 JabberBuddyState state;
103 int priority; 106 int priority;
107 const char *artist, *title, *source, *uri, *track;
108 int length;
109 gboolean allowBuzz;
104 110
105 if(NULL == status) { 111 if(NULL == status) {
106 PurplePresence *gpresence = purple_account_get_presence(account); 112 PurplePresence *gpresence = purple_account_get_presence(account);
107 status = purple_presence_get_active_status(gpresence); 113 status = purple_presence_get_active_status(gpresence);
108 } 114 }
125 purple_debug_info("jabber", "attempt to send presence before roster retrieved\n"); 131 purple_debug_info("jabber", "attempt to send presence before roster retrieved\n");
126 return; 132 return;
127 } 133 }
128 134
129 purple_status_to_jabber(status, &state, &stripped, &priority); 135 purple_status_to_jabber(status, &state, &stripped, &priority);
130 136
131 137 /* check for buzz support */
132 presence = jabber_presence_create(state, stripped, priority); 138 allowBuzz = purple_status_get_attr_boolean(status,"buzz");
133 g_free(stripped); 139 /* changing the buzz state has to trigger a re-broadcasting of the presence for caps */
134 140
135 if(js->avatar_hash) { 141 #define CHANGED(a,b) ((!a && b) || (a && a[0] == '\0' && b && b[0] != '\0') || \
136 x = xmlnode_new_child(presence, "x"); 142 (a && !b) || (a && a[0] != '\0' && b && b[0] == '\0') || (a && b && strcmp(a,b)))
137 xmlnode_set_namespace(x, "vcard-temp:x:update"); 143 /* check if there are any differences to the <presence> and send them in that case */
138 photo = xmlnode_new_child(x, "photo"); 144 if (allowBuzz != js->allowBuzz || js->old_state != state || CHANGED(js->old_msg, stripped) ||
139 xmlnode_insert_data(photo, js->avatar_hash, -1); 145 js->old_priority != priority || CHANGED(js->old_avatarhash, js->avatar_hash)) {
140 } 146 js->allowBuzz = allowBuzz;
141 147 presence = jabber_presence_create_js(js, state, stripped, priority);
142 jabber_send(js, presence); 148
143 149 if(js->avatar_hash) {
144 g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence); 150 x = xmlnode_new_child(presence, "x");
145 xmlnode_free(presence); 151 xmlnode_set_namespace(x, "vcard-temp:x:update");
146 152 photo = xmlnode_new_child(x, "photo");
153 xmlnode_insert_data(photo, js->avatar_hash, -1);
154 }
155
156 jabber_send(js, presence);
157
158 g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence);
159 xmlnode_free(presence);
160
161 /* update old values */
162
163 if(js->old_msg)
164 g_free(js->old_msg);
165 if(js->old_avatarhash)
166 g_free(js->old_avatarhash);
167 js->old_msg = g_strdup(stripped);
168 js->old_avatarhash = g_strdup(js->avatar_hash);
169 js->old_state = state;
170 js->old_priority = priority;
171 g_free(stripped);
172 }
173
174 /* next, check if there are any changes to the tune values */
175 artist = purple_status_get_attr_string(status, PURPLE_TUNE_ARTIST);
176 title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
177 source = purple_status_get_attr_string(status, PURPLE_TUNE_ALBUM);
178 uri = purple_status_get_attr_string(status, PURPLE_TUNE_URL);
179 track = purple_status_get_attr_string(status, PURPLE_TUNE_TRACK);
180 length = (!purple_status_get_attr_value(status, PURPLE_TUNE_TIME))?-1:purple_status_get_attr_int(status, PURPLE_TUNE_TIME);
181
182 if(CHANGED(artist, js->old_artist) || CHANGED(title, js->old_title) || CHANGED(source, js->old_source) ||
183 CHANGED(uri, js->old_uri) || CHANGED(track, js->old_track) || (length != js->old_length)) {
184 PurpleJabberTuneInfo tuneinfo = {
185 (char*)artist,
186 (char*)title,
187 (char*)source,
188 (char*)track,
189 length,
190 (char*)uri
191 };
192 jabber_tune_set(js->gc, &tuneinfo);
193
194 /* update old values */
195 if(js->old_artist)
196 g_free(js->old_artist);
197 if(js->old_title)
198 g_free(js->old_title);
199 if(js->old_source)
200 g_free(js->old_source);
201 if(js->old_uri)
202 g_free(js->old_uri);
203 if(js->old_track)
204 g_free(js->old_track);
205 js->old_artist = g_strdup(artist);
206 js->old_title = g_strdup(title);
207 js->old_source = g_strdup(source);
208 js->old_uri = g_strdup(uri);
209 js->old_length = length;
210 js->old_track = g_strdup(track);
211 }
212
213 #undef CHANGED(a,b)
214
147 jabber_presence_fake_to_self(js, status); 215 jabber_presence_fake_to_self(js, status);
148 } 216 }
149 217
150 xmlnode *jabber_presence_create(JabberBuddyState state, const char *msg, int priority) 218 xmlnode *jabber_presence_create(JabberBuddyState state, const char *msg, int priority)
219 {
220 return jabber_presence_create_js(NULL, state, msg, priority);
221 }
222
223 xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, const char *msg, int priority)
151 { 224 {
152 xmlnode *show, *status, *presence, *pri, *c; 225 xmlnode *show, *status, *presence, *pri, *c;
153 const char *show_string = NULL; 226 const char *show_string = NULL;
154 227
155 presence = xmlnode_new("presence"); 228 presence = xmlnode_new("presence");
181 /* JEP-0115 */ 254 /* JEP-0115 */
182 c = xmlnode_new_child(presence, "c"); 255 c = xmlnode_new_child(presence, "c");
183 xmlnode_set_namespace(c, "http://jabber.org/protocol/caps"); 256 xmlnode_set_namespace(c, "http://jabber.org/protocol/caps");
184 xmlnode_set_attrib(c, "node", CAPS0115_NODE); 257 xmlnode_set_attrib(c, "node", CAPS0115_NODE);
185 xmlnode_set_attrib(c, "ver", VERSION); 258 xmlnode_set_attrib(c, "ver", VERSION);
259
260 if(js != NULL) {
261 /* add the extensions */
262 char extlist[1024];
263 unsigned remaining = 1023; /* one less for the \0 */
264 GList *feature;
265
266 extlist[0] = '\0';
267 for(feature = jabber_features; feature && remaining > 0; feature = feature->next) {
268 JabberFeature *feat = (JabberFeature*)feature->data;
269 unsigned featlen;
270
271 if(feat->is_enabled != NULL && feat->is_enabled(js, feat->shortname, feat->namespace) == FALSE)
272 continue; /* skip this feature */
273
274 featlen = strlen(feat->shortname);
275
276 /* cut off when we don't have any more space left in our buffer (too bad) */
277 if(featlen > remaining)
278 break;
279
280 strncat(extlist,feat->shortname,remaining);
281 remaining -= featlen;
282 if(feature->next) { /* no space at the end */
283 strncat(extlist," ",remaining);
284 --remaining;
285 }
286 }
287 /* did we add anything? */
288 if(remaining < 1023)
289 xmlnode_set_attrib(c, "ext", extlist);
290 }
186 291
187 return presence; 292 return presence;
188 } 293 }
189 294
190 struct _jabber_add_permit { 295 struct _jabber_add_permit {
248 353
249 purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash); 354 purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash);
250 g_free(text); 355 g_free(text);
251 } 356 }
252 } 357 }
358 }
359
360 typedef struct _JabberPresenceCapabilities {
361 JabberStream *js;
362 JabberBuddyResource *jbr;
363 char *from;
364 } JabberPresenceCapabilities;
365
366 static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) {
367 JabberPresenceCapabilities *userdata = user_data;
368 GList *iter;
369
370 if(userdata->jbr->caps)
371 jabber_caps_free_clientinfo(userdata->jbr->caps);
372 userdata->jbr->caps = info;
373
374 for(iter = info->features; iter; iter = g_list_next(iter)) {
375 if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) {
376 JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
377 xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items");
378 xmlnode_set_attrib(iq->node, "to", userdata->from);
379 xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
380
381 jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
382 jabber_iq_send(iq);
383 break;
384 }
385 }
386 g_free(user_data);
253 } 387 }
254 388
255 void jabber_presence_parse(JabberStream *js, xmlnode *packet) 389 void jabber_presence_parse(JabberStream *js, xmlnode *packet)
256 { 390 {
257 const char *from = xmlnode_get_attrib(packet, "from"); 391 const char *from = xmlnode_get_attrib(packet, "from");
271 char *buddy_name; 405 char *buddy_name;
272 JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN; 406 JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN;
273 xmlnode *y; 407 xmlnode *y;
274 gboolean muc = FALSE; 408 gboolean muc = FALSE;
275 char *avatar_hash = NULL; 409 char *avatar_hash = NULL;
410 xmlnode *caps = NULL;
276 411
277 if(!(jb = jabber_buddy_find(js, from, TRUE))) 412 if(!(jb = jabber_buddy_find(js, from, TRUE)))
278 return; 413 return;
279 414
280 if(!(jid = jabber_id_new(from))) 415 if(!(jid = jabber_id_new(from)))
333 } 468 }
334 } 469 }
335 470
336 471
337 for(y = packet->child; y; y = y->next) { 472 for(y = packet->child; y; y = y->next) {
473 const char *xmlns;
338 if(y->type != XMLNODE_TYPE_TAG) 474 if(y->type != XMLNODE_TYPE_TAG)
339 continue; 475 continue;
476 xmlns = xmlnode_get_namespace(y);
340 477
341 if(!strcmp(y->name, "status")) { 478 if(!strcmp(y->name, "status")) {
342 g_free(status); 479 g_free(status);
343 status = xmlnode_get_data(y); 480 status = xmlnode_get_data(y);
344 } else if(!strcmp(y->name, "priority")) { 481 } else if(!strcmp(y->name, "priority")) {
345 char *p = xmlnode_get_data(y); 482 char *p = xmlnode_get_data(y);
346 if(p) { 483 if(p) {
347 priority = atoi(p); 484 priority = atoi(p);
348 g_free(p); 485 g_free(p);
349 } 486 }
487 } else if(!strcmp(y->name, "delay") && !strcmp(xmlns, "urn:xmpp:delay")) {
488 /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */
489 delayed = TRUE;
490 } else if(!strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) {
491 caps = y; /* store for later, when creating buddy resource */
350 } else if(!strcmp(y->name, "x")) { 492 } else if(!strcmp(y->name, "x")) {
351 const char *xmlns = xmlnode_get_namespace(y); 493 const char *xmlns = xmlnode_get_namespace(y);
352 if(xmlns && !strcmp(xmlns, "jabber:x:delay")) { 494 if(xmlns && !strcmp(xmlns, "jabber:x:delay")) {
353 /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ 495 /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */
354 delayed = TRUE; 496 delayed = TRUE;
522 flags); 664 flags);
523 } 665 }
524 g_free(room_jid); 666 g_free(room_jid);
525 } else { 667 } else {
526 buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "", 668 buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "",
527 jid->node ? "@" : "", jid->domain); 669 jid->node ? "@" : "", jid->domain);
528 if((b = purple_find_buddy(js->gc->account, buddy_name)) == NULL) { 670 if((b = purple_find_buddy(js->gc->account, buddy_name)) == NULL) {
529 purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%x)\n", 671 if(!jid->node || strcmp(jid->node,js->user->node) || strcmp(jid->domain,js->user->domain)) {
530 buddy_name, purple_account_get_username(js->gc->account), js->gc->account); 672 purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%x)\n",
531 jabber_id_free(jid); 673 buddy_name, purple_account_get_username(js->gc->account), js->gc->account);
532 g_free(avatar_hash); 674 jabber_id_free(jid);
533 g_free(buddy_name); 675 g_free(avatar_hash);
534 g_free(status); 676 g_free(buddy_name);
535 return; 677 g_free(status);
536 } 678 return;
537 679 } else {
538 if(avatar_hash) { 680 /* this is a different resource of our own account. Resume even when this account isn't on our blist */
681 }
682 }
683
684 if(b && avatar_hash) {
539 const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b); 685 const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b);
540 if(!avatar_hash2 || strcmp(avatar_hash, avatar_hash2)) { 686 if(!avatar_hash2 || strcmp(avatar_hash, avatar_hash2)) {
541 JabberIq *iq; 687 JabberIq *iq;
542 xmlnode *vcard; 688 xmlnode *vcard;
543 689
571 purple_conversation_set_name(conv, buddy_name); 717 purple_conversation_set_name(conv, buddy_name);
572 718
573 } else { 719 } else {
574 jbr = jabber_buddy_track_resource(jb, jid->resource, priority, 720 jbr = jabber_buddy_track_resource(jb, jid->resource, priority,
575 state, status); 721 state, status);
722 if(caps) {
723 const char *node = xmlnode_get_attrib(caps,"node");
724 const char *ver = xmlnode_get_attrib(caps,"ver");
725 const char *ext = xmlnode_get_attrib(caps,"ext");
726
727 if(node && ver) {
728 JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
729 userdata->js = js;
730 userdata->jbr = jbr;
731 userdata->from = g_strdup(from);
732 jabber_caps_get_info(js, from, node, ver, ext, jabber_presence_set_capabilities, userdata);
733 }
734 }
576 } 735 }
577 736
578 if((found_jbr = jabber_buddy_find_resource(jb, NULL))) { 737 if((found_jbr = jabber_buddy_find_resource(jb, NULL))) {
579 purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, found_jbr->status ? "message" : NULL, found_jbr->status, NULL); 738 purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, found_jbr->status ? "message" : NULL, found_jbr->status, NULL);
580 } else { 739 } else {