source: protocols/jabber/si.c @ 8a90001

Last change on this file since 8a90001 was 8a90001, checked in by ulim <a.sporto+bee@…>, at 2008-07-22T12:37:49Z

Added an account setting 'proxy'.

Note that this is only used for sending. The default <local>;<auto> means let
the receiver try a direct connection first and then the proxy discovered from
the server (if any). If you know you're firewalled you can remove the <local>.
If you want to provide your own proxy try something like
"<local>;JID,HOST,PORT". E.g.
"<local>;proxy.somewhere.org,123.123.123.123,7777".

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