| 91 } |
91 } |
| 92 } |
92 } |
| 93 g_free(my_base_jid); |
93 g_free(my_base_jid); |
| 94 } |
94 } |
| 95 |
95 |
| 96 |
96 void jabber_set_status(PurpleAccount *account, PurpleStatus *status) |
| 97 void jabber_presence_send(PurpleAccount *account, PurpleStatus *status) |
97 { |
| 98 { |
98 PurpleConnection *gc; |
| 99 PurpleConnection *gc = NULL; |
99 JabberStream *js; |
| 100 JabberStream *js = NULL; |
100 |
| |
101 if (!purple_account_is_connected(account)) |
| |
102 return; |
| |
103 |
| |
104 if (!purple_status_is_active(status)) |
| |
105 return; |
| |
106 |
| |
107 if (purple_status_is_exclusive(status) && !purple_status_is_active(status)) { |
| |
108 /* An exclusive status can't be deactivated. You should just |
| |
109 * activate some other exclusive status. */ |
| |
110 return; |
| |
111 } |
| |
112 |
| |
113 gc = purple_account_get_connection(account); |
| |
114 js = gc->proto_data; |
| |
115 jabber_presence_send(js, FALSE); |
| |
116 } |
| |
117 |
| |
118 void jabber_presence_send(JabberStream *js, gboolean force) |
| |
119 { |
| |
120 PurpleAccount *account; |
| 101 xmlnode *presence, *x, *photo; |
121 xmlnode *presence, *x, *photo; |
| 102 char *stripped = NULL; |
122 char *stripped = NULL; |
| 103 JabberBuddyState state; |
123 JabberBuddyState state; |
| 104 int priority; |
124 int priority; |
| 105 const char *artist = NULL, *title = NULL, *source = NULL, *uri = NULL, *track = NULL; |
125 const char *artist = NULL, *title = NULL, *source = NULL, *uri = NULL, *track = NULL; |
| 106 int length = -1; |
126 int length = -1; |
| 107 gboolean allowBuzz; |
127 gboolean allowBuzz; |
| 108 PurplePresence *p; |
128 PurplePresence *p; |
| 109 PurpleStatus *tune; |
129 PurpleStatus *status, *tune; |
| 110 |
130 |
| 111 if (purple_account_is_disconnected(account)) |
131 account = purple_connection_get_account(js->gc); |
| 112 return; |
|
| 113 |
|
| 114 p = purple_account_get_presence(account); |
132 p = purple_account_get_presence(account); |
| 115 if (NULL == status) { |
133 status = purple_presence_get_active_status(p); |
| 116 status = purple_presence_get_active_status(p); |
|
| 117 } |
|
| 118 |
|
| 119 if (purple_status_is_exclusive(status)) { |
|
| 120 /* An exclusive status can't be deactivated. You should just |
|
| 121 * activate some other exclusive status. */ |
|
| 122 if (!purple_status_is_active(status)) |
|
| 123 return; |
|
| 124 } else { |
|
| 125 /* Work with the exclusive status. */ |
|
| 126 status = purple_presence_get_active_status(p); |
|
| 127 } |
|
| 128 |
|
| 129 gc = purple_account_get_connection(account); |
|
| 130 js = gc->proto_data; |
|
| 131 |
134 |
| 132 /* we don't want to send presence before we've gotten our roster */ |
135 /* we don't want to send presence before we've gotten our roster */ |
| 133 if(!js->roster_parsed) { |
136 if(!js->roster_parsed) { |
| 134 purple_debug_info("jabber", "attempt to send presence before roster retrieved\n"); |
137 purple_debug_info("jabber", "attempt to send presence before roster retrieved\n"); |
| 135 return; |
138 return; |
| 139 |
142 |
| 140 /* check for buzz support */ |
143 /* check for buzz support */ |
| 141 allowBuzz = purple_status_get_attr_boolean(status,"buzz"); |
144 allowBuzz = purple_status_get_attr_boolean(status,"buzz"); |
| 142 /* changing the buzz state has to trigger a re-broadcasting of the presence for caps */ |
145 /* changing the buzz state has to trigger a re-broadcasting of the presence for caps */ |
| 143 |
146 |
| 144 if (js->googletalk && stripped == NULL && purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) { |
147 tune = purple_presence_get_status(p, "tune"); |
| 145 tune = purple_presence_get_status(p, "tune"); |
148 if (js->googletalk && !stripped && purple_status_is_active(tune)) { |
| 146 stripped = jabber_google_presence_outgoing(tune); |
149 stripped = jabber_google_presence_outgoing(tune); |
| 147 } |
150 } |
| 148 |
151 |
| 149 #define CHANGED(a,b) ((!a && b) || (a && a[0] == '\0' && b && b[0] != '\0') || \ |
152 #define CHANGED(a,b) ((!a && b) || (a && a[0] == '\0' && b && b[0] != '\0') || \ |
| 150 (a && !b) || (a && a[0] != '\0' && b && b[0] == '\0') || (a && b && strcmp(a,b))) |
153 (a && !b) || (a && a[0] != '\0' && b && b[0] == '\0') || (a && b && strcmp(a,b))) |
| 151 /* check if there are any differences to the <presence> and send them in that case */ |
154 /* check if there are any differences to the <presence> and send them in that case */ |
| 152 if (allowBuzz != js->allowBuzz || js->old_state != state || CHANGED(js->old_msg, stripped) || |
155 if (force || allowBuzz != js->allowBuzz || js->old_state != state || |
| 153 js->old_priority != priority || CHANGED(js->old_avatarhash, js->avatar_hash)) { |
156 CHANGED(js->old_msg, stripped) || js->old_priority != priority || |
| |
157 CHANGED(js->old_avatarhash, js->avatar_hash)) { |
| |
158 /* Need to update allowBuzz before creating the presence (with caps) */ |
| 154 js->allowBuzz = allowBuzz; |
159 js->allowBuzz = allowBuzz; |
| 155 |
160 |
| 156 presence = jabber_presence_create_js(js, state, stripped, priority); |
161 presence = jabber_presence_create_js(js, state, stripped, priority); |
| 157 |
162 |
| 158 if(js->avatar_hash) { |
163 if(js->avatar_hash) { |
| 179 js->old_priority = priority; |
184 js->old_priority = priority; |
| 180 } |
185 } |
| 181 g_free(stripped); |
186 g_free(stripped); |
| 182 |
187 |
| 183 /* next, check if there are any changes to the tune values */ |
188 /* next, check if there are any changes to the tune values */ |
| 184 tune = purple_presence_get_status(p, "tune"); |
189 if (purple_status_is_active(tune)) { |
| 185 if (tune && purple_status_is_active(tune)) { |
|
| 186 artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST); |
190 artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST); |
| 187 title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE); |
191 title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE); |
| 188 source = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM); |
192 source = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM); |
| 189 uri = purple_status_get_attr_string(tune, PURPLE_TUNE_URL); |
193 uri = purple_status_get_attr_string(tune, PURPLE_TUNE_URL); |
| 190 track = purple_status_get_attr_string(tune, PURPLE_TUNE_TRACK); |
194 track = purple_status_get_attr_string(tune, PURPLE_TUNE_TRACK); |
| 258 xmlnode_insert_data(pri, pstr, -1); |
262 xmlnode_insert_data(pri, pstr, -1); |
| 259 g_free(pstr); |
263 g_free(pstr); |
| 260 } |
264 } |
| 261 |
265 |
| 262 /* JEP-0115 */ |
266 /* JEP-0115 */ |
| |
267 /* calculate hash */ |
| |
268 jabber_caps_calculate_own_hash(js); |
| |
269 /* create xml */ |
| 263 c = xmlnode_new_child(presence, "c"); |
270 c = xmlnode_new_child(presence, "c"); |
| 264 xmlnode_set_namespace(c, "http://jabber.org/protocol/caps"); |
271 xmlnode_set_namespace(c, "http://jabber.org/protocol/caps"); |
| 265 xmlnode_set_attrib(c, "node", CAPS0115_NODE); |
272 xmlnode_set_attrib(c, "node", CAPS0115_NODE); |
| 266 xmlnode_set_attrib(c, "ver", VERSION); |
273 xmlnode_set_attrib(c, "hash", "sha-1"); |
| 267 |
274 xmlnode_set_attrib(c, "ver", jabber_caps_get_own_hash(js)); |
| |
275 |
| |
276 #if 0 |
| 268 if(js != NULL) { |
277 if(js != NULL) { |
| 269 /* add the extensions */ |
278 /* add the extensions */ |
| 270 char extlist[1024]; |
279 char extlist[1024]; |
| 271 unsigned remaining = 1023; /* one less for the \0 */ |
280 unsigned remaining = 1023; /* one less for the \0 */ |
| 272 GList *feature; |
281 GList *feature; |
| 274 extlist[0] = '\0'; |
283 extlist[0] = '\0'; |
| 275 for(feature = jabber_features; feature && remaining > 0; feature = feature->next) { |
284 for(feature = jabber_features; feature && remaining > 0; feature = feature->next) { |
| 276 JabberFeature *feat = (JabberFeature*)feature->data; |
285 JabberFeature *feat = (JabberFeature*)feature->data; |
| 277 unsigned featlen; |
286 unsigned featlen; |
| 278 |
287 |
| 279 if(feat->is_enabled != NULL && feat->is_enabled(js, feat->shortname, feat->namespace) == FALSE) |
288 if(feat->is_enabled != NULL && feat->is_enabled(js, feat->namespace) == FALSE) |
| 280 continue; /* skip this feature */ |
289 continue; /* skip this feature */ |
| 281 |
290 |
| 282 featlen = strlen(feat->shortname); |
291 featlen = strlen(feat->shortname); |
| 283 |
292 |
| 284 /* cut off when we don't have any more space left in our buffer (too bad) */ |
293 /* cut off when we don't have any more space left in our buffer (too bad) */ |
| 363 JabberStream *js; |
372 JabberStream *js; |
| 364 JabberBuddy *jb; |
373 JabberBuddy *jb; |
| 365 char *from; |
374 char *from; |
| 366 } JabberPresenceCapabilities; |
375 } JabberPresenceCapabilities; |
| 367 |
376 |
| 368 static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) { |
377 static void |
| 369 JabberPresenceCapabilities *userdata = user_data; |
378 jabber_presence_set_capabilities(JabberCapsClientInfo *info, GList *exts, |
| 370 JabberID *jid; |
379 JabberPresenceCapabilities *userdata) |
| |
380 { |
| 371 JabberBuddyResource *jbr; |
381 JabberBuddyResource *jbr; |
| 372 GList *iter; |
382 char *resource = g_utf8_strrchr(userdata->from, -1, '/'); |
| 373 |
383 resource += 1; |
| 374 jid = jabber_id_new(userdata->from); |
384 |
| 375 jbr = jabber_buddy_find_resource(userdata->jb, jid->resource); |
385 jbr = jabber_buddy_find_resource(userdata->jb, resource); |
| 376 jabber_id_free(jid); |
386 if (!jbr) { |
| 377 |
|
| 378 if(!jbr) { |
|
| 379 g_free(userdata->from); |
387 g_free(userdata->from); |
| 380 g_free(userdata); |
388 g_free(userdata); |
| 381 return; |
389 if (exts) { |
| 382 } |
390 g_list_foreach(exts, (GFunc)g_free, NULL); |
| 383 |
391 g_list_free(exts); |
| 384 if(jbr->caps) |
392 } |
| 385 jabber_caps_free_clientinfo(jbr->caps); |
393 return; |
| 386 jbr->caps = info; |
394 } |
| 387 |
395 |
| 388 if (info) { |
396 /* Any old jbr->caps.info is owned by the caps code */ |
| 389 for(iter = info->features; iter; iter = g_list_next(iter)) { |
397 if (jbr->caps.exts) { |
| 390 if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) { |
398 g_list_foreach(jbr->caps.exts, (GFunc)g_free, NULL); |
| 391 JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); |
399 g_list_free(jbr->caps.exts); |
| 392 xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items"); |
400 } |
| 393 xmlnode_set_attrib(iq->node, "to", userdata->from); |
401 |
| 394 xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); |
402 jbr->caps.info = info; |
| 395 |
403 jbr->caps.exts = exts; |
| 396 jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL); |
404 |
| 397 jabber_iq_send(iq); |
405 if (jabber_resource_has_capability(jbr, "http://jabber.org/protocol/commands")) { |
| 398 break; |
406 JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); |
| 399 } |
407 xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", "http://jabber.org/protocol/disco#items"); |
| 400 } |
408 xmlnode_set_attrib(iq->node, "to", userdata->from); |
| |
409 xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); |
| |
410 jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL); |
| |
411 jabber_iq_send(iq); |
| 401 } |
412 } |
| 402 |
413 |
| 403 g_free(userdata->from); |
414 g_free(userdata->from); |
| 404 g_free(userdata); |
415 g_free(userdata); |
| 405 } |
416 } |
| 740 |
751 |
| 741 } else { |
752 } else { |
| 742 jbr = jabber_buddy_track_resource(jb, jid->resource, priority, |
753 jbr = jabber_buddy_track_resource(jb, jid->resource, priority, |
| 743 state, status); |
754 state, status); |
| 744 if(caps) { |
755 if(caps) { |
| |
756 /* handle XEP-0115 */ |
| 745 const char *node = xmlnode_get_attrib(caps,"node"); |
757 const char *node = xmlnode_get_attrib(caps,"node"); |
| 746 const char *ver = xmlnode_get_attrib(caps,"ver"); |
758 const char *ver = xmlnode_get_attrib(caps,"ver"); |
| |
759 const char *hash = xmlnode_get_attrib(caps,"hash"); |
| 747 const char *ext = xmlnode_get_attrib(caps,"ext"); |
760 const char *ext = xmlnode_get_attrib(caps,"ext"); |
| 748 |
761 |
| 749 if(node && ver) { |
762 /* v1.3 uses: node, ver, and optionally ext. |
| |
763 * v1.5 uses: node, ver, and hash. */ |
| |
764 if (node && ver) { |
| 750 JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1); |
765 JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1); |
| 751 userdata->js = js; |
766 userdata->js = js; |
| 752 userdata->jb = jb; |
767 userdata->jb = jb; |
| 753 userdata->from = g_strdup(from); |
768 userdata->from = g_strdup(from); |
| 754 jabber_caps_get_info(js, from, node, ver, ext, jabber_presence_set_capabilities, userdata); |
769 jabber_caps_get_info(js, from, node, ver, hash, ext, |
| |
770 (jabber_caps_get_info_cb)jabber_presence_set_capabilities, |
| |
771 userdata); |
| 755 } |
772 } |
| 756 } |
773 } |
| 757 } |
774 } |
| 758 |
775 |
| 759 if((found_jbr = jabber_buddy_find_resource(jb, NULL))) { |
776 if((found_jbr = jabber_buddy_find_resource(jb, NULL))) { |