source: protocols/jabber/si.c @ 37c9653

Last change on this file since 37c9653 was e88fe7da, checked in by Veres Lajos <vlajos@…>, at 2015-08-07T21:53:25Z

typofix - https://github.com/vlajos/misspell_fixer

  • Property mode set to 100644
File size: 16.7 KB
Line 
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
27void jabber_si_answer_request(file_transfer_t *ft);
28int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf);
29
30/* file_transfer free() callback */
31void 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 */
60void 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
81int 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
110void 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
125gboolean 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
156void 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 */
219int 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 */
343void 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
388static 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
396        if (!(tgt_jid = xt_find_attr(node, "from")) ||
397            !(ini_jid = xt_find_attr(node, "to"))) {
398                imcb_log(ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid);
399                return XT_HANDLED;
400        }
401
402        /* All this means we expect something like this: ( I think )
403         * <iq from=... to=... id=...>
404         *      <si xmlns=si>
405         *      [       <file xmlns=ft/>    ] <-- not necessary
406         *              <feature xmlns=feature>
407         *                      <x xmlns=xdata type=submit>
408         *                              <field var=stream-method>
409         *                                      <value>
410         */
411        if (!(tgt_jid = xt_find_attr(node, "from")) ||
412            !(ini_jid = xt_find_attr(node, "to")) ||
413            !(iq_id   = xt_find_attr(node, "id")) ||
414            !(c = xt_find_node(node->children, "si")) ||
415            !(cmp = xt_find_attr(c, "xmlns")) ||
416            !(strcmp(cmp, XMLNS_SI) == 0) ||
417            !(d = xt_find_node(c->children, "feature")) ||
418            !(cmp = xt_find_attr(d, "xmlns")) ||
419            !(strcmp(cmp, XMLNS_FEATURE) == 0) ||
420            !(d = xt_find_node(d->children, "x")) ||
421            !(cmp = xt_find_attr(d, "xmlns")) ||
422            !(strcmp(cmp, XMLNS_XDATA) == 0) ||
423            !(cmp = xt_find_attr(d, "type")) ||
424            !(strcmp(cmp, "submit") == 0) ||
425            !(d = xt_find_node(d->children, "field")) ||
426            !(cmp = xt_find_attr(d, "var")) ||
427            !(strcmp(cmp, "stream-method") == 0) ||
428            !(d = xt_find_node(d->children, "value"))) {
429                imcb_log(ic, "WARNING: Received incomplete Stream Initiation response");
430                return XT_HANDLED;
431        }
432
433        if (!(strcmp(d->text, XMLNS_BYTESTREAMS) == 0)) {
434                /* since we should only have advertised what we can do and the peer should
435                 * only have chosen what we offered, this should never happen */
436                imcb_log(ic, "WARNING: Received invalid Stream Initiation response, method %s", d->text);
437
438                return XT_HANDLED;
439        }
440
441        /* Let's see if we can find out what this bytestream should be for... */
442
443        for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) {
444                struct jabber_transfer *tft = tflist->data;
445                if ((strcmp(tft->iq_id, iq_id) == 0)) {
446                        tf = tft;
447                        break;
448                }
449        }
450
451        if (!tf) {
452                imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
453                return XT_HANDLED;
454        }
455
456        tf->ini_jid = g_strdup(ini_jid);
457        tf->tgt_jid = g_strdup(tgt_jid);
458
459        imcb_log(ic, "File %s: %s accepted the transfer!", tf->ft->file_name, tgt_jid);
460
461        jabber_bs_send_start(tf);
462
463        return XT_HANDLED;
464}
465
466int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf)
467{
468        struct xt_node *node, *sinode;
469        struct jabber_buddy *bud;
470
471        /* who knows how many bits the future holds :) */
472        char filesizestr[ 1 + ( int ) (0.301029995663981198f * sizeof(size_t) * 8) ];
473
474        const char *methods[] =
475        {
476                XMLNS_BYTESTREAMS,
477                //XMLNS_IBB,
478                NULL
479        };
480        const char **m;
481        char *s;
482
483        /* Maybe we should hash this? */
484        tf->sid = g_strdup_printf("BitlBeeJabberSID%d", tf->ft->local_id);
485
486        if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
487                bud = jabber_buddy_by_ext_jid(ic, who, 0);
488        } else {
489                bud = jabber_buddy_by_jid(ic, who, 0);
490        }
491
492        /* start with the SI tag */
493        sinode = xt_new_node("si", NULL, NULL);
494        xt_add_attr(sinode, "xmlns", XMLNS_SI);
495        xt_add_attr(sinode, "profile", XMLNS_FILETRANSFER);
496        xt_add_attr(sinode, "id", tf->sid);
497
498/*      if( mimetype )
499                xt_add_attr( node, "mime-type", mimetype ); */
500
501        /* now the file tag */
502/*      if( desc )
503                node = xt_new_node( "desc", descr, NULL ); */
504        node = xt_new_node("range", NULL, NULL);
505
506        sprintf(filesizestr, "%zd", tf->ft->file_size);
507        node = xt_new_node("file", NULL, node);
508        xt_add_attr(node, "xmlns", XMLNS_FILETRANSFER);
509        xt_add_attr(node, "name", tf->ft->file_name);
510        xt_add_attr(node, "size", filesizestr);
511/*      if (hash)
512                xt_add_attr( node, "hash", hash );
513        if (date)
514                xt_add_attr( node, "date", date ); */
515
516        xt_add_child(sinode, node);
517
518        /* and finally the feature tag */
519        node = xt_new_node("field", NULL, NULL);
520        xt_add_attr(node, "var", "stream-method");
521        xt_add_attr(node, "type", "list-single");
522
523        for (m = methods; *m; m++) {
524                xt_add_child(node, xt_new_node("option", NULL, xt_new_node("value", (char *) *m, NULL)));
525        }
526
527        node = xt_new_node("x", NULL, node);
528        xt_add_attr(node, "xmlns", XMLNS_XDATA);
529        xt_add_attr(node, "type", "form");
530
531        node = xt_new_node("feature", NULL, node);
532        xt_add_attr(node, "xmlns", XMLNS_FEATURE);
533
534        xt_add_child(sinode, node);
535
536        /* and we are there... */
537        node = jabber_make_packet("iq", "set", bud ? bud->full_jid : who, sinode);
538        jabber_cache_add(ic, node, jabber_si_handle_response);
539        tf->iq_id = g_strdup(xt_find_attr(node, "id"));
540
541        return jabber_write_packet(ic, node);
542}
Note: See TracBrowser for help on using the repository browser.