source: dcc.c @ 87f525e

Last change on this file since 87f525e 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: 19.4 KB
RevLine 
[2c2df7d]1/********************************************************************\
2* BitlBee -- An IRC to other IM-networks gateway                     *
3*                                                                    *
4* Copyright 2007 Uli Meis <a.sporto+bee@gmail.com>                   *
5\********************************************************************/
6
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 with
19  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
20  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
21  Suite 330, Boston, MA  02111-1307  USA
22*/
23
24#define BITLBEE_CORE
25#include "bitlbee.h"
26#include "ft.h"
27#include "dcc.h"
28#include <poll.h>
29#include <netinet/tcp.h>
[2ff2076]30#include <regex.h>
[2c2df7d]31
[4358b10]32/* Some ifdefs for ulibc (Thanks to Whoopie) */
33#ifndef HOST_NAME_MAX
34#include <sys/param.h>
35#ifdef MAXHOSTNAMELEN
36#define HOST_NAME_MAX MAXHOSTNAMELEN
37#else
38#define HOST_NAME_MAX 255
39#endif
40#endif
41
42#ifndef AI_NUMERICSERV
43#define AI_NUMERICSERV 0x0400   /* Don't use name resolution.  */
44#endif
45
[2c2df7d]46/*
47 * Since that might be confusing a note on naming:
48 *
49 * Generic dcc functions start with
50 *
51 *      dcc_
52 *
53 * ,methods specific to DCC SEND start with
54 *
55 *      dccs_
56 *
57 * . Since we can be on both ends of a DCC SEND,
58 * functions specific to one end are called
59 *
60 *      dccs_send and dccs_recv
61 *
62 * ,respectively.
63 */
64
65
66/*
67 * used to generate a unique local transfer id the user
68 * can use to reject/cancel transfers
69 */
70unsigned int local_transfer_id=1;
71
72/*
73 * just for debugging the nr. of chunks we received from im-protocols and the total data
74 */
75unsigned int receivedchunks=0, receiveddata=0;
76
77int max_packet_size = 0;
78
79static void dcc_finish( file_transfer_t *file );
80static void dcc_close( file_transfer_t *file );
81gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond );
82gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr );
83int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr );
[2ff2076]84gboolean dccs_recv_start( file_transfer_t *ft );
85gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond);
[dce3903]86gboolean dccs_recv_write_request( file_transfer_t *ft );
[d56ee38]87gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond );
[2c2df7d]88
89/* As defined in ft.h */
90file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size )
91{
92        user_t *u = user_findhandle( ic, handle );
93        /* one could handle this more intelligent like imcb_buddy_msg.
94         * can't call it directly though cause it does some wrapping.
95         * Maybe give imcb_buddy_msg a parameter NO_WRAPPING? */
96        if (!u) return NULL;
97
98        return dccs_send_start( ic, u->nick, file_name, file_size );
99};
100
101/* As defined in ft.h */
102void imcb_file_canceled( file_transfer_t *file, char *reason )
103{
104        if( file->canceled )
105                file->canceled( file, reason );
106
107        dcc_close( file );
108}
109
[2ff2076]110/* As defined in ft.h */
111gboolean imcb_file_recv_start( file_transfer_t *ft )
112{
113        return dccs_recv_start( ft );
114}
115
[4ac647d]116/* As defined in ft.h */
117void imcb_file_finished( file_transfer_t *file )
118{
119        dcc_file_transfer_t *df = file->priv;
120
121        if( file->bytes_transferred >= file->file_size )
122                dcc_finish( file );
123        else
124                df->proto_finished = TRUE;
125}
126
[2ff2076]127dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic )
128{
129        file_transfer_t *file = g_new0( file_transfer_t, 1 );
130        dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1);
131        file->file_size = file_size;
132        file->file_name = g_strdup( file_name );
133        file->local_id = local_transfer_id++;
134        df->ic = ic;
135        df->ft = file;
136       
137        return df;
138}
139
[2c2df7d]140/* This is where the sending magic starts... */
141file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size )
142{
143        file_transfer_t *file;
144        dcc_file_transfer_t *df;
[2ff2076]145        struct sockaddr_storage *saddr;
[2c2df7d]146
147        if( file_size > global.conf->max_filetransfer_size )
148                return NULL;
149       
[2ff2076]150        df = dcc_alloc_transfer( file_name, file_size, ic );
151        file = df->ft;
[dce3903]152        file->write = dccs_send_write;
[2ff2076]153
[2c2df7d]154        /* listen and request */
[2ff2076]155        if( !dcc_listen( df, &saddr ) ||
156            !dccs_send_request( df, user_nick, saddr ) )
[2c2df7d]157                return NULL;
158
[2ff2076]159        g_free( saddr );
[2c2df7d]160
161        /* watch */
162        df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df );
163
164        df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file );
165
[d56ee38]166        df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df );
167
[4ac647d]168        imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", user_nick, file_name, file_size/1024 );
169
170        imcb_log( ic, "Accept the file transfer if you'd like the file. If you don't, issue the 'transfers reject' command.");
171
[2c2df7d]172        return file;
173}
174
175/* Used pretty much everywhere in the code to abort a transfer */
176gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... )
177{
178        file_transfer_t *file = df->ft;
179        va_list params;
180        va_start( params, reason );
181        char *msg = g_strdup_vprintf( reason, params );
182        va_end( params );
183       
184        file->status |= FT_STATUS_CANCELED;
185       
186        if( file->canceled )
187                file->canceled( file, msg );
[d56ee38]188
189        imcb_log( df->ic, "File %s: DCC transfer aborted: %s", file->file_name, msg );
[2c2df7d]190
191        g_free( msg );
192
193        dcc_close( df->ft );
194
195        return FALSE;
196}
197
[d56ee38]198gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond )
199{
200        struct dcc_file_transfer *df = data;
201
[b043ad5]202        if( df->bytes_sent == df->progress_bytes_last )
[d56ee38]203        {
204                /* no progress. cancel */
205                if( df->bytes_sent == 0 )
206                        return dcc_abort( df, "Couldnt establish transfer within %d seconds", DCC_MAX_STALL );
207                else 
[b043ad5]208                        return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->bytes_sent / 1024 );
[d56ee38]209
210        }
211
[b043ad5]212        df->progress_bytes_last = df->bytes_sent;
[d56ee38]213
214        return TRUE;
215}
216
[2c2df7d]217/* used extensively for socket operations */
218#define ASSERTSOCKOP(op, msg) \
219        if( (op) == -1 ) \
220                return dcc_abort( df , msg ": %s", strerror( errno ) );
221
222/* Creates the "DCC SEND" line and sends it to the server */
223int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr )
224{
225        char ipaddr[INET6_ADDRSTRLEN]; 
226        const void *netaddr;
227        int port;
228        char *cmd;
229
230        if( saddr->ss_family == AF_INET )
231        {
232                struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr;
233
234                sprintf( ipaddr, "%d", 
[dce3903]235                         ntohl( saddr_ipv4->sin_addr.s_addr ) );
[2c2df7d]236                port = saddr_ipv4->sin_port;
237        } else 
238        {
239                struct sockaddr_in6 *saddr_ipv6 = ( struct sockaddr_in6 *) saddr;
240
241                netaddr = &saddr_ipv6->sin6_addr.s6_addr;
242                port = saddr_ipv6->sin6_port;
243
244                /*
245                 * Didn't find docs about this, but it seems that's the way irssi does it
246                 */
247                if( !inet_ntop( saddr->ss_family, netaddr, ipaddr, sizeof( ipaddr ) ) )
248                        return dcc_abort( df, "inet_ntop failed: %s", strerror( errno ) );
249        }
250
251        port = ntohs( port );
252
253        cmd = g_strdup_printf( "\001DCC SEND %s %s %u %zu\001",
254                                df->ft->file_name, ipaddr, port, df->ft->file_size );
255       
256        if ( !irc_msgfrom( df->ic->irc, user_nick, cmd ) )
257                return dcc_abort( df, "couldn't send 'DCC SEND' message to %s", user_nick );
258
259        g_free( cmd );
260
261        return TRUE;
262}
263
264/*
265 * Creates a listening socket and returns it in saddr_ptr.
266 */
267gboolean dcc_listen( dcc_file_transfer_t *df, struct sockaddr_storage **saddr_ptr )
268{
269        file_transfer_t *file = df->ft;
270        struct sockaddr_storage *saddr;
[0cab388]271        int fd,gret;
[2c2df7d]272        char hostname[ HOST_NAME_MAX + 1 ];
273        struct addrinfo hints, *rp;
274        socklen_t ssize = sizeof( struct sockaddr_storage );
275
276        /* won't be long till someone asks for this to be configurable :) */
277
278        ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" );
279
280        memset( &hints, 0, sizeof( struct addrinfo ) );
281        hints.ai_socktype = SOCK_STREAM;
282        hints.ai_flags = AI_NUMERICSERV;
283
[0cab388]284        if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) )
285                return dcc_abort( df, "getaddrinfo(): %s", gai_strerror( gret ) );
[2c2df7d]286
287        saddr = g_new( struct sockaddr_storage, 1 );
288
289        *saddr_ptr = saddr;
290
291        memcpy( saddr, rp->ai_addr, rp->ai_addrlen );
292
293        ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" );
294
295        ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" );
296       
297        freeaddrinfo( rp );
298
299        ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" );
300
301        ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" );
302
303        file->status = FT_STATUS_LISTENING;
304
305        return TRUE;
306}
307
308/*
[2ff2076]309 * Checks poll(), same for receiving and sending
[2c2df7d]310 */
[2ff2076]311gboolean dcc_poll( dcc_file_transfer_t *df, int fd, short *revents )
[2c2df7d]312{
313        struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR|POLLIN|POLLOUT };
314
[2ff2076]315        ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" )
[2c2df7d]316
[2ff2076]317        if( pfd.revents & POLLERR )
[2c2df7d]318        {
319                int sockerror;
320                socklen_t errlen = sizeof( sockerror );
321
322                if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) )
323                        return dcc_abort( df, "getsockopt() failed, unknown socket error (weird!)" );
324
325                return dcc_abort( df, "Socket error: %s", strerror( sockerror ) );
326        }
327       
[2ff2076]328        if( pfd.revents & POLLHUP ) 
[2c2df7d]329                return dcc_abort( df, "Remote end closed connection" );
330       
[2ff2076]331        *revents = pfd.revents;
332
333        return TRUE;
334}
335
[dce3903]336/*
337 * fills max_packet_size with twice the TCP maximum segment size
338 */
[2ff2076]339gboolean  dcc_check_maxseg( dcc_file_transfer_t *df, int fd )
340{
341        /*
342         * use twice the maximum segment size as a maximum for calls to send().
343         */
344        if( max_packet_size == 0 )
345        {
346                unsigned int mpslen = sizeof( max_packet_size );
347                if( getsockopt( fd, IPPROTO_TCP, TCP_MAXSEG, &max_packet_size, &mpslen ) )
348                        return dcc_abort( df, "getsockopt() failed" );
349                max_packet_size *= 2;
350        }
351        return TRUE;
352}
353
354/*
355 * After setup, the transfer itself is handled entirely by this function.
356 * There are basically four things to handle: connect, receive, send, and error.
357 */
358gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond )
359{
360        dcc_file_transfer_t *df = data;
361        file_transfer_t *file = df->ft;
362        short revents;
363       
364        if( !dcc_poll( df, fd, &revents) )
365                return FALSE;
366
[2c2df7d]367        if( ( revents & POLLIN ) &&
368            ( file->status & FT_STATUS_LISTENING ) )
369        {       
370                struct sockaddr *clt_addr;
371                socklen_t ssize = sizeof( clt_addr );
372
373                /* Connect */
374
375                ASSERTSOCKOP( df->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" );
376
377                closesocket( fd );
378                fd = df->fd;
[2ff2076]379                file->status = FT_STATUS_TRANSFERRING;
[2c2df7d]380                sock_make_nonblocking( fd );
381
[2ff2076]382                if ( !dcc_check_maxseg( df, fd ) )
383                        return FALSE;
384
[2c2df7d]385                /* IM protocol callback */
386                if( file->accept )
387                        file->accept( file );
[dce3903]388
[2c2df7d]389                /* reschedule for reading on new fd */
390                df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df );
391
392                return FALSE;
393        }
394
395        if( revents & POLLIN ) 
396        {
397                int bytes_received;
398                int ret;
399               
400                ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received  ), MSG_PEEK ), "Receiving" );
401
402                if( ret == 0 )
403                        return dcc_abort( df, "Remote end closed connection" );
404                       
405                if( ret < 4 )
406                {
407                        imcb_log( df->ic, "WARNING: DCC SEND: receiver sent only 2 bytes instead of 4, shouldn't happen too often!" );
408                        return TRUE;
409                }
410
411                ASSERTSOCKOP( ret = recv( fd, &bytes_received, sizeof( bytes_received  ), 0 ), "Receiving" );
412                if( ret != 4 )
413                        return dcc_abort( df, "MSG_PEEK'ed 4, but can only dequeue %d bytes", ret );
414
415                bytes_received = ntohl( bytes_received );
416
417                /* If any of this is actually happening, the receiver should buy a new IRC client */
418
419                if ( bytes_received > df->bytes_sent )
420                        return dcc_abort( df, "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", bytes_received, df->bytes_sent );
421
422                if ( bytes_received < file->bytes_transferred )
423                        return dcc_abort( df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", bytes_received, file->bytes_transferred );
424               
425                file->bytes_transferred = bytes_received;
426       
427                if( file->bytes_transferred >= file->file_size ) {
[4ac647d]428                        if( df->proto_finished )
429                                dcc_finish( file );
[2c2df7d]430                        return FALSE;
431                }
432       
433                return TRUE;
434        }
435
436        return TRUE;
437}
438
[2ff2076]439gboolean dccs_recv_start( file_transfer_t *ft )
440{
441        dcc_file_transfer_t *df = ft->priv;
442        struct sockaddr_storage *saddr = &df->saddr;
443        int fd;
[0cab388]444        char ipaddr[INET6_ADDRSTRLEN]; 
[2ff2076]445        socklen_t sa_len = saddr->ss_family == AF_INET ? 
446                sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 );
447       
448        if( !ft->write )
[0cab388]449                return dcc_abort( df, "BUG: protocol didn't register write()" );
[2ff2076]450       
451        ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ) , "Opening Socket" );
452
453        sock_make_nonblocking( fd );
454
455        if( ( connect( fd, (struct sockaddr *)saddr, sa_len ) == -1 ) &&
456            ( errno != EINPROGRESS ) )
[0cab388]457                return dcc_abort( df, "Connecting to %s:%d : %s", 
458                        inet_ntop( saddr->ss_family, 
459                                saddr->ss_family == AF_INET ? 
460                                    ( void* ) &( ( struct sockaddr_in *) saddr )->sin_addr.s_addr :
461                                    ( void* ) &( ( struct sockaddr_in6 *) saddr )->sin6_addr.s6_addr,
462                                ipaddr, 
463                                sizeof( ipaddr ) ),
464                        ntohs( saddr->ss_family == AF_INET ?
465                            ( ( struct sockaddr_in *) saddr )->sin_port :
466                            ( ( struct sockaddr_in6 *) saddr )->sin6_port ),
467                        strerror( errno ) );
[2ff2076]468
469        ft->status = FT_STATUS_CONNECTING;
470
471        /* watch */
472        df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df );
[dce3903]473        ft->write_request = dccs_recv_write_request;
[2ff2076]474
[d56ee38]475        df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df );
476
[2ff2076]477        return TRUE;
478}
479
480gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond)
481{
482        dcc_file_transfer_t *df = data;
483        file_transfer_t *ft = df->ft;
484        short revents;
485
486        if( !dcc_poll( df, fd, &revents ) )
487                return FALSE;
488       
489        if( ( revents & POLLOUT ) &&
490            ( ft->status & FT_STATUS_CONNECTING ) )
491        {
492                ft->status = FT_STATUS_TRANSFERRING;
493                if ( !dcc_check_maxseg( df, fd ) )
494                        return FALSE;
495
496                //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df );
497
498                df->watch_out = 0;
499                return FALSE;
500        }
501
502        if( revents & POLLIN )
503        {
504                int ret, done;
505
[dce3903]506                ASSERTSOCKOP( ret = recv( fd, ft->buffer, sizeof( ft->buffer ), 0 ), "Receiving" );
[2ff2076]507
508                if( ret == 0 )
509                        return dcc_abort( df, "Remote end closed connection" );
510
[4ac647d]511                if( !ft->write( df->ft, ft->buffer, ret ) )
512                        return FALSE;
513
[2ff2076]514                df->bytes_sent += ret;
515
516                done = df->bytes_sent >= ft->file_size;
517
518                if( ( ( df->bytes_sent - ft->bytes_transferred ) > DCC_PACKET_SIZE ) ||
519                    done )
520                {
521                        int ack, ackret;
522                        ack = htonl( ft->bytes_transferred = df->bytes_sent );
523
524                        ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" );
525                       
526                        if ( ackret != 4 )
[dce3903]527                                return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ackret );
[2ff2076]528                }
529               
[4ac647d]530                if( df->bytes_sent == ret )
531                        ft->started = time( NULL );
[2ff2076]532
533                if( done )
534                {
[4ac647d]535                        if( df->watch_out )
536                                b_event_remove( df->watch_out );
537
538                        if( df->proto_finished )
539                                dcc_finish( ft );
[2ff2076]540
541                        df->watch_in = 0;
542                        return FALSE;
543                }
544
[dce3903]545                df->watch_in = 0;
546                return FALSE;
[2ff2076]547        }
548
549        return TRUE;
550}
551
[dce3903]552gboolean dccs_recv_write_request( file_transfer_t *ft )
[2ff2076]553{
554        dcc_file_transfer_t *df = ft->priv;
555
[dce3903]556        if( df->watch_in )
557                return dcc_abort( df, "BUG: write_request() called while watching" );
558
559        df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df );
560
561        return TRUE;
562}
563
564gboolean dccs_send_can_write( gpointer data, gint fd, b_input_condition cond )
565{
566        struct dcc_file_transfer *df = data;
567        df->watch_out = 0;
568
569        df->ft->write_request( df->ft );
570        return FALSE;
[2ff2076]571}
572
[2c2df7d]573/*
[dce3903]574 * Incoming data.
[2c2df7d]575 *
[dce3903]576 */
577gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_len )
[2c2df7d]578{
579        dcc_file_transfer_t *df = file->priv;
[dce3903]580        int ret;
581
582        receivedchunks++; receiveddata += data_len;
[2c2df7d]583
[dce3903]584        if( df->watch_out )
585                return dcc_abort( df, "BUG: write() called while watching" );
[2c2df7d]586
[dce3903]587        ASSERTSOCKOP( ret = send( df->fd, data, data_len, 0 ), "Sending data" );
[2c2df7d]588
[dce3903]589        if( ret == 0 )
590                return dcc_abort( df, "Remote end closed connection" );
[2c2df7d]591
[dce3903]592        /* TODO: this should really not be fatal */
593        if( ret < data_len )
594                return dcc_abort( df, "send() sent %d instead of %d", ret, data_len );
[2c2df7d]595
[4ac647d]596        if( df->bytes_sent == 0 )
597                file->started = time( NULL );
598
[dce3903]599        df->bytes_sent += ret;
600
601        if( df->bytes_sent < df->ft->file_size )
602                df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_can_write, df );
603
604        return TRUE;
[2c2df7d]605}
606
607/*
608 * Cleans up after a transfer.
609 */
610static void dcc_close( file_transfer_t *file )
611{
612        dcc_file_transfer_t *df = file->priv;
613
614        if( file->free )
615                file->free( file );
616       
617        closesocket( df->fd );
618
619        if( df->watch_in )
620                b_event_remove( df->watch_in );
621
622        if( df->watch_out )
623                b_event_remove( df->watch_out );
624       
[d56ee38]625        if( df->progress_timeout )
626                b_event_remove( df->progress_timeout );
627       
[2c2df7d]628        df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file );
629       
630        g_free( df );
631        g_free( file->file_name );
632        g_free( file );
633}
634
635void dcc_finish( file_transfer_t *file )
636{
[4ac647d]637        dcc_file_transfer_t *df = file->priv;
638        time_t diff = time( NULL ) - file->started ? : 1;
639
[2c2df7d]640        file->status |= FT_STATUS_FINISHED;
641       
642        if( file->finished )
643                file->finished( file );
644
[4ac647d]645        imcb_log( df->ic, "File %s transferred successfully at %d kb/s!" , file->file_name, (int) ( file->bytes_transferred / 1024 / diff ) );
[2c2df7d]646        dcc_close( file );
647}
[2ff2076]648
649/*
650 * DCC SEND <filename> <IP> <port> <filesize>
651 *
652 * filename can be in "" or not. If it is, " can probably be escaped...
653 * IP can be an unsigned int (IPV4) or something else (IPV6)
654 *
655 */
656file_transfer_t *dcc_request( struct im_connection *ic, char *line )
657{
658        char *pattern = "SEND"
[9afeefa]659                " (([^\"][^ ]*)|\"(([^\"]|\\\")*)\")"
[2ff2076]660                " (([0-9]*)|([^ ]*))"
661                " ([0-9]*)"
662                " ([0-9]*)\001";
[9afeefa]663        regmatch_t pmatch[10];
[2ff2076]664        regex_t re;
665        file_transfer_t *ft;
666        dcc_file_transfer_t *df;
[6cac643]667        char errbuf[256];
668        int regerrcode, gret;
669
670        if( ( regerrcode = regcomp( &re, pattern, REG_EXTENDED ) ) ||
[9afeefa]671            ( regerrcode = regexec( &re, line, 10, pmatch, 0 ) ) ) {
[6cac643]672                regerror( regerrcode,&re,errbuf,sizeof( errbuf ) );
673                imcb_log( ic, 
674                          "DCC: error parsing 'DCC SEND': %s, line: %s", 
675                          errbuf, line );
[2ff2076]676                return NULL;
[6cac643]677        }
[2ff2076]678
679        if( ( pmatch[1].rm_so > 0 ) &&
[9afeefa]680            ( pmatch[5].rm_so > 0 ) &&
681            ( pmatch[8].rm_so > 0 ) &&
682            ( pmatch[9].rm_so > 0 ) )
[2ff2076]683        {
684                char *input = g_strdup( line );
685                char *filename, *host, *port;
686                size_t filesize;
687                struct addrinfo hints, *rp;
688
689                /* "filename" or filename */
690                if ( pmatch[2].rm_so > 0 )
691                {
692                        input[pmatch[2].rm_eo] = '\0';
693                        filename = input + pmatch[2].rm_so;
694                } else
695                {
696                        input[pmatch[3].rm_eo] = '\0';
697                        filename = input + pmatch[3].rm_so;
698                }
699                       
[9afeefa]700                input[pmatch[5].rm_eo] = '\0';
[2ff2076]701
702                /* number means ipv4, something else means ipv6 */
[9afeefa]703                if ( pmatch[6].rm_so > 0 )
[2ff2076]704                {
[6cac643]705                        struct in_addr ipaddr = { .s_addr = htonl( atoi( input + pmatch[5].rm_so ) ) };
[2ff2076]706                        host = inet_ntoa( ipaddr );
707                } else
708                {
709                        /* Contains non-numbers, hopefully an IPV6 address */
[9afeefa]710                        host = input + pmatch[7].rm_so;
[2ff2076]711                }
712
713                input[pmatch[8].rm_eo] = '\0';
[9afeefa]714                input[pmatch[9].rm_eo] = '\0';
[2ff2076]715
[9afeefa]716                port = input + pmatch[8].rm_so;
717                filesize = atoll( input + pmatch[9].rm_so );
[2ff2076]718
719                memset( &hints, 0, sizeof ( struct addrinfo ) );
[6cac643]720                if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) )
[2ff2076]721                {
722                        g_free( input );
[6cac643]723                        imcb_log( ic, "DCC: getaddrinfo() failed with %s "
724                                  "when parsing incoming 'DCC SEND': "
725                                  "host %s, port %s", 
726                                  gai_strerror( gret ), host, port );
[2ff2076]727                        return NULL;
728                }
729
730                df = dcc_alloc_transfer( filename, filesize, ic );
731                ft = df->ft;
732                ft->sending = TRUE;
733                memcpy( &df->saddr, rp->ai_addr, rp->ai_addrlen );
734
735                freeaddrinfo( rp );
736                g_free( input );
737
738                df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, ft );
739
740                return ft;
741        }
742
[6cac643]743        imcb_log( ic, "DCC: couldnt parse 'DCC SEND' line: %s", line );
744
[2ff2076]745        return NULL;
746}
747
Note: See TracBrowser for help on using the repository browser.