source: protocols/jabber/iq.c @ 6f903c3

Last change on this file since 6f903c3 was 40cfbc5, checked in by dequis <dx@…>, at 2015-04-28T13:47:48Z

hipchat: Basic implementation: Auth, profile and mention names

This is enough to log in with their usernames, make 'chat add' based
groupchat joins slightly more smooth, and see mention names as nicks.

All the MUC list stuff is left out intentionally since that's not as
stable as I wish.

  • Property mode set to 100644
File size: 27.1 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 jabber_data *jd = ic->proto_data;
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                char *mention_name = xt_find_attr(c, "mention_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                                /* This could also be used to set the full name as nick for fb/gtalk,
402                                 * but i'm keeping the old (ugly?) default behavior just to be safe */
403                                if (mention_name && (jd->flags & JFLAG_HIPCHAT)) {
404                                        imcb_buddy_nick_hint(ic, jid, mention_name);
405                                }
406                        } else if (strcmp(sub, "remove") == 0) {
407                                jabber_buddy_remove_bare(ic, jid);
408                                imcb_remove_buddy(ic, jid, NULL);
409                        }
410                }
411
412                c = c->next;
413        }
414
415        if (initial) {
416                imcb_connected(ic);
417        }
418
419        return XT_HANDLED;
420}
421
422int jabber_get_vcard(struct im_connection *ic, char *bare_jid)
423{
424        struct xt_node *node;
425
426        if (strchr(bare_jid, '/')) {
427                return 1;       /* This was an error, but return 0 should only be done if the connection died... */
428
429        }
430        node = xt_new_node("vCard", NULL, NULL);
431        xt_add_attr(node, "xmlns", XMLNS_VCARD);
432        node = jabber_make_packet("iq", "get", bare_jid, node);
433
434        jabber_cache_add(ic, node, jabber_iq_display_vcard);
435        return jabber_write_packet(ic, node);
436}
437
438static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
439{
440        struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
441        GString *reply;
442        char *s;
443
444        if ((s = xt_find_attr(node, "type")) == NULL ||
445            strcmp(s, "result") != 0 ||
446            (vc = xt_find_node(node->children, "vCard")) == NULL) {
447                s = xt_find_attr(orig, "to");   /* If this returns NULL something's wrong.. */
448                imcb_log(ic, "Could not retrieve vCard of %s", s ? s : "(NULL)");
449                return XT_HANDLED;
450        }
451
452        s = xt_find_attr(orig, "to");
453        reply = g_string_new("vCard information for ");
454        reply = g_string_append(reply, s ? s : "(NULL)");
455        reply = g_string_append(reply, ":\n");
456
457        /* I hate this format, I really do... */
458
459        if ((c = xt_find_node(vc->children, "FN")) && c->text_len) {
460                g_string_append_printf(reply, "Name: %s\n", c->text);
461        }
462
463        if ((c = xt_find_node(vc->children, "N")) && c->children) {
464                reply = g_string_append(reply, "Full name:");
465
466                if ((sc = xt_find_node(c->children, "PREFIX")) && sc->text_len) {
467                        g_string_append_printf(reply, " %s", sc->text);
468                }
469                if ((sc = xt_find_node(c->children, "GIVEN")) && sc->text_len) {
470                        g_string_append_printf(reply, " %s", sc->text);
471                }
472                if ((sc = xt_find_node(c->children, "MIDDLE")) && sc->text_len) {
473                        g_string_append_printf(reply, " %s", sc->text);
474                }
475                if ((sc = xt_find_node(c->children, "FAMILY")) && sc->text_len) {
476                        g_string_append_printf(reply, " %s", sc->text);
477                }
478                if ((sc = xt_find_node(c->children, "SUFFIX")) && sc->text_len) {
479                        g_string_append_printf(reply, " %s", sc->text);
480                }
481
482                reply = g_string_append_c(reply, '\n');
483        }
484
485        if ((c = xt_find_node(vc->children, "NICKNAME")) && c->text_len) {
486                g_string_append_printf(reply, "Nickname: %s\n", c->text);
487        }
488
489        if ((c = xt_find_node(vc->children, "BDAY")) && c->text_len) {
490                g_string_append_printf(reply, "Date of birth: %s\n", c->text);
491        }
492
493        /* Slightly alternative use of for... ;-) */
494        for (c = vc->children; (c = xt_find_node(c, "EMAIL")); c = c->next) {
495                if ((sc = xt_find_node(c->children, "USERID")) == NULL || sc->text_len == 0) {
496                        continue;
497                }
498
499                if (xt_find_node(c->children, "HOME")) {
500                        s = "Home";
501                } else if (xt_find_node(c->children, "WORK")) {
502                        s = "Work";
503                } else {
504                        s = "Misc.";
505                }
506
507                g_string_append_printf(reply, "%s e-mail address: %s\n", s, sc->text);
508        }
509
510        if ((c = xt_find_node(vc->children, "URL")) && c->text_len) {
511                g_string_append_printf(reply, "Homepage: %s\n", c->text);
512        }
513
514        /* Slightly alternative use of for... ;-) */
515        for (c = vc->children; (c = xt_find_node(c, "ADR")); c = c->next) {
516                if (xt_find_node(c->children, "HOME")) {
517                        s = "Home";
518                } else if (xt_find_node(c->children, "WORK")) {
519                        s = "Work";
520                } else {
521                        s = "Misc.";
522                }
523
524                g_string_append_printf(reply, "%s address: ", s);
525
526                if ((sc = xt_find_node(c->children, "STREET")) && sc->text_len) {
527                        g_string_append_printf(reply, "%s ", sc->text);
528                }
529                if ((sc = xt_find_node(c->children, "EXTADR")) && sc->text_len) {
530                        g_string_append_printf(reply, "%s, ", sc->text);
531                }
532                if ((sc = xt_find_node(c->children, "PCODE")) && sc->text_len) {
533                        g_string_append_printf(reply, "%s, ", sc->text);
534                }
535                if ((sc = xt_find_node(c->children, "LOCALITY")) && sc->text_len) {
536                        g_string_append_printf(reply, "%s, ", sc->text);
537                }
538                if ((sc = xt_find_node(c->children, "REGION")) && sc->text_len) {
539                        g_string_append_printf(reply, "%s, ", sc->text);
540                }
541                if ((sc = xt_find_node(c->children, "CTRY")) && sc->text_len) {
542                        g_string_append_printf(reply, "%s", sc->text);
543                }
544
545                if (reply->str[reply->len - 2] == ',') {
546                        reply = g_string_truncate(reply, reply->len - 2);
547                }
548
549                reply = g_string_append_c(reply, '\n');
550        }
551
552        for (c = vc->children; (c = xt_find_node(c, "TEL")); c = c->next) {
553                if ((sc = xt_find_node(c->children, "NUMBER")) == NULL || sc->text_len == 0) {
554                        continue;
555                }
556
557                if (xt_find_node(c->children, "HOME")) {
558                        s = "Home";
559                } else if (xt_find_node(c->children, "WORK")) {
560                        s = "Work";
561                } else {
562                        s = "Misc.";
563                }
564
565                g_string_append_printf(reply, "%s phone number: %s\n", s, sc->text);
566        }
567
568        if ((c = xt_find_node(vc->children, "DESC")) && c->text_len) {
569                g_string_append_printf(reply, "Other information:\n%s", c->text);
570        }
571
572        /* *sigh* */
573
574        imcb_log(ic, "%s", reply->str);
575        g_string_free(reply, TRUE);
576
577        return XT_HANDLED;
578}
579
580static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
581
582int jabber_add_to_roster(struct im_connection *ic, const char *handle, const char *name, const char *group)
583{
584        struct xt_node *node;
585        int st;
586
587        /* Build the item entry */
588        node = xt_new_node("item", NULL, NULL);
589        xt_add_attr(node, "jid", handle);
590        if (name) {
591                xt_add_attr(node, "name", name);
592        }
593        if (group) {
594                xt_add_child(node, xt_new_node("group", group, NULL));
595        }
596
597        /* And pack it into a roster-add packet */
598        node = xt_new_node("query", NULL, node);
599        xt_add_attr(node, "xmlns", XMLNS_ROSTER);
600        node = jabber_make_packet("iq", "set", NULL, node);
601        jabber_cache_add(ic, node, jabber_add_to_roster_callback);
602
603        st = jabber_write_packet(ic, node);
604
605        return st;
606}
607
608static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
609{
610        char *s, *jid = NULL;
611        struct xt_node *c;
612
613        if ((c = xt_find_node(orig->children, "query")) &&
614            (c = xt_find_node(c->children, "item")) &&
615            (jid = xt_find_attr(c, "jid")) &&
616            (s = xt_find_attr(node, "type")) &&
617            strcmp(s, "result") == 0) {
618                if (bee_user_by_handle(ic->bee, ic, jid) == NULL) {
619                        imcb_add_buddy(ic, jid, NULL);
620                }
621        } else {
622                imcb_log(ic, "Error while adding `%s' to your contact list.",
623                         jid ? jid : "(unknown handle)");
624        }
625
626        return XT_HANDLED;
627}
628
629int jabber_remove_from_roster(struct im_connection *ic, char *handle)
630{
631        struct xt_node *node;
632        int st;
633
634        /* Build the item entry */
635        node = xt_new_node("item", NULL, NULL);
636        xt_add_attr(node, "jid", handle);
637        xt_add_attr(node, "subscription", "remove");
638
639        /* And pack it into a roster-add packet */
640        node = xt_new_node("query", NULL, node);
641        xt_add_attr(node, "xmlns", XMLNS_ROSTER);
642        node = jabber_make_packet("iq", "set", NULL, node);
643
644        st = jabber_write_packet(ic, node);
645
646        xt_free_node(node);
647        return st;
648}
649
650xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
651
652xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid)
653{
654        struct xt_node *node, *query;
655        struct jabber_buddy *bud;
656
657        if ((bud = jabber_buddy_by_jid(ic, bare_jid, 0)) == NULL) {
658                /* Who cares about the unknown... */
659                imcb_log(ic, "Couldn't find buddy: %s", bare_jid);
660                return XT_HANDLED;
661        }
662
663        if (bud->features) { /* been here already */
664                return XT_HANDLED;
665        }
666
667        node = xt_new_node("query", NULL, NULL);
668        xt_add_attr(node, "xmlns", XMLNS_DISCO_INFO);
669
670        if (!(query = jabber_make_packet("iq", "get", bare_jid, node))) {
671                imcb_log(ic, "WARNING: Couldn't generate feature query");
672                xt_free_node(node);
673                return XT_HANDLED;
674        }
675
676        jabber_cache_add(ic, query, jabber_iq_parse_features);
677
678        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
679}
680
681xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
682{
683        struct xt_node *c;
684        struct jabber_buddy *bud;
685        char *feature, *xmlns, *from;
686
687        if (!(from = xt_find_attr(node, "from")) ||
688            !(c = xt_find_node(node->children, "query")) ||
689            !(xmlns = xt_find_attr(c, "xmlns")) ||
690            !(strcmp(xmlns, XMLNS_DISCO_INFO) == 0)) {
691                imcb_log(ic, "WARNING: Received incomplete IQ-result packet for discover");
692                return XT_HANDLED;
693        }
694        if ((bud = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
695                /* Who cares about the unknown... */
696                imcb_log(ic, "Couldn't find buddy: %s", from);
697                return XT_HANDLED;
698        }
699
700        c = c->children;
701        while ((c = xt_find_node(c, "feature"))) {
702                feature = xt_find_attr(c, "var");
703                if (feature) {
704                        bud->features = g_slist_append(bud->features, g_strdup(feature));
705                }
706                c = c->next;
707        }
708
709        return XT_HANDLED;
710}
711
712xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
713
714xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns)
715{
716        struct xt_node *node, *query;
717        struct jabber_data *jd = ic->proto_data;
718
719        node = xt_new_node("query", NULL, NULL);
720        xt_add_attr(node, "xmlns", xmlns);
721
722        if (!(query = jabber_make_packet("iq", "get", jid, node))) {
723                imcb_log(ic, "WARNING: Couldn't generate server query");
724                xt_free_node(node);
725        }
726
727        jd->have_streamhosts--;
728        jabber_cache_add(ic, query, jabber_iq_parse_server_features);
729
730        return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
731}
732
733/*
734 * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info
735 */
736xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
737{
738        struct xt_node *c;
739        struct jabber_data *jd = ic->proto_data;
740        char *xmlns, *from;
741
742        if (!(c = xt_find_node(node->children, "query")) ||
743            !(from = xt_find_attr(node, "from")) ||
744            !(xmlns = xt_find_attr(c, "xmlns"))) {
745                imcb_log(ic, "WARNING: Received incomplete IQ-result packet for discover");
746                return XT_HANDLED;
747        }
748
749        jd->have_streamhosts++;
750
751        if (strcmp(xmlns, XMLNS_DISCO_ITEMS) == 0) {
752                char *itemjid;
753
754                /* answer from server */
755
756                c = c->children;
757                while ((c = xt_find_node(c, "item"))) {
758                        itemjid = xt_find_attr(c, "jid");
759
760                        if (itemjid) {
761                                jabber_iq_query_server(ic, itemjid, XMLNS_DISCO_INFO);
762                        }
763
764                        c = c->next;
765                }
766        } else if (strcmp(xmlns, XMLNS_DISCO_INFO) == 0) {
767                char *category, *type;
768
769                /* answer from potential proxy */
770
771                c = c->children;
772                while ((c = xt_find_node(c, "identity"))) {
773                        category = xt_find_attr(c, "category");
774                        type = xt_find_attr(c, "type");
775
776                        if (type && (strcmp(type, "bytestreams") == 0) &&
777                            category && (strcmp(category, "proxy") == 0)) {
778                                jabber_iq_query_server(ic, from, XMLNS_BYTESTREAMS);
779                        }
780
781                        c = c->next;
782                }
783        } else if (strcmp(xmlns, XMLNS_BYTESTREAMS) == 0) {
784                char *host, *jid, *port_s;
785                int port;
786
787                /* answer from proxy */
788
789                if ((c = xt_find_node(c->children, "streamhost")) &&
790                    (host = xt_find_attr(c, "host")) &&
791                    (port_s = xt_find_attr(c, "port")) &&
792                    (sscanf(port_s, "%d", &port) == 1) &&
793                    (jid = xt_find_attr(c, "jid"))) {
794                        jabber_streamhost_t *sh = g_new0(jabber_streamhost_t, 1);
795
796                        sh->jid = g_strdup(jid);
797                        sh->host = g_strdup(host);
798                        g_snprintf(sh->port, sizeof(sh->port), "%u", port);
799
800                        imcb_log(ic, "Proxy found: jid %s host %s port %u", jid, host, port);
801                        jd->streamhosts = g_slist_append(jd->streamhosts, sh);
802                }
803        }
804
805        if (jd->have_streamhosts == 0) {
806                jd->have_streamhosts++;
807        }
808
809        return XT_HANDLED;
810}
811
812static xt_status jabber_iq_version_response(struct im_connection *ic,
813                                            struct xt_node *node, struct xt_node *orig);
814
815void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data)
816{
817        struct xt_node *node, *query;
818
819        node = xt_new_node("query", NULL, NULL);
820        xt_add_attr(node, "xmlns", XMLNS_VERSION);
821        query = jabber_make_packet("iq", "get", bud->full_jid, node);
822        jabber_cache_add(ic, query, jabber_iq_version_response);
823
824        jabber_write_packet(ic, query);
825}
826
827static xt_status jabber_iq_version_response(struct im_connection *ic,
828                                            struct xt_node *node, struct xt_node *orig)
829{
830        struct xt_node *query;
831        GString *rets;
832        char *s;
833        char *ret[2] = {};
834        bee_user_t *bu;
835        struct jabber_buddy *bud = NULL;
836
837        if ((s = xt_find_attr(node, "from")) &&
838            (bud = jabber_buddy_by_jid(ic, s, 0)) &&
839            (query = xt_find_node(node->children, "query")) &&
840            (bu = bee_user_by_handle(ic->bee, ic, bud->bare_jid))) {
841                rets = g_string_new("Resource ");
842                g_string_append(rets, bud->resource);
843        } else {
844                return XT_HANDLED;
845        }
846
847        for (query = query->children; query; query = query->next) {
848                if (query->text_len > 0) {
849                        g_string_append_printf(rets, " %s: %s,", query->name, query->text);
850                }
851        }
852
853        g_string_truncate(rets, rets->len - 1);
854        ret[0] = rets->str;
855        imcb_buddy_action_response(bu, "VERSION", ret, NULL);
856        g_string_free(rets, TRUE);
857
858        return XT_HANDLED;
859}
860
861static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
862                                                 struct xt_node *node, struct xt_node *orig);
863
864int jabber_iq_disco_server(struct im_connection *ic)
865{
866        struct xt_node *node, *iq;
867        struct jabber_data *jd = ic->proto_data;
868
869        node = xt_new_node("query", NULL, NULL);
870        xt_add_attr(node, "xmlns", XMLNS_DISCO_INFO);
871        iq = jabber_make_packet("iq", "get", jd->server, node);
872
873        jabber_cache_add(ic, iq, jabber_iq_disco_server_response);
874        return jabber_write_packet(ic, iq);
875}
876
877static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
878                                                 struct xt_node *node, struct xt_node *orig)
879{
880        struct jabber_data *jd = ic->proto_data;
881        struct xt_node *id;
882
883        if ((id = xt_find_path(node, "query/identity"))) {
884                char *cat, *type, *name;
885
886                if (!(cat = xt_find_attr(id, "category")) ||
887                    !(type = xt_find_attr(id, "type")) ||
888                    !(name = xt_find_attr(id, "name"))) {
889                        return XT_HANDLED;
890                }
891
892                if (strcmp(cat, "server") == 0 && strcmp(type, "im") == 0 &&
893                    strstr(name, "Google") != NULL) {
894                        jd->flags |= JFLAG_GTALK;
895                }
896        }
897
898        return XT_HANDLED;
899}
Note: See TracBrowser for help on using the repository browser.