source: protocols/jabber/si.c

Last change on this file was a4ac9c4, checked in by GitHub <noreply@…>, at 2023-04-01T20:09:39Z

Deprecate sha1_* functions (#172)

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