source: protocols/jabber/iq.c @ c6bc434

Last change on this file since c6bc434 was 7554702, checked in by dequis <dx@…>, at 2015-11-25T15:56:09Z

jabber: Reply unknown IQs with service-unavailable instead

It was sending 'feature-not-implemented' for <query> with unknown xmlns
(which makes sense, but the RFC says that's wrong. idk.) and nothing at
all for IQs that don't have query/ping/time elements or an xmlns
attribute. Both get service-unavailable now.

Addresses the rest of trac ticket 533:

https://bugs.bitlbee.org/bitlbee/ticket/533

  • Property mode set to 100644
File size: 31.7 KB
RevLine 
[f06894d]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - IQ packets                                               *
5*                                                                           *
[0e788f5]6*  Copyright 2006-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
[f06894d]7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include "jabber.h"
[77bfd07]25#include "sha1.h"
[f06894d]26
[5ebff60]27static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
28static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
[dd43c62]29static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node);
[fa8f57b]30static xt_status jabber_iq_carbons_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
[abbd8ed]31
[5ebff60]32xt_status jabber_pkt_iq(struct xt_node *node, gpointer data)
[f06894d]33{
[0da65d5]34        struct im_connection *ic = data;
[68286eb]35        struct jabber_data *jd = ic->proto_data;
[58b5f62]36        struct xt_node *c, *reply = NULL;
[861c199]37        char *type, *s;
[259edd4]38        int st, pack = 1;
[5ebff60]39
40        type = xt_find_attr(node, "type");
41
42        if (!type) {
43                imcb_error(ic, "Received IQ packet without type.");
44                imc_logout(ic, TRUE);
[861c199]45                return XT_ABORT;
46        }
[5ebff60]47
48        if (strcmp(type, "result") == 0 || strcmp(type, "error") == 0) {
49                return jabber_cache_handle_packet(ic, node);
50        } else if (strcmp(type, "get") == 0) {
51                if (!((c = xt_find_node(node->children, "query")) ||
52                      (c = xt_find_node(node->children, "ping")) ||
53                      (c = xt_find_node(node->children, "time"))) ||
54                    !(s = xt_find_attr(c, "xmlns"))) {
[7554702]55
56                        reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL);
57                        st = jabber_write_packet(ic, reply);
58                        xt_free_node(reply);
59                        return st;
[58b5f62]60                }
[5ebff60]61
62                reply = xt_new_node("query", NULL, NULL);
63                xt_add_attr(reply, "xmlns", s);
64
[58b5f62]65                /* Of course this is a very essential query to support. ;-) */
[5ebff60]66                if (strcmp(s, XMLNS_VERSION) == 0) {
67                        xt_add_child(reply, xt_new_node("name", set_getstr(&ic->acc->set, "user_agent"), NULL));
68                        xt_add_child(reply, xt_new_node("version", BITLBEE_VERSION, NULL));
69                        xt_add_child(reply, xt_new_node("os", ARCH, NULL));
70                } else if (strcmp(s, XMLNS_TIME_OLD) == 0) {
[a4effbf]71                        time_t time_ep;
72                        char buf[1024];
[5ebff60]73
74                        buf[sizeof(buf) - 1] = 0;
75                        time_ep = time(NULL);
76
77                        strftime(buf, sizeof(buf) - 1, "%Y%m%dT%H:%M:%S", gmtime(&time_ep));
78                        xt_add_child(reply, xt_new_node("utc", buf, NULL));
79
80                        strftime(buf, sizeof(buf) - 1, "%Z", localtime(&time_ep));
81                        xt_add_child(reply, xt_new_node("tz", buf, NULL));
82                } else if (strcmp(s, XMLNS_TIME) == 0) {
[d76e12f]83                        time_t time_ep;
84                        char buf[1024];
[5ebff60]85
86                        buf[sizeof(buf) - 1] = 0;
87                        time_ep = time(NULL);
88
89                        xt_free_node(reply);
90                        reply = xt_new_node("time", NULL, NULL);
91                        xt_add_attr(reply, "xmlns", XMLNS_TIME);
92
93                        strftime(buf, sizeof(buf) - 1, "%Y%m%dT%H:%M:%SZ", gmtime(&time_ep));
94                        xt_add_child(reply, xt_new_node("utc", buf, NULL));
95
96                        strftime(buf, sizeof(buf) - 1, "%z", localtime(&time_ep));
97                        if (strlen(buf) >= 5) {
[d76e12f]98                                buf[6] = '\0';
99                                buf[5] = buf[4];
100                                buf[4] = buf[3];
101                                buf[3] = ':';
102                        }
[5ebff60]103                        xt_add_child(reply, xt_new_node("tzo", buf, NULL));
104                } else if (strcmp(s, XMLNS_PING) == 0) {
105                        xt_free_node(reply);
106                        reply = jabber_make_packet("iq", "result", xt_find_attr(node, "from"), NULL);
107                        if ((s = xt_find_attr(node, "id"))) {
108                                xt_add_attr(reply, "id", s);
109                        }
[eded1f7]110                        pack = 0;
[5ebff60]111                } else if (strcmp(s, XMLNS_DISCO_INFO) == 0) {
[1ba7e8f]112                        const char *features[] = { XMLNS_DISCO_INFO,
[5ebff60]113                                                   XMLNS_VERSION,
114                                                   XMLNS_TIME_OLD,
115                                                   XMLNS_TIME,
116                                                   XMLNS_CHATSTATES,
117                                                   XMLNS_MUC,
118                                                   XMLNS_PING,
119                                                   XMLNS_RECEIPTS,
120                                                   XMLNS_SI,
121                                                   XMLNS_BYTESTREAMS,
122                                                   XMLNS_FILETRANSFER,
[fa8f57b]123                                                   XMLNS_CARBONS,
[5ebff60]124                                                   NULL };
[40ef702]125                        const char **f;
[5ebff60]126
127                        c = xt_new_node("identity", NULL, NULL);
128                        xt_add_attr(c, "category", "client");
129                        xt_add_attr(c, "type", "pc");
130                        xt_add_attr(c, "name", set_getstr(&ic->acc->set, "user_agent"));
131                        xt_add_child(reply, c);
132
133                        for (f = features; *f; f++) {
134                                c = xt_new_node("feature", NULL, NULL);
135                                xt_add_attr(c, "var", *f);
136                                xt_add_child(reply, c);
[40ef702]137                        }
[5ebff60]138                } else {
139                        xt_free_node(reply);
[7554702]140                        reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL);
[259edd4]141                        pack = 0;
[58b5f62]142                }
[5ebff60]143        } else if (strcmp(type, "set") == 0) {
144                if ((c = xt_find_node(node->children, "si")) &&
145                    (s = xt_find_attr(c, "xmlns")) &&
146                    (strcmp(s, XMLNS_SI) == 0)) {
147                        return jabber_si_handle_request(ic, node, c);
[dd43c62]148                } else if ((c = xt_find_node(node->children, "new-mail")) &&
149                           (s = xt_find_attr(c, "xmlns")) &&
150                           (strcmp(s, XMLNS_GMAILNOTIFY) == 0)) {
151                        return jabber_gmail_handle_new(ic, node);
[5ebff60]152                } else if (!(c = xt_find_node(node->children, "query")) ||
153                           !(s = xt_find_attr(c, "xmlns"))) {
[dfa41a4]154                        return XT_HANDLED;
[5ebff60]155                } else if (strcmp(s, XMLNS_ROSTER) == 0) {
156                        /* This is a roster push. XMPP servers send this when someone
157                           was added to (or removed from) the buddy list. AFAIK they're
158                           sent even if we added this buddy in our own session. */
159                        int bare_len = strlen(jd->me);
160
161                        if ((s = xt_find_attr(node, "from")) == NULL ||
162                            (strncmp(s, jd->me, bare_len) == 0 &&
163                             (s[bare_len] == 0 || s[bare_len] == '/'))) {
164                                jabber_parse_roster(ic, node, NULL);
165
[abbd8ed]166                                /* Should we generate a reply here? Don't think it's
167                                   very important... */
[5ebff60]168                        } else {
169                                imcb_log(ic, "Warning: %s tried to fake a roster push!", s ? s : "(unknown)");
170
171                                xt_free_node(reply);
172                                reply = jabber_make_error_packet(node, "not-allowed", "cancel", NULL);
[abbd8ed]173                                pack = 0;
174                        }
[5ebff60]175                } else if (strcmp(s, XMLNS_BYTESTREAMS) == 0) {
[1c3008a]176                        /* Bytestream Request (stage 2 of file transfer) */
[5ebff60]177                        return jabber_bs_recv_request(ic, node, c);
178                } else {
179                        xt_free_node(reply);
180                        reply = jabber_make_error_packet(node, "feature-not-implemented", "cancel", NULL);
[dfa41a4]181                        pack = 0;
182                }
[259edd4]183        }
[5ebff60]184
[259edd4]185        /* If we recognized the xmlns and managed to generate a reply,
186           finish and send it. */
[5ebff60]187        if (reply) {
[259edd4]188                /* Normally we still have to pack it into an iq-result
189                   packet, but for errors, for example, we don't. */
[5ebff60]190                if (pack) {
191                        reply = jabber_make_packet("iq", "result", xt_find_attr(node, "from"), reply);
192                        if ((s = xt_find_attr(node, "id"))) {
193                                xt_add_attr(reply, "id", s);
194                        }
195                }
196
197                st = jabber_write_packet(ic, reply);
198                xt_free_node(reply);
199                if (!st) {
[259edd4]200                        return XT_ABORT;
[5ebff60]201                }
[58b5f62]202        }
[5ebff60]203
[861c199]204        return XT_HANDLED;
205}
206
[5ebff60]207static xt_status jabber_do_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
208static xt_status jabber_finish_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
[861c199]209
[5ebff60]210int jabber_init_iq_auth(struct im_connection *ic)
[861c199]211{
[0da65d5]212        struct jabber_data *jd = ic->proto_data;
[861c199]213        struct xt_node *node;
214        int st;
[5ebff60]215
216        node = xt_new_node("query", NULL, xt_new_node("username", jd->username, NULL));
217        xt_add_attr(node, "xmlns", XMLNS_AUTH);
218        node = jabber_make_packet("iq", "get", NULL, node);
219
220        jabber_cache_add(ic, node, jabber_do_iq_auth);
221        st = jabber_write_packet(ic, node);
222
[861c199]223        return st;
224}
225
[5ebff60]226static xt_status jabber_do_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
[861c199]227{
[0da65d5]228        struct jabber_data *jd = ic->proto_data;
[861c199]229        struct xt_node *reply, *query;
230        xt_status st;
231        char *s;
[5ebff60]232
233        if (!(query = xt_find_node(node->children, "query"))) {
234                imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
235                imc_logout(ic, FALSE);
[9bcbe48]236                return XT_HANDLED;
237        }
[5ebff60]238
[861c199]239        /* Time to authenticate ourselves! */
[5ebff60]240        reply = xt_new_node("query", NULL, NULL);
241        xt_add_attr(reply, "xmlns", XMLNS_AUTH);
242        xt_add_child(reply, xt_new_node("username", jd->username, NULL));
243        xt_add_child(reply, xt_new_node("resource", set_getstr(&ic->acc->set, "resource"), NULL));
244
245        if (xt_find_node(query->children, "digest") && (s = xt_find_attr(jd->xt->root, "id"))) {
[861c199]246                /* We can do digest authentication, it seems, and of
247                   course we prefer that. */
[77bfd07]248                sha1_state_t sha;
[e727608]249                char hash_hex[41];
[861c199]250                unsigned char hash[20];
251                int i;
[5ebff60]252
253                sha1_init(&sha);
254                sha1_append(&sha, (unsigned char *) s, strlen(s));
255                sha1_append(&sha, (unsigned char *) ic->acc->pass, strlen(ic->acc->pass));
256                sha1_finish(&sha, hash);
257
258                for (i = 0; i < 20; i++) {
259                        sprintf(hash_hex + i * 2, "%02x", hash[i]);
260                }
261
262                xt_add_child(reply, xt_new_node("digest", hash_hex, NULL));
263        } else if (xt_find_node(query->children, "password")) {
[861c199]264                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
[5ebff60]265                xt_add_child(reply, xt_new_node("password", ic->acc->pass, NULL));
266        } else {
267                xt_free_node(reply);
268
269                imcb_error(ic, "Can't find suitable authentication method");
270                imc_logout(ic, FALSE);
[861c199]271                return XT_ABORT;
[70f6aab8]272        }
[5ebff60]273
274        reply = jabber_make_packet("iq", "set", NULL, reply);
275        jabber_cache_add(ic, reply, jabber_finish_iq_auth);
276        st = jabber_write_packet(ic, reply);
277
[861c199]278        return st ? XT_HANDLED : XT_ABORT;
279}
280
[5ebff60]281static xt_status jabber_finish_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
[861c199]282{
[0da65d5]283        struct jabber_data *jd = ic->proto_data;
[9bcbe48]284        char *type;
[5ebff60]285
286        if (!(type = xt_find_attr(node, "type"))) {
287                imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
288                imc_logout(ic, FALSE);
[9bcbe48]289                return XT_HANDLED;
290        }
[5ebff60]291
292        if (strcmp(type, "error") == 0) {
293                imcb_error(ic, "Authentication failure");
294                imc_logout(ic, FALSE);
[861c199]295                return XT_ABORT;
[5ebff60]296        } else if (strcmp(type, "result") == 0) {
[861c199]297                /* This happens when we just successfully authenticated the
298                   old (non-SASL) way. */
299                jd->flags |= JFLAG_AUTHENTICATED;
[5ebff60]300                if (!jabber_get_roster(ic)) {
[70f6aab8]301                        return XT_ABORT;
[5ebff60]302                }
303                if (!jabber_iq_disco_server(ic)) {
[bb2d198]304                        return XT_ABORT;
[5ebff60]305                }
[70f6aab8]306        }
[5ebff60]307
[f06894d]308        return XT_HANDLED;
309}
310
[5ebff60]311xt_status jabber_pkt_bind_sess(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
[21167d2]312{
[0da65d5]313        struct jabber_data *jd = ic->proto_data;
[8fb1263]314        struct xt_node *c, *reply = NULL;
[861c199]315        char *s;
[5ebff60]316
317        if (node && (c = xt_find_node(node->children, "bind"))) {
318                c = xt_find_node(c->children, "jid");
319                if (!c || !c->text) {
[68286eb]320                        /* Server is crap, but this is no disaster. */
[5ebff60]321                } else if (jabber_compare_jid(jd->me, c->text) == 0) {
322                        s = strchr(c->text, '/');
323                        if (s) {
[68286eb]324                                *s = '\0';
[5ebff60]325                        }
326                        jabber_set_me(ic, c->text);
327                        if (s) {
[68286eb]328                                *s = '/';
[5ebff60]329                        }
330                } else if (c && c->text_len && (s = strchr(c->text, '/')) &&
331                           strcmp(s + 1, set_getstr(&ic->acc->set, "resource")) != 0) {
332                        imcb_log(ic, "Server changed session resource string to `%s'", s + 1);
[68286eb]333                }
[861c199]334        }
[5ebff60]335
336        if (jd->flags & JFLAG_WANT_BIND) {
337                reply = xt_new_node("bind", NULL, xt_new_node("resource", set_getstr(&ic->acc->set, "resource"), NULL));
338                xt_add_attr(reply, "xmlns", XMLNS_BIND);
[315dd4c]339                jd->flags &= ~JFLAG_WANT_BIND;
[5ebff60]340        } else if (jd->flags & JFLAG_WANT_SESSION) {
341                reply = xt_new_node("session", NULL, NULL);
342                xt_add_attr(reply, "xmlns", XMLNS_SESSION);
[315dd4c]343                jd->flags &= ~JFLAG_WANT_SESSION;
[861c199]344        }
[5ebff60]345
346        if (reply != NULL) {
347                reply = jabber_make_packet("iq", "set", NULL, reply);
348                jabber_cache_add(ic, reply, jabber_pkt_bind_sess);
349
350                if (!jabber_write_packet(ic, reply)) {
[8fb1263]351                        return XT_ABORT;
[5ebff60]352                }
[dd43c62]353                if (jd->flags & JFLAG_GMAILNOTIFY && node == NULL) {
354                        jabber_iq_query_server(ic, jd->server, XMLNS_DISCO_INFO);
355                }
[5ebff60]356        } else if ((jd->flags & (JFLAG_WANT_BIND | JFLAG_WANT_SESSION)) == 0) {
357                if (!jabber_get_roster(ic)) {
[861c199]358                        return XT_ABORT;
[5ebff60]359                }
360                if (!jabber_iq_disco_server(ic)) {
[bb2d198]361                        return XT_ABORT;
[5ebff60]362                }
[861c199]363        }
[5ebff60]364
[861c199]365        return XT_HANDLED;
[21167d2]366}
[70f6aab8]367
[5ebff60]368int jabber_get_roster(struct im_connection *ic)
[70f6aab8]369{
370        struct xt_node *node;
371        int st;
[5ebff60]372
373        imcb_log(ic, "Authenticated, requesting buddy list");
374
375        node = xt_new_node("query", NULL, NULL);
376        xt_add_attr(node, "xmlns", XMLNS_ROSTER);
377        node = jabber_make_packet("iq", "get", NULL, node);
378
379        jabber_cache_add(ic, node, jabber_parse_roster);
380        st = jabber_write_packet(ic, node);
381
[70f6aab8]382        return st;
383}
[cfbb3a6]384
[dd43c62]385xt_status jabber_iq_query_gmail(struct im_connection *ic);
386
387static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node)
388{
389        struct xt_node *response;
390        struct jabber_data *jd = ic->proto_data;
[df215a3]391       
392        response = jabber_make_packet("iq", "result", jd->me, NULL);
[dd43c62]393
394        jabber_cache_add(ic, response, NULL);
395        if (!jabber_write_packet(ic, response)) {
396                return XT_ABORT;
397        }
398
399        jabber_iq_query_gmail(ic);
400
401        return XT_HANDLED;
402}
403
[5ebff60]404static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
[861c199]405{
[40cfbc5]406        struct jabber_data *jd = ic->proto_data;
[861c199]407        struct xt_node *query, *c;
[5ebff60]408        int initial = (orig != NULL);
409
410        if (!(query = xt_find_node(node->children, "query"))) {
411                imcb_log(ic, "Warning: Received NULL roster packet");
[9bcbe48]412                return XT_HANDLED;
413        }
[5ebff60]414
[861c199]415        c = query->children;
[5ebff60]416        while ((c = xt_find_node(c, "item"))) {
417                struct xt_node *group = xt_find_node(c->children, "group");
418                char *jid = xt_find_attr(c, "jid");
419                char *name = xt_find_attr(c, "name");
420                char *sub = xt_find_attr(c, "subscription");
[40cfbc5]421                char *mention_name = xt_find_attr(c, "mention_name");
[5ebff60]422
423                if (jid && sub) {
424                        if ((strcmp(sub, "both") == 0 || strcmp(sub, "to") == 0)) {
425                                imcb_add_buddy(ic, jid, (group && group->text_len) ?
426                                               group->text : NULL);
427
428                                if (name) {
429                                        imcb_rename_buddy(ic, jid, name);
430                                }
[40cfbc5]431
432                                /* This could also be used to set the full name as nick for fb/gtalk,
433                                 * but i'm keeping the old (ugly?) default behavior just to be safe */
434                                if (mention_name && (jd->flags & JFLAG_HIPCHAT)) {
435                                        imcb_buddy_nick_hint(ic, jid, mention_name);
436                                }
[5ebff60]437                        } else if (strcmp(sub, "remove") == 0) {
438                                jabber_buddy_remove_bare(ic, jid);
439                                imcb_remove_buddy(ic, jid, NULL);
[abbd8ed]440                        }
441                }
[5ebff60]442
[861c199]443                c = c->next;
444        }
[5ebff60]445
446        if (initial) {
447                imcb_connected(ic);
448        }
449
[861c199]450        return XT_HANDLED;
451}
452
[5ebff60]453int jabber_get_vcard(struct im_connection *ic, char *bare_jid)
[1991be6]454{
455        struct xt_node *node;
[5ebff60]456
457        if (strchr(bare_jid, '/')) {
458                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
459
460        }
461        node = xt_new_node("vCard", NULL, NULL);
462        xt_add_attr(node, "xmlns", XMLNS_VCARD);
463        node = jabber_make_packet("iq", "get", bare_jid, node);
464
465        jabber_cache_add(ic, node, jabber_iq_display_vcard);
466        return jabber_write_packet(ic, node);
[1991be6]467}
468
[5ebff60]469static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
[1991be6]470{
[0da65d5]471        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
[1991be6]472        GString *reply;
473        char *s;
[5ebff60]474
475        if ((s = xt_find_attr(node, "type")) == NULL ||
476            strcmp(s, "result") != 0 ||
477            (vc = xt_find_node(node->children, "vCard")) == NULL) {
478                s = xt_find_attr(orig, "to");   /* If this returns NULL something's wrong.. */
479                imcb_log(ic, "Could not retrieve vCard of %s", s ? s : "(NULL)");
[1991be6]480                return XT_HANDLED;
481        }
[5ebff60]482
483        s = xt_find_attr(orig, "to");
484        reply = g_string_new("vCard information for ");
485        reply = g_string_append(reply, s ? s : "(NULL)");
486        reply = g_string_append(reply, ":\n");
487
[1991be6]488        /* I hate this format, I really do... */
[5ebff60]489
490        if ((c = xt_find_node(vc->children, "FN")) && c->text_len) {
491                g_string_append_printf(reply, "Name: %s\n", c->text);
492        }
493
494        if ((c = xt_find_node(vc->children, "N")) && c->children) {
495                reply = g_string_append(reply, "Full name:");
496
497                if ((sc = xt_find_node(c->children, "PREFIX")) && sc->text_len) {
498                        g_string_append_printf(reply, " %s", sc->text);
499                }
500                if ((sc = xt_find_node(c->children, "GIVEN")) && sc->text_len) {
501                        g_string_append_printf(reply, " %s", sc->text);
502                }
503                if ((sc = xt_find_node(c->children, "MIDDLE")) && sc->text_len) {
504                        g_string_append_printf(reply, " %s", sc->text);
505                }
506                if ((sc = xt_find_node(c->children, "FAMILY")) && sc->text_len) {
507                        g_string_append_printf(reply, " %s", sc->text);
508                }
509                if ((sc = xt_find_node(c->children, "SUFFIX")) && sc->text_len) {
510                        g_string_append_printf(reply, " %s", sc->text);
511                }
512
513                reply = g_string_append_c(reply, '\n');
514        }
515
516        if ((c = xt_find_node(vc->children, "NICKNAME")) && c->text_len) {
517                g_string_append_printf(reply, "Nickname: %s\n", c->text);
518        }
519
520        if ((c = xt_find_node(vc->children, "BDAY")) && c->text_len) {
521                g_string_append_printf(reply, "Date of birth: %s\n", c->text);
522        }
523
[1991be6]524        /* Slightly alternative use of for... ;-) */
[5ebff60]525        for (c = vc->children; (c = xt_find_node(c, "EMAIL")); c = c->next) {
526                if ((sc = xt_find_node(c->children, "USERID")) == NULL || sc->text_len == 0) {
[1991be6]527                        continue;
[5ebff60]528                }
529
530                if (xt_find_node(c->children, "HOME")) {
[1991be6]531                        s = "Home";
[5ebff60]532                } else if (xt_find_node(c->children, "WORK")) {
[1991be6]533                        s = "Work";
[5ebff60]534                } else {
[1991be6]535                        s = "Misc.";
[5ebff60]536                }
537
538                g_string_append_printf(reply, "%s e-mail address: %s\n", s, sc->text);
[1991be6]539        }
[5ebff60]540
541        if ((c = xt_find_node(vc->children, "URL")) && c->text_len) {
542                g_string_append_printf(reply, "Homepage: %s\n", c->text);
543        }
544
[1991be6]545        /* Slightly alternative use of for... ;-) */
[5ebff60]546        for (c = vc->children; (c = xt_find_node(c, "ADR")); c = c->next) {
547                if (xt_find_node(c->children, "HOME")) {
[1991be6]548                        s = "Home";
[5ebff60]549                } else if (xt_find_node(c->children, "WORK")) {
[1991be6]550                        s = "Work";
[5ebff60]551                } else {
[1991be6]552                        s = "Misc.";
[5ebff60]553                }
554
555                g_string_append_printf(reply, "%s address: ", s);
556
557                if ((sc = xt_find_node(c->children, "STREET")) && sc->text_len) {
558                        g_string_append_printf(reply, "%s ", sc->text);
559                }
560                if ((sc = xt_find_node(c->children, "EXTADR")) && sc->text_len) {
561                        g_string_append_printf(reply, "%s, ", sc->text);
562                }
563                if ((sc = xt_find_node(c->children, "PCODE")) && sc->text_len) {
564                        g_string_append_printf(reply, "%s, ", sc->text);
565                }
566                if ((sc = xt_find_node(c->children, "LOCALITY")) && sc->text_len) {
567                        g_string_append_printf(reply, "%s, ", sc->text);
568                }
569                if ((sc = xt_find_node(c->children, "REGION")) && sc->text_len) {
570                        g_string_append_printf(reply, "%s, ", sc->text);
571                }
572                if ((sc = xt_find_node(c->children, "CTRY")) && sc->text_len) {
573                        g_string_append_printf(reply, "%s", sc->text);
574                }
575
576                if (reply->str[reply->len - 2] == ',') {
577                        reply = g_string_truncate(reply, reply->len - 2);
578                }
579
580                reply = g_string_append_c(reply, '\n');
581        }
582
583        for (c = vc->children; (c = xt_find_node(c, "TEL")); c = c->next) {
584                if ((sc = xt_find_node(c->children, "NUMBER")) == NULL || sc->text_len == 0) {
[1991be6]585                        continue;
[5ebff60]586                }
587
588                if (xt_find_node(c->children, "HOME")) {
[1991be6]589                        s = "Home";
[5ebff60]590                } else if (xt_find_node(c->children, "WORK")) {
[1991be6]591                        s = "Work";
[5ebff60]592                } else {
[1991be6]593                        s = "Misc.";
[5ebff60]594                }
595
596                g_string_append_printf(reply, "%s phone number: %s\n", s, sc->text);
597        }
598
599        if ((c = xt_find_node(vc->children, "DESC")) && c->text_len) {
600                g_string_append_printf(reply, "Other information:\n%s", c->text);
[1991be6]601        }
[5ebff60]602
[1991be6]603        /* *sigh* */
[5ebff60]604
605        imcb_log(ic, "%s", reply->str);
606        g_string_free(reply, TRUE);
607
[1991be6]608        return XT_HANDLED;
609}
610
[5ebff60]611static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
[a73e91a]612
[5ebff60]613int jabber_add_to_roster(struct im_connection *ic, const char *handle, const char *name, const char *group)
[cfbb3a6]614{
615        struct xt_node *node;
616        int st;
[5ebff60]617
[cfbb3a6]618        /* Build the item entry */
[5ebff60]619        node = xt_new_node("item", NULL, NULL);
620        xt_add_attr(node, "jid", handle);
621        if (name) {
622                xt_add_attr(node, "name", name);
623        }
624        if (group) {
625                xt_add_child(node, xt_new_node("group", group, NULL));
626        }
627
[cfbb3a6]628        /* And pack it into a roster-add packet */
[5ebff60]629        node = xt_new_node("query", NULL, node);
630        xt_add_attr(node, "xmlns", XMLNS_ROSTER);
631        node = jabber_make_packet("iq", "set", NULL, node);
632        jabber_cache_add(ic, node, jabber_add_to_roster_callback);
633
634        st = jabber_write_packet(ic, node);
635
[cfbb3a6]636        return st;
637}
638
[5ebff60]639static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
[a73e91a]640{
641        char *s, *jid = NULL;
642        struct xt_node *c;
[5ebff60]643
644        if ((c = xt_find_node(orig->children, "query")) &&
645            (c = xt_find_node(c->children, "item")) &&
646            (jid = xt_find_attr(c, "jid")) &&
647            (s = xt_find_attr(node, "type")) &&
648            strcmp(s, "result") == 0) {
649                if (bee_user_by_handle(ic->bee, ic, jid) == NULL) {
650                        imcb_add_buddy(ic, jid, NULL);
651                }
652        } else {
653                imcb_log(ic, "Error while adding `%s' to your contact list.",
654                         jid ? jid : "(unknown handle)");
655        }
656
[a73e91a]657        return XT_HANDLED;
658}
659
[5ebff60]660int jabber_remove_from_roster(struct im_connection *ic, char *handle)
[cfbb3a6]661{
662        struct xt_node *node;
663        int st;
[5ebff60]664
[cfbb3a6]665        /* Build the item entry */
[5ebff60]666        node = xt_new_node("item", NULL, NULL);
667        xt_add_attr(node, "jid", handle);
668        xt_add_attr(node, "subscription", "remove");
669
[cfbb3a6]670        /* And pack it into a roster-add packet */
[5ebff60]671        node = xt_new_node("query", NULL, node);
672        xt_add_attr(node, "xmlns", XMLNS_ROSTER);
673        node = jabber_make_packet("iq", "set", NULL, node);
674
675        st = jabber_write_packet(ic, node);
676
677        xt_free_node(node);
[cfbb3a6]678        return st;
679}
[dc0ba9c]680
[5ebff60]681xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
[dc0ba9c]682
[5ebff60]683xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid)
[dc0ba9c]684{
685        struct xt_node *node, *query;
686        struct jabber_buddy *bud;
[5ebff60]687
688        if ((bud = jabber_buddy_by_jid(ic, bare_jid, 0)) == NULL) {
[dc0ba9c]689                /* Who cares about the unknown... */
[5ebff60]690                imcb_log(ic, "Couldn't find buddy: %s", bare_jid);
[54a2014]691                return XT_HANDLED;
[dc0ba9c]692        }
[5ebff60]693
694        if (bud->features) { /* been here already */
[dc0ba9c]695                return XT_HANDLED;
[5ebff60]696        }
697
698        node = xt_new_node("query", NULL, NULL);
699        xt_add_attr(node, "xmlns", XMLNS_DISCO_INFO);
700
701        if (!(query = jabber_make_packet("iq", "get", bare_jid, node))) {
702                imcb_log(ic, "WARNING: Couldn't generate feature query");
703                xt_free_node(node);
[54a2014]704                return XT_HANDLED;
[dc0ba9c]705        }
706
[5ebff60]707        jabber_cache_add(ic, query, jabber_iq_parse_features);
[dc0ba9c]708
[5ebff60]709        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
[dc0ba9c]710}
711
[5ebff60]712xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
[dc0ba9c]713{
714        struct xt_node *c;
715        struct jabber_buddy *bud;
[c1a3c27]716        char *feature, *xmlns, *from;
[dc0ba9c]717
[5ebff60]718        if (!(from = xt_find_attr(node, "from")) ||
719            !(c = xt_find_node(node->children, "query")) ||
720            !(xmlns = xt_find_attr(c, "xmlns")) ||
721            !(strcmp(xmlns, XMLNS_DISCO_INFO) == 0)) {
722                imcb_log(ic, "WARNING: Received incomplete IQ-result packet for discover");
[dc0ba9c]723                return XT_HANDLED;
724        }
[5ebff60]725        if ((bud = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
[dc0ba9c]726                /* Who cares about the unknown... */
[5ebff60]727                imcb_log(ic, "Couldn't find buddy: %s", from);
[54a2014]728                return XT_HANDLED;
[dc0ba9c]729        }
[5ebff60]730
[dc0ba9c]731        c = c->children;
[5ebff60]732        while ((c = xt_find_node(c, "feature"))) {
733                feature = xt_find_attr(c, "var");
734                if (feature) {
735                        bud->features = g_slist_append(bud->features, g_strdup(feature));
736                }
[dc0ba9c]737                c = c->next;
738        }
739
740        return XT_HANDLED;
741}
742
[dd43c62]743xt_status jabber_iq_parse_gmail(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
744
745xt_status jabber_iq_query_gmail(struct im_connection *ic)
746{
747        struct xt_node *node, *query;
748        struct jabber_data *jd = ic->proto_data;
749
750        node = xt_new_node("query", NULL, NULL);
751        xt_add_attr(node, "xmlns", XMLNS_GMAILNOTIFY);
752        if (jd->gmail_time) {
753                char *formatted = g_strdup_printf("%" G_GUINT64_FORMAT, (jd->gmail_time + 1));
754                xt_add_attr(node, "newer-than-time", formatted);
755                g_free(formatted);
756        }
757        if (jd->gmail_tid) {
758                xt_add_attr(node, "newer-than-tid", jd->gmail_tid);
759        }
760
761        if (!(query = jabber_make_packet("iq", "get", jd->me, node))) {
762                imcb_log(ic, "WARNING: Couldn't generate server query");
763                xt_free_node(node);
764        }
765
766        jabber_cache_add(ic, query, jabber_iq_parse_gmail);
767
768        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
769}
770
[5ebff60]771xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
[dc0ba9c]772
[5ebff60]773xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns)
[dc0ba9c]774{
775        struct xt_node *node, *query;
776        struct jabber_data *jd = ic->proto_data;
[5ebff60]777
778        node = xt_new_node("query", NULL, NULL);
779        xt_add_attr(node, "xmlns", xmlns);
780
781        if (!(query = jabber_make_packet("iq", "get", jid, node))) {
782                imcb_log(ic, "WARNING: Couldn't generate server query");
783                xt_free_node(node);
[dc0ba9c]784        }
785
786        jd->have_streamhosts--;
[5ebff60]787        jabber_cache_add(ic, query, jabber_iq_parse_server_features);
[dc0ba9c]788
[5ebff60]789        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
[dc0ba9c]790}
791
[dd43c62]792xt_status jabber_iq_parse_gmail(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
793{
794        struct xt_node *c;
795        struct jabber_data *jd = ic->proto_data;
796        char *xmlns, *from;
797        guint64 l_time = 0;
798        char *tid = NULL;
[faeb521]799        int max = 0;
[dd43c62]800
801        if (!(c = xt_find_node(node->children, "mailbox")) ||
802            !(from = xt_find_attr(node, "from")) ||
803            !(xmlns = xt_find_attr(c, "xmlns")) ||
804            (g_strcmp0(xmlns, XMLNS_GMAILNOTIFY) != 0)) {
805                imcb_log(ic, "WARNING: Received incomplete mailbox packet for gmail notify");
806                return XT_HANDLED;
807        }
808
[b38f655]809        max = set_getint(&ic->acc->set, "mail_notifications_limit");
[dd43c62]810        c = c->children;
811
[faeb521]812        while ((max-- > 0) && (c = xt_find_node(c, "mail-thread-info"))) {
813                struct xt_node *s;
814                char *subject = "<no subject>";
815                char *sender = "<no sender>";
[dd43c62]816                guint64 t_time;
817
818                t_time = g_ascii_strtoull(xt_find_attr(c, "date"), NULL, 10);
819                if (t_time && t_time > l_time) {
820                        l_time = t_time;
821                        tid = xt_find_attr(c, "tid");
822                }
823
[faeb521]824                if ((s = xt_find_node(c->children, "senders")) &&
825                    (s = xt_find_node_by_attr(s->children, "sender", "unread", "1"))) {
826                        sender = xt_find_attr(s, "name");
827                }
[dd43c62]828
[faeb521]829                if ((s = xt_find_node(c->children, "subject")) && s->text) {
[dd43c62]830                        subject = s->text;
831                }
832
[0864a52]833                imcb_notify_email(ic, "New mail from %s: %s", sender, subject);
[dd43c62]834
835                c = c->next;
836        }
837
838        if (l_time && (!jd->gmail_time || l_time > jd->gmail_time)) {
839                jd->gmail_time = l_time;
840                if (tid) {
841                        g_free(jd->gmail_tid);
842                        jd->gmail_tid = g_strdup(tid);
843                }
844        }
845
846        return XT_HANDLED;
847}
848
[dc0ba9c]849/*
850 * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info
851 */
[5ebff60]852xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
[dc0ba9c]853{
854        struct xt_node *c;
855        struct jabber_data *jd = ic->proto_data;
[c1a3c27]856        char *xmlns, *from;
[dc0ba9c]857
[5ebff60]858        if (!(c = xt_find_node(node->children, "query")) ||
859            !(from = xt_find_attr(node, "from")) ||
860            !(xmlns = xt_find_attr(c, "xmlns"))) {
861                imcb_log(ic, "WARNING: Received incomplete IQ-result packet for discover");
[dc0ba9c]862                return XT_HANDLED;
863        }
864
865        jd->have_streamhosts++;
866
[5ebff60]867        if (strcmp(xmlns, XMLNS_DISCO_ITEMS) == 0) {
[c1a3c27]868                char *itemjid;
[dc0ba9c]869
870                /* answer from server */
[5ebff60]871
[dc0ba9c]872                c = c->children;
[5ebff60]873                while ((c = xt_find_node(c, "item"))) {
874                        itemjid = xt_find_attr(c, "jid");
875
876                        if (itemjid) {
877                                jabber_iq_query_server(ic, itemjid, XMLNS_DISCO_INFO);
878                        }
[dc0ba9c]879
880                        c = c->next;
881                }
[5ebff60]882        } else if (strcmp(xmlns, XMLNS_DISCO_INFO) == 0) {
[dc0ba9c]883                char *category, *type;
884
885                /* answer from potential proxy */
886
887                c = c->children;
[5ebff60]888                while ((c = xt_find_node(c, "identity"))) {
889                        category = xt_find_attr(c, "category");
890                        type = xt_find_attr(c, "type");
[dc0ba9c]891
[5ebff60]892                        if (type && (strcmp(type, "bytestreams") == 0) &&
893                            category && (strcmp(category, "proxy") == 0)) {
894                                jabber_iq_query_server(ic, from, XMLNS_BYTESTREAMS);
895                        }
[dc0ba9c]896
897                        c = c->next;
898                }
[dd43c62]899
900                if (jd->flags & JFLAG_GMAILNOTIFY) {
901                        /* search for gmail notification feature */
902                        c = xt_find_node(node->children, "query");
903                        c = c->children;
904                        while ((c = xt_find_node(c, "feature"))) {
905                                if (strcmp(xt_find_attr(c, "var"), XMLNS_GMAILNOTIFY) == 0) {
906                                        jabber_iq_query_gmail(ic);
907                                }
908                                c = c->next;
909                        }
910                }
911
[5ebff60]912        } else if (strcmp(xmlns, XMLNS_BYTESTREAMS) == 0) {
[c1a3c27]913                char *host, *jid, *port_s;
[dc0ba9c]914                int port;
915
916                /* answer from proxy */
917
[5ebff60]918                if ((c = xt_find_node(c->children, "streamhost")) &&
919                    (host = xt_find_attr(c, "host")) &&
920                    (port_s = xt_find_attr(c, "port")) &&
921                    (sscanf(port_s, "%d", &port) == 1) &&
922                    (jid = xt_find_attr(c, "jid"))) {
923                        jabber_streamhost_t *sh = g_new0(jabber_streamhost_t, 1);
[dc0ba9c]924
[5ebff60]925                        sh->jid = g_strdup(jid);
926                        sh->host = g_strdup(host);
927                        g_snprintf(sh->port, sizeof(sh->port), "%u", port);
928
929                        imcb_log(ic, "Proxy found: jid %s host %s port %u", jid, host, port);
930                        jd->streamhosts = g_slist_append(jd->streamhosts, sh);
[dc0ba9c]931                }
932        }
933
[5ebff60]934        if (jd->have_streamhosts == 0) {
[dc0ba9c]935                jd->have_streamhosts++;
[5ebff60]936        }
[1c3008a]937
[dc0ba9c]938        return XT_HANDLED;
939}
[d88c92a]940
[5ebff60]941static xt_status jabber_iq_version_response(struct im_connection *ic,
942                                            struct xt_node *node, struct xt_node *orig);
[d88c92a]943
[5ebff60]944void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data)
[d88c92a]945{
946        struct xt_node *node, *query;
947
[5ebff60]948        node = xt_new_node("query", NULL, NULL);
949        xt_add_attr(node, "xmlns", XMLNS_VERSION);
950        query = jabber_make_packet("iq", "get", bud->full_jid, node);
951        jabber_cache_add(ic, query, jabber_iq_version_response);
952
953        jabber_write_packet(ic, query);
[d88c92a]954}
955
[5ebff60]956static xt_status jabber_iq_version_response(struct im_connection *ic,
957                                            struct xt_node *node, struct xt_node *orig)
[d88c92a]958{
959        struct xt_node *query;
960        GString *rets;
961        char *s;
962        char *ret[2] = {};
963        bee_user_t *bu;
964        struct jabber_buddy *bud = NULL;
[5ebff60]965
966        if ((s = xt_find_attr(node, "from")) &&
967            (bud = jabber_buddy_by_jid(ic, s, 0)) &&
968            (query = xt_find_node(node->children, "query")) &&
969            (bu = bee_user_by_handle(ic->bee, ic, bud->bare_jid))) {
970                rets = g_string_new("Resource ");
971                g_string_append(rets, bud->resource);
972        } else {
[d88c92a]973                return XT_HANDLED;
[5ebff60]974        }
975
976        for (query = query->children; query; query = query->next) {
977                if (query->text_len > 0) {
978                        g_string_append_printf(rets, " %s: %s,", query->name, query->text);
979                }
980        }
981
982        g_string_truncate(rets, rets->len - 1);
[d88c92a]983        ret[0] = rets->str;
[5ebff60]984        imcb_buddy_action_response(bu, "VERSION", ret, NULL);
985        g_string_free(rets, TRUE);
986
[d88c92a]987        return XT_HANDLED;
988}
[bb2d198]989
[5ebff60]990static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
991                                                 struct xt_node *node, struct xt_node *orig);
[bb2d198]992
[40cfbc5]993int jabber_iq_disco_server(struct im_connection *ic)
[bb2d198]994{
995        struct xt_node *node, *iq;
996        struct jabber_data *jd = ic->proto_data;
[5ebff60]997
998        node = xt_new_node("query", NULL, NULL);
999        xt_add_attr(node, "xmlns", XMLNS_DISCO_INFO);
1000        iq = jabber_make_packet("iq", "get", jd->server, node);
1001
1002        jabber_cache_add(ic, iq, jabber_iq_disco_server_response);
1003        return jabber_write_packet(ic, iq);
[bb2d198]1004}
1005
[5ebff60]1006static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
1007                                                 struct xt_node *node, struct xt_node *orig)
[bb2d198]1008{
1009        struct jabber_data *jd = ic->proto_data;
[fa8f57b]1010        struct xt_node *query, *id;
[5ebff60]1011
[fa8f57b]1012        if (!(query = xt_find_node(node->children, "query"))) {
1013                return XT_HANDLED;
1014        }
1015
1016        if (xt_find_node_by_attr(query->children, "feature", "var", XMLNS_CARBONS) &&
1017            set_getbool(&ic->acc->set, "carbons")) {
1018
1019                struct xt_node *enable, *iq;
1020
1021                enable = xt_new_node("enable", NULL, NULL);
1022                xt_add_attr(enable, "xmlns", XMLNS_CARBONS);
1023                iq = jabber_make_packet("iq", "set", NULL, enable);
1024
1025                jabber_cache_add(ic, iq, jabber_iq_carbons_response);
1026                jabber_write_packet(ic, iq);
1027        }
1028
1029        if ((id = xt_find_node(query->children, "identity"))) {
[bb2d198]1030                char *cat, *type, *name;
[5ebff60]1031
1032                if (!(cat = xt_find_attr(id, "category")) ||
1033                    !(type = xt_find_attr(id, "type")) ||
1034                    !(name = xt_find_attr(id, "name"))) {
[bb2d198]1035                        return XT_HANDLED;
[5ebff60]1036                }
1037
1038                if (strcmp(cat, "server") == 0 && strcmp(type, "im") == 0 &&
1039                    strstr(name, "Google") != NULL) {
[bb2d198]1040                        jd->flags |= JFLAG_GTALK;
[5ebff60]1041                }
[bb2d198]1042        }
[5ebff60]1043
[bb2d198]1044        return XT_HANDLED;
1045}
[fa8f57b]1046
1047static xt_status jabber_iq_carbons_response(struct im_connection *ic,
1048                                            struct xt_node *node, struct xt_node *orig)
1049{
1050        struct jabber_error *err;
1051
1052        if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
1053                imcb_error(ic, "Error enabling carbons: %s%s%s",
1054                           err->code, err->text ? ": " : "", err->text ? err->text : "");
1055                jabber_error_free(err);
1056        } else {
1057                imcb_log(ic, "Carbons enabled");
1058        }
1059
1060        return XT_HANDLED;
1061}
Note: See TracBrowser for help on using the repository browser.