source: protocols/jabber/si.c @ 1c40aa7

Last change on this file since 1c40aa7 was 17a6ee9, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-11T14:37:06Z

Including DCC stuff again, with a wonderful extra layer of abstraction.
Some hooks are missing so sending files doesn't work yet. Receiving also
still seems to have some issues. On the plus side, at least the MSN/Jabber
modules work again.

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