1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
4 * Copyright 2007 Uli Meis <a.sporto+bee@gmail.com> *
5 \********************************************************************/
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
28 #include <netinet/tcp.h>
30 #include "lib/ftutil.h"
33 * Since that might be confusing a note on naming:
35 * Generic dcc functions start with
39 * ,methods specific to DCC SEND start with
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
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;
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
;
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
;
94 char host
[HOST_NAME_MAX
];
97 if( file_size
> global
.conf
->ft_max_size
)
100 df
= dcc_alloc_transfer( file_name
, file_size
, ic
);
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
);
112 file
->status
= FT_STATUS_LISTENING
;
114 if( !dccs_send_request( df
, iu
, &saddr
) )
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 );
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
;
137 va_start( params
, reason
);
138 char *msg
= g_strdup_vprintf( reason
, params
);
141 file
->status
|= FT_STATUS_CANCELED
;
144 file
->canceled( file
, msg
);
146 imcb_log( df
->ic
, "File %s: DCC transfer aborted: %s", file
->file_name
, msg
);
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
);
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
;
174 /* used extensively for socket operations */
175 #define ASSERTSOCKOP(op, msg) \
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
];
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
;
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
);
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
);
238 ASSERTSOCKOP( df
->fd
= accept( fd
, (struct sockaddr
*) &clt_addr
, &ssize
), "Accepting connection" );
242 file
->status
= FT_STATUS_TRANSFERRING
;
243 sock_make_nonblocking( fd
);
245 /* IM protocol callback */
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
);
255 if( cond
& B_EV_IO_READ
)
259 ASSERTSOCKOP( ret
= recv( fd
, ( (char*) &df
->acked
) + df
->acked_len
,
260 sizeof( df
->acked
) - df
->acked_len
, 0 ), "Receiving" );
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 )
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
)
294 gboolean
dccs_recv_start( file_transfer_t
*ft
)
296 dcc_file_transfer_t
*df
= ft
->priv
;
297 struct sockaddr_storage
*saddr
= &df
->saddr
;
299 char ipaddr
[INET6_ADDRSTRLEN
];
300 socklen_t sa_len
= saddr
->ss_family
== AF_INET
?
301 sizeof( struct sockaddr_in
) : sizeof( struct sockaddr_in6
);
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
,
319 ntohs( saddr
->ss_family
== AF_INET
?
320 ( ( struct sockaddr_in
*) saddr
)->sin_port
:
321 ( ( struct sockaddr_in6
*) saddr
)->sin6_port
),
324 ft
->status
= FT_STATUS_CONNECTING
;
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
);
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 );
351 if( cond
& B_EV_IO_READ
)
355 ASSERTSOCKOP( ret
= recv( fd
, ft
->buffer
, sizeof( ft
->buffer
), 0 ), "Receiving" );
358 return dcc_abort( df
, "Remote end closed connection" );
360 if( !ft
->write( df
->ft
, ft
->buffer
, ret
) )
363 df
->bytes_sent
+= ret
;
365 done
= df
->bytes_sent
>= ft
->file_size
;
367 if( ( ( df
->bytes_sent
- ft
->bytes_transferred
) > DCC_PACKET_SIZE
) ||
370 guint32 ack
= htonl( ft
->bytes_transferred
= df
->bytes_sent
);
373 ASSERTSOCKOP( ackret
= send( fd
, &ack
, 4, 0 ), "Sending DCC ACK" );
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
);
385 b_event_remove( df
->watch_out
);
387 if( df
->proto_finished
)
401 gboolean
dccs_recv_write_request( file_transfer_t
*ft
)
403 dcc_file_transfer_t
*df
= ft
->priv
;
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
);
413 gboolean
dccs_send_can_write( gpointer data
, gint fd
, b_input_condition cond
)
415 struct dcc_file_transfer
*df
= data
;
418 df
->ft
->write_request( df
->ft
);
426 gboolean
dccs_send_write( file_transfer_t
*file
, char *data
, unsigned int data_len
)
428 dcc_file_transfer_t
*df
= file
->priv
;
431 receivedchunks
++; receiveddata
+= data_len
;
434 return dcc_abort( df
, "BUG: write() called while watching" );
436 ASSERTSOCKOP( ret
= send( df
->fd
, data
, data_len
, 0 ), "Sending data" );
439 return dcc_abort( df
, "Remote end closed connection" );
441 /* TODO: this should really not be fatal */
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
);
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
;
467 closesocket( df
->fd
);
470 b_event_remove( df
->watch_in
);
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
);
481 g_free( file
->file_name
);
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
;
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
) );
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
;
510 dcc_file_transfer_t
*df
;
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
;
524 while( *host
&& isdigit( *host
) ) host
++; /* Just digits? */
527 struct in_addr ipaddr
= { .s_addr
= htonl( atoll( ctcp
[3] ) ) };
528 host
= inet_ntoa( ipaddr
);
531 /* Contains non-numbers, hopefully an IPV6 address */
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': "
547 gai_strerror( gret
), host
, port
);
551 df
= dcc_alloc_transfer( filename
, filesize
, ic
);
554 memcpy( &df
->saddr
, rp
->ai_addr
, rp
->ai_addrlen
);
558 irc
->file_transfers
= g_slist_prepend( irc
->file_transfers
, ft
);
563 imcb_log( ic
, "DCC: couldnt parse `DCC SEND' line" );