source: protocols/jabber/iq.c @ 75ad761

Last change on this file since 75ad761 was 75ad761, checked in by dequis <dx@…>, at 2015-02-21T06:12:38Z

Use imcb_buddy_nick_hint to suggest jabber nicknames

Also grab potential nicks from the "mention_name" attribute (hipchat)

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