source: protocols/jabber/si.c @ b043ad5

Last change on this file since b043ad5 was 44961cb, checked in by ulim <a.sporto+bee@…>, at 2008-05-06T22:06:22Z

fix bug in new kb/s display for transfers of less than one second.

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