source: protocols/jabber/iq.c @ 1522faf

Last change on this file since 1522faf was 0e4c3dd, checked in by dequis <dx@…>, at 2015-02-21T06:18:21Z

Add hipchat support to the jabber module

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