source: protocols/jabber/si.c @ a07a8c2

Last change on this file since a07a8c2 was bd599b9, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-22T08:07:48Z

Fix two compiler warnings.

  • 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                        }
[1bb1e01]264                        else
265                        {
266                                c = c->next;
267                        }
[2ff2076]268
269                if ( !requestok )
270                        imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid);
[2c2df7d]271        }
272       
[b8a491d]273        if( requestok )
[2c2df7d]274        {
275                /* Figure out who the transfer should come frome... */
276
[b8a491d]277                ext_jid = ini_jid;
[2c2df7d]278                if( ( s = strchr( ini_jid, '/' ) ) )
279                {
280                        if( ( bud = jabber_buddy_by_jid( ic, ini_jid, GET_BUDDY_EXACT ) ) )
281                        {
[1cc0df3]282                                bud->last_msg = time( NULL );
[2c2df7d]283                                ext_jid = bud->ext_jid ? : bud->bare_jid;
284                        }
285                        else
286                                *s = 0; /* We need to generate a bare JID now. */
287                }
288
289                if( !( ft = imcb_file_send_start( ic, ext_jid, name, size ) ) )
290                { 
291                        imcb_log( ic, "WARNING: Error handling transfer request from %s", ini_jid);
292                        requestok = FALSE;
293                }
294
295                *s = '/';
[2ff2076]296        }
[b8a491d]297        else
[2c2df7d]298        { 
299                reply = jabber_make_error_packet( node, "item-not-found", "cancel", NULL );
300                if (!jabber_write_packet( ic, reply ))
301                        imcb_log( ic, "WARNING: Error generating reply to file transfer request" );
302                xt_free_node( reply );
303                return XT_HANDLED;
304        }
305
306        /* Request is fine. */
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;
[8a90001]316        tf->fd = -1;
[2c2df7d]317        tf->ft->data = tf;
318        tf->ft->accept = jabber_si_answer_request;
319        tf->ft->free = jabber_si_free_transfer;
320        tf->ft->canceled = jabber_si_canceled;
321
322        jd->filetransfers = g_slist_prepend( jd->filetransfers, tf );
323
324        return XT_HANDLED;
325}
326
327/*
[dce3903]328 * imc called the accept callback which probably means that the user accepted this file transfer.
[2c2df7d]329 * We send our response to the initiator.
330 * In the next step, the initiator will send us a request for the given stream type.
331 * (currently that can only be a SOCKS5 bytestream)
332 */
333void jabber_si_answer_request( file_transfer_t *ft ) {
334        struct jabber_transfer *tf = ft->data;
335        struct xt_node *node, *sinode, *reply;
336
337        /* generate response, start with the SI tag */
338        sinode = xt_new_node( "si", NULL, NULL );
339        xt_add_attr( sinode, "xmlns", XMLNS_SI );
340        xt_add_attr( sinode, "profile", XMLNS_FILETRANSFER );
341        xt_add_attr( sinode, "id", tf->sid );
342
343        /* now the file tag */
344        node = xt_new_node( "file", NULL, NULL );
345        xt_add_attr( node, "xmlns", XMLNS_FILETRANSFER );
346
347        xt_add_child( sinode, node );
348
349        /* and finally the feature tag */
350        node = xt_new_node( "field", NULL, NULL );
351        xt_add_attr( node, "var", "stream-method" );
352        xt_add_attr( node, "type", "list-single" );
353
354        /* Currently all we can do. One could also implement in-band (IBB) */
355        xt_add_child( node, xt_new_node( "value", XMLNS_BYTESTREAMS, NULL ) );
356
357        node = xt_new_node( "x", NULL, node );
358        xt_add_attr( node, "xmlns", XMLNS_XDATA );
359        xt_add_attr( node, "type", "submit" );
360
361        node = xt_new_node( "feature", NULL, node );
362        xt_add_attr( node, "xmlns", XMLNS_FEATURE );
363
364        xt_add_child( sinode, node );
365
366        reply = jabber_make_packet( "iq", "result", tf->ini_jid, sinode );
367        xt_add_attr( reply, "id", tf->iq_id );
368       
369        if( !jabber_write_packet( tf->ic, reply ) )
370                imcb_log( tf->ic, "WARNING: Error generating reply to file transfer request" );
371        else
372                tf->accepted = TRUE;
373        xt_free_node( reply );
374}
[2ff2076]375
376static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig )
377{
378        struct xt_node *c, *d;
[bd599b9]379        char *ini_jid = NULL, *tgt_jid, *iq_id, *cmp;
[2ff2076]380        GSList *tflist;
381        struct jabber_transfer *tf=NULL;
382        struct jabber_data *jd = ic->proto_data;
383
384        if( !( tgt_jid = xt_find_attr( node, "from" ) ) ||
385            !( ini_jid = xt_find_attr( node, "to" ) ) )
386        {
387                imcb_log( ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid );
388                return XT_HANDLED;
389        }
390       
391        /* All this means we expect something like this: ( I think )
[dce3903]392         * <iq from=... to=... id=...>
[2ff2076]393         *      <si xmlns=si>
[dce3903]394         *      [       <file xmlns=ft/>    ] <-- not neccessary
[2ff2076]395         *              <feature xmlns=feature>
396         *                      <x xmlns=xdata type=submit>
397         *                              <field var=stream-method>
398         *                                      <value>
399         */
400        if( !( tgt_jid = xt_find_attr( node, "from" ) ) ||
401            !( ini_jid = xt_find_attr( node, "to" ) ) ||
[dce3903]402            !( iq_id   = xt_find_attr( node, "id" ) ) ||
[2ff2076]403            !( c = xt_find_node( node->children, "si" ) ) ||
[aed152f]404            !( cmp = xt_find_attr( c, "xmlns" ) ) ||
405            !( strcmp( cmp, XMLNS_SI ) == 0 ) ||
[2ff2076]406            !( d = xt_find_node( c->children, "feature" ) ) ||
[aed152f]407            !( cmp = xt_find_attr( d, "xmlns" ) ) ||
408            !( strcmp( cmp, XMLNS_FEATURE ) == 0 ) ||
[2ff2076]409            !( d = xt_find_node( d->children, "x" ) ) ||
[aed152f]410            !( cmp = xt_find_attr( d, "xmlns" ) ) ||
411            !( strcmp( cmp, XMLNS_XDATA ) == 0 ) ||
412            !( cmp = xt_find_attr( d, "type" ) ) ||
413            !( strcmp( cmp, "submit" ) == 0 ) ||
[2ff2076]414            !( d = xt_find_node( d->children, "field" ) ) ||
[aed152f]415            !( cmp = xt_find_attr( d, "var" ) ) ||
416            !( strcmp( cmp, "stream-method" ) == 0 ) ||
[2ff2076]417            !( d = xt_find_node( d->children, "value" ) ) )
418        {
419                imcb_log( ic, "WARNING: Received incomplete Stream Initiation response" );
420                return XT_HANDLED;
421        }
422
423        if( !( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) { 
424                /* since we should only have advertised what we can do and the peer should
425                 * only have chosen what we offered, this should never happen */
426                imcb_log( ic, "WARNING: Received invalid Stream Initiation response, method %s", d->text );
427                       
428                return XT_HANDLED;
429        }
430       
431        /* Let's see if we can find out what this bytestream should be for... */
432
433        for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) )
434        {
435                struct jabber_transfer *tft = tflist->data;
[dce3903]436                if( ( strcmp( tft->iq_id, iq_id ) == 0 ) )
[2ff2076]437                {
438                        tf = tft;
439                        break;
440                }
441        }
442
443        if (!tf) 
444        {
445                imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid );
446                return XT_HANDLED;
447        }
448
449        tf->ini_jid = g_strdup( ini_jid );
450        tf->tgt_jid = g_strdup( tgt_jid );
451
[dce3903]452        imcb_log( ic, "File %s: %s accepted the transfer!", tf->ft->file_name, tgt_jid );
453
[2ff2076]454        jabber_bs_send_start( tf );
455
456        return XT_HANDLED;
457}
458
459int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf )
460{
461        struct xt_node *node, *sinode;
462        struct jabber_buddy *bud;
463
464        /* who knows how many bits the future holds :) */
465        char filesizestr[ 1 + ( int ) ( 0.301029995663981198f * sizeof( size_t ) * 8 ) ];
466
467        const char *methods[] = 
468        {       
469                XMLNS_BYTESTREAMS,
470                //XMLNS_IBB,
471                NULL 
472        };
473        const char **m;
474        char *s;
475
476        /* Maybe we should hash this? */
477        tf->sid = g_strdup_printf( "BitlBeeJabberSID%d", tf->ft->local_id );
478       
[793cc25]479        if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) )
[2ff2076]480                bud = jabber_buddy_by_ext_jid( ic, who, 0 );
481        else
482                bud = jabber_buddy_by_jid( ic, who, 0 );
483
484        /* start with the SI tag */
485        sinode = xt_new_node( "si", NULL, NULL );
486        xt_add_attr( sinode, "xmlns", XMLNS_SI );
487        xt_add_attr( sinode, "profile", XMLNS_FILETRANSFER );
488        xt_add_attr( sinode, "id", tf->sid );
489
490/*      if( mimetype )
491                xt_add_attr( node, "mime-type", mimetype ); */
492
493        /* now the file tag */
494/*      if( desc )
495                node = xt_new_node( "desc", descr, NULL ); */
496        node = xt_new_node( "range", NULL, NULL );
497
498        sprintf( filesizestr, "%zd", tf->ft->file_size );
499        node = xt_new_node( "file", NULL, node );
500        xt_add_attr( node, "xmlns", XMLNS_FILETRANSFER );
501        xt_add_attr( node, "name", tf->ft->file_name );
502        xt_add_attr( node, "size", filesizestr );
503/*      if (hash)
504                xt_add_attr( node, "hash", hash );
505        if (date)
506                xt_add_attr( node, "date", date ); */
507
508        xt_add_child( sinode, node );
509
510        /* and finally the feature tag */
511        node = xt_new_node( "field", NULL, NULL );
512        xt_add_attr( node, "var", "stream-method" );
513        xt_add_attr( node, "type", "list-single" );
514
515        for ( m = methods ; *m ; m ++ )
516                xt_add_child( node, xt_new_node( "option", NULL, xt_new_node( "value", (char *)*m, NULL ) ) );
517
518        node = xt_new_node( "x", NULL, node );
519        xt_add_attr( node, "xmlns", XMLNS_XDATA );
520        xt_add_attr( node, "type", "form" );
521
522        node = xt_new_node( "feature", NULL, node );
523        xt_add_attr( node, "xmlns", XMLNS_FEATURE );
524
525        xt_add_child( sinode, node );
526
527        /* and we are there... */
528        node = jabber_make_packet( "iq", "set", bud ? bud->full_jid : who, sinode );
529        jabber_cache_add( ic, node, jabber_si_handle_response );
[dce3903]530        tf->iq_id = g_strdup( xt_find_attr( node, "id" ) );
[2ff2076]531       
532        return jabber_write_packet( ic, node );
533}
Note: See TracBrowser for help on using the repository browser.