source: protocols/jabber/iq.c @ 684aca2

Last change on this file since 684aca2 was a4ac9c4, checked in by GitHub <noreply@…>, at 2023-04-01T20:09:39Z

Deprecate sha1_* functions (#172)

  • Migrate sha1 calls to direct use of GChecksum
  • Mark sha1.h functions as deprecated
  • Property mode set to 100644
File size: 33.3 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
26static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
27static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
28static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node);
29static xt_status jabber_iq_carbons_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
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
55                        reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL);
56                        st = jabber_write_packet(ic, reply);
57                        xt_free_node(reply);
58                        return st;
59                }
60
61                reply = xt_new_node("query", NULL, NULL);
62                xt_add_attr(reply, "xmlns", s);
63
64                /* Of course this is a very essential query to support. ;-) */
65                if (strcmp(s, XMLNS_VERSION) == 0) {
66                        xt_add_child(reply, xt_new_node("name", set_getstr(&ic->acc->set, "user_agent"), NULL));
67                        xt_add_child(reply, xt_new_node("version", BITLBEE_VERSION, 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, "service-unavailable", "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_ABORT;
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                GChecksum *sha;
247                char hash_hex[41];
248                unsigned char hash[20];
249                int i;
250                gsize digest_len = 20;
251
252                sha = g_checksum_new(G_CHECKSUM_SHA1);
253                g_checksum_update(sha, (unsigned char *) s, strlen(s));
254                g_checksum_update(sha, (unsigned char *) ic->acc->pass, strlen(ic->acc->pass));
255                g_checksum_get_digest(sha, hash, &digest_len);
256                g_checksum_free(sha);
257
258                for (i = 0; i < 20; i++) {
259                        sprintf(hash_hex + i * 2, "%02x", hash[i]);
260                }
261
262                xt_add_child(reply, xt_new_node("digest", hash_hex, NULL));
263        } else if (xt_find_node(query->children, "password")) {
264                /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
265                xt_add_child(reply, xt_new_node("password", ic->acc->pass, NULL));
266        } else {
267                xt_free_node(reply);
268
269                imcb_error(ic, "Can't find suitable authentication method");
270                imc_logout(ic, FALSE);
271                return XT_ABORT;
272        }
273
274        reply = jabber_make_packet("iq", "set", NULL, reply);
275        jabber_cache_add(ic, reply, jabber_finish_iq_auth);
276        st = jabber_write_packet(ic, reply);
277
278        return st ? XT_HANDLED : XT_ABORT;
279}
280
281static xt_status jabber_finish_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
282{
283        struct jabber_data *jd = ic->proto_data;
284        char *type;
285
286        if (!(type = xt_find_attr(node, "type"))) {
287                imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
288                imc_logout(ic, FALSE);
289                return XT_ABORT;
290        }
291
292        if (strcmp(type, "error") == 0) {
293                imcb_error(ic, "Authentication failure");
294                imc_logout(ic, FALSE);
295                return XT_ABORT;
296        } else if (strcmp(type, "result") == 0) {
297                /* This happens when we just successfully authenticated the
298                   old (non-SASL) way. */
299                jd->flags |= JFLAG_AUTHENTICATED;
300                if (!jabber_get_roster(ic)) {
301                        return XT_ABORT;
302                }
303                if (!jabber_iq_disco_server(ic)) {
304                        return XT_ABORT;
305                }
306        }
307
308        return XT_HANDLED;
309}
310
311xt_status jabber_pkt_bind_sess(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
312{
313        struct jabber_data *jd = ic->proto_data;
314        struct xt_node *c, *reply = NULL;
315        char *s;
316
317        if (node && (c = xt_find_node(node->children, "bind"))) {
318                c = xt_find_node(c->children, "jid");
319                if (!c || !c->text) {
320                        /* Server is crap, but this is no disaster. */
321                } else if (jabber_compare_jid(jd->me, c->text) == 0) {
322                        s = strchr(c->text, '/');
323                        if (s) {
324                                *s = '\0';
325                        }
326                        jabber_set_me(ic, c->text);
327                        if (s) {
328                                *s = '/';
329                        }
330                } else if (c && c->text_len && (s = strchr(c->text, '/')) &&
331                           strcmp(s + 1, set_getstr(&ic->acc->set, "resource")) != 0) {
332                        imcb_log(ic, "Server changed session resource string to `%s'", s + 1);
333                }
334        }
335
336        if (jd->flags & JFLAG_WANT_BIND) {
337                reply = xt_new_node("bind", NULL, xt_new_node("resource", set_getstr(&ic->acc->set, "resource"), NULL));
338                xt_add_attr(reply, "xmlns", XMLNS_BIND);
339                jd->flags &= ~JFLAG_WANT_BIND;
340        } else if (jd->flags & JFLAG_WANT_SESSION) {
341                reply = xt_new_node("session", NULL, NULL);
342                xt_add_attr(reply, "xmlns", XMLNS_SESSION);
343                jd->flags &= ~JFLAG_WANT_SESSION;
344        }
345
346        if (reply != NULL) {
347                reply = jabber_make_packet("iq", "set", NULL, reply);
348                jabber_cache_add(ic, reply, jabber_pkt_bind_sess);
349
350                if (!jabber_write_packet(ic, reply)) {
351                        return XT_ABORT;
352                }
353                if (jd->flags & JFLAG_GMAILNOTIFY && node == NULL) {
354                        jabber_iq_query_server(ic, jd->server, XMLNS_DISCO_INFO);
355                }
356        } else if ((jd->flags & (JFLAG_WANT_BIND | JFLAG_WANT_SESSION)) == 0) {
357                if (!jabber_get_roster(ic)) {
358                        return XT_ABORT;
359                }
360                if (!jabber_iq_disco_server(ic)) {
361                        return XT_ABORT;
362                }
363        }
364
365        return XT_HANDLED;
366}
367
368int jabber_get_roster(struct im_connection *ic)
369{
370        struct xt_node *node;
371        int st;
372
373        imcb_log(ic, "Authenticated, requesting buddy list");
374
375        node = xt_new_node("query", NULL, NULL);
376        xt_add_attr(node, "xmlns", XMLNS_ROSTER);
377        node = jabber_make_packet("iq", "get", NULL, node);
378
379        jabber_cache_add(ic, node, jabber_parse_roster);
380        st = jabber_write_packet(ic, node);
381
382        return st;
383}
384
385xt_status jabber_iq_query_gmail(struct im_connection *ic);
386
387static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node)
388{
389        struct xt_node *response;
390        struct jabber_data *jd = ic->proto_data;
391       
392        response = jabber_make_packet("iq", "result", jd->me, NULL);
393
394        jabber_cache_add(ic, response, NULL);
395        if (!jabber_write_packet(ic, response)) {
396                return XT_ABORT;
397        }
398
399        jabber_iq_query_gmail(ic);
400
401        return XT_HANDLED;
402}
403
404static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
405{
406        struct jabber_data *jd = ic->proto_data;
407        struct xt_node *query, *c;
408        int initial = (orig != NULL);
409
410        if (!(query = xt_find_node(node->children, "query"))) {
411                imcb_log(ic, "Warning: Received NULL roster packet");
412                return XT_HANDLED;
413        }
414
415        c = query->children;
416        while ((c = xt_find_node(c, "item"))) {
417                struct xt_node *group = xt_find_node(c->children, "group");
418                char *jid = xt_find_attr(c, "jid");
419                char *name = xt_find_attr(c, "name");
420                char *sub = xt_find_attr(c, "subscription");
421                char *mention_name = xt_find_attr(c, "mention_name");
422
423                if (jid && sub) {
424                        if ((strcmp(sub, "both") == 0 || strcmp(sub, "to") == 0)) {
425                                imcb_add_buddy(ic, jid, (group && group->text_len) ?
426                                               group->text : NULL);
427
428                                if (name) {
429                                        imcb_rename_buddy(ic, jid, name);
430                                }
431
432                                /* This could also be used to set the full name as nick for fb/gtalk,
433                                 * but i'm keeping the old (ugly?) default behavior just to be safe */
434                                if (mention_name && (jd->flags & JFLAG_HIPCHAT)) {
435                                        imcb_buddy_nick_hint(ic, jid, mention_name);
436                                }
437                        } else if (strcmp(sub, "remove") == 0) {
438                                jabber_buddy_remove_bare(ic, jid);
439                                imcb_remove_buddy(ic, jid, NULL);
440                        }
441                }
442
443                c = c->next;
444        }
445
446        if (initial) {
447                imcb_connected(ic);
448        }
449
450        return XT_HANDLED;
451}
452
453int jabber_get_vcard(struct im_connection *ic, char *bare_jid)
454{
455        struct xt_node *node;
456
457        if (strchr(bare_jid, '/')) {
458                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
459
460        }
461        node = xt_new_node("vCard", NULL, NULL);
462        xt_add_attr(node, "xmlns", XMLNS_VCARD);
463        node = jabber_make_packet("iq", "get", bare_jid, node);
464
465        jabber_cache_add(ic, node, jabber_iq_display_vcard);
466        return jabber_write_packet(ic, node);
467}
468
469static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
470{
471        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
472        GString *reply;
473        char *s;
474
475        if ((s = xt_find_attr(node, "type")) == NULL ||
476            strcmp(s, "result") != 0 ||
477            (vc = xt_find_node(node->children, "vCard")) == NULL) {
478                s = xt_find_attr(orig, "to");   /* If this returns NULL something's wrong.. */
479                imcb_log(ic, "Could not retrieve vCard of %s", s ? s : "(NULL)");
480                return XT_HANDLED;
481        }
482
483        s = xt_find_attr(orig, "to");
484        reply = g_string_new("vCard information for ");
485        reply = g_string_append(reply, s ? s : "(NULL)");
486        reply = g_string_append(reply, ":\n");
487
488        /* I hate this format, I really do... */
489
490        if ((c = xt_find_node(vc->children, "FN")) && c->text_len) {
491                g_string_append_printf(reply, "Name: %s\n", c->text);
492        }
493
494        if ((c = xt_find_node(vc->children, "N")) && c->children) {
495                reply = g_string_append(reply, "Full name:");
496
497                if ((sc = xt_find_node(c->children, "PREFIX")) && sc->text_len) {
498                        g_string_append_printf(reply, " %s", sc->text);
499                }
500                if ((sc = xt_find_node(c->children, "GIVEN")) && sc->text_len) {
501                        g_string_append_printf(reply, " %s", sc->text);
502                }
503                if ((sc = xt_find_node(c->children, "MIDDLE")) && sc->text_len) {
504                        g_string_append_printf(reply, " %s", sc->text);
505                }
506                if ((sc = xt_find_node(c->children, "FAMILY")) && sc->text_len) {
507                        g_string_append_printf(reply, " %s", sc->text);
508                }
509                if ((sc = xt_find_node(c->children, "SUFFIX")) && sc->text_len) {
510                        g_string_append_printf(reply, " %s", sc->text);
511                }
512
513                reply = g_string_append_c(reply, '\n');
514        }
515
516        if ((c = xt_find_node(vc->children, "NICKNAME")) && c->text_len) {
517                g_string_append_printf(reply, "Nickname: %s\n", c->text);
518        }
519
520        if ((c = xt_find_node(vc->children, "BDAY")) && c->text_len) {
521                g_string_append_printf(reply, "Date of birth: %s\n", c->text);
522        }
523
524        /* Slightly alternative use of for... ;-) */
525        for (c = vc->children; (c = xt_find_node(c, "EMAIL")); c = c->next) {
526                if ((sc = xt_find_node(c->children, "USERID")) == NULL || sc->text_len == 0) {
527                        continue;
528                }
529
530                if (xt_find_node(c->children, "HOME")) {
531                        s = "Home";
532                } else if (xt_find_node(c->children, "WORK")) {
533                        s = "Work";
534                } else {
535                        s = "Misc.";
536                }
537
538                g_string_append_printf(reply, "%s e-mail address: %s\n", s, sc->text);
539        }
540
541        if ((c = xt_find_node(vc->children, "URL")) && c->text_len) {
542                g_string_append_printf(reply, "Homepage: %s\n", c->text);
543        }
544
545        /* Slightly alternative use of for... ;-) */
546        for (c = vc->children; (c = xt_find_node(c, "ADR")); c = c->next) {
547                if (xt_find_node(c->children, "HOME")) {
548                        s = "Home";
549                } else if (xt_find_node(c->children, "WORK")) {
550                        s = "Work";
551                } else {
552                        s = "Misc.";
553                }
554
555                g_string_append_printf(reply, "%s address: ", s);
556
557                if ((sc = xt_find_node(c->children, "STREET")) && sc->text_len) {
558                        g_string_append_printf(reply, "%s ", sc->text);
559                }
560                if ((sc = xt_find_node(c->children, "EXTADR")) && sc->text_len) {
561                        g_string_append_printf(reply, "%s, ", sc->text);
562                }
563                if ((sc = xt_find_node(c->children, "PCODE")) && sc->text_len) {
564                        g_string_append_printf(reply, "%s, ", sc->text);
565                }
566                if ((sc = xt_find_node(c->children, "LOCALITY")) && sc->text_len) {
567                        g_string_append_printf(reply, "%s, ", sc->text);
568                }
569                if ((sc = xt_find_node(c->children, "REGION")) && sc->text_len) {
570                        g_string_append_printf(reply, "%s, ", sc->text);
571                }
572                if ((sc = xt_find_node(c->children, "CTRY")) && sc->text_len) {
573                        g_string_append_printf(reply, "%s", sc->text);
574                }
575
576                if (reply->str[reply->len - 2] == ',') {
577                        reply = g_string_truncate(reply, reply->len - 2);
578                }
579
580                reply = g_string_append_c(reply, '\n');
581        }
582
583        for (c = vc->children; (c = xt_find_node(c, "TEL")); c = c->next) {
584                if ((sc = xt_find_node(c->children, "NUMBER")) == NULL || sc->text_len == 0) {
585                        continue;
586                }
587
588                if (xt_find_node(c->children, "HOME")) {
589                        s = "Home";
590                } else if (xt_find_node(c->children, "WORK")) {
591                        s = "Work";
592                } else {
593                        s = "Misc.";
594                }
595
596                g_string_append_printf(reply, "%s phone number: %s\n", s, sc->text);
597        }
598
599        if ((c = xt_find_node(vc->children, "DESC")) && c->text_len) {
600                g_string_append_printf(reply, "Other information:\n%s", c->text);
601        }
602
603        /* *sigh* */
604
605        imcb_log(ic, "%s", reply->str);
606        g_string_free(reply, TRUE);
607
608        return XT_HANDLED;
609}
610
611static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
612
613int jabber_add_to_roster(struct im_connection *ic, const char *handle, const char *name, const char *group)
614{
615        struct xt_node *node;
616        int st;
617
618        /* Build the item entry */
619        node = xt_new_node("item", NULL, NULL);
620        xt_add_attr(node, "jid", handle);
621        if (name) {
622                xt_add_attr(node, "name", name);
623        }
624        if (group) {
625                xt_add_child(node, xt_new_node("group", group, NULL));
626        }
627
628        /* And pack it into a roster-add packet */
629        node = xt_new_node("query", NULL, node);
630        xt_add_attr(node, "xmlns", XMLNS_ROSTER);
631        node = jabber_make_packet("iq", "set", NULL, node);
632        jabber_cache_add(ic, node, jabber_add_to_roster_callback);
633
634        st = jabber_write_packet(ic, node);
635
636        return st;
637}
638
639static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
640{
641        char *s, *jid = NULL;
642        struct xt_node *c;
643
644        if ((c = xt_find_node(orig->children, "query")) &&
645            (c = xt_find_node(c->children, "item")) &&
646            (jid = xt_find_attr(c, "jid")) &&
647            (s = xt_find_attr(node, "type")) &&
648            strcmp(s, "result") == 0) {
649                if (bee_user_by_handle(ic->bee, ic, jid) == NULL) {
650                        imcb_add_buddy(ic, jid, NULL);
651                }
652        } else {
653                imcb_log(ic, "Error while adding `%s' to your contact list.",
654                         jid ? jid : "(unknown handle)");
655        }
656
657        return XT_HANDLED;
658}
659
660int jabber_remove_from_roster(struct im_connection *ic, char *handle)
661{
662        struct xt_node *node;
663        int st;
664
665        /* Build the item entry */
666        node = xt_new_node("item", NULL, NULL);
667        xt_add_attr(node, "jid", handle);
668        xt_add_attr(node, "subscription", "remove");
669
670        /* And pack it into a roster-add packet */
671        node = xt_new_node("query", NULL, node);
672        xt_add_attr(node, "xmlns", XMLNS_ROSTER);
673        node = jabber_make_packet("iq", "set", NULL, node);
674
675        st = jabber_write_packet(ic, node);
676
677        xt_free_node(node);
678        return st;
679}
680
681xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
682
683xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid)
684{
685        struct xt_node *node, *query;
686        struct jabber_buddy *bud;
687
688        if ((bud = jabber_buddy_by_jid(ic, bare_jid, 0)) == NULL) {
689                /* Who cares about the unknown... */
690                imcb_log(ic, "Couldn't find buddy: %s", bare_jid);
691                return XT_HANDLED;
692        }
693
694        if (bud->features) { /* been here already */
695                return XT_HANDLED;
696        }
697
698        node = xt_new_node("query", NULL, NULL);
699        xt_add_attr(node, "xmlns", XMLNS_DISCO_INFO);
700
701        if (!(query = jabber_make_packet("iq", "get", bare_jid, node))) {
702                imcb_log(ic, "WARNING: Couldn't generate feature query");
703                xt_free_node(node);
704                return XT_HANDLED;
705        }
706
707        jabber_cache_add(ic, query, jabber_iq_parse_features);
708
709        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
710}
711
712xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
713{
714        struct xt_node *c;
715        struct jabber_buddy *bud;
716        char *feature, *xmlns, *from;
717
718        if (!(from = xt_find_attr(node, "from")) ||
719            !(c = xt_find_node(node->children, "query")) ||
720            !(xmlns = xt_find_attr(c, "xmlns")) ||
721            !(strcmp(xmlns, XMLNS_DISCO_INFO) == 0)) {
722                imcb_log(ic, "WARNING: Received incomplete IQ-result packet for discover");
723                return XT_HANDLED;
724        }
725        if ((bud = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
726                /* Who cares about the unknown... */
727                imcb_log(ic, "Couldn't find buddy: %s", from);
728                return XT_HANDLED;
729        }
730
731        c = c->children;
732        while ((c = xt_find_node(c, "feature"))) {
733                feature = xt_find_attr(c, "var");
734                if (feature) {
735                        bud->features = g_slist_append(bud->features, g_strdup(feature));
736                }
737                c = c->next;
738        }
739
740        return XT_HANDLED;
741}
742
743xt_status jabber_iq_parse_gmail(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
744
745xt_status jabber_iq_query_gmail(struct im_connection *ic)
746{
747        struct xt_node *node, *query;
748        struct jabber_data *jd = ic->proto_data;
749
750        node = xt_new_node("query", NULL, NULL);
751        xt_add_attr(node, "xmlns", XMLNS_GMAILNOTIFY);
752        if (jd->gmail_time) {
753                char *formatted = g_strdup_printf("%" G_GUINT64_FORMAT, (jd->gmail_time + 1));
754                xt_add_attr(node, "newer-than-time", formatted);
755                g_free(formatted);
756        }
757        if (jd->gmail_tid) {
758                xt_add_attr(node, "newer-than-tid", jd->gmail_tid);
759        }
760
761        if (!(query = jabber_make_packet("iq", "get", jd->me, node))) {
762                imcb_log(ic, "WARNING: Couldn't generate server query");
763                xt_free_node(node);
764        }
765
766        jabber_cache_add(ic, query, jabber_iq_parse_gmail);
767
768        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
769}
770
771xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
772
773xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns)
774{
775        struct xt_node *node, *query;
776        struct jabber_data *jd = ic->proto_data;
777
778        node = xt_new_node("query", NULL, NULL);
779        xt_add_attr(node, "xmlns", xmlns);
780
781        if (!(query = jabber_make_packet("iq", "get", jid, node))) {
782                imcb_log(ic, "WARNING: Couldn't generate server query");
783                xt_free_node(node);
784        }
785
786        jd->have_streamhosts--;
787        jabber_cache_add(ic, query, jabber_iq_parse_server_features);
788
789        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
790}
791
792xt_status jabber_iq_parse_gmail(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
793{
794        struct xt_node *c;
795        struct jabber_data *jd = ic->proto_data;
796        char *xmlns, *from;
797        guint64 l_time = 0;
798        char *tid = NULL;
799        int max = 0;
800
801        if (!(c = xt_find_node(node->children, "mailbox")) ||
802            !(from = xt_find_attr(node, "from")) ||
803            !(xmlns = xt_find_attr(c, "xmlns")) ||
804            (g_strcmp0(xmlns, XMLNS_GMAILNOTIFY) != 0)) {
805                imcb_log(ic, "WARNING: Received incomplete mailbox packet for gmail notify");
806                return XT_HANDLED;
807        }
808
809        max = set_getint(&ic->acc->set, "mail_notifications_limit");
810        c = c->children;
811
812        while ((max-- > 0) && (c = xt_find_node(c, "mail-thread-info"))) {
813                struct xt_node *s;
814                char *subject = "<no subject>";
815                char *sender = "<no sender>";
816                guint64 t_time;
817
818                t_time = g_ascii_strtoull(xt_find_attr(c, "date"), NULL, 10);
819                if (t_time && t_time > l_time) {
820                        l_time = t_time;
821                        tid = xt_find_attr(c, "tid");
822                }
823
824                if ((s = xt_find_node(c->children, "senders")) &&
825                    (s = xt_find_node_by_attr(s->children, "sender", "unread", "1"))) {
826                        sender = xt_find_attr(s, "name");
827                }
828
829                if ((s = xt_find_node(c->children, "subject")) && s->text) {
830                        subject = s->text;
831                }
832
833                imcb_notify_email(ic, "New mail from %s: %s", sender, subject);
834
835                c = c->next;
836        }
837
838        if (l_time && (!jd->gmail_time || l_time > jd->gmail_time)) {
839                jd->gmail_time = l_time;
840                if (tid) {
841                        g_free(jd->gmail_tid);
842                        jd->gmail_tid = g_strdup(tid);
843                }
844        }
845
846        return XT_HANDLED;
847}
848
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 */
852xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
853{
854        struct xt_node *c;
855        struct jabber_data *jd = ic->proto_data;
856        char *xmlns, *from;
857
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");
862                return XT_HANDLED;
863        }
864
865        jd->have_streamhosts++;
866
867        if (strcmp(xmlns, XMLNS_DISCO_ITEMS) == 0) {
868                char *itemjid;
869
870                /* answer from server */
871
872                c = c->children;
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                        }
879
880                        c = c->next;
881                }
882        } else if (strcmp(xmlns, XMLNS_DISCO_INFO) == 0) {
883                char *category, *type;
884
885                /* answer from potential proxy */
886
887                c = c->children;
888                while ((c = xt_find_node(c, "identity"))) {
889                        category = xt_find_attr(c, "category");
890                        type = xt_find_attr(c, "type");
891
892                        if (type && (strcmp(type, "bytestreams") == 0) &&
893                            category && (strcmp(category, "proxy") == 0)) {
894                                jabber_iq_query_server(ic, from, XMLNS_BYTESTREAMS);
895                        }
896
897                        c = c->next;
898                }
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
912        } else if (strcmp(xmlns, XMLNS_BYTESTREAMS) == 0) {
913                char *host, *jid, *port_s;
914                int port;
915
916                /* answer from proxy */
917
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);
924
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);
931                }
932        }
933
934        if (jd->have_streamhosts == 0) {
935                jd->have_streamhosts++;
936        }
937
938        return XT_HANDLED;
939}
940
941static xt_status jabber_iq_version_response(struct im_connection *ic,
942                                            struct xt_node *node, struct xt_node *orig);
943
944void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data)
945{
946        struct xt_node *node, *query;
947
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);
954}
955
956static xt_status jabber_iq_version_response(struct im_connection *ic,
957                                            struct xt_node *node, struct xt_node *orig)
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;
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 {
973                return XT_HANDLED;
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);
983        ret[0] = rets->str;
984        imcb_buddy_action_response(bu, "VERSION", ret, NULL);
985        g_string_free(rets, TRUE);
986
987        return XT_HANDLED;
988}
989
990static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
991                                                 struct xt_node *node, struct xt_node *orig);
992
993int jabber_iq_disco_server(struct im_connection *ic)
994{
995        struct xt_node *node, *iq;
996        struct jabber_data *jd = ic->proto_data;
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);
1004}
1005
1006static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
1007                                                 struct xt_node *node, struct xt_node *orig)
1008{
1009        struct jabber_data *jd = ic->proto_data;
1010        struct xt_node *query, *id;
1011
1012        if (!(query = xt_find_node(node->children, "query"))) {
1013                return XT_HANDLED;
1014        }
1015
1016        if (xt_find_node_by_attr(query->children, "feature", "var", XMLNS_CARBONS) &&
1017            set_getbool(&ic->acc->set, "carbons")) {
1018
1019                struct xt_node *enable, *iq;
1020
1021                enable = xt_new_node("enable", NULL, NULL);
1022                xt_add_attr(enable, "xmlns", XMLNS_CARBONS);
1023                iq = jabber_make_packet("iq", "set", NULL, enable);
1024
1025                jabber_cache_add(ic, iq, jabber_iq_carbons_response);
1026                jabber_write_packet(ic, iq);
1027        }
1028
1029        if ((id = xt_find_node(query->children, "identity"))) {
1030                char *cat, *type, *name;
1031
1032                if (!(cat = xt_find_attr(id, "category")) ||
1033                    !(type = xt_find_attr(id, "type")) ||
1034                    !(name = xt_find_attr(id, "name"))) {
1035                        return XT_HANDLED;
1036                }
1037
1038                if (strcmp(cat, "server") == 0 && strcmp(type, "im") == 0 &&
1039                    strstr(name, "Google") != NULL) {
1040                        jd->flags |= JFLAG_GTALK;
1041                }
1042        }
1043
1044        return XT_HANDLED;
1045}
1046
1047static xt_status jabber_iq_carbons_response(struct im_connection *ic,
1048                                            struct xt_node *node, struct xt_node *orig)
1049{
1050        struct jabber_error *err;
1051
1052        if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
1053                imcb_error(ic, "Error enabling carbons: %s%s%s",
1054                           err->code, err->text ? ": " : "", err->text ? err->text : "");
1055                jabber_error_free(err);
1056        } else {
1057                imcb_log(ic, "Carbons enabled");
1058        }
1059
1060        return XT_HANDLED;
1061}
1062
1063xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
1064
1065int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server)
1066{
1067        struct xt_node *node;
1068        int st;
1069
1070        node = xt_new_node("query", NULL, NULL);
1071        xt_add_attr(node, "xmlns", XMLNS_DISCO_ITEMS);
1072        node = jabber_make_packet("iq", "get", (char *) muc_server, node);
1073
1074        jabber_cache_add(ic, node, jabber_iq_disco_muc_response);
1075        st = jabber_write_packet(ic, node);
1076
1077        return st;
1078}
1079
1080xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
1081{
1082        struct xt_node *query, *c;
1083        struct jabber_error *err;
1084        GSList *rooms =  NULL;
1085
1086        if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
1087                imcb_error(ic, "The server replied with an error: %s%s%s",
1088                           err->code, err->text ? ": " : "", err->text ? err->text : "");
1089                jabber_error_free(err);
1090                return XT_HANDLED;
1091        }
1092
1093        if (!(query = xt_find_node(node->children, "query"))) {
1094                imcb_error(ic, "Received incomplete MUC list reply");
1095                return XT_HANDLED;
1096        }
1097
1098        c = query->children;
1099        while ((c = xt_find_node(c, "item"))) {
1100                char *jid = xt_find_attr(c, "jid");
1101
1102                if (!jid || !strchr(jid, '@')) {
1103                        c = c->next;
1104                        continue;
1105                }
1106
1107                bee_chat_info_t *ci = g_new(bee_chat_info_t, 1);
1108                ci->title = g_strdup(xt_find_attr(c, "jid"));
1109                ci->topic = g_strdup(xt_find_attr(c, "name"));
1110                rooms = g_slist_prepend(rooms, ci);
1111
1112                c = c->next;
1113        }
1114
1115        imcb_chat_list_free(ic);
1116        ic->chatlist = g_slist_reverse(rooms);
1117        imcb_chat_list_finish(ic);
1118
1119        return XT_HANDLED;
1120}
Note: See TracBrowser for help on using the repository browser.