~vish/ubuntu/maverick/pidgin/bug25979

« back to all changes in this revision

Viewing changes to libpurple/protocols/jabber/presence.c

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2009-10-09 19:40:26 UTC
  • mfrom: (1.4.1 upstream) (46.1.10 karmic)
  • Revision ID: james.westby@ubuntu.com-20091009194026-wbqqh0bsbz19nx5q
Tags: 1:2.6.2-1ubuntu7
* Don't stick the buddy list window to all desktops as some
  window managers have trouble to properly unstick it (LP: #346840)
  - debian/patches/11_buddy_list_really_show.patch
* Always use default tray icon size on KDE (LP: #209440)
  - debian/patches/62_tray_icon_size_kde.patch
* Use scrollbars in the preferences dialog if the screen height is
  below 700 px instead of 600 px
  - debian/patches/60_1024x600_gtkprefs.c.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 * purple - Jabber Protocol Plugin
3
3
 *
4
 
 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com>
 
4
 * Purple is the legal property of its developers, whose names are too numerous
 
5
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 
6
 * source distribution.
5
7
 *
6
8
 * This program is free software; you can redistribute it and/or modify
7
9
 * it under the terms of the GNU General Public License as published by
59
61
        g_free(chat_full_jid);
60
62
}
61
63
 
62
 
void jabber_presence_fake_to_self(JabberStream *js, const PurpleStatus *gstatus) {
63
 
        char *my_base_jid;
64
 
 
65
 
        if(!js->user)
66
 
                return;
67
 
 
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)) {
70
 
                JabberBuddy *jb;
 
64
void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status)
 
65
{
 
66
        PurpleAccount *account;
 
67
        PurplePresence *presence;
 
68
        const char *username;
 
69
 
 
70
        g_return_if_fail(js->user != NULL);
 
71
 
 
72
        account = purple_connection_get_account(js->gc);
 
73
        username = purple_connection_get_display_name(js->gc);
 
74
        presence = purple_account_get_presence(account);
 
75
        if (status == NULL)
 
76
                status = purple_presence_get_active_status(presence);
 
77
 
 
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;
74
 
                        char *msg;
75
 
                        int priority;
76
 
 
77
 
                        purple_status_to_jabber(gstatus, &state, &msg, &priority);
78
 
 
79
 
                        if (state == JABBER_BUDDY_STATE_UNAVAILABLE || state == JABBER_BUDDY_STATE_UNKNOWN) {
80
 
                                jabber_buddy_remove_resource(jb, js->user->resource);
81
 
                        } else {
82
 
                                jabber_buddy_track_resource(jb, js->user->resource, priority, state, msg);
83
 
                        }
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);
86
 
                        } else {
87
 
                                purple_prpl_got_user_status(js->gc->account, my_base_jid, "offline", msg ? "message" : NULL, msg, NULL);
88
 
                        }
89
 
 
90
 
                        g_free(msg);
91
 
                }
92
 
        }
93
 
        g_free(my_base_jid);
94
 
}
95
 
 
96
 
 
97
 
void jabber_presence_send(PurpleAccount *account, PurpleStatus *status)
98
 
{
99
 
        PurpleConnection *gc = NULL;
100
 
        JabberStream *js = NULL;
 
81
                JabberBuddyState state;
 
82
                char *msg;
 
83
                int priority;
 
84
 
 
85
                g_return_if_fail(jb != NULL);
 
86
 
 
87
                purple_status_to_jabber(status, &state, &msg, &priority);
 
88
 
 
89
                if (state == JABBER_BUDDY_STATE_UNAVAILABLE ||
 
90
                                state == JABBER_BUDDY_STATE_UNKNOWN) {
 
91
                        jabber_buddy_remove_resource(jb, js->user->resource);
 
92
                } else {
 
93
                        jbr = jabber_buddy_track_resource(jb, js->user->resource, priority,
 
94
                                        state, msg);
 
95
                        jbr->idle = purple_presence_is_idle(presence) ?
 
96
                                        purple_presence_get_idle_time(presence) : 0;
 
97
                }
 
98
 
 
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,
 
104
                                        NULL);
 
105
                        purple_prpl_got_user_idle(account, username, jbr->idle, jbr->idle);
 
106
                } else {
 
107
                        purple_prpl_got_user_status(account, username, "offline",
 
108
                                        msg ? "message" : NULL, msg,
 
109
                                        NULL);
 
110
                }
 
111
                g_free(msg);
 
112
        }
 
113
}
 
114
 
 
115
void jabber_set_status(PurpleAccount *account, PurpleStatus *status)
 
116
{
 
117
        PurpleConnection *gc;
 
118
        JabberStream *js;
 
119
 
 
120
        if (!purple_account_is_connected(account))
 
121
                return;
 
122
 
 
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. */
 
126
                return;
 
127
        }
 
128
 
 
129
        gc = purple_account_get_connection(account);
 
130
        js = purple_connection_get_protocol_data(gc);
 
131
        jabber_presence_send(js, FALSE);
 
132
}
 
133
 
 
134
void jabber_presence_send(JabberStream *js, gboolean force)
 
135
{
 
136
        PurpleAccount *account;
101
137
        xmlnode *presence, *x, *photo;
102
138
        char *stripped = NULL;
103
139
        JabberBuddyState state;
106
142
        int length = -1;
107
143
        gboolean allowBuzz;
108
144
        PurplePresence *p;
109
 
        PurpleStatus *tune;
110
 
 
111
 
        if (purple_account_is_disconnected(account))
112
 
                return;
113
 
 
 
145
        PurpleStatus *status, *tune;
 
146
 
 
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);
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;
 
149
        status = purple_presence_get_active_status(p);
131
150
 
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");
135
154
                return;
136
155
        }
137
156
 
138
157
        purple_status_to_jabber(status, &state, &stripped, &priority);
139
 
        
 
158
 
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 */
143
162
 
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);
147
166
        }
148
 
        
 
167
 
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;
155
176
 
156
177
                presence = jabber_presence_create_js(js, state, stripped, priority);
157
178
 
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");
 
182
                /*
 
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.
 
186
                 */
 
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);
 
190
                        if (js->avatar_hash)
 
191
                                xmlnode_insert_data(photo, js->avatar_hash, -1);
163
192
                }
164
193
 
165
194
                jabber_send(js, presence);
166
195
 
167
196
                g_hash_table_foreach(js->chats, chats_send_presence_foreach, presence);
168
197
                xmlnode_free(presence);
169
 
                
 
198
 
170
199
                /* update old values */
171
 
                
 
200
 
172
201
                if(js->old_msg)
173
202
                        g_free(js->old_msg);
174
203
                if(js->old_avatarhash)
177
206
                js->old_avatarhash = g_strdup(js->avatar_hash);
178
207
                js->old_state = state;
179
208
                js->old_priority = priority;
 
209
                js->old_idle = js->idle;
180
210
        }
181
211
        g_free(stripped);
182
212
 
183
213
        /* next, check if there are any changes to the tune values */
184
 
        tune = purple_presence_get_status(p, "tune");
185
 
        if (tune && purple_status_is_active(tune)) {
 
214
        if (purple_status_is_active(tune)) {
186
215
                artist = purple_status_get_attr_string(tune, PURPLE_TUNE_ARTIST);
187
216
                title = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE);
188
217
                source = purple_status_get_attr_string(tune, PURPLE_TUNE_ALBUM);
191
220
                length = (!purple_status_get_attr_value(tune, PURPLE_TUNE_TIME)) ? -1 :
192
221
                                purple_status_get_attr_int(tune, PURPLE_TUNE_TIME);
193
222
        }
194
 
        
 
223
 
195
224
        if(CHANGED(artist, js->old_artist) || CHANGED(title, js->old_title) || CHANGED(source, js->old_source) ||
196
225
           CHANGED(uri, js->old_uri) || CHANGED(track, js->old_track) || (length != js->old_length)) {
197
226
                PurpleJabberTuneInfo tuneinfo = {
203
232
                        (char*)uri
204
233
                };
205
234
                jabber_tune_set(js->gc, &tuneinfo);
206
 
                
 
235
 
207
236
                /* update old values */
208
237
                g_free(js->old_artist);
209
238
                g_free(js->old_title);
232
261
{
233
262
        xmlnode *show, *status, *presence, *pri, *c;
234
263
        const char *show_string = NULL;
 
264
#ifdef USE_VV
 
265
        gboolean audio_enabled, video_enabled;
 
266
#endif
235
267
 
236
268
        presence = xmlnode_new("presence");
237
269
 
259
291
                g_free(pstr);
260
292
        }
261
293
 
 
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");
 
297
                gchar seconds[10];
 
298
                g_snprintf(seconds, 10, "%d", (int) (time(NULL) - js->idle));
 
299
 
 
300
                xmlnode_set_namespace(query, "jabber:iq:last");
 
301
                xmlnode_set_attrib(query, "seconds", seconds);
 
302
        }
 
303
 
262
304
        /* JEP-0115 */
 
305
        /* calculate hash */
 
306
        jabber_caps_calculate_own_hash(js);
 
307
        /* create xml */
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);
267
 
        
268
 
        if(js != NULL) {
269
 
                /* add the extensions */
270
 
                char extlist[1024];
271
 
                unsigned remaining = 1023; /* one less for the \0 */
272
 
                GList *feature;
273
 
                
274
 
                extlist[0] = '\0';
275
 
                for(feature = jabber_features; feature && remaining > 0; feature = feature->next) {
276
 
                        JabberFeature *feat = (JabberFeature*)feature->data;
277
 
                        unsigned featlen;
278
 
                        
279
 
                        if(feat->is_enabled != NULL && feat->is_enabled(js, feat->shortname, feat->namespace) == FALSE)
280
 
                                continue; /* skip this feature */
281
 
                        
282
 
                        featlen = strlen(feat->shortname);
283
 
                        
284
 
                        /* cut off when we don't have any more space left in our buffer (too bad) */
285
 
                        if(featlen > remaining)
286
 
                                break;
287
 
                        
288
 
                        strncat(extlist,feat->shortname,remaining);
289
 
                        remaining -= featlen;
290
 
                        if(feature->next) { /* no space at the end */
291
 
                                strncat(extlist," ",remaining);
292
 
                                --remaining;
293
 
                        }
294
 
                }
295
 
                /* did we add anything? */
296
 
                if(remaining < 1023)
297
 
                        xmlnode_set_attrib(c, "ext", extlist);
298
 
        }
299
 
        
 
311
        xmlnode_set_attrib(c, "hash", "sha-1");
 
312
        xmlnode_set_attrib(c, "ver", jabber_caps_get_own_hash(js));
 
313
 
 
314
#ifdef USE_VV
 
315
        /*
 
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
 
321
         * calls.
 
322
         *
 
323
         * Ditto for 'video-v1'.
 
324
         */
 
325
        audio_enabled = jabber_audio_enabled(js, NULL /* unused */);
 
326
        video_enabled = jabber_video_enabled(js, NULL /* unused */);
 
327
 
 
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");
 
334
#endif
 
335
 
300
336
        return presence;
301
337
}
302
338
 
326
362
        g_free(jap);
327
363
}
328
364
 
329
 
static void jabber_vcard_parse_avatar(JabberStream *js, xmlnode *packet, gpointer blah)
 
365
static void
 
366
jabber_vcard_parse_avatar(JabberStream *js, const char *from,
 
367
                          JabberIqType type, const char *id,
 
368
                          xmlnode *packet, gpointer blah)
330
369
{
331
370
        JabberBuddy *jb = NULL;
332
 
        xmlnode *vcard, *photo, *binval;
 
371
        xmlnode *vcard, *photo, *binval, *fn, *nick;
333
372
        char *text;
334
 
        guchar *data;
335
 
        gsize size;
336
 
        const char *from = xmlnode_get_attrib(packet, "from");
337
373
 
338
374
        if(!from)
339
375
                return;
344
380
 
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);
 
388
 
 
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) {
 
393
                                g_free(nickname);
 
394
                                nickname = tmp;
 
395
                        } else if (tmp)
 
396
                                g_free(tmp);
 
397
 
 
398
                        g_free(bare_jid);
 
399
                }
 
400
 
 
401
                if (nickname) {
 
402
                        serv_got_alias(js->gc, from, nickname);
 
403
                        g_free(nickname);
 
404
                }
 
405
 
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)))) {
351
 
                        char *hash;
 
410
                        guchar *data;
 
411
                        gchar *hash;
 
412
                        gsize size;
352
413
 
353
414
                        data = purple_base64_decode(text, &size);
354
415
                        hash = jabber_calculate_data_sha1sum(data, size);
 
416
 
355
417
                        purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash);
356
418
                        g_free(hash);
357
419
                        g_free(text);
365
427
        char *from;
366
428
} JabberPresenceCapabilities;
367
429
 
368
 
static void jabber_presence_set_capabilities(JabberCapsClientInfo *info, gpointer user_data) {
369
 
        JabberPresenceCapabilities *userdata = user_data;
370
 
        JabberID *jid;
 
430
static void
 
431
jabber_presence_set_capabilities(JabberCapsClientInfo *info, GList *exts,
 
432
                                 JabberPresenceCapabilities *userdata)
 
433
{
371
434
        JabberBuddyResource *jbr;
372
 
        GList *iter;
373
 
 
374
 
        jid = jabber_id_new(userdata->from);
375
 
        jbr = jabber_buddy_find_resource(userdata->jb, jid->resource);
376
 
        jabber_id_free(jid);
377
 
 
378
 
        if(!jbr) {
 
435
        char *resource = g_utf8_strchr(userdata->from, -1, '/');
 
436
 
 
437
        if (resource)
 
438
                resource += 1;
 
439
 
 
440
        jbr = jabber_buddy_find_resource(userdata->jb, resource);
 
441
        if (!jbr) {
379
442
                g_free(userdata->from);
380
443
                g_free(userdata);
 
444
                if (exts) {
 
445
                        g_list_foreach(exts, (GFunc)g_free, NULL);
 
446
                        g_list_free(exts);
 
447
                }
381
448
                return;
382
449
        }
383
450
 
384
 
        if(jbr->caps)
385
 
                jabber_caps_free_clientinfo(jbr->caps);
386
 
        jbr->caps = info;
387
 
 
388
 
        if (info) {
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");
395
 
 
396
 
                                jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
397
 
                                jabber_iq_send(iq);
398
 
                                break;
399
 
                        }
400
 
                }
401
 
        }
402
 
 
 
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);
 
455
        }
 
456
 
 
457
        jbr->caps.info = info;
 
458
        jbr->caps.exts = exts;
 
459
 
 
460
        if (info == NULL)
 
461
                goto out;
 
462
 
 
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);
 
469
                jabber_iq_send(iq);
 
470
 
 
471
                jbr->commands_fetched = TRUE;
 
472
        }
 
473
 
 
474
#if 0
 
475
        /*
 
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.
 
479
         */
 
480
        if (jabber_resource_has_capability(jbr, "http://jabber.org/protocol/chatstates"))
 
481
                jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
 
482
        else
 
483
                jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED;
 
484
#endif
 
485
 
 
486
out:
403
487
        g_free(userdata->from);
404
488
        g_free(userdata);
405
489
}
406
490
 
407
491
void jabber_presence_parse(JabberStream *js, xmlnode *packet)
408
492
{
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;
 
493
        const char *from;
 
494
        const char *type;
414
495
        char *status = NULL;
415
496
        int priority = 0;
416
497
        JabberID *jid;
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;
425
507
        xmlnode *y;
426
 
        gboolean muc = FALSE;
427
508
        char *avatar_hash = NULL;
428
509
        xmlnode *caps = NULL;
429
 
 
430
 
        if(!(jb = jabber_buddy_find(js, from, TRUE)))
431
 
                return;
432
 
 
433
 
        if(!(jid = jabber_id_new(from)))
434
 
                return;
 
510
        int idle = 0;
 
511
        gchar *nickname = NULL;
 
512
        gboolean signal_return;
 
513
 
 
514
        from = xmlnode_get_attrib(packet, "from");
 
515
        type = xmlnode_get_attrib(packet, "type");
 
516
 
 
517
        jb = jabber_buddy_find(js, from, TRUE);
 
518
        g_return_if_fail(jb != NULL);
 
519
 
 
520
        signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin,
 
521
                        "jabber-receiving-presence", js->gc, type, from, packet));
 
522
        if (signal_return)
 
523
                return;
 
524
 
 
525
        jid = jabber_id_new(from);
 
526
        if (jid == NULL) {
 
527
                purple_debug_error("jabber", "Ignoring presence with malformed 'from' "
 
528
                                   "JID: %s\n", from);
 
529
                return;
 
530
        }
435
531
 
436
532
        if(jb->error_msg) {
437
533
                g_free(jb->error_msg);
438
534
                jb->error_msg = NULL;
439
535
        }
440
536
 
441
 
        if(type && !strcmp(type, "error")) {
 
537
        if (type == NULL) {
 
538
                xmlnode *show;
 
539
                char *show_data = NULL;
 
540
 
 
541
                state = JABBER_BUDDY_STATE_ONLINE;
 
542
 
 
543
                show = xmlnode_get_child(packet, "show");
 
544
                if (show) {
 
545
                        show_data = xmlnode_get_data(show);
 
546
                        if (show_data) {
 
547
                                state = jabber_buddy_show_get_state(show_data);
 
548
                                g_free(show_data);
 
549
                        } else
 
550
                                purple_debug_warning("jabber", "<show/> present on presence, "
 
551
                                                     "but no contents!\n");
 
552
                }
 
553
        } else if (g_str_equal(type, "error")) {
442
554
                char *msg = jabber_parse_error(js, packet, NULL);
443
555
 
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;
 
562
                PurpleBuddy *buddy;
450
563
                JabberBuddy *jb = NULL;
 
564
                xmlnode *nick;
 
565
 
 
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");
 
569
                if (nick)
 
570
                        nickname = xmlnode_get_data(nick);
451
571
 
452
572
                if (buddy) {
453
573
                        jb = jabber_buddy_find(js, from, TRUE);
459
579
                jap->who = g_strdup(from);
460
580
                jap->js = js;
461
581
 
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);
 
584
 
 
585
                g_free(nickname);
464
586
                jabber_id_free(jid);
465
587
                return;
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);
469
591
                return;
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);
478
600
                return;
 
601
        } else if (g_str_equal(type, "probe")) {
 
602
                purple_debug_warning("jabber", "Ignoring presence probe\n");
 
603
                jabber_id_free(jid);
 
604
                return;
 
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;
479
609
        } else {
480
 
                if((y = xmlnode_get_child(packet, "show"))) {
481
 
                        char *show = xmlnode_get_data(y);
482
 
                        state = jabber_buddy_show_get_state(show);
483
 
                        g_free(show);
484
 
                } else {
485
 
                        state = JABBER_BUDDY_STATE_ONLINE;
486
 
                }
 
610
                purple_debug_warning("jabber", "Ignoring presence with invalid type "
 
611
                                     "'%s'\n", type);
 
612
                jabber_id_free(jid);
 
613
                return;
487
614
        }
488
615
 
489
616
 
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 */
510
637
                        delayed = TRUE;
 
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 */
516
646
                                delayed = TRUE;
 
647
                                stamp = xmlnode_get_attrib(y, "stamp");
517
648
                        } else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) {
518
 
                                xmlnode *z;
519
 
 
520
 
                                muc = TRUE;
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,
535
 
                                                                                chat, 2,
536
 
                                                                                _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
537
 
                                                                                _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
538
 
                                                }
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);
544
 
                                                }
545
 
                                        }
546
 
                                }
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;
553
 
                                        if (role != NULL) {
554
 
                                                if (!strcmp(role, "moderator"))
555
 
                                                        flags |= PURPLE_CBFLAGS_OP;
556
 
                                                else if (!strcmp(role, "participant"))
557
 
                                                        flags |= PURPLE_CBFLAGS_VOICE;
558
 
                                        }
559
 
                                }
560
649
                        } else if(!strcmp(xmlns, "vcard-temp:x:update")) {
561
650
                                xmlnode *photo = xmlnode_get_child(y, "photo");
562
651
                                if(photo) {
564
653
                                        avatar_hash = xmlnode_get_data(photo);
565
654
                                }
566
655
                        }
 
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");
 
660
                        if (seconds) {
 
661
                                /* we may need to take "delayed" into account here */
 
662
                                idle = atoi(seconds);
 
663
                        }
567
664
                }
568
665
        }
569
666
 
 
667
        if (idle && delayed && stamp) {
 
668
                /* if we have a delayed presence, we need to add the delay to the idle
 
669
                 value */
 
670
                time_t offset = time(NULL) - purple_str_to_time(stamp, TRUE, NULL, NULL,
 
671
                        NULL);
 
672
                purple_debug_info("jabber", "got delay %s yielding %ld s offset\n",
 
673
                        stamp, offset);
 
674
                idle += offset;
 
675
        }
570
676
 
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);
574
679
 
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);
594
699
                        g_free(status);
595
 
                        g_free(room_jid);
596
700
                        g_free(avatar_hash);
 
701
                        g_free(nickname);
597
702
                        return;
598
703
                }
599
704
 
600
 
 
601
 
                if(type && !strcmp(type, "unavailable")) {
 
705
                if (type == NULL) {
 
706
                        xmlnode *x;
 
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? */
 
711
 
 
712
                        /*
 
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.
 
716
                         */
 
717
                        if (!jid->resource) {
 
718
                                jabber_id_free(jid);
 
719
                                g_free(avatar_hash);
 
720
                                g_free(nickname);
 
721
                                g_free(status);
 
722
                                return;
 
723
                        }
 
724
 
 
725
                        x = xmlnode_get_child_with_namespace(packet, "x",
 
726
                                        "http://jabber.org/protocol/muc#user");
 
727
                        if (x) {
 
728
                                xmlnode *status_node;
 
729
                                xmlnode *item_node;
 
730
 
 
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");
 
734
                                        if (!code)
 
735
                                                continue;
 
736
 
 
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,
 
751
                                                                                chat, 2,
 
752
                                                                                _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
 
753
                                                                                _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
 
754
                                                }
 
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);
 
760
                                                }
 
761
                                        }
 
762
                                }
 
763
 
 
764
                                item_node = xmlnode_get_child(x, "item");
 
765
                                if (item_node) {
 
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");
 
769
 
 
770
                                        if (purple_strequal(affiliation, "owner"))
 
771
                                                flags |= PURPLE_CBFLAGS_FOUNDER;
 
772
                                        if (role) {
 
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;
 
777
                                        }
 
778
                                }
 
779
                        }
 
780
 
 
781
                        if(!chat->conv) {
 
782
                                char *room_jid = g_strdup_printf("%s@%s", jid->node, jid->domain);
 
783
                                chat->id = i++;
 
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);
 
787
 
 
788
                                jabber_chat_disco_traffic(chat);
 
789
                                g_free(room_jid);
 
790
                        }
 
791
 
 
792
                        jabber_buddy_track_resource(jb, jid->resource, priority, state,
 
793
                                        status);
 
794
 
 
795
                        jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role);
 
796
 
 
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);
 
800
                        else
 
801
                                purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource,
 
802
                                                flags);
 
803
                } else if (g_str_equal(type, "unavailable")) {
 
804
                        xmlnode *x;
602
805
                        gboolean nick_change = FALSE;
 
806
                        gboolean kick = FALSE;
 
807
                        gboolean is_our_resource = FALSE; /* Is the presence about us? */
603
808
 
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);
613
818
                                g_free(status);
614
 
                                g_free(room_jid);
615
819
                                g_free(avatar_hash);
 
820
                                g_free(nickname);
616
821
                                return;
617
822
                        }
618
823
 
 
824
                        is_our_resource = (0 == g_utf8_collate(jid->resource, chat->handle));
 
825
 
619
826
                        jabber_buddy_remove_resource(jb, jid->resource);
620
 
                        if(chat->muc) {
621
 
                                xmlnode *x;
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"))
627
 
                                                continue;
628
 
                                        if(!(stat = xmlnode_get_child(x, "status")))
629
 
                                                continue;
630
 
                                        if(!(code = xmlnode_get_attrib(stat, "code")))
631
 
                                                continue;
632
 
                                        if(!strcmp(code, "301")) {
 
827
 
 
828
                        x = xmlnode_get_child_with_namespace(packet, "x",
 
829
                                        "http://jabber.org/protocol/muc#user");
 
830
                        if (chat->muc && x) {
 
831
                                const char *nick;
 
832
                                const char *item_jid = NULL;
 
833
                                const char *to;
 
834
                                xmlnode *stat;
 
835
                                xmlnode *item;
 
836
 
 
837
                                item = xmlnode_get_child(x, "item");
 
838
                                if (item)
 
839
                                        item_jid = xmlnode_get_attrib(item, "jid");
 
840
 
 
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");
 
844
 
 
845
                                        if (!code)
 
846
                                                continue;
 
847
 
 
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")))
636
 
                                                        continue;
637
 
                                                if(!(nick = xmlnode_get_attrib(item, "nick")))
638
 
                                                        continue;
 
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);
643
858
                                                }
 
859
 
 
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);
646
 
                                                break;
 
863
                                                continue;
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;
 
869
                                                char *tmp;
 
870
 
 
871
                                                kick = TRUE;
 
872
 
 
873
                                                if (item) {
 
874
                                                        reason = xmlnode_get_child(item, "reason");
 
875
                                                        actor = xmlnode_get_child(item, "actor");
 
876
 
 
877
                                                        if (reason != NULL)
 
878
                                                                reason_text = xmlnode_get_data(reason);
 
879
                                                        if (actor != NULL)
 
880
                                                                actor_name = xmlnode_get_attrib(actor, "jid");
 
881
                                                }
 
882
 
 
883
                                                if (reason_text == NULL)
 
884
                                                        reason_text = g_strdup(_("No reason"));
 
885
 
 
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);
 
890
                                                        else
 
891
                                                                tmp = g_strdup_printf(_("You have been kicked: (%s)"),
 
892
                                                                                reason_text);
 
893
                                                } else {
 
894
                                                        if (actor_name != NULL)
 
895
                                                                tmp = g_strdup_printf(_("Kicked by %s (%s)"),
 
896
                                                                                actor_name, reason_text);
 
897
                                                        else
 
898
                                                                tmp = g_strdup_printf(_("Kicked (%s)"),
 
899
                                                                                reason_text);
 
900
                                                }
 
901
 
 
902
                                                g_free(reason_text);
 
903
                                                g_free(status);
 
904
                                                status = tmp;
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 */
655
911
                                        }
656
912
                                }
 
913
 
 
914
                                /*
 
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
 
919
                                 * #8319.
 
920
                                 */
 
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;
 
928
                                }
657
929
                        }
658
930
                        if(!nick_change) {
659
 
                                if(!g_utf8_collate(jid->resource, chat->handle)) {
 
931
                                if (is_our_resource) {
 
932
                                        if (kick)
 
933
                                                purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), jid->resource,
 
934
                                                                status, PURPLE_MESSAGE_SYSTEM, time(NULL));
 
935
 
660
936
                                        serv_got_chat_left(js->gc, chat->id);
661
937
                                        jabber_chat_destroy(chat);
662
938
                                } else {
666
942
                                }
667
943
                        }
668
944
                } else {
669
 
                        /*
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.
673
 
                         */
674
 
                        if (!jid->resource) {
675
 
                                jabber_id_free(jid);
676
 
                                g_free(avatar_hash);
677
 
                                g_free(status);
678
 
                                return;
679
 
                        }
680
 
 
681
 
                        if(!chat->conv) {
682
 
                                chat->id = i++;
683
 
                                chat->muc = muc;
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);
686
 
 
687
 
                                jabber_chat_disco_traffic(chat);
688
 
                        }
689
 
 
690
 
                        jabber_buddy_track_resource(jb, jid->resource, priority, state,
691
 
                                        status);
692
 
 
693
 
                        jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role);
694
 
 
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);
698
 
                        else
699
 
                                purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource,
700
 
                                                flags);
 
945
                        /* A type that isn't available or unavailable */
 
946
                        purple_debug_error("jabber", "MUC presence with bad type: %s\n",
 
947
                                           type);
 
948
 
 
949
                        jabber_id_free(jid);
 
950
                        g_free(avatar_hash);
 
951
                        g_free(status);
 
952
                        g_free(nickname);
 
953
                        g_return_if_reached();
701
954
                }
702
 
                g_free(room_jid);
703
955
        } else {
704
956
                buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "",
705
957
                                                                         jid->node ? "@" : "", jid->domain);
710
962
                                jabber_id_free(jid);
711
963
                                g_free(avatar_hash);
712
964
                                g_free(buddy_name);
 
965
                                g_free(nickname);
713
966
                                g_free(status);
714
967
                                return;
715
968
                        } else {
744
997
                }
745
998
 
746
999
                if(state == JABBER_BUDDY_STATE_ERROR ||
747
 
                                (type && (!strcmp(type, "unavailable") ||
748
 
                                                  !strcmp(type, "unsubscribed")))) {
 
1000
                                (type && (g_str_equal(type, "unavailable") ||
 
1001
                                          g_str_equal(type, "unsubscribed")))) {
749
1002
                        PurpleConversation *conv;
750
1003
 
751
1004
                        jabber_buddy_remove_resource(jb, jid->resource);
755
1008
                } else {
756
1009
                        jbr = jabber_buddy_track_resource(jb, jid->resource, priority,
757
1010
                                        state, status);
758
 
                        if(caps) {
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");
762
 
                                
763
 
                                if(node && ver) {
764
 
                                        JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
765
 
                                        userdata->js = js;
766
 
                                        userdata->jb = jb;
767
 
                                        userdata->from = g_strdup(from);
768
 
                                        jabber_caps_get_info(js, from, node, ver, ext, jabber_presence_set_capabilities, userdata);
769
 
                                }
 
1011
                        if (idle) {
 
1012
                                jbr->idle = time(NULL) - idle;
 
1013
                        } else {
 
1014
                                jbr->idle = 0;
770
1015
                        }
771
1016
                }
772
1017
 
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);
 
1022
                        if (nickname)
 
1023
                                serv_got_alias(js->gc, buddy_name, nickname);
776
1024
                } else {
777
1025
                        purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL);
778
1026
                }
779
1027
                g_free(buddy_name);
780
1028
        }
 
1029
 
 
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");
 
1036
 
 
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);
 
1042
 
 
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);
 
1050
                                userdata->js = js;
 
1051
                                userdata->jb = jb;
 
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,
 
1055
                                    userdata);
 
1056
                        } else {
 
1057
                                if (exts)
 
1058
                                        g_strfreev(exts);
 
1059
                        }
 
1060
                }
 
1061
        }
 
1062
 
 
1063
        g_free(nickname);
781
1064
        g_free(status);
782
1065
        jabber_id_free(jid);
783
1066
        g_free(avatar_hash);
815
1098
                        formatted_msg = purple_status_get_attr_string(status, "message");
816
1099
 
817
1100
                        /* if the message is blank, then there really isn't a message */
818
 
                        if(formatted_msg && !*formatted_msg)
819
 
                                formatted_msg = NULL;
820
 
 
821
 
                        if(formatted_msg)
 
1101
                        if(formatted_msg && *formatted_msg)
822
1102
                                *msg = purple_markup_strip_html(formatted_msg);
823
1103
                }
824
1104