Mercurial > pidgin.yaz
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 { |