source: protocols/jabber/iq.c @ 0b9daac

Last change on this file since 0b9daac was 0b9daac, checked in by dequis <dx@…>, at 2015-02-20T23:16:08Z

Reorganize include files to avoid conflicts with other libs

  • Change all header includes to be relative to the project root
  • Remove -I${includedir} from bitlbee.pc Cflags
  • Install lib and protocols headers to their own directories. So now it is:

/usr/include/bitlbee/*.h
/usr/include/bitlbee/lib/*.h
/usr/include/bitlbee/protocols/*.h

This breaks backwards compatibility of third party plugins, but now
they don't have to do ambiguous includes like #include <proxy.h>

This also fixes trac ticket 1170 - conflicts when libproxy and liboauth
are installed at the same time bitlbee is built, which the macports
project ran into several times.

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