source: protocols/jabber/si.c @ 4ac647d

Last change on this file since 4ac647d was 4ac647d, checked in by ulim <a.sporto+bee@…>, at 2008-08-04T14:21:49Z

moved some stuff around in preperation for MSN merge.

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