source: protocols/jabber/iq.c @ 10d089d

Last change on this file since 10d089d was b38f655, checked in by dequis <dx@…>, at 2015-05-28T05:26:30Z

Rename mail notification related settings for consistency

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