59
61
g_free(chat_full_jid);
62
void jabber_presence_fake_to_self(JabberStream *js, const PurpleStatus *gstatus) {
68
my_base_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
69
if(purple_find_buddy(js->gc->account, my_base_jid)) {
64
void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status)
66
PurpleAccount *account;
67
PurplePresence *presence;
70
g_return_if_fail(js->user != NULL);
72
account = purple_connection_get_account(js->gc);
73
username = purple_connection_get_display_name(js->gc);
74
presence = purple_account_get_presence(account);
76
status = purple_presence_get_active_status(presence);
78
if (purple_find_buddy(account, username)) {
79
JabberBuddy *jb = jabber_buddy_find(js, username, TRUE);
71
80
JabberBuddyResource *jbr;
72
if((jb = jabber_buddy_find(js, my_base_jid, TRUE))) {
73
JabberBuddyState state;
77
purple_status_to_jabber(gstatus, &state, &msg, &priority);
79
if (state == JABBER_BUDDY_STATE_UNAVAILABLE || state == JABBER_BUDDY_STATE_UNKNOWN) {
80
jabber_buddy_remove_resource(jb, js->user->resource);
82
jabber_buddy_track_resource(jb, js->user->resource, priority, state, msg);
84
if((jbr = jabber_buddy_find_resource(jb, NULL))) {
85
purple_prpl_got_user_status(js->gc->account, my_base_jid, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL);
87
purple_prpl_got_user_status(js->gc->account, my_base_jid, "offline", msg ? "message" : NULL, msg, NULL);
97
void jabber_presence_send(PurpleAccount *account, PurpleStatus *status)
99
PurpleConnection *gc = NULL;
100
JabberStream *js = NULL;
81
JabberBuddyState state;
85
g_return_if_fail(jb != NULL);
87
purple_status_to_jabber(status, &state, &msg, &priority);
89
if (state == JABBER_BUDDY_STATE_UNAVAILABLE ||
90
state == JABBER_BUDDY_STATE_UNKNOWN) {
91
jabber_buddy_remove_resource(jb, js->user->resource);
93
jbr = jabber_buddy_track_resource(jb, js->user->resource, priority,
95
jbr->idle = purple_presence_is_idle(presence) ?
96
purple_presence_get_idle_time(presence) : 0;
99
if ((jbr = jabber_buddy_find_resource(jb, NULL))) {
100
purple_prpl_got_user_status(account, username,
101
jabber_buddy_state_get_status_id(jbr->state),
102
"priority", jbr->priority,
103
jbr->status ? "message" : NULL, jbr->status,
105
purple_prpl_got_user_idle(account, username, jbr->idle, jbr->idle);
107
purple_prpl_got_user_status(account, username, "offline",
108
msg ? "message" : NULL, msg,
115
void jabber_set_status(PurpleAccount *account, PurpleStatus *status)
117
PurpleConnection *gc;
120
if (!purple_account_is_connected(account))
123
if (purple_status_is_exclusive(status) && !purple_status_is_active(status)) {
124
/* An exclusive status can't be deactivated. You should just
125
* activate some other exclusive status. */
129
gc = purple_account_get_connection(account);
130
js = purple_connection_get_protocol_data(gc);
131
jabber_presence_send(js, FALSE);
134
void jabber_presence_send(JabberStream *js, gboolean force)
136
PurpleAccount *account;
101
137
xmlnode *presence, *x, *photo;
102
138
char *stripped = NULL;
103
139
JabberBuddyState state;
107
143
gboolean allowBuzz;
108
144
PurplePresence *p;
111
if (purple_account_is_disconnected(account))
145
PurpleStatus *status, *tune;
147
account = purple_connection_get_account(js->gc);
114
148
p = purple_account_get_presence(account);
115
if (NULL == status) {
116
status = purple_presence_get_active_status(p);
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))
125
/* Work with the exclusive status. */
126
status = purple_presence_get_active_status(p);
129
gc = purple_account_get_connection(account);
149
status = purple_presence_get_active_status(p);
132
151
/* we don't want to send presence before we've gotten our roster */
133
if(!js->roster_parsed) {
152
if (js->state != JABBER_STREAM_CONNECTED) {
134
153
purple_debug_info("jabber", "attempt to send presence before roster retrieved\n");
138
157
purple_status_to_jabber(status, &state, &stripped, &priority);
140
159
/* check for buzz support */
141
160
allowBuzz = purple_status_get_attr_boolean(status,"buzz");
142
161
/* changing the buzz state has to trigger a re-broadcasting of the presence for caps */
144
if (js->googletalk && stripped == NULL && purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) {
145
tune = purple_presence_get_status(p, "tune");
163
tune = purple_presence_get_status(p, "tune");
164
if (js->googletalk && !stripped && purple_status_is_active(tune)) {
146
165
stripped = jabber_google_presence_outgoing(tune);
149
168
#define CHANGED(a,b) ((!a && b) || (a && a[0] == '\0' && b && b[0] != '\0') || \
150
169
(a && !b) || (a && a[0] != '\0' && b && b[0] == '\0') || (a && b && strcmp(a,b)))
151
170
/* 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) ||
153
js->old_priority != priority || CHANGED(js->old_avatarhash, js->avatar_hash)) {
171
if (force || allowBuzz != js->allowBuzz || js->old_state != state ||
172
CHANGED(js->old_msg, stripped) || js->old_priority != priority ||
173
CHANGED(js->old_avatarhash, js->avatar_hash) || js->old_idle != js->idle) {
174
/* Need to update allowBuzz before creating the presence (with caps) */
154
175
js->allowBuzz = allowBuzz;
156
177
presence = jabber_presence_create_js(js, state, stripped, priority);
158
if(js->avatar_hash) {
159
x = xmlnode_new_child(presence, "x");
160
xmlnode_set_namespace(x, "vcard-temp:x:update");
179
/* Per XEP-0153 4.1, we must always send the <x> */
180
x = xmlnode_new_child(presence, "x");
181
xmlnode_set_namespace(x, "vcard-temp:x:update");
183
* FIXME: Per XEP-0153 4.3.2 bullet 2, we must not publish our
184
* image hash if another resource has logged in and updated the
185
* vcard avatar. Requires changes in jabber_presence_parse.
187
if (js->vcard_fetched) {
188
/* Always publish a <photo>; it's empty if we have no image. */
161
189
photo = xmlnode_new_child(x, "photo");
162
xmlnode_insert_data(photo, js->avatar_hash, -1);
191
xmlnode_insert_data(photo, js->avatar_hash, -1);
165
194
jabber_send(js, presence);
167
196
g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence);
168
197
xmlnode_free(presence);
170
199
/* update old values */
173
202
g_free(js->old_msg);
174
203
if(js->old_avatarhash)
294
/* if we are idle and not offline, include idle */
295
if (js->idle && state != JABBER_BUDDY_STATE_UNAVAILABLE) {
296
xmlnode *query = xmlnode_new_child(presence, "query");
298
g_snprintf(seconds, 10, "%d", (int) (time(NULL) - js->idle));
300
xmlnode_set_namespace(query, "jabber:iq:last");
301
xmlnode_set_attrib(query, "seconds", seconds);
306
jabber_caps_calculate_own_hash(js);
263
308
c = xmlnode_new_child(presence, "c");
264
309
xmlnode_set_namespace(c, "http://jabber.org/protocol/caps");
265
310
xmlnode_set_attrib(c, "node", CAPS0115_NODE);
266
xmlnode_set_attrib(c, "ver", VERSION);
269
/* add the extensions */
271
unsigned remaining = 1023; /* one less for the \0 */
275
for(feature = jabber_features; feature && remaining > 0; feature = feature->next) {
276
JabberFeature *feat = (JabberFeature*)feature->data;
279
if(feat->is_enabled != NULL && feat->is_enabled(js, feat->shortname, feat->namespace) == FALSE)
280
continue; /* skip this feature */
282
featlen = strlen(feat->shortname);
284
/* cut off when we don't have any more space left in our buffer (too bad) */
285
if(featlen > remaining)
288
strncat(extlist,feat->shortname,remaining);
289
remaining -= featlen;
290
if(feature->next) { /* no space at the end */
291
strncat(extlist," ",remaining);
295
/* did we add anything? */
297
xmlnode_set_attrib(c, "ext", extlist);
311
xmlnode_set_attrib(c, "hash", "sha-1");
312
xmlnode_set_attrib(c, "ver", jabber_caps_get_own_hash(js));
316
* MASSIVE HUGE DISGUSTING HACK
317
* This is a huge hack. As far as I can tell, Google Talk's gmail client
318
* doesn't bother to check the actual features we advertise; they
319
* just assume that if we specify a 'voice-v1' ext (ignoring that
320
* these are to be assigned no semantic value), we support receiving voice
323
* Ditto for 'video-v1'.
325
audio_enabled = jabber_audio_enabled(js, NULL /* unused */);
326
video_enabled = jabber_video_enabled(js, NULL /* unused */);
328
if (audio_enabled && video_enabled)
329
xmlnode_set_attrib(c, "ext", "voice-v1 camera-v1 video-v1");
330
else if (audio_enabled)
331
xmlnode_set_attrib(c, "ext", "voice-v1");
332
else if (video_enabled)
333
xmlnode_set_attrib(c, "ext", "camera-v1 video-v1");
329
static void jabber_vcard_parse_avatar(JabberStream *js, xmlnode *packet, gpointer blah)
366
jabber_vcard_parse_avatar(JabberStream *js, const char *from,
367
JabberIqType type, const char *id,
368
xmlnode *packet, gpointer blah)
331
370
JabberBuddy *jb = NULL;
332
xmlnode *vcard, *photo, *binval;
371
xmlnode *vcard, *photo, *binval, *fn, *nick;
336
const char *from = xmlnode_get_attrib(packet, "from");
345
381
if((vcard = xmlnode_get_child(packet, "vCard")) ||
346
382
(vcard = xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) {
383
/* The logic here regarding the nickname and full name is copied from
384
* buddy.c:jabber_vcard_parse. */
385
gchar *nickname = NULL;
386
if ((fn = xmlnode_get_child(vcard, "FN")))
387
nickname = xmlnode_get_data(fn);
389
if ((nick = xmlnode_get_child(vcard, "NICKNAME"))) {
390
char *tmp = xmlnode_get_data(nick);
391
char *bare_jid = jabber_get_bare_jid(from);
392
if (tmp && strstr(bare_jid, tmp) == NULL) {
402
serv_got_alias(js->gc, from, nickname);
347
406
if((photo = xmlnode_get_child(vcard, "PHOTO")) &&
348
407
(( (binval = xmlnode_get_child(photo, "BINVAL")) &&
349
408
(text = xmlnode_get_data(binval))) ||
350
409
(text = xmlnode_get_data(photo)))) {
353
414
data = purple_base64_decode(text, &size);
354
415
hash = jabber_calculate_data_sha1sum(data, size);
355
417
purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash);
366
428
} JabberPresenceCapabilities;
368
static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) {
369
JabberPresenceCapabilities *userdata = user_data;
431
jabber_presence_set_capabilities(JabberCapsClientInfo *info, GList *exts,
432
JabberPresenceCapabilities *userdata)
371
434
JabberBuddyResource *jbr;
374
jid = jabber_id_new(userdata->from);
375
jbr = jabber_buddy_find_resource(userdata->jb, jid->resource);
435
char *resource = g_utf8_strchr(userdata->from, -1, '/');
440
jbr = jabber_buddy_find_resource(userdata->jb, resource);
379
442
g_free(userdata->from);
380
443
g_free(userdata);
445
g_list_foreach(exts, (GFunc)g_free, NULL);
385
jabber_caps_free_clientinfo(jbr->caps);
389
for(iter = info->features; iter; iter = g_list_next(iter)) {
390
if(!strcmp((const char*)iter->data, "http://jabber.org/protocol/commands")) {
391
JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
392
xmlnode *query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items");
393
xmlnode_set_attrib(iq->node, "to", userdata->from);
394
xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
396
jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
451
/* Any old jbr->caps.info is owned by the caps code */
452
if (jbr->caps.exts) {
453
g_list_foreach(jbr->caps.exts, (GFunc)g_free, NULL);
454
g_list_free(jbr->caps.exts);
457
jbr->caps.info = info;
458
jbr->caps.exts = exts;
463
if (!jbr->commands_fetched && jabber_resource_has_capability(jbr, "http://jabber.org/protocol/commands")) {
464
JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
465
xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", "http://jabber.org/protocol/disco#items");
466
xmlnode_set_attrib(iq->node, "to", userdata->from);
467
xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
468
jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
471
jbr->commands_fetched = TRUE;
476
* Versions of libpurple before 2.6.0 didn't advertise this capability, so
477
* we can't yet use Entity Capabilities to determine whether or not the
478
* other client supports Entity Capabilities.
480
if (jabber_resource_has_capability(jbr, "http://jabber.org/protocol/chatstates"))
481
jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
483
jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED;
403
487
g_free(userdata->from);
404
488
g_free(userdata);
407
491
void jabber_presence_parse(JabberStream *js, xmlnode *packet)
409
const char *from = xmlnode_get_attrib(packet, "from");
410
const char *type = xmlnode_get_attrib(packet, "type");
411
const char *real_jid = NULL;
412
const char *affiliation = NULL;
413
const char *role = NULL;
414
495
char *status = NULL;
415
496
int priority = 0;
419
500
JabberBuddyResource *jbr = NULL, *found_jbr = NULL;
420
501
PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
421
502
gboolean delayed = FALSE;
503
const gchar *stamp = NULL; /* from <delayed/> element */
422
504
PurpleBuddy *b = NULL;
423
505
char *buddy_name;
424
506
JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN;
426
gboolean muc = FALSE;
427
508
char *avatar_hash = NULL;
428
509
xmlnode *caps = NULL;
430
if(!(jb = jabber_buddy_find(js, from, TRUE)))
433
if(!(jid = jabber_id_new(from)))
511
gchar *nickname = NULL;
512
gboolean signal_return;
514
from = xmlnode_get_attrib(packet, "from");
515
type = xmlnode_get_attrib(packet, "type");
517
jb = jabber_buddy_find(js, from, TRUE);
518
g_return_if_fail(jb != NULL);
520
signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin,
521
"jabber-receiving-presence", js->gc, type, from, packet));
525
jid = jabber_id_new(from);
527
purple_debug_error("jabber", "Ignoring presence with malformed 'from' "
436
532
if(jb->error_msg) {
437
533
g_free(jb->error_msg);
438
534
jb->error_msg = NULL;
441
if(type && !strcmp(type, "error")) {
539
char *show_data = NULL;
541
state = JABBER_BUDDY_STATE_ONLINE;
543
show = xmlnode_get_child(packet, "show");
545
show_data = xmlnode_get_data(show);
547
state = jabber_buddy_show_get_state(show_data);
550
purple_debug_warning("jabber", "<show/> present on presence, "
551
"but no contents!\n");
553
} else if (g_str_equal(type, "error")) {
442
554
char *msg = jabber_parse_error(js, packet, NULL);
444
556
state = JABBER_BUDDY_STATE_ERROR;
445
557
jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence"));
446
} else if(type && !strcmp(type, "subscribe")) {
558
} else if (g_str_equal(type, "subscribe")) {
447
559
struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1);
448
560
gboolean onlist = FALSE;
449
PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(js->gc), from);
561
PurpleAccount *account;
450
563
JabberBuddy *jb = NULL;
566
account = purple_connection_get_account(js->gc);
567
buddy = purple_find_buddy(account, from);
568
nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick");
570
nickname = xmlnode_get_data(nick);
453
573
jb = jabber_buddy_find(js, from, TRUE);
459
579
jap->who = g_strdup(from);
462
purple_account_request_authorization(purple_connection_get_account(js->gc), from, NULL, NULL, NULL, onlist,
463
authorize_add_cb, deny_add_cb, jap);
582
purple_account_request_authorization(account, from, NULL, nickname,
583
NULL, onlist, authorize_add_cb, deny_add_cb, jap);
464
586
jabber_id_free(jid);
466
} else if(type && !strcmp(type, "subscribed")) {
588
} else if (g_str_equal(type, "subscribed")) {
467
589
/* we've been allowed to see their presence, but we don't care */
468
590
jabber_id_free(jid);
470
} else if(type && !strcmp(type, "unsubscribe")) {
592
} else if (g_str_equal(type, "unsubscribe")) {
471
593
/* XXX I'm not sure this is the right way to handle this, it
472
594
* might be better to add "unsubscribe" to the presence status
473
595
* if lower down, but I'm not sure. */
476
598
* acknowledging this (and the others) at some point. */
477
599
jabber_id_free(jid);
601
} else if (g_str_equal(type, "probe")) {
602
purple_debug_warning("jabber", "Ignoring presence probe\n");
605
} else if (g_str_equal(type, "unavailable")) {
606
state = JABBER_BUDDY_STATE_UNAVAILABLE;
607
} else if (g_str_equal(type, "unsubscribed")) {
608
state = JABBER_BUDDY_STATE_UNKNOWN;
480
if((y = xmlnode_get_child(packet, "show"))) {
481
char *show = xmlnode_get_data(y);
482
state = jabber_buddy_show_get_state(show);
485
state = JABBER_BUDDY_STATE_ONLINE;
610
purple_debug_warning("jabber", "Ignoring presence with invalid type "
508
635
} else if(!strcmp(y->name, "delay") && !strcmp(xmlns, "urn:xmpp:delay")) {
509
636
/* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */
638
stamp = xmlnode_get_attrib(y, "stamp");
511
639
} else if(!strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) {
512
640
caps = y; /* store for later, when creating buddy resource */
641
} else if (g_str_equal(y->name, "nick") && g_str_equal(xmlns, "http://jabber.org/protocol/nick")) {
642
nickname = xmlnode_get_data(y);
513
643
} else if(!strcmp(y->name, "x")) {
514
644
if(!strcmp(xmlns, "jabber:x:delay")) {
515
645
/* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */
647
stamp = xmlnode_get_attrib(y, "stamp");
517
648
} else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) {
521
if((z = xmlnode_get_child(y, "status"))) {
522
const char *code = xmlnode_get_attrib(z, "code");
523
if(code && !strcmp(code, "201")) {
524
if((chat = jabber_chat_find(js, jid->node, jid->domain))) {
525
chat->config_dialog_type = PURPLE_REQUEST_ACTION;
526
chat->config_dialog_handle =
527
purple_request_action(js->gc,
528
_("Create New Room"),
529
_("Create New Room"),
530
_("You are creating a new room. Would"
531
" you like to configure it, or"
532
" accept the default settings?"),
533
/* Default Action */ 1,
534
purple_connection_get_account(js->gc), NULL, chat->conv,
536
_("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
537
_("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
539
} else if(code && !strcmp(code, "210")) {
540
/* server rewrote room-nick */
541
if((chat = jabber_chat_find(js, jid->node, jid->domain))) {
542
g_free(chat->handle);
543
chat->handle = g_strdup(jid->resource);
547
if((z = xmlnode_get_child(y, "item"))) {
548
real_jid = xmlnode_get_attrib(z, "jid");
549
affiliation = xmlnode_get_attrib(z, "affiliation");
550
role = xmlnode_get_attrib(z, "role");
551
if(affiliation != NULL && !strcmp(affiliation, "owner"))
552
flags |= PURPLE_CBFLAGS_FOUNDER;
554
if (!strcmp(role, "moderator"))
555
flags |= PURPLE_CBFLAGS_OP;
556
else if (!strcmp(role, "participant"))
557
flags |= PURPLE_CBFLAGS_VOICE;
560
649
} else if(!strcmp(xmlns, "vcard-temp:x:update")) {
561
650
xmlnode *photo = xmlnode_get_child(y, "photo");
564
653
avatar_hash = xmlnode_get_data(photo);
656
} else if (!strcmp(y->name, "query") &&
657
!strcmp(xmlnode_get_namespace(y), "jabber:iq:last")) {
658
/* resource has specified idle */
659
const gchar *seconds = xmlnode_get_attrib(y, "seconds");
661
/* we may need to take "delayed" into account here */
662
idle = atoi(seconds);
667
if (idle && delayed && stamp) {
668
/* if we have a delayed presence, we need to add the delay to the idle
670
time_t offset = time(NULL) - purple_str_to_time(stamp, TRUE, NULL, NULL,
672
purple_debug_info("jabber", "got delay %s yielding %ld s offset\n",
571
677
if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) {
572
678
static int i = 1;
573
char *room_jid = g_strdup_printf("%s@%s", jid->node, jid->domain);
575
680
if(state == JABBER_BUDDY_STATE_ERROR) {
576
681
char *title, *msg = jabber_parse_error(js, packet, NULL);
592
697
jabber_chat_destroy(chat);
593
698
jabber_id_free(jid);
596
700
g_free(avatar_hash);
601
if(type && !strcmp(type, "unavailable")) {
707
const char *real_jid = NULL;
708
const char *affiliation = NULL;
709
const char *role = NULL;
710
gboolean is_our_resource = FALSE; /* Is the presence about us? */
713
* XEP-0045 mandates the presence to include a resource (which is
714
* treated as the chat nick). Some non-compliant servers allow
715
* joining without a nick.
717
if (!jid->resource) {
725
x = xmlnode_get_child_with_namespace(packet, "x",
726
"http://jabber.org/protocol/muc#user");
728
xmlnode *status_node;
731
for (status_node = xmlnode_get_child(x, "status"); status_node;
732
status_node = xmlnode_get_next_twin(status_node)) {
733
const char *code = xmlnode_get_attrib(status_node, "code");
737
if (g_str_equal(code, "110")) {
738
is_our_resource = TRUE;
739
} else if (g_str_equal(code, "201")) {
740
if ((chat = jabber_chat_find(js, jid->node, jid->domain))) {
741
chat->config_dialog_type = PURPLE_REQUEST_ACTION;
742
chat->config_dialog_handle =
743
purple_request_action(js->gc,
744
_("Create New Room"),
745
_("Create New Room"),
746
_("You are creating a new room. Would"
747
" you like to configure it, or"
748
" accept the default settings?"),
749
/* Default Action */ 1,
750
purple_connection_get_account(js->gc), NULL, chat->conv,
752
_("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
753
_("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
755
} else if (g_str_equal(code, "210")) {
756
/* server rewrote room-nick */
757
if((chat = jabber_chat_find(js, jid->node, jid->domain))) {
758
g_free(chat->handle);
759
chat->handle = g_strdup(jid->resource);
764
item_node = xmlnode_get_child(x, "item");
766
real_jid = xmlnode_get_attrib(item_node, "jid");
767
affiliation = xmlnode_get_attrib(item_node, "affiliation");
768
role = xmlnode_get_attrib(item_node, "role");
770
if (purple_strequal(affiliation, "owner"))
771
flags |= PURPLE_CBFLAGS_FOUNDER;
773
if (g_str_equal(role, "moderator"))
774
flags |= PURPLE_CBFLAGS_OP;
775
else if (g_str_equal(role, "participant"))
776
flags |= PURPLE_CBFLAGS_VOICE;
782
char *room_jid = g_strdup_printf("%s@%s", jid->node, jid->domain);
784
chat->muc = (x != NULL);
785
chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid);
786
purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle);
788
jabber_chat_disco_traffic(chat);
792
jabber_buddy_track_resource(jb, jid->resource, priority, state,
795
jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role);
797
if(!jabber_chat_find_buddy(chat->conv, jid->resource))
798
purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), jid->resource,
799
real_jid, flags, !delayed);
801
purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource,
803
} else if (g_str_equal(type, "unavailable")) {
602
805
gboolean nick_change = FALSE;
806
gboolean kick = FALSE;
807
gboolean is_our_resource = FALSE; /* Is the presence about us? */
604
809
/* If the chat nick is invalid, we haven't yet joined, or we've
605
810
* already left (it was probably us leaving after we closed the
611
816
jabber_chat_destroy(chat);
612
817
jabber_id_free(jid);
615
819
g_free(avatar_hash);
824
is_our_resource = (0 == g_utf8_collate(jid->resource, chat->handle));
619
826
jabber_buddy_remove_resource(jb, jid->resource);
622
for(x = xmlnode_get_child(packet, "x"); x; x = xmlnode_get_next_twin(x)) {
623
const char *xmlns, *nick, *code;
624
xmlnode *stat, *item;
625
if(!(xmlns = xmlnode_get_namespace(x)) ||
626
strcmp(xmlns, "http://jabber.org/protocol/muc#user"))
628
if(!(stat = xmlnode_get_child(x, "status")))
630
if(!(code = xmlnode_get_attrib(stat, "code")))
632
if(!strcmp(code, "301")) {
828
x = xmlnode_get_child_with_namespace(packet, "x",
829
"http://jabber.org/protocol/muc#user");
830
if (chat->muc && x) {
832
const char *item_jid = NULL;
837
item = xmlnode_get_child(x, "item");
839
item_jid = xmlnode_get_attrib(item, "jid");
841
for (stat = xmlnode_get_child(x, "status"); stat;
842
stat = xmlnode_get_next_twin(stat)) {
843
const char *code = xmlnode_get_attrib(stat, "code");
848
if (g_str_equal(code, "110")) {
849
is_our_resource = TRUE;
850
} else if(!strcmp(code, "301")) {
633
851
/* XXX: we got banned */
634
} else if(!strcmp(code, "303")) {
635
if(!(item = xmlnode_get_child(x, "item")))
637
if(!(nick = xmlnode_get_attrib(item, "nick")))
852
} else if(!strcmp(code, "303") && item &&
853
(nick = xmlnode_get_attrib(item, "nick"))) {
639
854
nick_change = TRUE;
640
855
if(!strcmp(jid->resource, chat->handle)) {
641
856
g_free(chat->handle);
642
857
chat->handle = g_strdup(nick);
860
/* TODO: This should probably be moved out of the loop */
644
861
purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, nick);
645
862
jabber_chat_remove_handle(chat, jid->resource);
647
864
} else if(!strcmp(code, "307")) {
648
/* XXX: we got kicked */
865
/* Someone was kicked from the room */
866
xmlnode *reason = NULL, *actor = NULL;
867
const char *actor_name = NULL;
868
char *reason_text = NULL;
874
reason = xmlnode_get_child(item, "reason");
875
actor = xmlnode_get_child(item, "actor");
878
reason_text = xmlnode_get_data(reason);
880
actor_name = xmlnode_get_attrib(actor, "jid");
883
if (reason_text == NULL)
884
reason_text = g_strdup(_("No reason"));
886
if (is_our_resource) {
887
if (actor_name != NULL)
888
tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"),
889
actor_name, reason_text);
891
tmp = g_strdup_printf(_("You have been kicked: (%s)"),
894
if (actor_name != NULL)
895
tmp = g_strdup_printf(_("Kicked by %s (%s)"),
896
actor_name, reason_text);
898
tmp = g_strdup_printf(_("Kicked (%s)"),
649
905
} else if(!strcmp(code, "321")) {
650
906
/* XXX: removed due to an affiliation change */
651
907
} else if(!strcmp(code, "322")) {
654
910
/* XXX: removed due to system shutdown */
915
* Possibly another connected resource of our JID (see XEP-0045
916
* v1.24 section 7.1.10) being disconnected. Should be
917
* distinguished by the item_jid.
918
* Also possibly works around bits of an Openfire bug. See
921
to = xmlnode_get_attrib(packet, "to");
922
if (is_our_resource && item_jid && !purple_strequal(to, item_jid)) {
923
/* TODO: When the above is a loop, this needs to still act
924
* sanely for all cases (this code is a little fragile). */
925
if (!kick && !nick_change)
926
/* Presumably, kicks and nick changes also affect us. */
927
is_our_resource = FALSE;
658
930
if(!nick_change) {
659
if(!g_utf8_collate(jid->resource, chat->handle)) {
931
if (is_our_resource) {
933
purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), jid->resource,
934
status, PURPLE_MESSAGE_SYSTEM, time(NULL));
660
936
serv_got_chat_left(js->gc, chat->id);
661
937
jabber_chat_destroy(chat);
670
* XEP-0045 mandates the presence to include a resource (which is
671
* treated as the chat nick). Some non-compliant servers allow
672
* joining without a nick.
674
if (!jid->resource) {
684
chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid);
685
purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle);
687
jabber_chat_disco_traffic(chat);
690
jabber_buddy_track_resource(jb, jid->resource, priority, state,
693
jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role);
695
if(!jabber_chat_find_buddy(chat->conv, jid->resource))
696
purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), jid->resource,
697
real_jid, flags, !delayed);
699
purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource,
945
/* A type that isn't available or unavailable */
946
purple_debug_error("jabber", "MUC presence with bad type: %s\n",
953
g_return_if_reached();
704
956
buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "",
705
957
jid->node ? "@" : "", jid->domain);
756
1009
jbr = jabber_buddy_track_resource(jb, jid->resource, priority,
759
const char *node = xmlnode_get_attrib(caps,"node");
760
const char *ver = xmlnode_get_attrib(caps,"ver");
761
const char *ext = xmlnode_get_attrib(caps,"ext");
764
JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
767
userdata->from = g_strdup(from);
768
jabber_caps_get_info(js, from, node, ver, ext, jabber_presence_set_capabilities, userdata);
1012
jbr->idle = time(NULL) - idle;
773
1018
if((found_jbr = jabber_buddy_find_resource(jb, NULL))) {
774
1019
jabber_google_presence_incoming(js, buddy_name, found_jbr);
775
1020
purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL);
1021
purple_prpl_got_user_idle(js->gc->account, buddy_name, found_jbr->idle, found_jbr->idle);
1023
serv_got_alias(js->gc, buddy_name, nickname);
777
1025
purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL);
779
1027
g_free(buddy_name);
1030
if (caps && !type) {
1031
/* handle Entity Capabilities (XEP-0115) */
1032
const char *node = xmlnode_get_attrib(caps, "node");
1033
const char *ver = xmlnode_get_attrib(caps, "ver");
1034
const char *hash = xmlnode_get_attrib(caps, "hash");
1035
const char *ext = xmlnode_get_attrib(caps, "ext");
1037
/* v1.3 uses: node, ver, and optionally ext.
1038
* v1.5 uses: node, ver, and hash. */
1039
if (node && *node && ver && *ver) {
1040
gchar **exts = ext && *ext ? g_strsplit(ext, " ", -1) : NULL;
1041
jbr = jabber_buddy_find_resource(jb, jid->resource);
1043
/* Look it up if we don't already have all this information */
1044
if (!jbr || !jbr->caps.info ||
1045
!g_str_equal(node, jbr->caps.info->tuple.node) ||
1046
!g_str_equal(ver, jbr->caps.info->tuple.ver) ||
1047
!purple_strequal(hash, jbr->caps.info->tuple.hash) ||
1048
!jabber_caps_exts_known(jbr->caps.info, (gchar **)exts)) {
1049
JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
1052
userdata->from = g_strdup(from);
1053
jabber_caps_get_info(js, from, node, ver, hash, exts,
1054
(jabber_caps_get_info_cb)jabber_presence_set_capabilities,
782
1065
jabber_id_free(jid);
783
1066
g_free(avatar_hash);