1 /***************************************************************************\
2 * *
3 * BitlBee - An IRC to IM gateway *
4 * Jabber module - SI packets *
5 * *
6 * Copyright 2007 Uli Meis <a.sporto+bee@gmail.com> *
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 void jabber_si_answer_request(file_transfer_t *ft);
28 int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf);
29
30 /* file_transfer free() callback */
jabber_si_free_transfer(file_transfer_t * ft)31 void jabber_si_free_transfer(file_transfer_t *ft)
32 {
33 struct jabber_transfer *tf = ft->data;
34 struct jabber_data *jd = tf->ic->proto_data;
35
36 if (tf->watch_in) {
37 b_event_remove(tf->watch_in);
38 tf->watch_in = 0;
39 }
40
41 jd->filetransfers = g_slist_remove(jd->filetransfers, tf);
42
43 if (tf->fd != -1) {
44 closesocket(tf->fd);
45 tf->fd = -1;
46 }
47
48 if (tf->disco_timeout) {
49 b_event_remove(tf->disco_timeout);
50 }
51
52 g_free(tf->ini_jid);
53 g_free(tf->tgt_jid);
54 g_free(tf->iq_id);
55 g_free(tf->sid);
56 g_free(tf);
57 }
58
59 /* file_transfer canceled() callback */
jabber_si_canceled(file_transfer_t * ft,char * reason)60 void jabber_si_canceled(file_transfer_t *ft, char *reason)
61 {
62 struct jabber_transfer *tf = ft->data;
63 struct xt_node *reply, *iqnode;
64
65 if (tf->accepted) {
66 return;
67 }
68
69 iqnode = jabber_make_packet("iq", "error", tf->ini_jid, NULL);
70 xt_add_attr(iqnode, "id", tf->iq_id);
71 reply = jabber_make_error_packet(iqnode, "forbidden", "cancel", "403");
72 xt_free_node(iqnode);
73
74 if (!jabber_write_packet(tf->ic, reply)) {
75 imcb_log(tf->ic, "WARNING: Error generating reply to file transfer request");
76 }
77 xt_free_node(reply);
78
79 }
80
jabber_si_check_features(struct jabber_transfer * tf,GSList * features)81 int jabber_si_check_features(struct jabber_transfer *tf, GSList *features)
82 {
83 int foundft = FALSE, foundbt = FALSE, foundsi = FALSE;
84
85 while (features) {
86 if (!strcmp(features->data, XMLNS_FILETRANSFER)) {
87 foundft = TRUE;
88 }
89 if (!strcmp(features->data, XMLNS_BYTESTREAMS)) {
90 foundbt = TRUE;
91 }
92 if (!strcmp(features->data, XMLNS_SI)) {
93 foundsi = TRUE;
94 }
95
96 features = g_slist_next(features);
97 }
98
99 if (!foundft) {
100 imcb_file_canceled(tf->ic, tf->ft, "Buddy's client doesn't feature file transfers");
101 } else if (!foundbt) {
102 imcb_file_canceled(tf->ic, tf->ft, "Buddy's client doesn't feature byte streams (required)");
103 } else if (!foundsi) {
104 imcb_file_canceled(tf->ic, tf->ft, "Buddy's client doesn't feature stream initiation (required)");
105 }
106
107 return foundft && foundbt && foundsi;
108 }
109
jabber_si_transfer_start(struct jabber_transfer * tf)110 void jabber_si_transfer_start(struct jabber_transfer *tf)
111 {
112
113 if (!jabber_si_check_features(tf, tf->bud->features)) {
114 return;
115 }
116
117 /* send the request to our buddy */
118 jabber_si_send_request(tf->ic, tf->bud->full_jid, tf);
119
120 /* and start the receive logic */
121 imcb_file_recv_start(tf->ic, tf->ft);
122
123 }
124
jabber_si_waitfor_disco(gpointer data,gint fd,b_input_condition cond)125 gboolean jabber_si_waitfor_disco(gpointer data, gint fd, b_input_condition cond)
126 {
127 struct jabber_transfer *tf = data;
128 struct jabber_data *jd = tf->ic->proto_data;
129
130 tf->disco_timeout_fired++;
131
132 if (tf->bud->features && jd->have_streamhosts == 1) {
133 tf->disco_timeout = 0;
134 jabber_si_transfer_start(tf);
135 return FALSE;
136 }
137
138 /* 8 seconds should be enough for server and buddy to respond */
139 if (tf->disco_timeout_fired < 16) {
140 return TRUE;
141 }
142
143 if (!tf->bud->features && jd->have_streamhosts != 1) {
144 imcb_log(tf->ic, "Couldn't get buddy's features nor discover all services of the server");
145 } else if (!tf->bud->features) {
146 imcb_log(tf->ic, "Couldn't get buddy's features");
147 } else {
148 imcb_log(tf->ic, "Couldn't discover some of the server's services");
149 }
150
151 tf->disco_timeout = 0;
152 jabber_si_transfer_start(tf);
153 return FALSE;
154 }
155
jabber_si_transfer_request(struct im_connection * ic,file_transfer_t * ft,char * who)156 void jabber_si_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *who)
157 {
158 struct jabber_transfer *tf;
159 struct jabber_data *jd = ic->proto_data;
160 struct jabber_buddy *bud;
161 char *server = jd->server, *s;
162
163 if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
164 bud = jabber_buddy_by_ext_jid(ic, who, 0);
165 } else {
166 bud = jabber_buddy_by_jid(ic, who, 0);
167 }
168
169 if (bud == NULL) {
170 imcb_file_canceled(ic, ft, "Couldn't find buddy (BUG?)");
171 return;
172 }
173
174 imcb_log(ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who);
175
176 tf = g_new0(struct jabber_transfer, 1);
177
178 tf->ic = ic;
179 tf->ft = ft;
180 tf->fd = -1;
181 tf->ft->data = tf;
182 tf->ft->free = jabber_si_free_transfer;
183 tf->bud = bud;
184 ft->write = jabber_bs_send_write;
185
186 jd->filetransfers = g_slist_prepend(jd->filetransfers, tf);
187
188 /* query buddy's features and server's streaming proxies if necessary */
189
190 if (!tf->bud->features) {
191 jabber_iq_query_features(ic, bud->full_jid);
192 }
193
194 /* If <auto> is not set don't check for proxies */
195 if ((jd->have_streamhosts != 1) && (jd->streamhosts == NULL) &&
196 (strstr(set_getstr(&ic->acc->set, "proxy"), "<auto>") != NULL)) {
197 jd->have_streamhosts = 0;
198 jabber_iq_query_server(ic, server, XMLNS_DISCO_ITEMS);
199 } else if (jd->streamhosts != NULL) {
200 jd->have_streamhosts = 1;
201 }
202
203 /* if we had to do a query, wait for the result.
204 * Otherwise fire away. */
205 if (!tf->bud->features || jd->have_streamhosts != 1) {
206 tf->disco_timeout = b_timeout_add(500, jabber_si_waitfor_disco, tf);
207 } else {
208 jabber_si_transfer_start(tf);
209 }
210 }
211
212 /*
213 * First function that gets called when a file transfer request comes in.
214 * A lot to parse.
215 *
216 * We choose a stream type from the options given by the initiator.
217 * Then we wait for imcb to call the accept or cancel callbacks.
218 */
jabber_si_handle_request(struct im_connection * ic,struct xt_node * node,struct xt_node * sinode)219 int jabber_si_handle_request(struct im_connection *ic, struct xt_node *node, struct xt_node *sinode)
220 {
221 struct xt_node *c, *d, *reply;
222 char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid, *size_s;
223 struct jabber_buddy *bud;
224 int requestok = FALSE;
225 char *name, *cmp;
226 size_t size;
227 struct jabber_transfer *tf;
228 struct jabber_data *jd = ic->proto_data;
229 file_transfer_t *ft;
230
231 /* All this means we expect something like this: ( I think )
232 * <iq from=... to=... id=...>
233 * <si id=id xmlns=si profile=ft>
234 * <file xmlns=ft/>
235 * <feature xmlns=feature>
236 * <x xmlns=xdata type=submit>
237 * <field var=stream-method>
238 *
239 */
240 if (!(ini_jid = xt_find_attr(node, "from")) ||
241 !(tgt_jid = xt_find_attr(node, "to")) ||
242 !(iq_id = xt_find_attr(node, "id")) ||
243 !(sid = xt_find_attr(sinode, "id")) ||
244 !(cmp = xt_find_attr(sinode, "profile")) ||
245 !(0 == strcmp(cmp, XMLNS_FILETRANSFER)) ||
246 !(d = xt_find_node(sinode->children, "file")) ||
247 !(cmp = xt_find_attr(d, "xmlns")) ||
248 !(0 == strcmp(cmp, XMLNS_FILETRANSFER)) ||
249 !(name = xt_find_attr(d, "name")) ||
250 !(size_s = xt_find_attr(d, "size")) ||
251 !(1 == sscanf(size_s, "%zd", &size)) ||
252 !(d = xt_find_node(sinode->children, "feature")) ||
253 !(cmp = xt_find_attr(d, "xmlns")) ||
254 !(0 == strcmp(cmp, XMLNS_FEATURE)) ||
255 !(d = xt_find_node(d->children, "x")) ||
256 !(cmp = xt_find_attr(d, "xmlns")) ||
257 !(0 == strcmp(cmp, XMLNS_XDATA)) ||
258 !(cmp = xt_find_attr(d, "type")) ||
259 !(0 == strcmp(cmp, "form")) ||
260 !(d = xt_find_node(d->children, "field")) ||
261 !(cmp = xt_find_attr(d, "var")) ||
262 !(0 == strcmp(cmp, "stream-method"))) {
263 imcb_log(ic, "WARNING: Received incomplete Stream Initiation request");
264 } else {
265 /* Check if we support one of the options */
266
267 c = d->children;
268 while ((c = xt_find_node(c, "option"))) {
269 if ((d = xt_find_node(c->children, "value")) &&
270 (d->text != NULL) &&
271 (strcmp(d->text, XMLNS_BYTESTREAMS) == 0)) {
272 requestok = TRUE;
273 break;
274 } else {
275 c = c->next;
276 }
277 }
278
279 if (!requestok) {
280 imcb_log(ic, "WARNING: Unsupported file transfer request from %s", ini_jid);
281 }
282 }
283
284 if (requestok) {
285 /* Figure out who the transfer should come from... */
286
287 ext_jid = ini_jid;
288 if ((s = strchr(ini_jid, '/'))) {
289 if ((bud = jabber_buddy_by_jid(ic, ini_jid, GET_BUDDY_EXACT))) {
290 bud->last_msg = time(NULL);
291 ext_jid = bud->ext_jid ? : bud->bare_jid;
292 } else {
293 *s = 0; /* We need to generate a bare JID now. */
294 }
295 }
296
297 if (!(ft = imcb_file_send_start(ic, ext_jid, name, size))) {
298 imcb_log(ic, "WARNING: Error handling transfer request from %s", ini_jid);
299 requestok = FALSE;
300 }
301
302 if (s) {
303 *s = '/';
304 }
305 }
306
307 if (!requestok) {
308 reply = jabber_make_error_packet(node, "item-not-found", "cancel", NULL);
309 if (!jabber_write_packet(ic, reply)) {
310 imcb_log(ic, "WARNING: Error generating reply to file transfer request");
311 }
312 xt_free_node(reply);
313 return XT_HANDLED;
314 }
315
316 /* Request is fine. */
317
318 tf = g_new0(struct jabber_transfer, 1);
319
320 tf->ini_jid = g_strdup(ini_jid);
321 tf->tgt_jid = g_strdup(tgt_jid);
322 tf->iq_id = g_strdup(iq_id);
323 tf->sid = g_strdup(sid);
324 tf->ic = ic;
325 tf->ft = ft;
326 tf->fd = -1;
327 tf->ft->data = tf;
328 tf->ft->accept = jabber_si_answer_request;
329 tf->ft->free = jabber_si_free_transfer;
330 tf->ft->canceled = jabber_si_canceled;
331
332 jd->filetransfers = g_slist_prepend(jd->filetransfers, tf);
333
334 return XT_HANDLED;
335 }
336
337 /*
338 * imc called the accept callback which probably means that the user accepted this file transfer.
339 * We send our response to the initiator.
340 * In the next step, the initiator will send us a request for the given stream type.
341 * (currently that can only be a SOCKS5 bytestream)
342 */
jabber_si_answer_request(file_transfer_t * ft)343 void jabber_si_answer_request(file_transfer_t *ft)
344 {
345 struct jabber_transfer *tf = ft->data;
346 struct xt_node *node, *sinode, *reply;
347
348 /* generate response, start with the SI tag */
349 sinode = xt_new_node("si", NULL, NULL);
350 xt_add_attr(sinode, "xmlns", XMLNS_SI);
351 xt_add_attr(sinode, "profile", XMLNS_FILETRANSFER);
352 xt_add_attr(sinode, "id", tf->sid);
353
354 /* now the file tag */
355 node = xt_new_node("file", NULL, NULL);
356 xt_add_attr(node, "xmlns", XMLNS_FILETRANSFER);
357
358 xt_add_child(sinode, node);
359
360 /* and finally the feature tag */
361 node = xt_new_node("field", NULL, NULL);
362 xt_add_attr(node, "var", "stream-method");
363 xt_add_attr(node, "type", "list-single");
364
365 /* Currently all we can do. One could also implement in-band (IBB) */
366 xt_add_child(node, xt_new_node("value", XMLNS_BYTESTREAMS, NULL));
367
368 node = xt_new_node("x", NULL, node);
369 xt_add_attr(node, "xmlns", XMLNS_XDATA);
370 xt_add_attr(node, "type", "submit");
371
372 node = xt_new_node("feature", NULL, node);
373 xt_add_attr(node, "xmlns", XMLNS_FEATURE);
374
375 xt_add_child(sinode, node);
376
377 reply = jabber_make_packet("iq", "result", tf->ini_jid, sinode);
378 xt_add_attr(reply, "id", tf->iq_id);
379
380 if (!jabber_write_packet(tf->ic, reply)) {
381 imcb_log(tf->ic, "WARNING: Error generating reply to file transfer request");
382 } else {
383 tf->accepted = TRUE;
384 }
385 xt_free_node(reply);
386 }
387
jabber_si_handle_response(struct im_connection * ic,struct xt_node * node,struct xt_node * orig)388 static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
389 {
390 struct xt_node *c, *d;
391 char *ini_jid = NULL, *tgt_jid, *iq_id, *cmp;
392 GSList *tflist;
393 struct jabber_transfer *tf = NULL;
394 struct jabber_data *jd = ic->proto_data;
395 struct jabber_error *err;
396
397 if (!(tgt_jid = xt_find_attr(node, "from")) ||
398 !(ini_jid = xt_find_attr(node, "to")) ||
399 !(iq_id = xt_find_attr(node, "id"))) {
400 imcb_log(ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid);
401 return XT_HANDLED;
402 }
403
404 /* Let's see if we can find out what this bytestream should be for... */
405
406 for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) {
407 struct jabber_transfer *tft = tflist->data;
408 if ((strcmp(tft->iq_id, iq_id) == 0)) {
409 tf = tft;
410 break;
411 }
412 }
413
414 if (!tf) {
415 imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
416 return XT_HANDLED;
417 }
418
419 err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR);
420
421 if (err) {
422 if (g_strcmp0(err->code, "forbidden") == 0) {
423 imcb_log(ic, "File %s: %s rejected the transfer", tf->ft->file_name, tgt_jid);
424 } else {
425 imcb_log(ic, "Error: Stream initiation request failed: %s (%s)", err->code, err->text);
426 }
427 imcb_file_canceled(ic, tf->ft, "Stream initiation request failed");
428 jabber_error_free(err);
429 return XT_HANDLED;
430 }
431
432 /* All this means we expect something like this: ( I think )
433 * <iq from=... to=... id=...>
434 * <si xmlns=si>
435 * [ <file xmlns=ft/> ] <-- not necessary
436 * <feature xmlns=feature>
437 * <x xmlns=xdata type=submit>
438 * <field var=stream-method>
439 * <value>
440 */
441 if (!(c = xt_find_node(node->children, "si")) ||
442 !(cmp = xt_find_attr(c, "xmlns")) ||
443 !(strcmp(cmp, XMLNS_SI) == 0) ||
444 !(d = xt_find_node(c->children, "feature")) ||
445 !(cmp = xt_find_attr(d, "xmlns")) ||
446 !(strcmp(cmp, XMLNS_FEATURE) == 0) ||
447 !(d = xt_find_node(d->children, "x")) ||
448 !(cmp = xt_find_attr(d, "xmlns")) ||
449 !(strcmp(cmp, XMLNS_XDATA) == 0) ||
450 !(cmp = xt_find_attr(d, "type")) ||
451 !(strcmp(cmp, "submit") == 0) ||
452 !(d = xt_find_node(d->children, "field")) ||
453 !(cmp = xt_find_attr(d, "var")) ||
454 !(strcmp(cmp, "stream-method") == 0) ||
455 !(d = xt_find_node(d->children, "value"))) {
456 imcb_log(ic, "WARNING: Received incomplete Stream Initiation response");
457 return XT_HANDLED;
458 }
459
460 if (!(strcmp(d->text, XMLNS_BYTESTREAMS) == 0)) {
461 /* since we should only have advertised what we can do and the peer should
462 * only have chosen what we offered, this should never happen */
463 imcb_log(ic, "WARNING: Received invalid Stream Initiation response, method %s", d->text);
464
465 return XT_HANDLED;
466 }
467
468 tf->ini_jid = g_strdup(ini_jid);
469 tf->tgt_jid = g_strdup(tgt_jid);
470
471 imcb_log(ic, "File %s: %s accepted the transfer!", tf->ft->file_name, tgt_jid);
472
473 jabber_bs_send_start(tf);
474
475 return XT_HANDLED;
476 }
477
jabber_si_send_request(struct im_connection * ic,char * who,struct jabber_transfer * tf)478 int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf)
479 {
480 struct xt_node *node, *sinode;
481 struct jabber_buddy *bud;
482
483 /* who knows how many bits the future holds :) */
484 char filesizestr[ 1 + ( int ) (0.301029995663981198f * sizeof(size_t) * 8) ];
485
486 const char *methods[] =
487 {
488 XMLNS_BYTESTREAMS,
489 //XMLNS_IBB,
490 NULL
491 };
492 const char **m;
493 char *s;
494
495 /* Maybe we should hash this? */
496 tf->sid = g_strdup_printf("BitlBeeJabberSID%d", tf->ft->local_id);
497
498 if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
499 bud = jabber_buddy_by_ext_jid(ic, who, 0);
500 } else {
501 bud = jabber_buddy_by_jid(ic, who, 0);
502 }
503
504 /* start with the SI tag */
505 sinode = xt_new_node("si", NULL, NULL);
506 xt_add_attr(sinode, "xmlns", XMLNS_SI);
507 xt_add_attr(sinode, "profile", XMLNS_FILETRANSFER);
508 xt_add_attr(sinode, "id", tf->sid);
509
510 /* if( mimetype )
511 xt_add_attr( node, "mime-type", mimetype ); */
512
513 /* now the file tag */
514 /* if( desc )
515 node = xt_new_node( "desc", descr, NULL ); */
516 node = xt_new_node("range", NULL, NULL);
517
518 sprintf(filesizestr, "%zd", tf->ft->file_size);
519 node = xt_new_node("file", NULL, node);
520 xt_add_attr(node, "xmlns", XMLNS_FILETRANSFER);
521 xt_add_attr(node, "name", tf->ft->file_name);
522 xt_add_attr(node, "size", filesizestr);
523 /* if (hash)
524 xt_add_attr( node, "hash", hash );
525 if (date)
526 xt_add_attr( node, "date", date ); */
527
528 xt_add_child(sinode, node);
529
530 /* and finally the feature tag */
531 node = xt_new_node("field", NULL, NULL);
532 xt_add_attr(node, "var", "stream-method");
533 xt_add_attr(node, "type", "list-single");
534
535 for (m = methods; *m; m++) {
536 xt_add_child(node, xt_new_node("option", NULL, xt_new_node("value", (char *) *m, NULL)));
537 }
538
539 node = xt_new_node("x", NULL, node);
540 xt_add_attr(node, "xmlns", XMLNS_XDATA);
541 xt_add_attr(node, "type", "form");
542
543 node = xt_new_node("feature", NULL, node);
544 xt_add_attr(node, "xmlns", XMLNS_FEATURE);
545
546 xt_add_child(sinode, node);
547
548 /* and we are there... */
549 node = jabber_make_packet("iq", "set", bud ? bud->full_jid : who, sinode);
550 jabber_cache_add(ic, node, jabber_si_handle_response);
551 tf->iq_id = g_strdup(xt_find_attr(node, "id"));
552
553 return jabber_write_packet(ic, node);
554 }
555