diff options
Diffstat (limited to 'protocols')
-rw-r--r-- | protocols/ft.h | 31 | ||||
-rw-r--r-- | protocols/jabber/jabber.h | 3 | ||||
-rw-r--r-- | protocols/jabber/s5bytestream.c | 137 | ||||
-rw-r--r-- | protocols/jabber/si.c | 23 |
4 files changed, 108 insertions, 86 deletions
diff --git a/protocols/ft.h b/protocols/ft.h index d41eb6c1..d35580d0 100644 --- a/protocols/ft.h +++ b/protocols/ft.h @@ -26,6 +26,13 @@ #ifndef _FT_H #define _FT_H +/* + * One buffer is needed for each transfer. The receiver stores a message + * in it and gives it to the sender. The sender will stall the receiver + * till the buffer has been sent out. + */ +#define FT_BUFFER_SIZE 2048 + typedef enum { FT_STATUS_LISTENING = 1, FT_STATUS_TRANSFERRING = 2, @@ -130,15 +137,24 @@ typedef struct file_transfer { void (*canceled) ( struct file_transfer *file, char *reason ); /* - * If set, called when the transfer queue is running empty and - * more data can be added. + * called by the sending side to indicate that it is writable. + * The callee should check if data is available and call the + * function(as seen below) if that is the case. */ - void (*out_of_data) ( struct file_transfer *file ); + gboolean (*write_request) ( struct file_transfer *file ); /* * When sending files, protocols register this function to receive data. + * This should only be called once after write_request is called. The caller + * should not read more data until write_request is called again. This technique + * avoids buffering. + */ + gboolean (*write) (struct file_transfer *file, char *buffer, unsigned int len ); + + /* The send buffer associated with this transfer. + * Since receivers always wait for a write_request call one is enough. */ - gboolean (*write) (struct file_transfer *file, char *buffer, int len ); + char buffer[FT_BUFFER_SIZE]; } file_transfer_t; @@ -153,12 +169,5 @@ file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *user_nick */ void imcb_file_canceled( file_transfer_t *file, char *reason ); -/* - * The given buffer is queued for transfer and MUST NOT be freed by the caller. - * When the method returns false the caller should not invoke this method again - * until out_of_data has been called. - */ -gboolean imcb_file_write( file_transfer_t *file, gpointer data, size_t data_size ); - gboolean imcb_file_recv_start( file_transfer_t *ft ); #endif diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index cb52d396..45082fee 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -145,7 +145,6 @@ struct jabber_transfer int accepted; size_t bytesread, byteswritten; - int receiver_overflow; int fd; struct sockaddr_storage saddr; }; @@ -208,7 +207,7 @@ void jabber_si_free_transfer( file_transfer_t *ft); /* s5bytestream.c */ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); gboolean jabber_bs_send_start( struct jabber_transfer *tf ); -gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, int len ); +gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int len ); /* message.c */ xt_status jabber_pkt_message( struct xt_node *node, gpointer data ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index e2f32bd0..de173d19 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -71,8 +71,6 @@ struct socks5_message if( (op) == -1 ) \ return jabber_bs_abort( bt , msg ": %s", strerror( errno ) ); -#define JABBER_BS_BUFSIZE 65536 - gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ); void jabber_bs_canceled( file_transfer_t *ft , char *reason ); void jabber_bs_free_transfer( file_transfer_t *ft); @@ -82,7 +80,7 @@ gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ); void jabber_bs_recv_answer_request( struct bs_transfer *bt ); gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ); -void jabber_bs_recv_out_of_data( file_transfer_t *ft ); +gboolean jabber_bs_recv_write_request( file_transfer_t *ft ); gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ); gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ); int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); @@ -108,7 +106,7 @@ void jabber_bs_free_transfer( file_transfer_t *ft) { g_free( bt->pseudoadr ); xt_free_node( bt->qnode ); g_free( bt ); -//iq_id + jabber_si_free_transfer( ft ); } @@ -325,7 +323,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con sock_make_nonblocking( fd ); - imcb_log( bt->tf->ic, "Transferring file %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, host, port ); + imcb_log( bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, host, port ); if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && ( errno != EINPROGRESS ) ) @@ -425,7 +423,6 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con jabber_bs_recv_answer_request( bt ); - // reset in answer_request bt->tf->watch_in = 0; return FALSE; } default: @@ -440,8 +437,10 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con /* * If the handshake failed we can try the next streamhost, if there is one. * An intelligent sender would probably specify himself as the first streamhost and - * a proxy as the second (Kopete is an example here). That way, a (potentially) - * slow proxy is only used if neccessary. + * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially) + * slow proxy is only used if neccessary. This of course also means, that the timeout + * per streamhost should be kept short. If one or two firewalled adresses are specified, + * they have to timeout first before a proxy is tried. */ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) { @@ -493,15 +492,15 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) struct jabber_transfer *tf = bt->tf; struct xt_node *reply; - imcb_log( tf->ic, "Transferring file %s: established SOCKS5 connection to %s:%s", + imcb_log( tf->ic, "File %s: established SOCKS5 connection to %s:%s", tf->ft->file_name, xt_find_attr( bt->shnode, "host" ), xt_find_attr( bt->shnode, "port" ) ); tf->ft->data = tf; tf->ft->started = time( NULL ); - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, tf ); - tf->ft->out_of_data = jabber_bs_recv_out_of_data; + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); + tf->ft->write_request = jabber_bs_recv_write_request; reply = xt_new_node( "streamhost-used", NULL, NULL ); xt_add_attr( reply, "jid", xt_find_attr( bt->shnode, "jid" ) ); @@ -518,90 +517,107 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) xt_free_node( reply ); } -/* Reads till it is unscheduled or the receiver signifies an overflow. */ +/* + * This function is called from write_request directly. If no data is available, it will install itself + * as a watcher for input on fd and once that happens, deliver the data and unschedule itself again. + */ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) { int ret; - struct jabber_transfer *tf = data; - struct bs_transfer *bt = tf->streamhandle; - char *buffer = g_malloc( JABBER_BS_BUFSIZE ); + struct bs_transfer *bt = data; + struct jabber_transfer *tf = bt->tf; - if (tf->receiver_overflow) + if( fd != 0 ) /* called via event thread */ + { + tf->watch_in = 0; + ASSERTSOCKOP( ret = recv( fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) , "Receiving" ); + } + else { - if( tf->watch_in ) + /* called directly. There might not be any data available. */ + if( ( ( ret = recv( tf->fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) ) == -1 ) && + ( errno != EAGAIN ) ) + return jabber_bs_abort( bt, "Receiving: %s", strerror( errno ) ); + + if( ( ret == -1 ) && ( errno == EAGAIN ) ) { - /* should never happen, BUG */ - imcb_file_canceled( tf->ft, "Bug in jabber file transfer code: read while overflow is true. Please report" ); + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); return FALSE; } } - ASSERTSOCKOP( ret = recv( fd, buffer, JABBER_BS_BUFSIZE, 0 ) , "Receiving" ); - - /* that should be all */ + /* shouldn't happen since we know the file size */ if( ret == 0 ) - return FALSE; + return jabber_bs_abort( bt, "Remote end closed connection" ); tf->bytesread += ret; - buffer = g_realloc( buffer, ret ); + tf->ft->write( tf->ft, tf->ft->buffer, ret ); - if ( ( tf->receiver_overflow = imcb_file_write( tf->ft, buffer, ret ) ) ) - { - /* wait for imcb to run out of data */ - tf->watch_in = 0; - return FALSE; - } - - return TRUE; + return FALSE; } -/* imcb callback that is invoked when it runs out of data. - * We reschedule jabber_bs_read here if neccessary. */ -void jabber_bs_recv_out_of_data( file_transfer_t *ft ) +/* + * imc callback that is invoked when it is ready to receive some data. + */ +gboolean jabber_bs_recv_write_request( file_transfer_t *ft ) { struct jabber_transfer *tf = ft->data; - tf->receiver_overflow = FALSE; + if( tf->watch_in ) + { + imcb_file_canceled( ft, "BUG in jabber file transfer: write_request called when already watching for input" ); + return FALSE; + } + + jabber_bs_recv_read( tf->streamhandle, 0 , 0 ); - if ( !tf->watch_in ) - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, tf ); + return TRUE; } -/* signal ood and be done */ +/* + * Issues a write_request to imc. + * */ gboolean jabber_bs_send_can_write( gpointer data, gint fd, b_input_condition cond ) { struct bs_transfer *bt = data; - bt->tf->ft->out_of_data( bt->tf->ft ); - bt->tf->watch_out = 0; + + bt->tf->ft->write_request( bt->tf->ft ); + return FALSE; } -/* try to send the stuff. If you can't return false and wait for writable */ -gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, int len ) +/* + * This should only be called if we can write, so just do it. + * Add a write watch so we can write more during the next cycle (if possible). + */ +gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int len ) { struct jabber_transfer *tf = ft->data; struct bs_transfer *bt = tf->streamhandle; int ret; - if ( ( ( ret = send( tf->fd, buffer, len, 0 ) ) == -1 ) && - ( errno != EAGAIN ) ) - return jabber_bs_abort( bt, "send failed on socket with: %s", strerror( errno ) ); + if( tf->watch_out ) + return jabber_bs_abort( bt, "BUG: write() called while watching " ); - if( ret == 0 ) - return jabber_bs_abort( bt, "Remote end closed connection" ); + ASSERTSOCKOP( ret = send( tf->fd, buffer, len, 0 ), "Sending" ); + + tf->byteswritten += ret; - if( ret == -1 ) - { - bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); - return FALSE; - } + /* TODO: this should really not be fatal */ + if( ret < len ) + return jabber_bs_abort( bt, "send() sent %d instead of %d (send buffer too big!)", ret, len ); + + bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); return TRUE; } +/* + * Handles the reply by the receiver containing the used streamhost. + */ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct jabber_transfer *tf = NULL; struct jabber_data *jd = ic->proto_data; @@ -650,12 +666,11 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt if( bt->phase == BS_PHASE_REPLY ) { + /* handshake went through, let's start transferring */ tf->ft->started = time( NULL ); - tf->ft->out_of_data( tf->ft ); + tf->ft->write_request( tf->ft ); } - //bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_write, tf ); - return XT_HANDLED; } @@ -680,8 +695,6 @@ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) bt = g_new0( struct bs_transfer, 1 ); bt->tf = tf; - //bt->qnode = xt_dup( qnode ); - //bt->shnode = bt->qnode->children; bt->phase = BS_PHASE_CONNECT; bt->pseudoadr = g_strdup( hash_hex ); tf->streamhandle = bt; @@ -714,8 +727,6 @@ gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *p iq = jabber_make_packet( "iq", "set", tf->tgt_jid, query ); xt_add_attr( iq, "from", tf->ini_jid ); - //xt_free_node( query ); - jabber_cache_add( tf->ic, iq, jabber_bs_send_handle_reply ); if( !jabber_write_packet( tf->ic, iq ) ) @@ -884,11 +895,13 @@ gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_REPLY; - /* don't start sending till the streamhost-used message comes in */ + imcb_log( tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name ); + if( tf->accepted ) { + /* streamhost-used message came already in(possible?), let's start sending */ tf->ft->started = time( NULL ); - tf->ft->out_of_data( tf->ft ); + tf->ft->write_request( tf->ft ); } tf->watch_in = 0; diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 598cbd03..ffde6418 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -83,7 +83,7 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, struct jabber_transfer *tf; struct jabber_data *jd = ic->proto_data; - imcb_log( ic, "Incoming file from %s : %s %zd bytes", ic->irc->nick, ft->file_name, ft->file_size ); + imcb_log( ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who ); tf = g_new0( struct jabber_transfer, 1 ); @@ -223,7 +223,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st } /* - * imcb called the accept callback which probably means that the user accepted this file transfer. + * imc called the accept callback which probably means that the user accepted this file transfer. * We send our response to the initiator. * In the next step, the initiator will send us a request for the given stream type. * (currently that can only be a SOCKS5 bytestream) @@ -274,11 +274,10 @@ void jabber_si_answer_request( file_transfer_t *ft ) { static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) { struct xt_node *c, *d; - char *ini_jid, *tgt_jid; + char *ini_jid, *tgt_jid, *iq_id; GSList *tflist; struct jabber_transfer *tf=NULL; struct jabber_data *jd = ic->proto_data; - char *sid; if( !( tgt_jid = xt_find_attr( node, "from" ) ) || !( ini_jid = xt_find_attr( node, "to" ) ) ) @@ -287,11 +286,10 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n return XT_HANDLED; } - imcb_log( ic, "GOT RESPONSE TO FILE" ); /* All this means we expect something like this: ( I think ) - * <iq from=... to=...> + * <iq from=... to=... id=...> * <si xmlns=si> - * <file xmlns=ft/> + * [ <file xmlns=ft/> ] <-- not neccessary * <feature xmlns=feature> * <x xmlns=xdata type=submit> * <field var=stream-method> @@ -299,11 +297,11 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n */ if( !( tgt_jid = xt_find_attr( node, "from" ) ) || !( ini_jid = xt_find_attr( node, "to" ) ) || + !( iq_id = xt_find_attr( node, "id" ) ) || !( c = xt_find_node( node->children, "si" ) ) || !( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) || - !( sid = xt_find_attr( c, "id" ) )|| - !( d = xt_find_node( c->children, "file" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || +/* !( d = xt_find_node( c->children, "file" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || */ !( d = xt_find_node( c->children, "feature" ) ) || !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || !( d = xt_find_node( d->children, "x" ) ) || @@ -330,7 +328,7 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) { struct jabber_transfer *tft = tflist->data; - if( ( strcmp( tft->sid, sid ) == 0 ) ) + if( ( strcmp( tft->iq_id, iq_id ) == 0 ) ) { tf = tft; break; @@ -346,6 +344,8 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n tf->ini_jid = g_strdup( ini_jid ); tf->tgt_jid = g_strdup( tgt_jid ); + imcb_log( ic, "File %s: %s accepted the transfer!", tf->ft->file_name, tgt_jid ); + jabber_bs_send_start( tf ); return XT_HANDLED; @@ -422,6 +422,7 @@ int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_tr /* and we are there... */ node = jabber_make_packet( "iq", "set", bud ? bud->full_jid : who, sinode ); jabber_cache_add( ic, node, jabber_si_handle_response ); + tf->iq_id = g_strdup( xt_find_attr( node, "id" ) ); return jabber_write_packet( ic, node ); } |