source: protocols/jabber/si.c @ a6cd799

Last change on this file since a6cd799 was 8256ad5, checked in by dequis <dx@…>, at 2014-10-27T06:36:05Z

Some more g_source_remove warning fixes

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