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