source: protocols/jabber/iq.c @ faeb521

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

Simplify display of gmail notifications

  • Add gmail_notifications_limit hidden setting, set to 5 by default.
  • Don't show "snippets" in email notifications. Not very useful and they make the whole thing seem too spammy
  • Show sender name instead of your own email
  • Default values for empty subject / sender
  • Property mode set to 100644
File size: 30.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);
[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
[faeb521]805        max = set_getint(&ic->acc->set, "gmail_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                char *msg = NULL;
813                guint64 t_time;
814
815                t_time = g_ascii_strtoull(xt_find_attr(c, "date"), NULL, 10);
816                if (t_time && t_time > l_time) {
817                        l_time = t_time;
818                        tid = xt_find_attr(c, "tid");
819                }
820
[faeb521]821                if ((s = xt_find_node(c->children, "senders")) &&
822                    (s = xt_find_node_by_attr(s->children, "sender", "unread", "1"))) {
823                        sender = xt_find_attr(s, "name");
824                }
[dd43c62]825
[faeb521]826                if ((s = xt_find_node(c->children, "subject")) && s->text) {
[dd43c62]827                        subject = s->text;
828                }
829
[faeb521]830                msg = g_strdup_printf("New mail from %s: %s", sender, subject);
[dd43c62]831
832                imcb_notify_email(ic, set_getstr(&ic->acc->set, "notify_handle"), msg, 0, 0);
833
834                c = c->next;
835                g_free(msg);
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;
1010        struct xt_node *id;
[5ebff60]1011
1012        if ((id = xt_find_path(node, "query/identity"))) {
[bb2d198]1013                char *cat, *type, *name;
[5ebff60]1014
1015                if (!(cat = xt_find_attr(id, "category")) ||
1016                    !(type = xt_find_attr(id, "type")) ||
1017                    !(name = xt_find_attr(id, "name"))) {
[bb2d198]1018                        return XT_HANDLED;
[5ebff60]1019                }
1020
1021                if (strcmp(cat, "server") == 0 && strcmp(type, "im") == 0 &&
1022                    strstr(name, "Google") != NULL) {
[bb2d198]1023                        jd->flags |= JFLAG_GTALK;
[5ebff60]1024                }
[bb2d198]1025        }
[5ebff60]1026
[bb2d198]1027        return XT_HANDLED;
1028}
Note: See TracBrowser for help on using the repository browser.