source: protocols/jabber/iq.c @ 1e2094e

Last change on this file since 1e2094e was fa8f57b, checked in by dequis <dx@…>, at 2015-11-21T03:01:05Z

jabber: Implement carbons (XEP-0280)

"Message carbons" (XEP-0280) is a server feature to get copies of
outgoing messages sent from other clients connected to the same account.
It's not widely supported by most public XMPP servers (easier if you
host your own), but this will probably change in the next few years.

This is enabled by default if the server supports it. It can also be
disabled with the "carbons" account setting.

Loosely based on a patch by kormat from trac ticket 1021. (Thanks!)
I moved stuff around, simplified things, fixed a few bugs, and used the
new self-messages feature.

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