Commit two patches from RH package maintainer (#956).
[bitlbee.git] / dcc.c
blob794b361359ab21da7b75ae8121d2bd875b549934
1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2007 Uli Meis <a.sporto+bee@gmail.com> *
5 \********************************************************************/
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.
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.
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
24 #define BITLBEE_CORE
25 #include "bitlbee.h"
26 #include "ft.h"
27 #include "dcc.h"
28 #include <netinet/tcp.h>
29 #include <regex.h>
30 #include "lib/ftutil.h"
32 /*
33 * Since that might be confusing a note on naming:
35 * Generic dcc functions start with
37 * dcc_
39 * ,methods specific to DCC SEND start with
41 * dccs_
43 * . Since we can be on both ends of a DCC SEND,
44 * functions specific to one end are called
46 * dccs_send and dccs_recv
48 * ,respectively.
52 /*
53 * used to generate a unique local transfer id the user
54 * can use to reject/cancel transfers
56 unsigned int local_transfer_id=1;
58 /*
59 * just for debugging the nr. of chunks we received from im-protocols and the total data
61 unsigned int receivedchunks=0, receiveddata=0;
63 void dcc_finish( file_transfer_t *file );
64 void dcc_close( file_transfer_t *file );
65 gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond );
66 int dccs_send_request( struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr );
67 gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond);
68 gboolean dccs_recv_write_request( file_transfer_t *ft );
69 gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond );
70 gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... );
72 dcc_file_transfer_t *dcc_alloc_transfer( const char *file_name, size_t file_size, struct im_connection *ic )
74 file_transfer_t *file = g_new0( file_transfer_t, 1 );
75 dcc_file_transfer_t *df = file->priv = g_new0( dcc_file_transfer_t, 1 );
77 file->file_size = file_size;
78 file->file_name = g_strdup( file_name );
79 file->local_id = local_transfer_id++;
80 file->ic = df->ic = ic;
81 df->ft = file;
83 return df;
86 /* This is where the sending magic starts... */
87 file_transfer_t *dccs_send_start( struct im_connection *ic, irc_user_t *iu, const char *file_name, size_t file_size )
89 file_transfer_t *file;
90 dcc_file_transfer_t *df;
91 irc_t *irc = (irc_t *) ic->bee->ui_data;
92 struct sockaddr_storage saddr;
93 char *errmsg;
94 char host[HOST_NAME_MAX];
95 char port[6];
97 if( file_size > global.conf->ft_max_size )
98 return NULL;
100 df = dcc_alloc_transfer( file_name, file_size, ic );
101 file = df->ft;
102 file->write = dccs_send_write;
104 /* listen and request */
106 if( ( df->fd = ft_listen( &saddr, host, port, irc->fd, TRUE, &errmsg ) ) == -1 )
108 dcc_abort( df, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg );
109 return NULL;
112 file->status = FT_STATUS_LISTENING;
114 if( !dccs_send_request( df, iu, &saddr ) )
115 return NULL;
117 /* watch */
118 df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_send_proto, df );
120 irc->file_transfers = g_slist_prepend( irc->file_transfers, file );
122 df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df );
124 imcb_log( ic, "File transfer request from %s for %s (%zd kb).\n"
125 "Accept the file transfer if you'd like the file. If you don't, "
126 "issue the 'transfer reject' command.",
127 iu->nick, file_name, file_size / 1024 );
129 return file;
132 /* Used pretty much everywhere in the code to abort a transfer */
133 gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... )
135 file_transfer_t *file = df->ft;
136 va_list params;
137 va_start( params, reason );
138 char *msg = g_strdup_vprintf( reason, params );
139 va_end( params );
141 file->status |= FT_STATUS_CANCELED;
143 if( file->canceled )
144 file->canceled( file, msg );
146 imcb_log( df->ic, "File %s: DCC transfer aborted: %s", file->file_name, msg );
148 g_free( msg );
150 dcc_close( df->ft );
152 return FALSE;
155 gboolean dcc_progress( gpointer data, gint fd, b_input_condition cond )
157 struct dcc_file_transfer *df = data;
159 if( df->bytes_sent == df->progress_bytes_last )
161 /* no progress. cancel */
162 if( df->bytes_sent == 0 )
163 return dcc_abort( df, "Couldn't establish transfer within %d seconds", DCC_MAX_STALL );
164 else
165 return dcc_abort( df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL, df->bytes_sent / 1024 );
169 df->progress_bytes_last = df->bytes_sent;
171 return TRUE;
174 /* used extensively for socket operations */
175 #define ASSERTSOCKOP(op, msg) \
176 if( (op) == -1 ) \
177 return dcc_abort( df , msg ": %s", strerror( errno ) );
179 /* Creates the "DCC SEND" line and sends it to the server */
180 int dccs_send_request( struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr )
182 char ipaddr[INET6_ADDRSTRLEN];
183 const void *netaddr;
184 int port;
185 char *cmd;
187 if( saddr->ss_family == AF_INET )
189 struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr;
191 sprintf( ipaddr, "%d",
192 ntohl( saddr_ipv4->sin_addr.s_addr ) );
193 port = saddr_ipv4->sin_port;
195 else
197 struct sockaddr_in6 *saddr_ipv6 = ( struct sockaddr_in6 *) saddr;
199 netaddr = &saddr_ipv6->sin6_addr.s6_addr;
200 port = saddr_ipv6->sin6_port;
203 * Didn't find docs about this, but it seems that's the way irssi does it
205 if( !inet_ntop( saddr->ss_family, netaddr, ipaddr, sizeof( ipaddr ) ) )
206 return dcc_abort( df, "inet_ntop failed: %s", strerror( errno ) );
209 port = ntohs( port );
211 cmd = g_strdup_printf( "\001DCC SEND %s %s %u %zu\001",
212 df->ft->file_name, ipaddr, port, df->ft->file_size );
214 irc_send_msg_raw( iu, "PRIVMSG", iu->irc->user->nick, cmd );
216 g_free( cmd );
218 return TRUE;
222 * After setup, the transfer itself is handled entirely by this function.
223 * There are basically four things to handle: connect, receive, send, and error.
225 gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond )
227 dcc_file_transfer_t *df = data;
228 file_transfer_t *file = df->ft;
230 if( ( cond & B_EV_IO_READ ) &&
231 ( file->status & FT_STATUS_LISTENING ) )
233 struct sockaddr *clt_addr;
234 socklen_t ssize = sizeof( clt_addr );
236 /* Connect */
238 ASSERTSOCKOP( df->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" );
240 closesocket( fd );
241 fd = df->fd;
242 file->status = FT_STATUS_TRANSFERRING;
243 sock_make_nonblocking( fd );
245 /* IM protocol callback */
246 if( file->accept )
247 file->accept( file );
249 /* reschedule for reading on new fd */
250 df->watch_in = b_input_add( fd, B_EV_IO_READ, dccs_send_proto, df );
252 return FALSE;
255 if( cond & B_EV_IO_READ )
257 int ret;
259 ASSERTSOCKOP( ret = recv( fd, ( (char*) &df->acked ) + df->acked_len,
260 sizeof( df->acked ) - df->acked_len, 0 ), "Receiving" );
262 if( ret == 0 )
263 return dcc_abort( df, "Remote end closed connection" );
265 /* How likely is it that a 32-bit integer gets split accross
266 packet boundaries? Chances are rarely 0 so let's be sure. */
267 if( ( df->acked_len = ( df->acked_len + ret ) % 4 ) > 0 )
268 return TRUE;
270 df->acked = ntohl( df->acked );
272 /* If any of this is actually happening, the receiver should buy a new IRC client */
274 if ( df->acked > df->bytes_sent )
275 return dcc_abort( df, "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", df->acked, df->bytes_sent );
277 if ( df->acked < file->bytes_transferred )
278 return dcc_abort( df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", df->acked, file->bytes_transferred );
280 file->bytes_transferred = df->acked;
282 if( file->bytes_transferred >= file->file_size ) {
283 if( df->proto_finished )
284 dcc_finish( file );
285 return FALSE;
288 return TRUE;
291 return TRUE;
294 gboolean dccs_recv_start( file_transfer_t *ft )
296 dcc_file_transfer_t *df = ft->priv;
297 struct sockaddr_storage *saddr = &df->saddr;
298 int fd;
299 char ipaddr[INET6_ADDRSTRLEN];
300 socklen_t sa_len = saddr->ss_family == AF_INET ?
301 sizeof( struct sockaddr_in ) : sizeof( struct sockaddr_in6 );
303 if( !ft->write )
304 return dcc_abort( df, "BUG: protocol didn't register write()" );
306 ASSERTSOCKOP( fd = df->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening Socket" );
308 sock_make_nonblocking( fd );
310 if( ( connect( fd, (struct sockaddr *)saddr, sa_len ) == -1 ) &&
311 ( errno != EINPROGRESS ) )
312 return dcc_abort( df, "Connecting to %s:%d : %s",
313 inet_ntop( saddr->ss_family,
314 saddr->ss_family == AF_INET ?
315 ( void* ) &( ( struct sockaddr_in *) saddr )->sin_addr.s_addr :
316 ( void* ) &( ( struct sockaddr_in6 *) saddr )->sin6_addr.s6_addr,
317 ipaddr,
318 sizeof( ipaddr ) ),
319 ntohs( saddr->ss_family == AF_INET ?
320 ( ( struct sockaddr_in *) saddr )->sin_port :
321 ( ( struct sockaddr_in6 *) saddr )->sin6_port ),
322 strerror( errno ) );
324 ft->status = FT_STATUS_CONNECTING;
326 /* watch */
327 df->watch_out = b_input_add( df->fd, B_EV_IO_WRITE, dccs_recv_proto, df );
328 ft->write_request = dccs_recv_write_request;
330 df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df );
332 return TRUE;
335 gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond )
337 dcc_file_transfer_t *df = data;
338 file_transfer_t *ft = df->ft;
340 if( ( cond & B_EV_IO_WRITE ) &&
341 ( ft->status & FT_STATUS_CONNECTING ) )
343 ft->status = FT_STATUS_TRANSFERRING;
345 //df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_recv_proto, df );
347 df->watch_out = 0;
348 return FALSE;
351 if( cond & B_EV_IO_READ )
353 int ret, done;
355 ASSERTSOCKOP( ret = recv( fd, ft->buffer, sizeof( ft->buffer ), 0 ), "Receiving" );
357 if( ret == 0 )
358 return dcc_abort( df, "Remote end closed connection" );
360 if( !ft->write( df->ft, ft->buffer, ret ) )
361 return FALSE;
363 df->bytes_sent += ret;
365 done = df->bytes_sent >= ft->file_size;
367 if( ( ( df->bytes_sent - ft->bytes_transferred ) > DCC_PACKET_SIZE ) ||
368 done )
370 guint32 ack = htonl( ft->bytes_transferred = df->bytes_sent );
371 int ackret;
373 ASSERTSOCKOP( ackret = send( fd, &ack, 4, 0 ), "Sending DCC ACK" );
375 if ( ackret != 4 )
376 return dcc_abort( df, "Error sending DCC ACK, sent %d instead of 4 bytes", ackret );
379 if( df->bytes_sent == ret )
380 ft->started = time( NULL );
382 if( done )
384 if( df->watch_out )
385 b_event_remove( df->watch_out );
387 if( df->proto_finished )
388 dcc_finish( ft );
390 df->watch_in = 0;
391 return FALSE;
394 df->watch_in = 0;
395 return FALSE;
398 return TRUE;
401 gboolean dccs_recv_write_request( file_transfer_t *ft )
403 dcc_file_transfer_t *df = ft->priv;
405 if( df->watch_in )
406 return dcc_abort( df, "BUG: write_request() called while watching" );
408 df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_recv_proto, df );
410 return TRUE;
413 gboolean dccs_send_can_write( gpointer data, gint fd, b_input_condition cond )
415 struct dcc_file_transfer *df = data;
416 df->watch_out = 0;
418 df->ft->write_request( df->ft );
419 return FALSE;
423 * Incoming data.
426 gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_len )
428 dcc_file_transfer_t *df = file->priv;
429 int ret;
431 receivedchunks++; receiveddata += data_len;
433 if( df->watch_out )
434 return dcc_abort( df, "BUG: write() called while watching" );
436 ASSERTSOCKOP( ret = send( df->fd, data, data_len, 0 ), "Sending data" );
438 if( ret == 0 )
439 return dcc_abort( df, "Remote end closed connection" );
441 /* TODO: this should really not be fatal */
442 if( ret < data_len )
443 return dcc_abort( df, "send() sent %d instead of %d", ret, data_len );
445 if( df->bytes_sent == 0 )
446 file->started = time( NULL );
448 df->bytes_sent += ret;
450 if( df->bytes_sent < df->ft->file_size )
451 df->watch_out = b_input_add( df->fd, B_EV_IO_WRITE, dccs_send_can_write, df );
453 return TRUE;
457 * Cleans up after a transfer.
459 void dcc_close( file_transfer_t *file )
461 dcc_file_transfer_t *df = file->priv;
462 irc_t *irc = (irc_t *) df->ic->bee->ui_data;
464 if( file->free )
465 file->free( file );
467 closesocket( df->fd );
469 if( df->watch_in )
470 b_event_remove( df->watch_in );
472 if( df->watch_out )
473 b_event_remove( df->watch_out );
475 if( df->progress_timeout )
476 b_event_remove( df->progress_timeout );
478 irc->file_transfers = g_slist_remove( irc->file_transfers, file );
480 g_free( df );
481 g_free( file->file_name );
482 g_free( file );
485 void dcc_finish( file_transfer_t *file )
487 dcc_file_transfer_t *df = file->priv;
488 time_t diff = time( NULL ) - file->started ? : 1;
490 file->status |= FT_STATUS_FINISHED;
492 if( file->finished )
493 file->finished( file );
495 imcb_log( df->ic, "File %s transferred successfully at %d kb/s!" , file->file_name, (int) ( file->bytes_transferred / 1024 / diff ) );
496 dcc_close( file );
500 * DCC SEND <filename> <IP> <port> <filesize>
502 * filename can be in "" or not. If it is, " can probably be escaped...
503 * IP can be an unsigned int (IPV4) or something else (IPV6)
506 file_transfer_t *dcc_request( struct im_connection *ic, char* const* ctcp )
508 irc_t *irc = (irc_t *) ic->bee->ui_data;
509 file_transfer_t *ft;
510 dcc_file_transfer_t *df;
511 int gret;
512 size_t filesize;
514 if( ctcp[5] != NULL &&
515 sscanf( ctcp[4], "%zd", &filesize ) == 1 && /* Just int. validation. */
516 sscanf( ctcp[5], "%zd", &filesize ) == 1 )
518 char *filename, *host, *port;
519 struct addrinfo hints, *rp;
521 filename = ctcp[2];
523 host = ctcp[3];
524 while( *host && isdigit( *host ) ) host++; /* Just digits? */
525 if( *host == '\0' )
527 struct in_addr ipaddr = { .s_addr = htonl( atoll( ctcp[3] ) ) };
528 host = inet_ntoa( ipaddr );
529 } else
531 /* Contains non-numbers, hopefully an IPV6 address */
532 host = ctcp[3];
535 port = ctcp[4];
536 filesize = atoll( ctcp[5] );
538 memset( &hints, 0, sizeof ( struct addrinfo ) );
539 hints.ai_socktype = SOCK_STREAM;
540 hints.ai_flags = AI_NUMERICSERV;
542 if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) )
544 imcb_log( ic, "DCC: getaddrinfo() failed with %s "
545 "when parsing incoming 'DCC SEND': "
546 "host %s, port %s",
547 gai_strerror( gret ), host, port );
548 return NULL;
551 df = dcc_alloc_transfer( filename, filesize, ic );
552 ft = df->ft;
553 ft->sending = TRUE;
554 memcpy( &df->saddr, rp->ai_addr, rp->ai_addrlen );
556 freeaddrinfo( rp );
558 irc->file_transfers = g_slist_prepend( irc->file_transfers, ft );
560 return ft;
562 else
563 imcb_log( ic, "DCC: couldnt parse `DCC SEND' line" );
565 return NULL;