source: protocols/jabber/iq.c @ 0864a52

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

imcb_notify_email: change parameters to take a format string

Saves some messing with g_strdup_printf for the callers, and
flags/sent_at weren't used anyway.

Also check if the mail_notifications setting is enabled

  • Property mode set to 100644
File size: 30.6 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Jabber module - IQ packets                                               *
5*                                                                           *
6*  Copyright 2006-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
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"
25#include "sha1.h"
26
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);
29static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node);
30
31xt_status jabber_pkt_iq(struct xt_node *node, gpointer data)
32{
33        struct im_connection *ic = data;
34        struct jabber_data *jd = ic->proto_data;
35        struct xt_node *c, *reply = NULL;
36        char *type, *s;
37        int st, pack = 1;
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);
44                return XT_ABORT;
45        }
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"))) {
54                        /* Sigh. Who decided to suddenly invent new elements
55                           instead of just sticking with <query/>? */
56                        return XT_HANDLED;
57                }
58
59                reply = xt_new_node("query", NULL, NULL);
60                xt_add_attr(reply, "xmlns", s);
61
62                /* Of course this is a very essential query to support. ;-) */
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) {
68                        time_t time_ep;
69                        char buf[1024];
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) {
80                        time_t time_ep;
81                        char buf[1024];
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) {
95                                buf[6] = '\0';
96                                buf[5] = buf[4];
97                                buf[4] = buf[3];
98                                buf[3] = ':';
99                        }
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                        }
107                        pack = 0;
108                } else if (strcmp(s, XMLNS_DISCO_INFO) == 0) {
109                        const char *features[] = { XMLNS_DISCO_INFO,
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 };
121                        const char **f;
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);
133                        }
134                } else {
135                        xt_free_node(reply);
136                        reply = jabber_make_error_packet(node, "feature-not-implemented", "cancel", NULL);
137                        pack = 0;
138                }
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);
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);
148                } else if (!(c = xt_find_node(node->children, "query")) ||
149                           !(s = xt_find_attr(c, "xmlns"))) {
150                        return XT_HANDLED;
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
162                                /* Should we generate a reply here? Don't think it's
163                                   very important... */
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);
169                                pack = 0;
170                        }
171                } else if (strcmp(s, XMLNS_BYTESTREAMS) == 0) {
172                        /* Bytestream Request (stage 2 of file transfer) */
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);
177                        pack = 0;
178                }
179        }
180
181        /* If we recognized the xmlns and managed to generate a reply,
182           finish and send it. */
183        if (reply) {
184                /* Normally we still have to pack it into an iq-result
185                   packet, but for errors, for example, we don't. */
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) {
196                        return XT_ABORT;
197                }
198        }
199
200        return XT_HANDLED;
201}
202
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);
205
206int jabber_init_iq_auth(struct im_connection *ic)
207{
208        struct jabber_data *jd = ic->proto_data;
209        struct xt_node *node;
210        int st;
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
219        return st;
220}
221
222static xt_status jabber_do_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
223{
224        struct jabber_data *jd = ic->proto_data;
225        struct xt_node *reply, *query;
226        xt_status st;
227        char *s;
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);
232                return XT_HANDLED;
233        }
234
235        /* Time to authenticate ourselves! */
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"))) {
242                /* We can do digest authentication, it seems, and of
243                   course we prefer that. */
244                sha1_state_t sha;
245                char hash_hex[41];
246                unsigned char hash[20];
247                int i;
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")) {
260                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
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);
267                return XT_ABORT;
268        }
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
274        return st ? XT_HANDLED : XT_ABORT;
275}
276
277static xt_status jabber_finish_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
278{
279        struct jabber_data *jd = ic->proto_data;
280        char *type;
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);
285                return XT_HANDLED;
286        }
287
288        if (strcmp(type, "error") == 0) {
289                imcb_error(ic, "Authentication failure");
290                imc_logout(ic, FALSE);
291                return XT_ABORT;
292        } else if (strcmp(type, "result") == 0) {
293                /* This happens when we just successfully authenticated the
294                   old (non-SASL) way. */
295                jd->flags |= JFLAG_AUTHENTICATED;
296                if (!jabber_get_roster(ic)) {
297                        return XT_ABORT;
298                }
299                if (!jabber_iq_disco_server(ic)) {
300                        return XT_ABORT;
301                }
302        }
303
304        return XT_HANDLED;
305}
306
307xt_status jabber_pkt_bind_sess(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
308{
309        struct jabber_data *jd = ic->proto_data;
310        struct xt_node *c, *reply = NULL;
311        char *s;
312
313        if (node && (c = xt_find_node(node->children, "bind"))) {
314                c = xt_find_node(c->children, "jid");
315                if (!c || !c->text) {
316                        /* Server is crap, but this is no disaster. */
317                } else if (jabber_compare_jid(jd->me, c->text) == 0) {
318                        s = strchr(c->text, '/');
319                        if (s) {
320                                *s = '\0';
321                        }
322                        jabber_set_me(ic, c->text);
323                        if (s) {
324                                *s = '/';
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);
329                }
330        }
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);
335                jd->flags &= ~JFLAG_WANT_BIND;
336        } else if (jd->flags & JFLAG_WANT_SESSION) {
337                reply = xt_new_node("session", NULL, NULL);
338                xt_add_attr(reply, "xmlns", XMLNS_SESSION);
339                jd->flags &= ~JFLAG_WANT_SESSION;
340        }
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)) {
347                        return XT_ABORT;
348                }
349                if (jd->flags & JFLAG_GMAILNOTIFY && node == NULL) {
350                        jabber_iq_query_server(ic, jd->server, XMLNS_DISCO_INFO);
351                }
352        } else if ((jd->flags & (JFLAG_WANT_BIND | JFLAG_WANT_SESSION)) == 0) {
353                if (!jabber_get_roster(ic)) {
354                        return XT_ABORT;
355                }
356                if (!jabber_iq_disco_server(ic)) {
357                        return XT_ABORT;
358                }
359        }
360
361        return XT_HANDLED;
362}
363
364int jabber_get_roster(struct im_connection *ic)
365{
366        struct xt_node *node;
367        int st;
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
378        return st;
379}
380
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
400static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
401{
402        struct jabber_data *jd = ic->proto_data;
403        struct xt_node *query, *c;
404        int initial = (orig != NULL);
405
406        if (!(query = xt_find_node(node->children, "query"))) {
407                imcb_log(ic, "Warning: Received NULL roster packet");
408                return XT_HANDLED;
409        }
410
411        c = query->children;
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");
417                char *mention_name = xt_find_attr(c, "mention_name");
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                                }
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                                }
433                        } else if (strcmp(sub, "remove") == 0) {
434                                jabber_buddy_remove_bare(ic, jid);
435                                imcb_remove_buddy(ic, jid, NULL);
436                        }
437                }
438
439                c = c->next;
440        }
441
442        if (initial) {
443                imcb_connected(ic);
444        }
445
446        return XT_HANDLED;
447}
448
449int jabber_get_vcard(struct im_connection *ic, char *bare_jid)
450{
451        struct xt_node *node;
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);
463}
464
465static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
466{
467        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
468        GString *reply;
469        char *s;
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)");
476                return XT_HANDLED;
477        }
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
484        /* I hate this format, I really do... */
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
520        /* Slightly alternative use of for... ;-) */
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) {
523                        continue;
524                }
525
526                if (xt_find_node(c->children, "HOME")) {
527                        s = "Home";
528                } else if (xt_find_node(c->children, "WORK")) {
529                        s = "Work";
530                } else {
531                        s = "Misc.";
532                }
533
534                g_string_append_printf(reply, "%s e-mail address: %s\n", s, sc->text);
535        }
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
541        /* Slightly alternative use of for... ;-) */
542        for (c = vc->children; (c = xt_find_node(c, "ADR")); c = c->next) {
543                if (xt_find_node(c->children, "HOME")) {
544                        s = "Home";
545                } else if (xt_find_node(c->children, "WORK")) {
546                        s = "Work";
547                } else {
548                        s = "Misc.";
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) {
581                        continue;
582                }
583
584                if (xt_find_node(c->children, "HOME")) {
585                        s = "Home";
586                } else if (xt_find_node(c->children, "WORK")) {
587                        s = "Work";
588                } else {
589                        s = "Misc.";
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);
597        }
598
599        /* *sigh* */
600
601        imcb_log(ic, "%s", reply->str);
602        g_string_free(reply, TRUE);
603
604        return XT_HANDLED;
605}
606
607static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
608
609int jabber_add_to_roster(struct im_connection *ic, const char *handle, const char *name, const char *group)
610{
611        struct xt_node *node;
612        int st;
613
614        /* Build the item entry */
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
624        /* And pack it into a roster-add packet */
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
632        return st;
633}
634
635static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
636{
637        char *s, *jid = NULL;
638        struct xt_node *c;
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
653        return XT_HANDLED;
654}
655
656int jabber_remove_from_roster(struct im_connection *ic, char *handle)
657{
658        struct xt_node *node;
659        int st;
660
661        /* Build the item entry */
662        node = xt_new_node("item", NULL, NULL);
663        xt_add_attr(node, "jid", handle);
664        xt_add_attr(node, "subscription", "remove");
665
666        /* And pack it into a roster-add packet */
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);
674        return st;
675}
676
677xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
678
679xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid)
680{
681        struct xt_node *node, *query;
682        struct jabber_buddy *bud;
683
684        if ((bud = jabber_buddy_by_jid(ic, bare_jid, 0)) == NULL) {
685                /* Who cares about the unknown... */
686                imcb_log(ic, "Couldn't find buddy: %s", bare_jid);
687                return XT_HANDLED;
688        }
689
690        if (bud->features) { /* been here already */
691                return XT_HANDLED;
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);
700                return XT_HANDLED;
701        }
702
703        jabber_cache_add(ic, query, jabber_iq_parse_features);
704
705        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
706}
707
708xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
709{
710        struct xt_node *c;
711        struct jabber_buddy *bud;
712        char *feature, *xmlns, *from;
713
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");
719                return XT_HANDLED;
720        }
721        if ((bud = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
722                /* Who cares about the unknown... */
723                imcb_log(ic, "Couldn't find buddy: %s", from);
724                return XT_HANDLED;
725        }
726
727        c = c->children;
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                }
733                c = c->next;
734        }
735
736        return XT_HANDLED;
737}
738
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
767xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
768
769xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns)
770{
771        struct xt_node *node, *query;
772        struct jabber_data *jd = ic->proto_data;
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);
780        }
781
782        jd->have_streamhosts--;
783        jabber_cache_add(ic, query, jabber_iq_parse_server_features);
784
785        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
786}
787
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;
795        int max = 0;
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
805        max = set_getint(&ic->acc->set, "gmail_notifications_limit");
806        c = c->children;
807
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>";
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
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                }
824
825                if ((s = xt_find_node(c->children, "subject")) && s->text) {
826                        subject = s->text;
827                }
828
829                imcb_notify_email(ic, "New mail from %s: %s", sender, subject);
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
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 */
848xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
849{
850        struct xt_node *c;
851        struct jabber_data *jd = ic->proto_data;
852        char *xmlns, *from;
853
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");
858                return XT_HANDLED;
859        }
860
861        jd->have_streamhosts++;
862
863        if (strcmp(xmlns, XMLNS_DISCO_ITEMS) == 0) {
864                char *itemjid;
865
866                /* answer from server */
867
868                c = c->children;
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                        }
875
876                        c = c->next;
877                }
878        } else if (strcmp(xmlns, XMLNS_DISCO_INFO) == 0) {
879                char *category, *type;
880
881                /* answer from potential proxy */
882
883                c = c->children;
884                while ((c = xt_find_node(c, "identity"))) {
885                        category = xt_find_attr(c, "category");
886                        type = xt_find_attr(c, "type");
887
888                        if (type && (strcmp(type, "bytestreams") == 0) &&
889                            category && (strcmp(category, "proxy") == 0)) {
890                                jabber_iq_query_server(ic, from, XMLNS_BYTESTREAMS);
891                        }
892
893                        c = c->next;
894                }
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
908        } else if (strcmp(xmlns, XMLNS_BYTESTREAMS) == 0) {
909                char *host, *jid, *port_s;
910                int port;
911
912                /* answer from proxy */
913
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);
920
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);
927                }
928        }
929
930        if (jd->have_streamhosts == 0) {
931                jd->have_streamhosts++;
932        }
933
934        return XT_HANDLED;
935}
936
937static xt_status jabber_iq_version_response(struct im_connection *ic,
938                                            struct xt_node *node, struct xt_node *orig);
939
940void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data)
941{
942        struct xt_node *node, *query;
943
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);
950}
951
952static xt_status jabber_iq_version_response(struct im_connection *ic,
953                                            struct xt_node *node, struct xt_node *orig)
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;
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 {
969                return XT_HANDLED;
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);
979        ret[0] = rets->str;
980        imcb_buddy_action_response(bu, "VERSION", ret, NULL);
981        g_string_free(rets, TRUE);
982
983        return XT_HANDLED;
984}
985
986static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
987                                                 struct xt_node *node, struct xt_node *orig);
988
989int jabber_iq_disco_server(struct im_connection *ic)
990{
991        struct xt_node *node, *iq;
992        struct jabber_data *jd = ic->proto_data;
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);
1000}
1001
1002static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
1003                                                 struct xt_node *node, struct xt_node *orig)
1004{
1005        struct jabber_data *jd = ic->proto_data;
1006        struct xt_node *id;
1007
1008        if ((id = xt_find_path(node, "query/identity"))) {
1009                char *cat, *type, *name;
1010
1011                if (!(cat = xt_find_attr(id, "category")) ||
1012                    !(type = xt_find_attr(id, "type")) ||
1013                    !(name = xt_find_attr(id, "name"))) {
1014                        return XT_HANDLED;
1015                }
1016
1017                if (strcmp(cat, "server") == 0 && strcmp(type, "im") == 0 &&
1018                    strstr(name, "Google") != NULL) {
1019                        jd->flags |= JFLAG_GTALK;
1020                }
1021        }
1022
1023        return XT_HANDLED;
1024}
Note: See TracBrowser for help on using the repository browser.