From 2c2df7dd91930345a9b22a8bb61327d1dcc7e3d5 Mon Sep 17 00:00:00 2001 From: ulim Date: Wed, 28 Nov 2007 22:07:30 +0100 Subject: Initial import of jabber file receive and DCC send support. This introduces only a few changes to bitlbees code, mainly the addition of the "transfers" command. This is known to work with Kopete, Psi, and Pidgin (formerly known as gaim). At least with Pidgin also over a proxy. DCC has only been tested with irssi. IPV6 is untested but should work. Currently, only receiving via SOCKS5BYTESREAMS is implemented. I'm not sure if the alternative(in-band bytestreams IBB) is worth implementing since I didn't see a client yet that can do it. Additionally, it is probably very slow and needs support by the server as well. --- protocols/jabber/Makefile | 2 +- protocols/jabber/iq.c | 28 +- protocols/jabber/jabber.h | 58 +++- protocols/jabber/jabber_util.c | 6 +- protocols/jabber/si.c | 246 +++++++++++++++++ protocols/jabber/stream.c | 593 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 911 insertions(+), 22 deletions(-) create mode 100644 protocols/jabber/si.c create mode 100644 protocols/jabber/stream.c (limited to 'protocols/jabber') diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index e042f812..47c832ae 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o +objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o si.o stream.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 595718fb..df0102b8 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -103,6 +103,9 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) XMLNS_TIME, XMLNS_CHATSTATES, XMLNS_MUC, + XMLNS_SI, + XMLNS_BYTESTREAMS, + XMLNS_FILETRANSFER, NULL }; const char **f; @@ -122,24 +125,26 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) else { xt_free_node( reply ); - reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" ); + reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL ); pack = 0; } } else if( strcmp( type, "set" ) == 0 ) { - if( !( c = xt_find_node( node->children, "query" ) ) || + if( ( c = xt_find_node( node->children, "si" ) ) && + ( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) ) + { + return jabber_si_handle_request( ic, node, c ); + } else if( !( c = xt_find_node( node->children, "query" ) ) || !( s = xt_find_attr( c, "xmlns" ) ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-%s packet", type ); return XT_HANDLED; - } - + } else if( strcmp( s, XMLNS_ROSTER ) == 0 ) + { /* This is a roster push. XMPP servers send this when someone was added to (or removed from) the buddy list. AFAIK they're sent even if we added this buddy in our own session. */ - if( strcmp( s, XMLNS_ROSTER ) == 0 ) - { int bare_len = strlen( ic->acc->user ); if( ( s = xt_find_attr( node, "from" ) ) == NULL || @@ -156,14 +161,17 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) imcb_log( ic, "WARNING: %s tried to fake a roster push!", s ? s : "(unknown)" ); xt_free_node( reply ); - reply = jabber_make_error_packet( node, "not-allowed", "cancel" ); + reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL ); pack = 0; } - } - else + } else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 ) + { + /* Bytestream Request (stage 2 of file transfer) */ + return jabber_bs_request( ic, node, c ); + } else { xt_free_node( reply ); - reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel" ); + reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL ); pack = 0; } } diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index fc9d2fc4..0cb2b733 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -80,6 +80,8 @@ struct jabber_data char *cached_id_prefix; GHashTable *node_cache; GHashTable *buddies; + + GSList *filetransfers; }; struct jabber_away_state @@ -123,6 +125,30 @@ struct jabber_chat struct jabber_buddy *me; }; +struct jabber_transfer +{ + /* bitlbee's handle for this transfer */ + file_transfer_t *ft; + + /* the stream's private handle */ + gpointer streamhandle; + + struct im_connection *ic; + + int watch_in; + int watch_out; + + char *ini_jid; + char *tgt_jid; + char *iq_id; + char *sid; + int accepted; + + size_t bytesread, byteswritten; + int receiver_overflow; + int fd; +}; + #define JABBER_XMLCONSOLE_HANDLE "xmlconsole" #define JABBER_PORT_DEFAULT "5222" @@ -148,15 +174,21 @@ struct jabber_chat #define XMLNS_ROSTER "jabber:iq:roster" /* Some supported extensions/legacy stuff */ -#define XMLNS_AUTH "jabber:iq:auth" /* XEP-0078 */ -#define XMLNS_VERSION "jabber:iq:version" /* XEP-0092 */ -#define XMLNS_TIME "jabber:iq:time" /* XEP-0090 */ -#define XMLNS_VCARD "vcard-temp" /* XEP-0054 */ -#define XMLNS_DELAY "jabber:x:delay" /* XEP-0091 */ -#define XMLNS_CHATSTATES "http://jabber.org/protocol/chatstates" /* 0085 */ -#define XMLNS_DISCOVER "http://jabber.org/protocol/disco#info" /* 0030 */ -#define XMLNS_MUC "http://jabber.org/protocol/muc" /* XEP-0045 */ -#define XMLNS_MUC_USER "http://jabber.org/protocol/muc#user"/* XEP-0045 */ +#define XMLNS_AUTH "jabber:iq:auth" /* XEP-0078 */ +#define XMLNS_VERSION "jabber:iq:version" /* XEP-0092 */ +#define XMLNS_TIME "jabber:iq:time" /* XEP-0090 */ +#define XMLNS_VCARD "vcard-temp" /* XEP-0054 */ +#define XMLNS_DELAY "jabber:x:delay" /* XEP-0091 */ +#define XMLNS_XDATA "jabber:x:data" /* XEP-0004 */ +#define XMLNS_CHATSTATES "http://jabber.org/protocol/chatstates" /* XEP-0085 */ +#define XMLNS_DISCOVER "http://jabber.org/protocol/disco#info" /* XEP-0030 */ +#define XMLNS_MUC "http://jabber.org/protocol/muc" /* XEP-0045 */ +#define XMLNS_MUC_USER "http://jabber.org/protocol/muc#user" /* XEP-0045 */ +#define XMLNS_FEATURE "http://jabber.org/protocol/feature-neg" /* XEP-0020 */ +#define XMLNS_SI "http://jabber.org/protocol/si" /* XEP-0095 */ +#define XMLNS_FILETRANSFER "http://jabber.org/protocol/si/profile/file-transfer" /* XEP-0096 */ +#define XMLNS_BYTESTREAMS "http://jabber.org/protocol/bytestreams" /* XEP-0065 */ +#define XMLNS_IBB "http://jabber.org/protocol/ibb" /* XEP-0047 */ /* iq.c */ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ); @@ -167,6 +199,12 @@ int jabber_get_vcard( struct im_connection *ic, char *bare_jid ); int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ); int jabber_remove_from_roster( struct im_connection *ic, char *handle ); +/* si.c */ +int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode); + +/* stream.c */ +int jabber_bs_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); + /* message.c */ xt_status jabber_pkt_message( struct xt_node *node, gpointer data ); @@ -179,7 +217,7 @@ int presence_send_request( struct im_connection *ic, char *handle, char *request char *set_eval_priority( set_t *set, char *value ); char *set_eval_tls( set_t *set, char *value ); struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children ); -struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type ); +struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type, char *err_code ); void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func ); struct xt_node *jabber_cache_get( struct im_connection *ic, char *id ); void jabber_cache_entry_free( gpointer entry ); diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 43b91fe3..0c5b813e 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -96,7 +96,7 @@ struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_ return node; } -struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type ) +struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type, char *err_code ) { struct xt_node *node, *c; char *to; @@ -109,6 +109,10 @@ struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, c = xt_new_node( "error", NULL, c ); xt_add_attr( c, "type", err_type ); + /* Add the error code, if present */ + if (err_code) + xt_add_attr( c, "code", err_code ); + /* To make the actual error packet, we copy the original packet and add our /type="error" tag. Including the original packet is recommended, so let's just do it. */ diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c new file mode 100644 index 00000000..d16f723a --- /dev/null +++ b/protocols/jabber/si.c @@ -0,0 +1,246 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Jabber module - SI packets * +* * +* Copyright 2007 Uli Meis * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "jabber.h" +#include "sha1.h" + +void jabber_si_answer_request( file_transfer_t *ft ); + +/* imcb callback */ +void jabber_si_free_transfer( file_transfer_t *ft) +{ + struct jabber_transfer *tf = ft->data; + struct jabber_data *jd = tf->ic->proto_data; + + if ( tf->watch_in ) + b_event_remove( tf->watch_in ); + + jd->filetransfers = g_slist_remove( jd->filetransfers, tf ); + + if( tf->fd ) + { + close( tf->fd ); + tf->fd = 0; + } + + g_free( tf->ini_jid ); + g_free( tf->tgt_jid ); + g_free( tf->iq_id ); + g_free( tf->sid ); +} + +/* imcb callback */ +void jabber_si_finished( file_transfer_t *ft ) +{ + struct jabber_transfer *tf = ft->data; + + imcb_log( tf->ic, "File %s transferred successfully!" , ft->file_name ); +} + +/* imcb callback */ +void jabber_si_canceled( file_transfer_t *ft, char *reason ) +{ + struct jabber_transfer *tf = ft->data; + struct xt_node *reply, *iqnode; + + if( tf->accepted ) + return; + + iqnode = jabber_make_packet( "iq", "error", tf->ini_jid, NULL ); + xt_add_attr( iqnode, "id", tf->iq_id ); + reply = jabber_make_error_packet( iqnode, "forbidden", "cancel", "403" ); + xt_free_node( iqnode ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_log( tf->ic, "WARNING: Error generating reply to file transfer request" ); + xt_free_node( reply ); + +} + +/* + * First function that gets called when a file transfer request comes in. + * A lot to parse. + * + * We choose a stream type from the options given by the initiator. + * Then we wait for imcb to call the accept or cancel callbacks. + */ +int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode) +{ + struct xt_node *c, *d, *reply; + char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid; + struct jabber_buddy *bud; + int requestok = FALSE; + char *name; + size_t size; + struct jabber_transfer *tf; + struct jabber_data *jd = ic->proto_data; + file_transfer_t *ft; + + /* All this means we expect something like this: ( I think ) + * + * + * + * + * + * + * + */ + if( !( ini_jid = xt_find_attr( node, "from" ) ) || + !( tgt_jid = xt_find_attr( node, "to" ) ) || + !( iq_id = xt_find_attr( node, "id" ) ) || + !( sid = xt_find_attr( sinode, "id" ) ) || + !( strcmp( xt_find_attr( sinode, "profile" ), XMLNS_FILETRANSFER ) == 0 ) || + !( d = xt_find_node( sinode->children, "file" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || + !( name = xt_find_attr( d, "name" ) ) || + !( size = (size_t) atoll( xt_find_attr( d, "size" ) ) ) || + !( d = xt_find_node( sinode->children, "feature" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || + !( d = xt_find_node( d->children, "x" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_XDATA ) == 0 ) || + !( strcmp( xt_find_attr( d, "type" ), "form" ) == 0 ) || + !( d = xt_find_node( d->children, "field" ) ) || + !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) ) + { + imcb_log( ic, "WARNING: Received incomplete Stream Initiation request" ); + } else + { + /* Check if we support one of the options */ + + c = d->children; + while( ( c = xt_find_node( c, "option" ) ) ) + if( ( d = xt_find_node( c->children, "value" ) ) && + ( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) + { + requestok = TRUE; + break; + } + } + + if ( requestok ) + { + /* Figure out who the transfer should come frome... */ + + if( ( s = strchr( ini_jid, '/' ) ) ) + { + if( ( bud = jabber_buddy_by_jid( ic, ini_jid, GET_BUDDY_EXACT ) ) ) + { + bud->last_act = time( NULL ); + ext_jid = bud->ext_jid ? : bud->bare_jid; + } + else + *s = 0; /* We need to generate a bare JID now. */ + } + + if( !( ft = imcb_file_send_start( ic, ext_jid, name, size ) ) ) + { + imcb_log( ic, "WARNING: Error handling transfer request from %s", ini_jid); + requestok = FALSE; + } + + *s = '/'; + } else + imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid); + + if ( !requestok ) + { + reply = jabber_make_error_packet( node, "item-not-found", "cancel", NULL ); + if (!jabber_write_packet( ic, reply )) + imcb_log( ic, "WARNING: Error generating reply to file transfer request" ); + xt_free_node( reply ); + return XT_HANDLED; + } + + /* Request is fine. */ + + imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", xt_find_attr( node, "from" ), name, size/1024 ); + + imcb_log( ic, "Accept the DCC transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); + + tf = g_new0( struct jabber_transfer, 1 ); + + tf->ini_jid = g_strdup( ini_jid ); + tf->tgt_jid = g_strdup( tgt_jid ); + tf->iq_id = g_strdup( iq_id ); + tf->sid = g_strdup( sid ); + tf->ic = ic; + tf->ft = ft; + tf->ft->data = tf; + tf->ft->accept = jabber_si_answer_request; + tf->ft->free = jabber_si_free_transfer; + tf->ft->finished = jabber_si_finished; + tf->ft->canceled = jabber_si_canceled; + + jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); + + return XT_HANDLED; +} + +/* + * imcb 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) + */ +void jabber_si_answer_request( file_transfer_t *ft ) { + struct jabber_transfer *tf = ft->data; + struct xt_node *node, *sinode, *reply; + + /* generate response, start with the SI tag */ + sinode = xt_new_node( "si", NULL, NULL ); + xt_add_attr( sinode, "xmlns", XMLNS_SI ); + xt_add_attr( sinode, "profile", XMLNS_FILETRANSFER ); + xt_add_attr( sinode, "id", tf->sid ); + + /* now the file tag */ + node = xt_new_node( "file", NULL, NULL ); + xt_add_attr( node, "xmlns", XMLNS_FILETRANSFER ); + + xt_add_child( sinode, node ); + + /* and finally the feature tag */ + node = xt_new_node( "field", NULL, NULL ); + xt_add_attr( node, "var", "stream-method" ); + xt_add_attr( node, "type", "list-single" ); + + /* Currently all we can do. One could also implement in-band (IBB) */ + xt_add_child( node, xt_new_node( "value", XMLNS_BYTESTREAMS, NULL ) ); + + node = xt_new_node( "x", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_XDATA ); + xt_add_attr( node, "type", "submit" ); + + node = xt_new_node( "feature", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_FEATURE ); + + xt_add_child( sinode, node ); + + reply = jabber_make_packet( "iq", "result", tf->ini_jid, sinode ); + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_log( tf->ic, "WARNING: Error generating reply to file transfer request" ); + else + tf->accepted = TRUE; + xt_free_node( reply ); +} diff --git a/protocols/jabber/stream.c b/protocols/jabber/stream.c new file mode 100644 index 00000000..c88a72fd --- /dev/null +++ b/protocols/jabber/stream.c @@ -0,0 +1,593 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Jabber module - stream handling * +* * +* Copyright 2007 Uli Meis * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "jabber.h" +#include "sha1.h" +#include + +/* Some structs for the SOCKS5 handshake */ + +struct bs_handshake_data { + + struct jabber_transfer *tf; + + /* element and elements */ + struct xt_node *qnode, *shnode; + + enum + { + BS_PHASE_CONNECT, + BS_PHASE_CONNECTED, + BS_PHASE_REQUEST, + BS_PHASE_REPLY, + BS_PHASE_REPLY_HAVE_LEN + } phase; + + /* SHA1( SID + Initiator JID + Target JID) */ + char *pseudoadr; + + void (*parentfree) ( file_transfer_t *ft ); + + gint connect_timeout; +}; + +struct socks5_hdr +{ + unsigned char ver; + union + { + unsigned char cmd; + unsigned char rep; + } cmdrep; + unsigned char rsv; + unsigned char atyp; +}; + +struct socks5_message +{ + struct socks5_hdr hdr; + unsigned char addrlen; + unsigned char address[64]; +}; + +/* connect() timeout in seconds. */ +#define JABBER_BS_CONTIMEOUT 15 + +/* shouldn't matter if it's mostly too much, kernel's smart about that + * and will only reserve some address space */ +#define JABBER_BS_BUFSIZE 65536 + +gboolean jabber_bs_handshake( gpointer data, gint fd, b_input_condition cond ); + +gboolean jabber_bs_handshake_abort( struct bs_handshake_data *bhd, char *format, ... ); + +void jabber_bs_answer_request( struct bs_handshake_data *bhd ); + +gboolean jabber_bs_read( gpointer data, gint fd, b_input_condition cond ); + +void jabber_bs_out_of_data( file_transfer_t *ft ); + +void jabber_bs_canceled( file_transfer_t *ft , char *reason ); + + +void jabber_bs_free_transfer( file_transfer_t *ft) { + struct jabber_transfer *tf = ft->data; + struct bs_handshake_data *bhd = tf->streamhandle; + void (*parentfree) ( file_transfer_t *ft ); + + parentfree = bhd->parentfree; + + if ( tf->watch_in ) + b_event_remove( tf->watch_in ); + + if( tf->watch_out ) + b_event_remove( tf->watch_out ); + + g_free( bhd->pseudoadr ); + xt_free_node( bhd->qnode ); + g_free( bhd ); + + parentfree( ft ); +} + +/* + * Parses an incoming bytestream request and calls jabber_bs_handshake on success. + */ +int jabber_bs_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode) +{ + char *sid, *ini_jid, *tgt_jid, *mode, *iq_id; + struct jabber_data *jd = ic->proto_data; + struct jabber_transfer *tf = NULL; + GSList *tflist; + struct bs_handshake_data *bhd; + + sha1_state_t sha; + char hash_hex[41]; + unsigned char hash[20]; + int i; + + if( !(iq_id = xt_find_attr( node, "id" ) ) || + !(ini_jid = xt_find_attr( node, "from" ) ) || + !(tgt_jid = xt_find_attr( node, "to" ) ) || + !(sid = xt_find_attr( qnode, "sid" ) ) ) + { + imcb_log( ic, "WARNING: Received incomplete SI bytestream request"); + return XT_HANDLED; + } + + if( ( mode = xt_find_attr( qnode, "mode" ) ) && + ( strcmp( mode, "tcp" ) != 0 ) ) + { + imcb_log( ic, "WARNING: Received SI Request for unsupported bytestream mode %s", xt_find_attr( qnode, "mode" ) ); + return XT_HANDLED; + } + + /* Let's see if we can find out what this bytestream should be for... */ + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) && + ( strcmp( tft->ini_jid, ini_jid ) == 0 ) && + ( strcmp( tft->tgt_jid, tgt_jid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if (!tf) + { + imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); + return XT_HANDLED; + } + + /* iq_id and canceled can be reused since SI is done */ + g_free( tf->iq_id ); + tf->iq_id = g_strdup( iq_id ); + + tf->ft->canceled = jabber_bs_canceled; + + /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ + sha1_init( &sha ); + sha1_append( &sha, (unsigned char*) sid, strlen( sid ) ); + sha1_append( &sha, (unsigned char*) ini_jid, strlen( ini_jid ) ); + sha1_append( &sha, (unsigned char*) tgt_jid, strlen( tgt_jid ) ); + sha1_finish( &sha, hash ); + + for( i = 0; i < 20; i ++ ) + sprintf( hash_hex + i * 2, "%02x", hash[i] ); + + bhd = g_new0( struct bs_handshake_data, 1 ); + bhd->tf = tf; + bhd->qnode = xt_dup( qnode ); + bhd->shnode = bhd->qnode->children; + bhd->phase = BS_PHASE_CONNECT; + bhd->pseudoadr = g_strdup( hash_hex ); + tf->streamhandle = bhd; + bhd->parentfree = tf->ft->free; + tf->ft->free = jabber_bs_free_transfer; + + jabber_bs_handshake( bhd, 0, 0 ); + + return XT_HANDLED; +} + +/* + * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect(). + */ +gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ) +{ + struct bs_handshake_data *bhd = data; + + bhd->connect_timeout = 0; + + jabber_bs_handshake_abort( bhd, "no connection after %d seconds", JABBER_BS_CONTIMEOUT ); + + return FALSE; +} + +/* + * This is what a protocol handshake can look like in cooperative multitasking :) + * Might be confusing at first because it's called from different places and is recursing. + * (places being the event thread, bs_request, bs_handshake_abort, and itself) + * + * All in all, it turned out quite nice :) + */ +gboolean jabber_bs_handshake( gpointer data, gint fd, b_input_condition cond ) +{ + +/* very useful */ +#define ASSERTSOCKOP(op, msg) \ + if( (op) == -1 ) \ + return jabber_bs_handshake_abort( bhd , msg ": %s", strerror( errno ) ); + + struct bs_handshake_data *bhd = data; + struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; + short revents; + + if ( bhd->connect_timeout ) + { + b_event_remove( bhd->connect_timeout ); + bhd->connect_timeout = 0; + } + + + /* we need the real io condition */ + if ( poll( &pfd, 1, 0 ) == -1 ) + { + imcb_log( bhd->tf->ic, "poll() failed, weird!" ); + revents = 0; + }; + + revents = pfd.revents; + + if( revents & POLLERR ) + { + int sockerror; + socklen_t errlen = sizeof( sockerror ); + + if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) + return jabber_bs_handshake_abort( bhd, "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)" ); + + if ( bhd->phase == BS_PHASE_CONNECTED ) + return jabber_bs_handshake_abort( bhd, "connect() failed: %s", strerror( sockerror ) ); + + return jabber_bs_handshake_abort( bhd, "Socket error during SOCKS5 handshake(weird!): %s", strerror( sockerror ) ); + } + + if( revents & POLLHUP ) + return jabber_bs_handshake_abort( bhd, "Remote end closed connection" ); + + + switch( bhd->phase ) + { + case BS_PHASE_CONNECT: + { + struct xt_node *c; + char *host, *port; + struct addrinfo hints, *rp; + + if( ( c = bhd->shnode = xt_find_node( bhd->shnode, "streamhost" ) ) && + ( port = xt_find_attr( c, "port" ) ) && + ( host = xt_find_attr( c, "host" ) ) && + xt_find_attr( c, "jid" ) ) + { + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + + if ( getaddrinfo( host, port, &hints, &rp ) != 0 ) + return jabber_bs_handshake_abort( bhd, "getaddrinfo() failed: %s", strerror( errno ) ); + + ASSERTSOCKOP( bhd->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); + + sock_make_nonblocking( fd ); + + imcb_log( bhd->tf->ic, "Transferring file %s: Connecting to streamhost %s:%s", bhd->tf->ft->file_name, host, port ); + + if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && + ( errno != EINPROGRESS ) ) + return jabber_bs_handshake_abort( bhd , "connect() failed: %s", strerror( errno ) ); + + freeaddrinfo( rp ); + + bhd->phase = BS_PHASE_CONNECTED; + + bhd->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_handshake, bhd ); + + /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ + bhd->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bhd ); + + bhd->tf->watch_in = 0; + return FALSE; + } else + return jabber_bs_handshake_abort( bhd, c ? "incomplete streamhost entry: host=%s port=%s jid=%s" : NULL, + host, port, xt_find_attr( c, "jid" ) ); + } + case BS_PHASE_CONNECTED: + { + struct { + unsigned char ver; + unsigned char nmethods; + unsigned char method; + } socks5_hello = { + .ver = 5, + .nmethods = 1, + .method = 0x00 /* no auth */ + /* one could also implement username/password. If you know + * a jabber client or proxy that actually does it, tell me. + */ + }; + + ASSERTSOCKOP( send( fd, &socks5_hello, sizeof( socks5_hello ) , 0 ), "Sending auth request" ); + + bhd->phase = BS_PHASE_REQUEST; + + bhd->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_handshake, bhd ); + + bhd->tf->watch_out = 0; + return FALSE; + } + case BS_PHASE_REQUEST: + { + struct socks5_message socks5_connect = + { + .hdr = + { + .ver = 5, + .cmdrep.cmd = 0x01, + .rsv = 0, + .atyp = 0x03 + }, + .addrlen = strlen( bhd->pseudoadr ) + }; + int ret; + char buf[2]; + + /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */ + ASSERTSOCKOP( ret = recv( fd, buf, 2, 0 ) , "Receiving auth reply" ); + + if( !( ret == 2 ) || + !( buf[0] == 5 ) || + !( buf[1] == 0 ) ) + return jabber_bs_handshake_abort( bhd, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)", + ret, buf[0], buf[1] ); + + /* copy hash into connect message */ + memcpy( socks5_connect.address, bhd->pseudoadr, socks5_connect.addrlen ); + + /* after the address comes the port, which is always 0 */ + memset( socks5_connect.address + socks5_connect.addrlen, 0, sizeof( in_port_t ) ); + + ASSERTSOCKOP( send( fd, &socks5_connect, sizeof( struct socks5_hdr ) + 1 + socks5_connect.addrlen + sizeof( in_port_t ), 0 ) , "Sending SOCKS5 Connect" ); + + bhd->phase = BS_PHASE_REPLY; + + return TRUE; + } + case BS_PHASE_REPLY: + case BS_PHASE_REPLY_HAVE_LEN: + { + /* we have to wait till we have the address length, then we know how much data is left + * (not that we'd actually care about that data, but we need to eat it all up anyway) + */ + struct socks5_message socks5_reply; + int ret; + int expectedbytes = + sizeof( struct socks5_hdr ) + 1 + + ( bhd->phase == BS_PHASE_REPLY_HAVE_LEN ? socks5_reply.addrlen + sizeof( in_port_t ) : 0 ); + + /* notice the peek, we're doing this till enough is there */ + ASSERTSOCKOP( ret = recv( fd, &socks5_reply, expectedbytes, MSG_PEEK ) , "Peeking for SOCKS5 CONNECT reply" ); + + if ( ret == 0 ) + return jabber_bs_handshake_abort( bhd , "peer has shutdown connection" ); + + /* come again */ + if ( ret < expectedbytes ) + return TRUE; + + if ( bhd->phase == BS_PHASE_REPLY ) + { + if( !( socks5_reply.hdr.ver == 5 ) || + !( socks5_reply.hdr.cmdrep.rep == 0 ) || + !( socks5_reply.hdr.atyp == 3 ) || + !( socks5_reply.addrlen <= 62 ) ) /* should also be 40, but who cares as long as all fits in the buffer... */ + return jabber_bs_handshake_abort( bhd, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", + socks5_reply.hdr.ver, + socks5_reply.hdr.cmdrep.rep, + socks5_reply.hdr.atyp, + socks5_reply.addrlen); + + /* and again for the rest */ + bhd->phase = BS_PHASE_REPLY_HAVE_LEN; + + /* since it's very likely that the rest is there as well, + * let's not wait for the event loop to call us again */ + return jabber_bs_handshake( bhd , fd, 0 ); + } + + /* got it all, remove it from the queue */ + ASSERTSOCKOP( ret = recv( fd, &socks5_reply, expectedbytes, 0 ) , "Dequeueing MSG_PEEK'ed data after SOCKS5 CONNECT" ); + + /* this shouldn't happen */ + if ( ret < expectedbytes ) + return jabber_bs_handshake_abort( bhd, "internal error, couldn't dequeue MSG_PEEK'ed data after SOCKS5 CONNECT" ); + + /* we're actually done now... */ + + jabber_bs_answer_request( bhd ); + + bhd->tf->watch_in = 0; + return FALSE; + } + default: + /* BUG */ + imcb_log( bhd->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + + bhd->tf->watch_in = 0; + return FALSE; + } +#undef ASSERTSOCKOP +#undef JABBER_BS_ERR_CONDS +} + +/* + * 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. + */ +gboolean jabber_bs_handshake_abort( struct bs_handshake_data *bhd, char *format, ... ) +{ + struct jabber_transfer *tf = bhd->tf; + struct xt_node *reply, *iqnode; + + if( bhd->shnode ) + { + if( format ) { + va_list params; + va_start( params, format ); + char error[128]; + + if( vsnprintf( error, 128, format, params ) < 0 ) + sprintf( error, "internal error parsing error string (BUG)" ); + va_end( params ); + + imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", + tf->ft->file_name, + xt_find_attr( bhd->shnode, "host" ), + xt_find_attr( bhd->shnode, "port" ), + error ); + } + + /* Alright, this streamhost failed, let's try the next... */ + bhd->phase = BS_PHASE_CONNECT; + bhd->shnode = bhd->shnode->next; + + /* the if is not neccessary but saves us one recursion */ + if( bhd->shnode ) + return jabber_bs_handshake( bhd, 0, 0 ); + } + + /* out of stream hosts */ + + iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); + reply = jabber_make_error_packet( iqnode, "item-not-found", "cancel" , "404" ); + xt_free_node( iqnode ); + + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); + xt_free_node( reply ); + + imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); + + bhd->tf->watch_in = 0; + return FALSE; +} + +/* + * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose. + * If he is the streamhost himself, he might already know that. However, if it's a proxy, + * the initiator will have to make a connection himself. + */ +void jabber_bs_answer_request( struct bs_handshake_data *bhd ) +{ + struct jabber_transfer *tf = bhd->tf; + struct xt_node *reply; + + imcb_log( tf->ic, "Transferring file %s: established SOCKS5 connection to %s:%s", + tf->ft->file_name, + xt_find_attr( bhd->shnode, "host" ), + xt_find_attr( bhd->shnode, "port" ) ); + + tf->ft->data = tf; + tf->ft->started = time( NULL ); + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_read, tf ); + tf->ft->out_of_data = jabber_bs_out_of_data; + + reply = xt_new_node( "streamhost-used", NULL, NULL ); + xt_add_attr( reply, "jid", xt_find_attr( bhd->shnode, "jid" ) ); + + reply = xt_new_node( "query", NULL, reply ); + xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); + + reply = jabber_make_packet( "iq", "result", tf->ini_jid, reply ); + + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_file_canceled( tf->ft, "Error transmitting bytestream response" ); + xt_free_node( reply ); +} + +/* Reads till it is unscheduled or the receiver signifies an overflow. */ +gboolean jabber_bs_read( gpointer data, gint fd, b_input_condition cond ) +{ + int ret; + struct jabber_transfer *tf = data; + char *buffer = g_malloc( JABBER_BS_BUFSIZE ); + + if (tf->receiver_overflow) + { + if( tf->watch_in ) + { + /* should never happen, BUG */ + imcb_file_canceled( tf->ft, "Bug in jabber file transfer code: read while overflow is true. Please report" ); + return FALSE; + } + } + + ret = recv( fd, buffer, JABBER_BS_BUFSIZE, 0 ); + + if( ret == -1 ) + { + /* shouldn't actually happen */ + if( errno == EAGAIN ) + return TRUE; + + imcb_file_canceled( tf->ft, "Error reading tcp socket" ); /* , strerror( errnum ) */ + + return FALSE; + } + + /* that should be all */ + if( ret == 0 ) + return FALSE; + + tf->bytesread += ret; + + buffer = g_realloc( 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; +} + +/* imcb callback that is invoked when it runs out of data. + * We reschedule jabber_bs_read here if neccessary. */ +void jabber_bs_out_of_data( file_transfer_t *ft ) +{ + struct jabber_transfer *tf = ft->data; + + tf->receiver_overflow = FALSE; + + if ( !tf->watch_in ) + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_read, tf ); +} + +/* Bad luck */ +void jabber_bs_canceled( file_transfer_t *ft , char *reason ) +{ + struct jabber_transfer *tf = ft->data; + + imcb_log( tf->ic, "File transfer aborted: %s", reason ); +} -- cgit v1.2.3 From 2ff20765990c756533957e8da9c7c29dd3102e79 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 3 Dec 2007 15:28:45 +0100 Subject: Intermediate commit. Sending seems to work. TODOs: * move from out_of_data to is_writable, eliminate buffers * implement "transfers reject [id]" * documentation in commands.xml * implement throughput and cummulative throughput boundaries * feature discovery before sending * implement sending over a proxy (proxy discovery, socks5 client handshake for sending, activate message) * integrate toxik-mek-ft --- protocols/jabber/Makefile | 2 +- protocols/jabber/iq.c | 2 +- protocols/jabber/jabber.c | 1 + protocols/jabber/jabber.h | 13 +- protocols/jabber/jabber_util.c | 7 + protocols/jabber/s5bytestream.c | 906 ++++++++++++++++++++++++++++++++++++++++ protocols/jabber/si.c | 191 ++++++++- protocols/jabber/stream.c | 593 -------------------------- 8 files changed, 1111 insertions(+), 604 deletions(-) create mode 100644 protocols/jabber/s5bytestream.c delete mode 100644 protocols/jabber/stream.c (limited to 'protocols/jabber') diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index 47c832ae..21d7ef07 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o si.o stream.o +objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o si.o s5bytestream.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index df0102b8..77def222 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -167,7 +167,7 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) } else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 ) { /* Bytestream Request (stage 2 of file transfer) */ - return jabber_bs_request( ic, node, c ); + return jabber_bs_recv_request( ic, node, c ); } else { xt_free_node( reply ); diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index b0651a59..1f4f42ea 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -501,6 +501,7 @@ void jabber_initmodule() ret->keepalive = jabber_keepalive; ret->send_typing = jabber_send_typing; ret->handle_cmp = g_strcasecmp; + ret->transfer_request = jabber_si_transfer_request; register_protocol( ret ); } diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 0cb2b733..cb52d396 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -147,6 +147,7 @@ struct jabber_transfer size_t bytesread, byteswritten; int receiver_overflow; int fd; + struct sockaddr_storage saddr; }; #define JABBER_XMLCONSOLE_HANDLE "xmlconsole" @@ -200,10 +201,14 @@ int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ); int jabber_remove_from_roster( struct im_connection *ic, char *handle ); /* si.c */ -int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode); - -/* stream.c */ -int jabber_bs_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode); +int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode ); +void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ); +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 ); /* message.c */ xt_status jabber_pkt_message( struct xt_node *node, gpointer data ); diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 0c5b813e..6bb65878 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -264,7 +264,14 @@ char *jabber_normalize( const char *orig ) len = strlen( orig ); new = g_new( char, len + 1 ); for( i = 0; i < len; i ++ ) + { + /* don't normalize the resource */ + if( orig[i] == '/' ) + break; new[i] = tolower( orig[i] ); + } + for( ; i < len; i ++ ) + new[i] = orig[i]; new[i] = 0; return new; diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c new file mode 100644 index 00000000..e2f32bd0 --- /dev/null +++ b/protocols/jabber/s5bytestream.c @@ -0,0 +1,906 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Jabber module - SOCKS5 Bytestreams ( XEP-0065 ) * +* * +* Copyright 2007 Uli Meis * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "jabber.h" +#include "sha1.h" +#include + +struct bs_transfer { + + struct jabber_transfer *tf; + + /* element and elements */ + struct xt_node *qnode, *shnode; + + enum + { + BS_PHASE_CONNECT, + BS_PHASE_CONNECTED, + BS_PHASE_REQUEST, + BS_PHASE_REPLY + } phase; + + /* SHA1( SID + Initiator JID + Target JID) */ + char *pseudoadr; + + gint connect_timeout; +}; + +struct socks5_message +{ + unsigned char ver; + union + { + unsigned char cmd; + unsigned char rep; + } cmdrep; + unsigned char rsv; + unsigned char atyp; + unsigned char addrlen; + unsigned char address[40]; + in_port_t port; +} __attribute__ ((packed)); + +/* connect() timeout in seconds. */ +#define JABBER_BS_CONTIMEOUT 15 +/* listen timeout */ +#define JABBER_BS_LISTEN_TIMEOUT 90 + +/* very useful */ +#define ASSERTSOCKOP(op, msg) \ + 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); +gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ); +gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ); +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_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); + +gboolean jabber_bs_send_handshake_abort( struct bs_transfer *bt, char *error ); +gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *port ); +gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ); +gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ); + +/* + * Frees a bs_transfer struct and calls the SI free function + */ +void jabber_bs_free_transfer( file_transfer_t *ft) { + struct jabber_transfer *tf = ft->data; + struct bs_transfer *bt = tf->streamhandle; + + if ( tf->watch_in ) + b_event_remove( tf->watch_in ); + + if( tf->watch_out ) + b_event_remove( tf->watch_out ); + + g_free( bt->pseudoadr ); + xt_free_node( bt->qnode ); + g_free( bt ); +//iq_id + jabber_si_free_transfer( ft ); +} + +gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ) +{ + int ret; + int fd = bt->tf->fd; + + ASSERTSOCKOP( ret = recv( fd, buffer, buflen, MSG_PEEK ), "MSG_PEEK'ing" ); + + if( ret == 0 ) + return jabber_bs_abort( bt, "Remote end closed connection" ); + + if( ret < buflen ) + return ret; + + ASSERTSOCKOP( ret = recv( fd, buffer, buflen, 0 ), "Dequeuing after MSG_PEEK" ); + + if( ret != buflen ) + return jabber_bs_abort( bt, "recv returned less than previous recv with MSG_PEEK" ); + + return ret; +} + + +/* + * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect(). + */ +gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ) +{ + struct bs_transfer *bt = data; + + bt->connect_timeout = 0; + + jabber_bs_abort( bt, "no connection after %d seconds", bt->tf->ft->sending ? JABBER_BS_LISTEN_TIMEOUT : JABBER_BS_CONTIMEOUT ); + + return FALSE; +} + +gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ) +{ + struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; + + if ( bt->connect_timeout ) + { + b_event_remove( bt->connect_timeout ); + bt->connect_timeout = 0; + } + + ASSERTSOCKOP( poll( &pfd, 1, 0 ), "poll()" ) + + if( pfd.revents & POLLERR ) + { + int sockerror; + socklen_t errlen = sizeof( sockerror ); + + if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) + return jabber_bs_abort( bt, "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)" ); + + if ( bt->phase == BS_PHASE_CONNECTED ) + return jabber_bs_abort( bt, "connect failed: %s", strerror( sockerror ) ); + + return jabber_bs_abort( bt, "Socket error during SOCKS5 handshake(weird!): %s", strerror( sockerror ) ); + } + + if( pfd.revents & POLLHUP ) + return jabber_bs_abort( bt, "Remote end closed connection" ); + + *revents = pfd.revents; + + return TRUE; +} + +gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ) +{ + va_list params; + va_start( params, format ); + char error[128]; + + if( vsnprintf( error, 128, format, params ) < 0 ) + sprintf( error, "internal error parsing error string (BUG)" ); + va_end( params ); + if( bt->tf->ft->sending ) + return jabber_bs_recv_handshake_abort( bt, error ); + else + return jabber_bs_send_handshake_abort( bt, error ); +} + +/* Bad luck */ +void jabber_bs_canceled( file_transfer_t *ft , char *reason ) +{ + struct jabber_transfer *tf = ft->data; + + imcb_log( tf->ic, "File transfer aborted: %s", reason ); +} + +/* + * Parses an incoming bytestream request and calls jabber_bs_handshake on success. + */ +int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode) +{ + char *sid, *ini_jid, *tgt_jid, *mode, *iq_id; + struct jabber_data *jd = ic->proto_data; + struct jabber_transfer *tf = NULL; + GSList *tflist; + struct bs_transfer *bt; + + sha1_state_t sha; + char hash_hex[41]; + unsigned char hash[20]; + int i; + + if( !(iq_id = xt_find_attr( node, "id" ) ) || + !(ini_jid = xt_find_attr( node, "from" ) ) || + !(tgt_jid = xt_find_attr( node, "to" ) ) || + !(sid = xt_find_attr( qnode, "sid" ) ) ) + { + imcb_log( ic, "WARNING: Received incomplete SI bytestream request"); + return XT_HANDLED; + } + + if( ( mode = xt_find_attr( qnode, "mode" ) ) && + ( strcmp( mode, "tcp" ) != 0 ) ) + { + imcb_log( ic, "WARNING: Received SI Request for unsupported bytestream mode %s", xt_find_attr( qnode, "mode" ) ); + return XT_HANDLED; + } + + /* Let's see if we can find out what this bytestream should be for... */ + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) && + ( strcmp( tft->ini_jid, ini_jid ) == 0 ) && + ( strcmp( tft->tgt_jid, tgt_jid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if (!tf) + { + imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); + return XT_HANDLED; + } + + /* iq_id and canceled can be reused since SI is done */ + g_free( tf->iq_id ); + tf->iq_id = g_strdup( iq_id ); + + tf->ft->canceled = jabber_bs_canceled; + + /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ + sha1_init( &sha ); + sha1_append( &sha, (unsigned char*) sid, strlen( sid ) ); + sha1_append( &sha, (unsigned char*) ini_jid, strlen( ini_jid ) ); + sha1_append( &sha, (unsigned char*) tgt_jid, strlen( tgt_jid ) ); + sha1_finish( &sha, hash ); + + for( i = 0; i < 20; i ++ ) + sprintf( hash_hex + i * 2, "%02x", hash[i] ); + + 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; + tf->ft->free = jabber_bs_free_transfer; + + jabber_bs_recv_handshake( bt, 0, 0 ); + + return XT_HANDLED; +} +/* + * This is what a protocol handshake can look like in cooperative multitasking :) + * Might be confusing at first because it's called from different places and is recursing. + * (places being the event thread, bs_request, bs_handshake_abort, and itself) + * + * All in all, it turned out quite nice :) + */ +gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition cond ) +{ + + struct bs_transfer *bt = data; + short revents; + + if ( !jabber_bs_poll( bt, fd, &revents ) ) + return FALSE; + + switch( bt->phase ) + { + case BS_PHASE_CONNECT: + { + struct xt_node *c; + char *host, *port; + struct addrinfo hints, *rp; + + if( ( c = bt->shnode = xt_find_node( bt->shnode, "streamhost" ) ) && + ( port = xt_find_attr( c, "port" ) ) && + ( host = xt_find_attr( c, "host" ) ) && + xt_find_attr( c, "jid" ) ) + { + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + + if ( getaddrinfo( host, port, &hints, &rp ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo() failed: %s", strerror( errno ) ); + + ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); + + sock_make_nonblocking( fd ); + + imcb_log( bt->tf->ic, "Transferring 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 ) ) + return jabber_bs_abort( bt , "connect() failed: %s", strerror( errno ) ); + + freeaddrinfo( rp ); + + bt->phase = BS_PHASE_CONNECTED; + + bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt ); + + /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ + bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + + bt->tf->watch_in = 0; + return FALSE; + } else + return jabber_bs_abort( bt, c ? "incomplete streamhost entry: host=%s port=%s jid=%s" : NULL, + host, port, xt_find_attr( c, "jid" ) ); + } + case BS_PHASE_CONNECTED: + { + struct { + unsigned char ver; + unsigned char nmethods; + unsigned char method; + } socks5_hello = { + .ver = 5, + .nmethods = 1, + .method = 0x00 /* no auth */ + /* one could also implement username/password. If you know + * a jabber client or proxy that actually does it, tell me. + */ + }; + + ASSERTSOCKOP( send( fd, &socks5_hello, sizeof( socks5_hello ) , 0 ), "Sending auth request" ); + + bt->phase = BS_PHASE_REQUEST; + + bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_recv_handshake, bt ); + + bt->tf->watch_out = 0; + return FALSE; + } + case BS_PHASE_REQUEST: + { + struct socks5_message socks5_connect = + { + .ver = 5, + .cmdrep.cmd = 0x01, + .rsv = 0, + .atyp = 0x03, + .addrlen = strlen( bt->pseudoadr ), + .port = 0 + }; + int ret; + char buf[2]; + + /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */ + ASSERTSOCKOP( ret = recv( fd, buf, 2, 0 ) , "Receiving auth reply" ); + + if( !( ret == 2 ) || + !( buf[0] == 5 ) || + !( buf[1] == 0 ) ) + return jabber_bs_abort( bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)", + ret, buf[0], buf[1] ); + + /* copy hash into connect message */ + memcpy( socks5_connect.address, bt->pseudoadr, socks5_connect.addrlen ); + + ASSERTSOCKOP( send( fd, &socks5_connect, sizeof( struct socks5_message ), 0 ) , "Sending SOCKS5 Connect" ); + + bt->phase = BS_PHASE_REPLY; + + return TRUE; + } + case BS_PHASE_REPLY: + { + struct socks5_message socks5_reply; + int ret; + + if ( !( ret = jabber_bs_peek( bt, &socks5_reply, sizeof( struct socks5_message ) ) ) ) + return FALSE; + + if ( ret < sizeof( socks5_reply ) ) + return TRUE; + + if( !( socks5_reply.ver == 5 ) || + !( socks5_reply.cmdrep.rep == 0 ) || + !( socks5_reply.atyp == 3 ) || + !( socks5_reply.addrlen == 40 ) ) + return jabber_bs_abort( bt, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", + socks5_reply.ver, + socks5_reply.cmdrep.rep, + socks5_reply.atyp, + socks5_reply.addrlen); + + jabber_bs_recv_answer_request( bt ); + + // reset in answer_request bt->tf->watch_in = 0; + return FALSE; + } + default: + /* BUG */ + imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + + bt->tf->watch_in = 0; + return FALSE; + } +} + +/* + * 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. + */ +gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) +{ + struct jabber_transfer *tf = bt->tf; + struct xt_node *reply, *iqnode; + + if( bt->shnode ) + { + imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", + tf->ft->file_name, + xt_find_attr( bt->shnode, "host" ), + xt_find_attr( bt->shnode, "port" ), + error ); + + /* Alright, this streamhost failed, let's try the next... */ + bt->phase = BS_PHASE_CONNECT; + bt->shnode = bt->shnode->next; + + /* the if is not neccessary but saves us one recursion */ + if( bt->shnode ) + return jabber_bs_recv_handshake( bt, 0, 0 ); + } + + /* out of stream hosts */ + + iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); + reply = jabber_make_error_packet( iqnode, "item-not-found", "cancel" , "404" ); + xt_free_node( iqnode ); + + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); + xt_free_node( reply ); + + imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); + + bt->tf->watch_in = 0; + return FALSE; +} + +/* + * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose. + * If he is the streamhost himself, he might already know that. However, if it's a proxy, + * the initiator will have to make a connection himself. + */ +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", + 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; + + reply = xt_new_node( "streamhost-used", NULL, NULL ); + xt_add_attr( reply, "jid", xt_find_attr( bt->shnode, "jid" ) ); + + reply = xt_new_node( "query", NULL, reply ); + xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); + + reply = jabber_make_packet( "iq", "result", tf->ini_jid, reply ); + + xt_add_attr( reply, "id", tf->iq_id ); + + if( !jabber_write_packet( tf->ic, reply ) ) + imcb_file_canceled( tf->ft, "Error transmitting bytestream response" ); + xt_free_node( reply ); +} + +/* Reads till it is unscheduled or the receiver signifies an overflow. */ +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 ); + + if (tf->receiver_overflow) + { + if( tf->watch_in ) + { + /* should never happen, BUG */ + imcb_file_canceled( tf->ft, "Bug in jabber file transfer code: read while overflow is true. Please report" ); + return FALSE; + } + } + + ASSERTSOCKOP( ret = recv( fd, buffer, JABBER_BS_BUFSIZE, 0 ) , "Receiving" ); + + /* that should be all */ + if( ret == 0 ) + return FALSE; + + tf->bytesread += ret; + + buffer = g_realloc( 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; +} + +/* 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 ) +{ + struct jabber_transfer *tf = ft->data; + + tf->receiver_overflow = FALSE; + + if ( !tf->watch_in ) + tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, tf ); +} + +/* signal ood and be done */ +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; + 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 ) +{ + 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( ret == 0 ) + return jabber_bs_abort( bt, "Remote end closed connection" ); + + if( ret == -1 ) + { + bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); + return FALSE; + } + + return TRUE; +} + +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; + struct bs_transfer *bt; + GSList *tflist; + struct xt_node *c; + char *sid, *jid; + + if( !( c = xt_find_node( node->children, "query" ) ) || + !( c = xt_find_node( c->children, "streamhost-used" ) ) || + !( jid = xt_find_attr( c, "jid" ) ) ) + + { + imcb_log( ic, "WARNING: Received incomplete bytestream reply" ); + return XT_HANDLED; + } + + if( !( c = xt_find_node( orig->children, "query" ) ) || + !( sid = xt_find_attr( c, "sid" ) ) ) + { + imcb_log( ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply" ); + return XT_HANDLED; + } + + /* Let's see if we can find out what this bytestream should be for... */ + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if( !tf ) + { + imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply to unknown request" ); + return XT_HANDLED; + } + + bt = tf->streamhandle; + + tf->accepted = TRUE; + + if( bt->phase == BS_PHASE_REPLY ) + { + tf->ft->started = time( NULL ); + tf->ft->out_of_data( tf->ft ); + } + + //bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_write, tf ); + + return XT_HANDLED; +} + +gboolean jabber_bs_send_start( struct jabber_transfer *tf ) +{ + char host[INET6_ADDRSTRLEN], port[6]; + struct bs_transfer *bt; + sha1_state_t sha; + char hash_hex[41]; + unsigned char hash[20]; + int i; + + /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ + sha1_init( &sha ); + sha1_append( &sha, (unsigned char*) tf->sid, strlen( tf->sid ) ); + sha1_append( &sha, (unsigned char*) tf->ini_jid, strlen( tf->ini_jid ) ); + sha1_append( &sha, (unsigned char*) tf->tgt_jid, strlen( tf->tgt_jid ) ); + sha1_finish( &sha, hash ); + + for( i = 0; i < 20; i ++ ) + sprintf( hash_hex + i * 2, "%02x", hash[i] ); + + 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; + tf->ft->free = jabber_bs_free_transfer; + tf->ft->canceled = jabber_bs_canceled; + + if ( !jabber_bs_send_listen( bt, &tf->saddr, host, port ) ) + return FALSE; + + bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + return jabber_bs_send_request( tf, host, port ); +} + +gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *port ) +{ + struct xt_node *sh, *query, *iq; + + sh = xt_new_node( "streamhost", NULL, NULL ); + xt_add_attr( sh, "jid", tf->ini_jid ); + xt_add_attr( sh, "host", host ); + xt_add_attr( sh, "port", port ); + + query = xt_new_node( "query", NULL, NULL ); + xt_add_attr( query, "xmlns", XMLNS_BYTESTREAMS ); + xt_add_attr( query, "sid", tf->sid ); + xt_add_attr( query, "mode", "tcp" ); + xt_add_child( query, sh ); + + 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 ) ) + imcb_file_canceled( tf->ft, "Error transmitting bytestream request" ); + return TRUE; +} + +gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) +{ + struct jabber_transfer *tf = bt->tf; + + imcb_log( tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s", + tf->ft->file_name, + error ); + + imcb_file_canceled( tf->ft, error ); + + return FALSE; +} + +/* + * Creates a listening socket and returns it in saddr_ptr. + */ +gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ) +{ + struct jabber_transfer *tf = bt->tf; + int fd; + char hostname[ HOST_NAME_MAX + 1 ]; + struct addrinfo hints, *rp; + socklen_t ssize = sizeof( struct sockaddr_storage ); + + /* won't be long till someone asks for this to be configurable :) */ + + ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); + + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + + if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo()" ); + + memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); + + ASSERTSOCKOP( fd = tf->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); + + ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); + + freeaddrinfo( rp ); + + ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); + + if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? + ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr + , host, INET6_ADDRSTRLEN ) ) + return jabber_bs_abort( bt, "inet_ntop failed on listening socket" ); + + ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); + + if( saddr->ss_family == AF_INET ) + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); + else + sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); + + return TRUE; +} + +/* + * SOCKS5BYTESTREAM protocol for the sender + */ +gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ) +{ + struct bs_transfer *bt = data; + struct jabber_transfer *tf = bt->tf; + short revents; + + if ( !jabber_bs_poll( bt, fd, &revents ) ) + return FALSE; + + switch( bt->phase ) + { + case BS_PHASE_CONNECT: + { + struct sockaddr_storage clt_addr; + socklen_t ssize = sizeof( clt_addr ); + + /* Connect */ + + ASSERTSOCKOP( tf->fd = accept( fd, (struct sockaddr *) &clt_addr, &ssize ), "Accepting connection" ); + + closesocket( fd ); + fd = tf->fd; + sock_make_nonblocking( fd ); + + bt->phase = BS_PHASE_CONNECTED; + + bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + return FALSE; + } + case BS_PHASE_CONNECTED: + { + int ret, have_noauth=FALSE; + struct { + unsigned char ver; + unsigned char method; + } socks5_auth_reply = { .ver = 5, .method = 0 }; + struct { + unsigned char ver; + unsigned char nmethods; + unsigned char method; + } socks5_hello; + + if( !( ret = jabber_bs_peek( bt, &socks5_hello, sizeof( socks5_hello ) ) ) ) + return FALSE; + + if( ret < sizeof( socks5_hello ) ) + return TRUE; + + if( !( socks5_hello.ver == 5 ) || + !( socks5_hello.nmethods >= 1 ) || + !( socks5_hello.nmethods < 32 ) ) + return jabber_bs_abort( bt, "Invalid auth request ver=%d nmethods=%d method=%d", socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method ); + + have_noauth = socks5_hello.method == 0; + + if( socks5_hello.nmethods > 1 ) + { + char mbuf[32]; + int i; + ASSERTSOCKOP( ret = recv( fd, mbuf, socks5_hello.nmethods - 1, 0 ) , "Receiving auth methods" ); + if( ret < ( socks5_hello.nmethods - 1 ) ) + return jabber_bs_abort( bt, "Partial auth request"); + for( i = 0 ; !have_noauth && ( i < socks5_hello.nmethods - 1 ) ; i ++ ) + if( mbuf[i] == 0 ) + have_noauth = TRUE; + } + + if( !have_noauth ) + return jabber_bs_abort( bt, "Auth request didn't include no authentication" ); + + ASSERTSOCKOP( send( fd, &socks5_auth_reply, sizeof( socks5_auth_reply ) , 0 ), "Sending auth reply" ); + + bt->phase = BS_PHASE_REQUEST; + + return TRUE; + } + case BS_PHASE_REQUEST: + { + struct socks5_message socks5_connect; + int msgsize = sizeof( struct socks5_message ); + + if( !jabber_bs_peek( bt, &socks5_connect, msgsize ) ) + return FALSE; + + if( !( socks5_connect.ver == 5) || + !( socks5_connect.cmdrep.cmd == 1 ) || + !( socks5_connect.atyp == 3 ) || + !(socks5_connect.addrlen == 40 ) ) + return jabber_bs_abort( bt, "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)", socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd, socks5_connect.atyp ); + if( !( memcmp( socks5_connect.address, bt->pseudoadr, 40 ) == 0 ) ) + return jabber_bs_abort( bt, "SOCKS5 Connect message contained wrong digest"); + + socks5_connect.cmdrep.rep = 0; + + ASSERTSOCKOP( send( fd, &socks5_connect, msgsize, 0 ), "Sending connect reply" ); + + bt->phase = BS_PHASE_REPLY; + + /* don't start sending till the streamhost-used message comes in */ + if( tf->accepted ) + { + tf->ft->started = time( NULL ); + tf->ft->out_of_data( tf->ft ); + } + + tf->watch_in = 0; + return FALSE; + + } + default: + /* BUG */ + imcb_log( bt->tf->ic, "BUG in file transfer code: undefined handshake phase" ); + + bt->tf->watch_in = 0; + return FALSE; + } +} +#undef ASSERTSOCKOP diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index d16f723a..598cbd03 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -25,8 +25,9 @@ #include "sha1.h" void jabber_si_answer_request( file_transfer_t *ft ); +int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf ); -/* imcb callback */ +/* file_transfer free() callback */ void jabber_si_free_transfer( file_transfer_t *ft) { struct jabber_transfer *tf = ft->data; @@ -49,7 +50,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) g_free( tf->sid ); } -/* imcb callback */ +/* file_transfer finished() callback */ void jabber_si_finished( file_transfer_t *ft ) { struct jabber_transfer *tf = ft->data; @@ -57,7 +58,7 @@ void jabber_si_finished( file_transfer_t *ft ) imcb_log( tf->ic, "File %s transferred successfully!" , ft->file_name ); } -/* imcb callback */ +/* file_transfer canceled() callback */ void jabber_si_canceled( file_transfer_t *ft, char *reason ) { struct jabber_transfer *tf = ft->data; @@ -77,6 +78,29 @@ void jabber_si_canceled( file_transfer_t *ft, char *reason ) } +void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ) +{ + 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 ); + + tf = g_new0( struct jabber_transfer, 1 ); + + tf->ic = ic; + tf->ft = ft; + tf->ft->data = tf; + tf->ft->free = jabber_si_free_transfer; + tf->ft->finished = jabber_si_finished; + ft->write = jabber_bs_send_write; + + jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); + + jabber_si_send_request( ic, who, tf ); + + imcb_file_recv_start( ft ); +} + /* * First function that gets called when a file transfer request comes in. * A lot to parse. @@ -135,6 +159,9 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st requestok = TRUE; break; } + + if ( !requestok ) + imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid); } if ( requestok ) @@ -159,8 +186,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st } *s = '/'; - } else - imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid); + } if ( !requestok ) { @@ -244,3 +270,158 @@ void jabber_si_answer_request( file_transfer_t *ft ) { tf->accepted = TRUE; xt_free_node( reply ); } + +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; + 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" ) ) ) + { + imcb_log( ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid ); + return XT_HANDLED; + } + + imcb_log( ic, "GOT RESPONSE TO FILE" ); + /* All this means we expect something like this: ( I think ) + * + * + * + * + * + * + * + */ + if( !( tgt_jid = xt_find_attr( node, "from" ) ) || + !( ini_jid = xt_find_attr( node, "to" ) ) || + !( 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, "feature" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || + !( d = xt_find_node( d->children, "x" ) ) || + !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_XDATA ) == 0 ) || + !( strcmp( xt_find_attr( d, "type" ), "submit" ) == 0 ) || + !( d = xt_find_node( d->children, "field" ) ) || + !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) || + !( d = xt_find_node( d->children, "value" ) ) ) + { + imcb_log( ic, "WARNING: Received incomplete Stream Initiation response" ); + return XT_HANDLED; + } + + if( !( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) { + /* since we should only have advertised what we can do and the peer should + * only have chosen what we offered, this should never happen */ + imcb_log( ic, "WARNING: Received invalid Stream Initiation response, method %s", d->text ); + + return XT_HANDLED; + } + + /* Let's see if we can find out what this bytestream should be for... */ + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if (!tf) + { + imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); + return XT_HANDLED; + } + + tf->ini_jid = g_strdup( ini_jid ); + tf->tgt_jid = g_strdup( tgt_jid ); + + jabber_bs_send_start( tf ); + + return XT_HANDLED; +} + +int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf ) +{ + struct xt_node *node, *sinode; + struct jabber_buddy *bud; + + /* who knows how many bits the future holds :) */ + char filesizestr[ 1 + ( int ) ( 0.301029995663981198f * sizeof( size_t ) * 8 ) ]; + + const char *methods[] = + { + XMLNS_BYTESTREAMS, + //XMLNS_IBB, + NULL + }; + const char **m; + char *s; + + /* Maybe we should hash this? */ + tf->sid = g_strdup_printf( "BitlBeeJabberSID%d", tf->ft->local_id ); + + if( ( s = strchr( who, '=' ) ) && jabber_chat_by_name( ic, s + 1 ) ) + bud = jabber_buddy_by_ext_jid( ic, who, 0 ); + else + bud = jabber_buddy_by_jid( ic, who, 0 ); + + /* start with the SI tag */ + sinode = xt_new_node( "si", NULL, NULL ); + xt_add_attr( sinode, "xmlns", XMLNS_SI ); + xt_add_attr( sinode, "profile", XMLNS_FILETRANSFER ); + xt_add_attr( sinode, "id", tf->sid ); + +/* if( mimetype ) + xt_add_attr( node, "mime-type", mimetype ); */ + + /* now the file tag */ +/* if( desc ) + node = xt_new_node( "desc", descr, NULL ); */ + node = xt_new_node( "range", NULL, NULL ); + + sprintf( filesizestr, "%zd", tf->ft->file_size ); + node = xt_new_node( "file", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_FILETRANSFER ); + xt_add_attr( node, "name", tf->ft->file_name ); + xt_add_attr( node, "size", filesizestr ); +/* if (hash) + xt_add_attr( node, "hash", hash ); + if (date) + xt_add_attr( node, "date", date ); */ + + xt_add_child( sinode, node ); + + /* and finally the feature tag */ + node = xt_new_node( "field", NULL, NULL ); + xt_add_attr( node, "var", "stream-method" ); + xt_add_attr( node, "type", "list-single" ); + + for ( m = methods ; *m ; m ++ ) + xt_add_child( node, xt_new_node( "option", NULL, xt_new_node( "value", (char *)*m, NULL ) ) ); + + node = xt_new_node( "x", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_XDATA ); + xt_add_attr( node, "type", "form" ); + + node = xt_new_node( "feature", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_FEATURE ); + + xt_add_child( sinode, node ); + + /* 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 ); + + return jabber_write_packet( ic, node ); +} diff --git a/protocols/jabber/stream.c b/protocols/jabber/stream.c deleted file mode 100644 index c88a72fd..00000000 --- a/protocols/jabber/stream.c +++ /dev/null @@ -1,593 +0,0 @@ -/***************************************************************************\ -* * -* BitlBee - An IRC to IM gateway * -* Jabber module - stream handling * -* * -* Copyright 2007 Uli Meis * -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU General Public License for more details. * -* * -* You should have received a copy of the GNU General Public License along * -* with this program; if not, write to the Free Software Foundation, Inc., * -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * -* * -\***************************************************************************/ - -#include "jabber.h" -#include "sha1.h" -#include - -/* Some structs for the SOCKS5 handshake */ - -struct bs_handshake_data { - - struct jabber_transfer *tf; - - /* element and elements */ - struct xt_node *qnode, *shnode; - - enum - { - BS_PHASE_CONNECT, - BS_PHASE_CONNECTED, - BS_PHASE_REQUEST, - BS_PHASE_REPLY, - BS_PHASE_REPLY_HAVE_LEN - } phase; - - /* SHA1( SID + Initiator JID + Target JID) */ - char *pseudoadr; - - void (*parentfree) ( file_transfer_t *ft ); - - gint connect_timeout; -}; - -struct socks5_hdr -{ - unsigned char ver; - union - { - unsigned char cmd; - unsigned char rep; - } cmdrep; - unsigned char rsv; - unsigned char atyp; -}; - -struct socks5_message -{ - struct socks5_hdr hdr; - unsigned char addrlen; - unsigned char address[64]; -}; - -/* connect() timeout in seconds. */ -#define JABBER_BS_CONTIMEOUT 15 - -/* shouldn't matter if it's mostly too much, kernel's smart about that - * and will only reserve some address space */ -#define JABBER_BS_BUFSIZE 65536 - -gboolean jabber_bs_handshake( gpointer data, gint fd, b_input_condition cond ); - -gboolean jabber_bs_handshake_abort( struct bs_handshake_data *bhd, char *format, ... ); - -void jabber_bs_answer_request( struct bs_handshake_data *bhd ); - -gboolean jabber_bs_read( gpointer data, gint fd, b_input_condition cond ); - -void jabber_bs_out_of_data( file_transfer_t *ft ); - -void jabber_bs_canceled( file_transfer_t *ft , char *reason ); - - -void jabber_bs_free_transfer( file_transfer_t *ft) { - struct jabber_transfer *tf = ft->data; - struct bs_handshake_data *bhd = tf->streamhandle; - void (*parentfree) ( file_transfer_t *ft ); - - parentfree = bhd->parentfree; - - if ( tf->watch_in ) - b_event_remove( tf->watch_in ); - - if( tf->watch_out ) - b_event_remove( tf->watch_out ); - - g_free( bhd->pseudoadr ); - xt_free_node( bhd->qnode ); - g_free( bhd ); - - parentfree( ft ); -} - -/* - * Parses an incoming bytestream request and calls jabber_bs_handshake on success. - */ -int jabber_bs_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode) -{ - char *sid, *ini_jid, *tgt_jid, *mode, *iq_id; - struct jabber_data *jd = ic->proto_data; - struct jabber_transfer *tf = NULL; - GSList *tflist; - struct bs_handshake_data *bhd; - - sha1_state_t sha; - char hash_hex[41]; - unsigned char hash[20]; - int i; - - if( !(iq_id = xt_find_attr( node, "id" ) ) || - !(ini_jid = xt_find_attr( node, "from" ) ) || - !(tgt_jid = xt_find_attr( node, "to" ) ) || - !(sid = xt_find_attr( qnode, "sid" ) ) ) - { - imcb_log( ic, "WARNING: Received incomplete SI bytestream request"); - return XT_HANDLED; - } - - if( ( mode = xt_find_attr( qnode, "mode" ) ) && - ( strcmp( mode, "tcp" ) != 0 ) ) - { - imcb_log( ic, "WARNING: Received SI Request for unsupported bytestream mode %s", xt_find_attr( qnode, "mode" ) ); - return XT_HANDLED; - } - - /* Let's see if we can find out what this bytestream should be for... */ - - for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) - { - struct jabber_transfer *tft = tflist->data; - if( ( strcmp( tft->sid, sid ) == 0 ) && - ( strcmp( tft->ini_jid, ini_jid ) == 0 ) && - ( strcmp( tft->tgt_jid, tgt_jid ) == 0 ) ) - { - tf = tft; - break; - } - } - - if (!tf) - { - imcb_log( ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid ); - return XT_HANDLED; - } - - /* iq_id and canceled can be reused since SI is done */ - g_free( tf->iq_id ); - tf->iq_id = g_strdup( iq_id ); - - tf->ft->canceled = jabber_bs_canceled; - - /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ - sha1_init( &sha ); - sha1_append( &sha, (unsigned char*) sid, strlen( sid ) ); - sha1_append( &sha, (unsigned char*) ini_jid, strlen( ini_jid ) ); - sha1_append( &sha, (unsigned char*) tgt_jid, strlen( tgt_jid ) ); - sha1_finish( &sha, hash ); - - for( i = 0; i < 20; i ++ ) - sprintf( hash_hex + i * 2, "%02x", hash[i] ); - - bhd = g_new0( struct bs_handshake_data, 1 ); - bhd->tf = tf; - bhd->qnode = xt_dup( qnode ); - bhd->shnode = bhd->qnode->children; - bhd->phase = BS_PHASE_CONNECT; - bhd->pseudoadr = g_strdup( hash_hex ); - tf->streamhandle = bhd; - bhd->parentfree = tf->ft->free; - tf->ft->free = jabber_bs_free_transfer; - - jabber_bs_handshake( bhd, 0, 0 ); - - return XT_HANDLED; -} - -/* - * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect(). - */ -gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ) -{ - struct bs_handshake_data *bhd = data; - - bhd->connect_timeout = 0; - - jabber_bs_handshake_abort( bhd, "no connection after %d seconds", JABBER_BS_CONTIMEOUT ); - - return FALSE; -} - -/* - * This is what a protocol handshake can look like in cooperative multitasking :) - * Might be confusing at first because it's called from different places and is recursing. - * (places being the event thread, bs_request, bs_handshake_abort, and itself) - * - * All in all, it turned out quite nice :) - */ -gboolean jabber_bs_handshake( gpointer data, gint fd, b_input_condition cond ) -{ - -/* very useful */ -#define ASSERTSOCKOP(op, msg) \ - if( (op) == -1 ) \ - return jabber_bs_handshake_abort( bhd , msg ": %s", strerror( errno ) ); - - struct bs_handshake_data *bhd = data; - struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; - short revents; - - if ( bhd->connect_timeout ) - { - b_event_remove( bhd->connect_timeout ); - bhd->connect_timeout = 0; - } - - - /* we need the real io condition */ - if ( poll( &pfd, 1, 0 ) == -1 ) - { - imcb_log( bhd->tf->ic, "poll() failed, weird!" ); - revents = 0; - }; - - revents = pfd.revents; - - if( revents & POLLERR ) - { - int sockerror; - socklen_t errlen = sizeof( sockerror ); - - if ( getsockopt( fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen ) ) - return jabber_bs_handshake_abort( bhd, "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)" ); - - if ( bhd->phase == BS_PHASE_CONNECTED ) - return jabber_bs_handshake_abort( bhd, "connect() failed: %s", strerror( sockerror ) ); - - return jabber_bs_handshake_abort( bhd, "Socket error during SOCKS5 handshake(weird!): %s", strerror( sockerror ) ); - } - - if( revents & POLLHUP ) - return jabber_bs_handshake_abort( bhd, "Remote end closed connection" ); - - - switch( bhd->phase ) - { - case BS_PHASE_CONNECT: - { - struct xt_node *c; - char *host, *port; - struct addrinfo hints, *rp; - - if( ( c = bhd->shnode = xt_find_node( bhd->shnode, "streamhost" ) ) && - ( port = xt_find_attr( c, "port" ) ) && - ( host = xt_find_attr( c, "host" ) ) && - xt_find_attr( c, "jid" ) ) - { - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; - - if ( getaddrinfo( host, port, &hints, &rp ) != 0 ) - return jabber_bs_handshake_abort( bhd, "getaddrinfo() failed: %s", strerror( errno ) ); - - ASSERTSOCKOP( bhd->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); - - sock_make_nonblocking( fd ); - - imcb_log( bhd->tf->ic, "Transferring file %s: Connecting to streamhost %s:%s", bhd->tf->ft->file_name, host, port ); - - if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && - ( errno != EINPROGRESS ) ) - return jabber_bs_handshake_abort( bhd , "connect() failed: %s", strerror( errno ) ); - - freeaddrinfo( rp ); - - bhd->phase = BS_PHASE_CONNECTED; - - bhd->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_handshake, bhd ); - - /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ - bhd->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bhd ); - - bhd->tf->watch_in = 0; - return FALSE; - } else - return jabber_bs_handshake_abort( bhd, c ? "incomplete streamhost entry: host=%s port=%s jid=%s" : NULL, - host, port, xt_find_attr( c, "jid" ) ); - } - case BS_PHASE_CONNECTED: - { - struct { - unsigned char ver; - unsigned char nmethods; - unsigned char method; - } socks5_hello = { - .ver = 5, - .nmethods = 1, - .method = 0x00 /* no auth */ - /* one could also implement username/password. If you know - * a jabber client or proxy that actually does it, tell me. - */ - }; - - ASSERTSOCKOP( send( fd, &socks5_hello, sizeof( socks5_hello ) , 0 ), "Sending auth request" ); - - bhd->phase = BS_PHASE_REQUEST; - - bhd->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_handshake, bhd ); - - bhd->tf->watch_out = 0; - return FALSE; - } - case BS_PHASE_REQUEST: - { - struct socks5_message socks5_connect = - { - .hdr = - { - .ver = 5, - .cmdrep.cmd = 0x01, - .rsv = 0, - .atyp = 0x03 - }, - .addrlen = strlen( bhd->pseudoadr ) - }; - int ret; - char buf[2]; - - /* If someone's trying to be funny and sends only one byte at a time we'll fail :) */ - ASSERTSOCKOP( ret = recv( fd, buf, 2, 0 ) , "Receiving auth reply" ); - - if( !( ret == 2 ) || - !( buf[0] == 5 ) || - !( buf[1] == 0 ) ) - return jabber_bs_handshake_abort( bhd, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)", - ret, buf[0], buf[1] ); - - /* copy hash into connect message */ - memcpy( socks5_connect.address, bhd->pseudoadr, socks5_connect.addrlen ); - - /* after the address comes the port, which is always 0 */ - memset( socks5_connect.address + socks5_connect.addrlen, 0, sizeof( in_port_t ) ); - - ASSERTSOCKOP( send( fd, &socks5_connect, sizeof( struct socks5_hdr ) + 1 + socks5_connect.addrlen + sizeof( in_port_t ), 0 ) , "Sending SOCKS5 Connect" ); - - bhd->phase = BS_PHASE_REPLY; - - return TRUE; - } - case BS_PHASE_REPLY: - case BS_PHASE_REPLY_HAVE_LEN: - { - /* we have to wait till we have the address length, then we know how much data is left - * (not that we'd actually care about that data, but we need to eat it all up anyway) - */ - struct socks5_message socks5_reply; - int ret; - int expectedbytes = - sizeof( struct socks5_hdr ) + 1 + - ( bhd->phase == BS_PHASE_REPLY_HAVE_LEN ? socks5_reply.addrlen + sizeof( in_port_t ) : 0 ); - - /* notice the peek, we're doing this till enough is there */ - ASSERTSOCKOP( ret = recv( fd, &socks5_reply, expectedbytes, MSG_PEEK ) , "Peeking for SOCKS5 CONNECT reply" ); - - if ( ret == 0 ) - return jabber_bs_handshake_abort( bhd , "peer has shutdown connection" ); - - /* come again */ - if ( ret < expectedbytes ) - return TRUE; - - if ( bhd->phase == BS_PHASE_REPLY ) - { - if( !( socks5_reply.hdr.ver == 5 ) || - !( socks5_reply.hdr.cmdrep.rep == 0 ) || - !( socks5_reply.hdr.atyp == 3 ) || - !( socks5_reply.addrlen <= 62 ) ) /* should also be 40, but who cares as long as all fits in the buffer... */ - return jabber_bs_handshake_abort( bhd, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", - socks5_reply.hdr.ver, - socks5_reply.hdr.cmdrep.rep, - socks5_reply.hdr.atyp, - socks5_reply.addrlen); - - /* and again for the rest */ - bhd->phase = BS_PHASE_REPLY_HAVE_LEN; - - /* since it's very likely that the rest is there as well, - * let's not wait for the event loop to call us again */ - return jabber_bs_handshake( bhd , fd, 0 ); - } - - /* got it all, remove it from the queue */ - ASSERTSOCKOP( ret = recv( fd, &socks5_reply, expectedbytes, 0 ) , "Dequeueing MSG_PEEK'ed data after SOCKS5 CONNECT" ); - - /* this shouldn't happen */ - if ( ret < expectedbytes ) - return jabber_bs_handshake_abort( bhd, "internal error, couldn't dequeue MSG_PEEK'ed data after SOCKS5 CONNECT" ); - - /* we're actually done now... */ - - jabber_bs_answer_request( bhd ); - - bhd->tf->watch_in = 0; - return FALSE; - } - default: - /* BUG */ - imcb_log( bhd->tf->ic, "BUG in file transfer code: undefined handshake phase" ); - - bhd->tf->watch_in = 0; - return FALSE; - } -#undef ASSERTSOCKOP -#undef JABBER_BS_ERR_CONDS -} - -/* - * 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. - */ -gboolean jabber_bs_handshake_abort( struct bs_handshake_data *bhd, char *format, ... ) -{ - struct jabber_transfer *tf = bhd->tf; - struct xt_node *reply, *iqnode; - - if( bhd->shnode ) - { - if( format ) { - va_list params; - va_start( params, format ); - char error[128]; - - if( vsnprintf( error, 128, format, params ) < 0 ) - sprintf( error, "internal error parsing error string (BUG)" ); - va_end( params ); - - imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", - tf->ft->file_name, - xt_find_attr( bhd->shnode, "host" ), - xt_find_attr( bhd->shnode, "port" ), - error ); - } - - /* Alright, this streamhost failed, let's try the next... */ - bhd->phase = BS_PHASE_CONNECT; - bhd->shnode = bhd->shnode->next; - - /* the if is not neccessary but saves us one recursion */ - if( bhd->shnode ) - return jabber_bs_handshake( bhd, 0, 0 ); - } - - /* out of stream hosts */ - - iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); - reply = jabber_make_error_packet( iqnode, "item-not-found", "cancel" , "404" ); - xt_free_node( iqnode ); - - xt_add_attr( reply, "id", tf->iq_id ); - - if( !jabber_write_packet( tf->ic, reply ) ) - imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); - xt_free_node( reply ); - - imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); - - bhd->tf->watch_in = 0; - return FALSE; -} - -/* - * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose. - * If he is the streamhost himself, he might already know that. However, if it's a proxy, - * the initiator will have to make a connection himself. - */ -void jabber_bs_answer_request( struct bs_handshake_data *bhd ) -{ - struct jabber_transfer *tf = bhd->tf; - struct xt_node *reply; - - imcb_log( tf->ic, "Transferring file %s: established SOCKS5 connection to %s:%s", - tf->ft->file_name, - xt_find_attr( bhd->shnode, "host" ), - xt_find_attr( bhd->shnode, "port" ) ); - - tf->ft->data = tf; - tf->ft->started = time( NULL ); - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_read, tf ); - tf->ft->out_of_data = jabber_bs_out_of_data; - - reply = xt_new_node( "streamhost-used", NULL, NULL ); - xt_add_attr( reply, "jid", xt_find_attr( bhd->shnode, "jid" ) ); - - reply = xt_new_node( "query", NULL, reply ); - xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); - - reply = jabber_make_packet( "iq", "result", tf->ini_jid, reply ); - - xt_add_attr( reply, "id", tf->iq_id ); - - if( !jabber_write_packet( tf->ic, reply ) ) - imcb_file_canceled( tf->ft, "Error transmitting bytestream response" ); - xt_free_node( reply ); -} - -/* Reads till it is unscheduled or the receiver signifies an overflow. */ -gboolean jabber_bs_read( gpointer data, gint fd, b_input_condition cond ) -{ - int ret; - struct jabber_transfer *tf = data; - char *buffer = g_malloc( JABBER_BS_BUFSIZE ); - - if (tf->receiver_overflow) - { - if( tf->watch_in ) - { - /* should never happen, BUG */ - imcb_file_canceled( tf->ft, "Bug in jabber file transfer code: read while overflow is true. Please report" ); - return FALSE; - } - } - - ret = recv( fd, buffer, JABBER_BS_BUFSIZE, 0 ); - - if( ret == -1 ) - { - /* shouldn't actually happen */ - if( errno == EAGAIN ) - return TRUE; - - imcb_file_canceled( tf->ft, "Error reading tcp socket" ); /* , strerror( errnum ) */ - - return FALSE; - } - - /* that should be all */ - if( ret == 0 ) - return FALSE; - - tf->bytesread += ret; - - buffer = g_realloc( 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; -} - -/* imcb callback that is invoked when it runs out of data. - * We reschedule jabber_bs_read here if neccessary. */ -void jabber_bs_out_of_data( file_transfer_t *ft ) -{ - struct jabber_transfer *tf = ft->data; - - tf->receiver_overflow = FALSE; - - if ( !tf->watch_in ) - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_read, tf ); -} - -/* Bad luck */ -void jabber_bs_canceled( file_transfer_t *ft , char *reason ) -{ - struct jabber_transfer *tf = ft->data; - - imcb_log( tf->ic, "File transfer aborted: %s", reason ); -} -- cgit v1.2.3 From dce390357114e30a424106c99e49cef1e682e1af Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 4 Dec 2007 01:48:57 +0100 Subject: Send and receive seems to work now! Also adopted the new buffering strategy, only one buffer of 2k per transfer now. --- protocols/jabber/jabber.h | 3 +- protocols/jabber/s5bytestream.c | 137 ++++++++++++++++++++++------------------ protocols/jabber/si.c | 23 +++---- 3 files changed, 88 insertions(+), 75 deletions(-) (limited to 'protocols/jabber') 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 ) - * + * * - * + * [ ] <-- not neccessary * * * @@ -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 ); } -- cgit v1.2.3 From 793cc254ad2479d95d00266d6cb7ab2bcd158834 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 4 Dec 2007 02:21:45 +0100 Subject: Follow rename of jabber_chat_by_jid from upstrem --- protocols/jabber/si.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index ffde6418..0b94f81b 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -371,7 +371,7 @@ int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_tr /* Maybe we should hash this? */ tf->sid = g_strdup_printf( "BitlBeeJabberSID%d", tf->ft->local_id ); - if( ( s = strchr( who, '=' ) ) && jabber_chat_by_name( ic, s + 1 ) ) + if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) ) bud = jabber_buddy_by_ext_jid( ic, who, 0 ); else bud = jabber_buddy_by_jid( ic, who, 0 ); -- cgit v1.2.3 From dc0ba9c85539533349353713162f94077fb27be3 Mon Sep 17 00:00:00 2001 From: kenobi Date: Tue, 18 Dec 2007 03:07:59 +0100 Subject: sending via proxy --- protocols/jabber/iq.c | 162 +++++++++++++++++++++- protocols/jabber/jabber.h | 16 ++- protocols/jabber/s5bytestream.c | 298 ++++++++++++++++++++++++++++++---------- protocols/jabber/si.c | 10 ++ 4 files changed, 410 insertions(+), 76 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 8cf6c7f1..8bd1111a 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -80,7 +80,7 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) ); xt_add_child( reply, xt_new_node( "tz", buf, NULL ) ); } - else if( strcmp( s, XMLNS_DISCOVER ) == 0 ) + else if( strcmp( s, XMLNS_DISCO_INFO ) == 0 ) { const char *features[] = { XMLNS_VERSION, XMLNS_TIME, @@ -564,3 +564,163 @@ int jabber_remove_from_roster( struct im_connection *ic, char *handle ) xt_free_node( node ); return st; } + +xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); + +xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) +{ + struct xt_node *node, *query; + struct jabber_buddy *bud; + + if( ( bud = jabber_buddy_by_jid( ic, bare_jid , 0 ) ) == NULL ) + { + /* Who cares about the unknown... */ + imcb_log( ic, "Couldnt find the man: %s", bare_jid); + return 0; + } + + if( bud->features ) /* been here already */ + return XT_HANDLED; + + node = xt_new_node( "query", NULL, NULL ); + xt_add_attr( node, "xmlns", XMLNS_DISCO_INFO ); + + if( !( query = jabber_make_packet( "iq", "get", bare_jid, node ) ) ) + { + imcb_log( ic, "WARNING: Couldn't generate feature query" ); + xt_free_node( node ); + } + + jabber_cache_add( ic, query, jabber_iq_parse_features ); + + return jabber_write_packet( ic, query ); +} + +xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ + struct xt_node *c; + struct jabber_buddy *bud; + char *feature; + + if( !( c = xt_find_node( node->children, "query" ) ) || + !( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) ) + { + imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); + return XT_HANDLED; + } + if( ( bud = jabber_buddy_by_jid( ic, xt_find_attr( node, "from") , 0 ) ) == NULL ) + { + /* Who cares about the unknown... */ + imcb_log( ic, "Couldnt find the man: %s", xt_find_attr( node, "from")); + return 0; + } + + c = c->children; + while( ( c = xt_find_node( c, "feature" ) ) ) { + feature = xt_find_attr( c, "var" ); + bud->features = g_slist_append(bud->features, g_strdup(feature) ); + c = c->next; + } + + return XT_HANDLED; +} + +xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); + +xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns ) +{ + struct xt_node *node, *query; + struct jabber_data *jd = ic->proto_data; + + node = xt_new_node( "query", NULL, NULL ); + xt_add_attr( node, "xmlns", xmlns ); + + if( !( query = jabber_make_packet( "iq", "get", jid, node ) ) ) + { + imcb_log( ic, "WARNING: Couldn't generate server query" ); + xt_free_node( node ); + } + + jd->have_streamhosts--; + jabber_cache_add( ic, query, jabber_iq_parse_server_features ); + + return jabber_write_packet( ic, query ); +} + +/* + * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info + */ +xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ + struct xt_node *c; + struct jabber_data *jd = ic->proto_data; + + if( !( c = xt_find_node( node->children, "query" ) ) || + !xt_find_attr( node, "from" ) ) + { + imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); + return XT_HANDLED; + } + + jd->have_streamhosts++; + + if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_ITEMS ) == 0 ) + { + char *item, *itemjid; + + /* answer from server */ + + c = c->children; + while( ( c = xt_find_node( c, "item" ) ) ) + { + item = xt_find_attr( c, "name" ); + itemjid = xt_find_attr( c, "jid" ); + + jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO ); + + c = c->next; + } + } else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) + { + char *category, *type; + + /* answer from potential proxy */ + + c = c->children; + while( ( c = xt_find_node( c, "identity" ) ) ) + { + category = xt_find_attr( c, "category" ); + type = xt_find_attr( c, "type" ); + + if( type && ( strcmp( type, "bytestreams" ) == 0 ) && + category && ( strcmp( category, "proxy" ) == 0 ) ) + jabber_iq_query_server( ic, xt_find_attr( node, "from" ), XMLNS_BYTESTREAMS ); + + c = c->next; + } + } else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_BYTESTREAMS ) == 0 ) + { + char *host, *jid; + int port; + + /* answer from proxy */ + + if( ( c = xt_find_node( c->children, "streamhost" ) ) && + ( host = xt_find_attr( c, "host" ) ) && + ( port = atoi( xt_find_attr( c, "port" ) ) ) && + ( jid = xt_find_attr( c, "jid" ) ) ) + { + jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( jid ); + sh->host = g_strdup( host ); + sprintf( sh->port, "%u", port ); + + imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port ); + jd->streamhosts = g_slist_append( jd->streamhosts, sh ); + } + } + + if( jd->have_streamhosts == 0 ) + jd->have_streamhosts++; + return XT_HANDLED; +} diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index c518f541..9edb844e 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -56,6 +56,14 @@ typedef enum have a real JID. */ } jabber_buddy_flags_t; +/* Stores a streamhost's(a.k.a. proxy) data */ +typedef struct +{ + char *jid; + char *host; + char port[6]; +} jabber_streamhost_t; + struct jabber_data { struct im_connection *ic; @@ -82,6 +90,8 @@ struct jabber_data GHashTable *buddies; GSList *filetransfers; + GSList *streamhosts; + int have_streamhosts; }; struct jabber_away_state @@ -110,6 +120,7 @@ struct jabber_buddy int priority; struct jabber_away_state *away_state; char *away_message; + GSList *features; time_t last_act; jabber_buddy_flags_t flags; @@ -181,7 +192,8 @@ struct jabber_transfer #define XMLNS_DELAY "jabber:x:delay" /* XEP-0091 */ #define XMLNS_XDATA "jabber:x:data" /* XEP-0004 */ #define XMLNS_CHATSTATES "http://jabber.org/protocol/chatstates" /* XEP-0085 */ -#define XMLNS_DISCOVER "http://jabber.org/protocol/disco#info" /* XEP-0030 */ +#define XMLNS_DISCO_INFO "http://jabber.org/protocol/disco#info" /* XEP-0030 */ +#define XMLNS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" /* XEP-0030 */ #define XMLNS_MUC "http://jabber.org/protocol/muc" /* XEP-0045 */ #define XMLNS_MUC_USER "http://jabber.org/protocol/muc#user" /* XEP-0045 */ #define XMLNS_FEATURE "http://jabber.org/protocol/feature-neg" /* XEP-0020 */ @@ -198,6 +210,8 @@ int jabber_get_roster( struct im_connection *ic ); int jabber_get_vcard( struct im_connection *ic, char *bare_jid ); int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ); int jabber_remove_from_roster( struct im_connection *ic, char *handle ); +xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ); +xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns ); /* si.c */ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index de173d19..4770feda 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -29,8 +29,8 @@ struct bs_transfer { struct jabber_transfer *tf; - /* element and elements */ - struct xt_node *qnode, *shnode; + jabber_streamhost_t *sh; + GSList *streamhosts; enum { @@ -73,7 +73,7 @@ struct socks5_message 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); +void jabber_bs_free_transfer( file_transfer_t *ft ); gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition cond ); gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ); gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ); @@ -83,12 +83,14 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ); 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); +int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, struct xt_node *qnode ); gboolean jabber_bs_send_handshake_abort( struct bs_transfer *bt, char *error ); -gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *port ); +gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ); gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ); gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ); +static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); +void jabber_bs_send_activate( struct bs_transfer *bt ); /* * Frees a bs_transfer struct and calls the SI free function @@ -96,6 +98,7 @@ gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage void jabber_bs_free_transfer( file_transfer_t *ft) { struct jabber_transfer *tf = ft->data; struct bs_transfer *bt = tf->streamhandle; + jabber_streamhost_t *sh; if ( tf->watch_in ) b_event_remove( tf->watch_in ); @@ -104,12 +107,25 @@ void jabber_bs_free_transfer( file_transfer_t *ft) { b_event_remove( tf->watch_out ); g_free( bt->pseudoadr ); - xt_free_node( bt->qnode ); + + while( bt->streamhosts ) + { + sh = bt->streamhosts->data; + bt->streamhosts = g_slist_remove( bt->streamhosts, sh ); + g_free( sh->jid ); + g_free( sh->host ); + g_free( sh ); + } + g_free( bt ); jabber_si_free_transfer( ft ); } +/* + * Checks if buflen data is available on the socket and + * writes it to buffer if that's the case. + */ gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ) { int ret; @@ -146,6 +162,10 @@ gboolean jabber_bs_connect_timeout( gpointer data, gint fd, b_input_condition co return FALSE; } +/* + * Polls the socket, checks for errors and removes a connect timer + * if there is one. + */ gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ) { struct pollfd pfd = { .fd = fd, .events = POLLHUP|POLLERR }; @@ -180,6 +200,9 @@ gboolean jabber_bs_poll( struct bs_transfer *bt, int fd, short *revents ) return TRUE; } +/* + * Used for receive and send path. + */ gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ) { va_list params; @@ -190,9 +213,9 @@ gboolean jabber_bs_abort( struct bs_transfer *bt, char *format, ... ) sprintf( error, "internal error parsing error string (BUG)" ); va_end( params ); if( bt->tf->ft->sending ) - return jabber_bs_recv_handshake_abort( bt, error ); - else return jabber_bs_send_handshake_abort( bt, error ); + else + return jabber_bs_recv_handshake_abort( bt, error ); } /* Bad luck */ @@ -213,6 +236,8 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru struct jabber_transfer *tf = NULL; GSList *tflist; struct bs_transfer *bt; + GSList *shlist=NULL; + struct xt_node *shnode; sha1_state_t sha; char hash_hex[41]; @@ -235,6 +260,30 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru return XT_HANDLED; } + shnode = qnode->children; + while( ( shnode = xt_find_node( shnode, "streamhost" ) ) ) + { + char *jid, *host; + int port; + if( ( jid = xt_find_attr( shnode, "jid" ) ) && + ( host = xt_find_attr( shnode, "host" ) ) && + ( ( port = atoi( xt_find_attr( shnode, "port" ) ) ) ) ) + { + jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup(jid); + sh->host = g_strdup(host); + sprintf( sh->port, "%u", port ); + shlist = g_slist_append( shlist, sh ); + } + shnode = shnode->next; + } + + if( !shlist ) + { + imcb_log( ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries"); + return XT_HANDLED; + } + /* Let's see if we can find out what this bytestream should be for... */ for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) @@ -273,8 +322,8 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru bt = g_new0( struct bs_transfer, 1 ); bt->tf = tf; - bt->qnode = xt_dup( qnode ); - bt->shnode = bt->qnode->children; + bt->streamhosts = shlist; + bt->sh = shlist->data; bt->phase = BS_PHASE_CONNECT; bt->pseudoadr = g_strdup( hash_hex ); tf->streamhandle = bt; @@ -284,6 +333,7 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru return XT_HANDLED; } + /* * This is what a protocol handshake can look like in cooperative multitasking :) * Might be confusing at first because it's called from different places and is recursing. @@ -304,45 +354,35 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con { case BS_PHASE_CONNECT: { - struct xt_node *c; - char *host, *port; struct addrinfo hints, *rp; - if( ( c = bt->shnode = xt_find_node( bt->shnode, "streamhost" ) ) && - ( port = xt_find_attr( c, "port" ) ) && - ( host = xt_find_attr( c, "host" ) ) && - xt_find_attr( c, "jid" ) ) - { - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; - if ( getaddrinfo( host, port, &hints, &rp ) != 0 ) - return jabber_bs_abort( bt, "getaddrinfo() failed: %s", strerror( errno ) ); + if ( getaddrinfo( bt->sh->host, bt->sh->port, &hints, &rp ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo() failed: %s", strerror( errno ) ); - ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); + ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); - sock_make_nonblocking( fd ); + sock_make_nonblocking( fd ); - imcb_log( bt->tf->ic, "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, bt->sh->host, bt->sh->port ); - if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && - ( errno != EINPROGRESS ) ) - return jabber_bs_abort( bt , "connect() failed: %s", strerror( errno ) ); + if( ( connect( fd, rp->ai_addr, rp->ai_addrlen ) == -1 ) && + ( errno != EINPROGRESS ) ) + return jabber_bs_abort( bt , "connect() failed: %s", strerror( errno ) ); - freeaddrinfo( rp ); + freeaddrinfo( rp ); - bt->phase = BS_PHASE_CONNECTED; - - bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt ); + bt->phase = BS_PHASE_CONNECTED; + + bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt ); - /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ - bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ + bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); - bt->tf->watch_in = 0; - return FALSE; - } else - return jabber_bs_abort( bt, c ? "incomplete streamhost entry: host=%s port=%s jid=%s" : NULL, - host, port, xt_find_attr( c, "jid" ) ); + bt->tf->watch_in = 0; + return FALSE; } case BS_PHASE_CONNECTED: { @@ -421,7 +461,10 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con socks5_reply.atyp, socks5_reply.addrlen); - jabber_bs_recv_answer_request( bt ); + if( bt->tf->ft->sending ) + jabber_bs_send_activate( bt ); + else + jabber_bs_recv_answer_request( bt ); return FALSE; } @@ -446,24 +489,24 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) { struct jabber_transfer *tf = bt->tf; struct xt_node *reply, *iqnode; + GSList *shlist; + + imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", + tf->ft->file_name, + bt->sh->host, + bt->sh->port, + error ); - if( bt->shnode ) + /* Alright, this streamhost failed, let's try the next... */ + bt->phase = BS_PHASE_CONNECT; + shlist = g_slist_find( bt->streamhosts, bt->sh ); + if( shlist && shlist->next ) { - imcb_log( tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)", - tf->ft->file_name, - xt_find_attr( bt->shnode, "host" ), - xt_find_attr( bt->shnode, "port" ), - error ); - - /* Alright, this streamhost failed, let's try the next... */ - bt->phase = BS_PHASE_CONNECT; - bt->shnode = bt->shnode->next; - - /* the if is not neccessary but saves us one recursion */ - if( bt->shnode ) - return jabber_bs_recv_handshake( bt, 0, 0 ); + bt->sh = shlist->next->data; + return jabber_bs_recv_handshake( bt, 0, 0 ); } + /* out of stream hosts */ iqnode = jabber_make_packet( "iq", "result", tf->ini_jid, NULL ); @@ -494,16 +537,15 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) 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" ) ); + bt->sh->host, + bt->sh->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, 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" ) ); + xt_add_attr( reply, "jid", bt->sh->jid ); reply = xt_new_node( "query", NULL, reply ); xt_add_attr( reply, "xmlns", XMLNS_BYTESTREAMS ); @@ -550,6 +592,9 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) if( ret == 0 ) return jabber_bs_abort( bt, "Remote end closed connection" ); + if( tf->bytesread == 0 ) + tf->ft->started = time( NULL ); + tf->bytesread += ret; tf->ft->write( tf->ft, tf->ft->buffer, ret ); @@ -602,8 +647,12 @@ gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int l if( tf->watch_out ) return jabber_bs_abort( bt, "BUG: write() called while watching " ); + /* TODO: catch broken pipe */ ASSERTSOCKOP( ret = send( tf->fd, buffer, len, 0 ), "Sending" ); + if( tf->byteswritten == 0 ) + tf->ft->started = time( NULL ); + tf->byteswritten += ret; /* TODO: this should really not be fatal */ @@ -664,24 +713,110 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt tf->accepted = TRUE; - if( bt->phase == BS_PHASE_REPLY ) + if( strcmp( jid, tf->ini_jid ) == 0 ) { - /* handshake went through, let's start transferring */ - tf->ft->started = time( NULL ); - tf->ft->write_request( tf->ft ); + /* we're streamhost and target */ + if( bt->phase == BS_PHASE_REPLY ) + { + /* handshake went through, let's start transferring */ + tf->ft->write_request( tf->ft ); + } + } else + { + /* using a proxy */ + GSList *shlist; + for( shlist = jd->streamhosts ; shlist ; shlist = g_slist_next( shlist ) ) + { + jabber_streamhost_t *sh = shlist->data; + if( strcmp( sh->jid, jid ) == 0 ) + { + bt->sh = sh; + jabber_bs_recv_handshake( bt, 0, 0 ); + return XT_HANDLED; + } + } + + imcb_log( ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid ); } return XT_HANDLED; } +/* + * Tell the proxy to activate the stream. Looks like this: + * + * + * + * tgt_jid + * + * + */ +void jabber_bs_send_activate( struct bs_transfer *bt ) +{ + struct xt_node *node; + + node = xt_new_node( "activate", bt->tf->tgt_jid, NULL ); + node = xt_new_node( "query", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_BYTESTREAMS ); + xt_add_attr( node, "sid", bt->tf->sid ); + node = jabber_make_packet( "iq", "set", bt->sh->jid, node ); + + jabber_cache_add( bt->tf->ic, node, jabber_bs_send_handle_activate ); + + jabber_write_packet( bt->tf->ic, node ); +} + +/* + * The proxy has activated the bytestream. + * We can finally start pushing some data out. + */ +static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) +{ + char *sid; + GSList *tflist; + struct jabber_transfer *tf; + struct xt_node *query; + struct jabber_data *jd = ic->proto_data; + + query = xt_find_node( orig->children, "query" ); + sid = xt_find_attr( query, "sid" ); + + for( tflist = jd->filetransfers ; tflist; tflist = g_slist_next(tflist) ) + { + struct jabber_transfer *tft = tflist->data; + if( ( strcmp( tft->sid, sid ) == 0 ) ) + { + tf = tft; + break; + } + } + + if( !tf ) + { + imcb_log( ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream" ); + return XT_HANDLED; + } + + /* handshake went through, let's start transferring */ + tf->ft->write_request( tf->ft ); + + return XT_HANDLED; +} + +/* + * Starts a bytestream. + */ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) { - char host[INET6_ADDRSTRLEN], port[6]; + char host[INET6_ADDRSTRLEN]; struct bs_transfer *bt; sha1_state_t sha; char hash_hex[41]; unsigned char hash[20]; - int i; + int i,ret; + struct jabber_data *jd = tf->ic->proto_data; + jabber_streamhost_t sh; + GSList *streamhosts = jd->streamhosts; /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ sha1_init( &sha ); @@ -701,28 +836,43 @@ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) tf->ft->free = jabber_bs_free_transfer; tf->ft->canceled = jabber_bs_canceled; - if ( !jabber_bs_send_listen( bt, &tf->saddr, host, port ) ) + if ( !jabber_bs_send_listen( bt, &tf->saddr, sh.host = host, sh.port ) ) return FALSE; bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); - return jabber_bs_send_request( tf, host, port ); + + sh.jid = tf->ini_jid; + + /* temporarily add listen address to streamhosts, send the request and remove it */ + streamhosts = g_slist_prepend( streamhosts, &sh ); + ret = jabber_bs_send_request( tf, streamhosts); + streamhosts = g_slist_remove( streamhosts, &sh ); + + return ret; } -gboolean jabber_bs_send_request( struct jabber_transfer *tf, char *host, char *port ) +gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ) { - struct xt_node *sh, *query, *iq; - - sh = xt_new_node( "streamhost", NULL, NULL ); - xt_add_attr( sh, "jid", tf->ini_jid ); - xt_add_attr( sh, "host", host ); - xt_add_attr( sh, "port", port ); + struct xt_node *shnode, *query, *iq; query = xt_new_node( "query", NULL, NULL ); xt_add_attr( query, "xmlns", XMLNS_BYTESTREAMS ); xt_add_attr( query, "sid", tf->sid ); xt_add_attr( query, "mode", "tcp" ); - xt_add_child( query, sh ); + + while( streamhosts ) { + jabber_streamhost_t *sh = streamhosts->data; + shnode = xt_new_node( "streamhost", NULL, NULL ); + xt_add_attr( shnode, "jid", sh->jid ); + xt_add_attr( shnode, "host", sh->host ); + xt_add_attr( shnode, "port", sh->port ); + + xt_add_child( query, shnode ); + + streamhosts = g_slist_next( streamhosts ); + } + iq = jabber_make_packet( "iq", "set", tf->tgt_jid, query ); xt_add_attr( iq, "from", tf->ini_jid ); @@ -738,6 +888,7 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) { struct jabber_transfer *tf = bt->tf; + /* TODO: did the receiver get here somehow??? */ imcb_log( tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s", tf->ft->file_name, error ); @@ -900,7 +1051,6 @@ gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition con if( tf->accepted ) { /* streamhost-used message came already in(possible?), let's start sending */ - tf->ft->started = time( NULL ); tf->ft->write_request( tf->ft ); } diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 0b94f81b..1d649da3 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -82,6 +82,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; + char *server = jd->server; imcb_log( ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who ); @@ -96,8 +97,17 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); + /* query the buddy's features */ + jabber_iq_query_features( ic, who ); + + /* query proxies from the server */ + if( !jd->have_streamhosts ) + jabber_iq_query_server( ic, server, XMLNS_DISCO_ITEMS ); + + /* send the request to our buddy */ jabber_si_send_request( ic, who, tf ); + /* and start the receive logic */ imcb_file_recv_start( ft ); } -- cgit v1.2.3 From eded1f703a8f5d2272b9d294d8e3dfb48fa302b4 Mon Sep 17 00:00:00 2001 From: kenobi Date: Wed, 19 Dec 2007 00:59:35 +0100 Subject: Merged in 280..288 from upstream (e.g. PING) --- protocols/jabber/conference.c | 21 +++++++++++++++++++++ protocols/jabber/io.c | 4 ++-- protocols/jabber/iq.c | 12 +++++++++++- protocols/jabber/jabber.c | 16 +++++++++++++++- protocols/jabber/jabber.h | 2 ++ 5 files changed, 51 insertions(+), 4 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index c5bc0e68..074412ec 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -175,6 +175,27 @@ int jabber_chat_leave( struct groupchat *c, const char *reason ) return 1; } +void jabber_chat_invite( struct groupchat *c, char *who, char *message ) +{ + struct xt_node *node; + struct im_connection *ic = c->ic; + struct jabber_chat *jc = c->data; + + node = xt_new_node( "reason", message, NULL ); + + node = xt_new_node( "invite", NULL, node ); + xt_add_attr( node, "to", who ); + + node = xt_new_node( "x", NULL, node ); + xt_add_attr( node, "xmlns", XMLNS_MUC_USER ); + + node = jabber_make_packet( "message", NULL, jc->name, node ); + + jabber_write_packet( ic, node ); + + xt_free_node( node ); +} + /* Not really the same syntax as the normal pkt_ functions, but this isn't called by the xmltree parser directly and this way I can add some extra parameters so we won't have to repeat too many things done by the caller diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index 61cd142e..29561b86 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -119,7 +119,7 @@ static gboolean jabber_write_queue( struct im_connection *ic ) return TRUE; } - else if( st == 0 || ( st < 0 && !sockerr_again() ) ) + else if( st == 0 || ( st < 0 && !ssl_sockerr_again( jd->ssl ) ) ) { /* Set fd to -1 to make sure we won't write to it anymore. */ closesocket( jd->fd ); /* Shouldn't be necessary after errors? */ @@ -230,7 +230,7 @@ static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition } } } - else if( st == 0 || ( st < 0 && !sockerr_again() ) ) + else if( st == 0 || ( st < 0 && !ssl_sockerr_again( jd->ssl ) ) ) { closesocket( jd->fd ); jd->fd = -1; diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 8bd1111a..2f0959b0 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -49,7 +49,8 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) } else if( strcmp( type, "get" ) == 0 ) { - if( !( c = xt_find_node( node->children, "query" ) ) || + if( !( ( c = xt_find_node( node->children, "query" ) ) || + ( c = xt_find_node( node->children, "ping" ) ) ) || /* O_o WHAT is wrong with just ????? */ !( s = xt_find_attr( c, "xmlns" ) ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-%s packet", type ); @@ -80,12 +81,21 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) strftime( buf, sizeof( buf ) - 1, "%Z", localtime( &time_ep ) ); xt_add_child( reply, xt_new_node( "tz", buf, NULL ) ); } + else if( strcmp( s, XMLNS_PING ) == 0 ) + { + xt_free_node( reply ); + reply = jabber_make_packet( "iq", "result", xt_find_attr( node, "from" ), NULL ); + if( ( s = xt_find_attr( node, "id" ) ) ) + xt_add_attr( reply, "id", s ); + pack = 0; + } else if( strcmp( s, XMLNS_DISCO_INFO ) == 0 ) { const char *features[] = { XMLNS_VERSION, XMLNS_TIME, XMLNS_CHATSTATES, XMLNS_MUC, + XMLNS_PING, XMLNS_SI, XMLNS_BYTESTREAMS, XMLNS_FILETRANSFER, diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 98d2dadf..1d3225dd 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -422,6 +422,20 @@ static void jabber_chat_leave_( struct groupchat *c ) jabber_chat_leave( c, NULL ); } +static void jabber_chat_invite_( struct groupchat *c, char *who, char *msg ) +{ + struct jabber_chat *jc = c->data; + gchar *msg_alt = NULL; + + if( msg == NULL ) + msg_alt = g_strdup_printf( "%s invited you to %s", c->ic->acc->user, jc->name ); + + if( c && who ) + jabber_chat_invite( c, who, msg ? msg : msg_alt ); + + g_free( msg_alt ); +} + static void jabber_keepalive( struct im_connection *ic ) { /* Just any whitespace character is enough as a keepalive for XMPP sessions. */ @@ -493,7 +507,7 @@ void jabber_initmodule() ret->remove_buddy = jabber_remove_buddy; ret->chat_msg = jabber_chat_msg_; ret->chat_topic = jabber_chat_topic_; -// ret->chat_invite = jabber_chat_invite; + ret->chat_invite = jabber_chat_invite_; ret->chat_leave = jabber_chat_leave_; ret->chat_join = jabber_chat_join_; ret->keepalive = jabber_keepalive; diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 9edb844e..3251b49b 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -188,6 +188,7 @@ struct jabber_transfer #define XMLNS_AUTH "jabber:iq:auth" /* XEP-0078 */ #define XMLNS_VERSION "jabber:iq:version" /* XEP-0092 */ #define XMLNS_TIME "jabber:iq:time" /* XEP-0090 */ +#define XMLNS_PING "urn:xmpp:ping" /* XEP-0199 */ #define XMLNS_VCARD "vcard-temp" /* XEP-0054 */ #define XMLNS_DELAY "jabber:x:delay" /* XEP-0091 */ #define XMLNS_XDATA "jabber:x:data" /* XEP-0004 */ @@ -292,5 +293,6 @@ int jabber_chat_topic( struct groupchat *c, char *topic ); int jabber_chat_leave( struct groupchat *c, const char *reason ); void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ); void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ); +void jabber_chat_invite( struct groupchat *c, char *who, char *message ); #endif -- cgit v1.2.3 From 506e61b853ba969bc2d4d2878e87b975bd9e431c Mon Sep 17 00:00:00 2001 From: ulim Date: Sun, 17 Feb 2008 13:34:47 +0100 Subject: Removes the word "dcc" from 2 comments and one message. Thanks to vmiklos for this! --- protocols/jabber/si.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 1d649da3..aab71c8b 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -211,7 +211,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", xt_find_attr( node, "from" ), name, size/1024 ); - imcb_log( ic, "Accept the DCC transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); + imcb_log( ic, "Accept the file transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); tf = g_new0( struct jabber_transfer, 1 ); -- cgit v1.2.3 From 2625d6de47df4054f793990118fa99f3d04d694f Mon Sep 17 00:00:00 2001 From: ulim Date: Wed, 20 Feb 2008 21:55:38 +0100 Subject: some fixes related to connection timeout --- protocols/jabber/s5bytestream.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 4770feda..0abc2c1b 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -100,6 +100,12 @@ void jabber_bs_free_transfer( file_transfer_t *ft) { struct bs_transfer *bt = tf->streamhandle; jabber_streamhost_t *sh; + if ( bt->connect_timeout ) + { + b_event_remove( bt->connect_timeout ); + bt->connect_timeout = 0; + } + if ( tf->watch_in ) b_event_remove( tf->watch_in ); @@ -347,7 +353,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con struct bs_transfer *bt = data; short revents; - if ( !jabber_bs_poll( bt, fd, &revents ) ) + if ( ( fd != 0 ) && !jabber_bs_poll( bt, fd, &revents ) ) return FALSE; switch( bt->phase ) @@ -723,7 +729,17 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt } } else { - /* using a proxy */ + /* using a proxy, abort listen */ + + closesocket( tf->fd ); + tf->fd = 0; + + if ( bt->connect_timeout ) + { + b_event_remove( bt->connect_timeout ); + bt->connect_timeout = 0; + } + GSList *shlist; for( shlist = jd->streamhosts ; shlist ; shlist = g_slist_next( shlist ) ) { @@ -887,13 +903,15 @@ gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) { struct jabber_transfer *tf = bt->tf; + struct jabber_data *jd = tf->ic->proto_data; /* TODO: did the receiver get here somehow??? */ imcb_log( tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s", tf->ft->file_name, error ); - imcb_file_canceled( tf->ft, error ); + if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */ + imcb_file_canceled( tf->ft, error ); return FALSE; } -- cgit v1.2.3 From 6cac643f6933e431b90fcb937dec505f989e6a53 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 14 Apr 2008 14:59:15 +0200 Subject: more verbose error logging --- protocols/jabber/s5bytestream.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 0abc2c1b..1008f162 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -352,6 +352,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con struct bs_transfer *bt = data; short revents; + int gret; if ( ( fd != 0 ) && !jabber_bs_poll( bt, fd, &revents ) ) return FALSE; @@ -365,8 +366,8 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con memset( &hints, 0, sizeof( struct addrinfo ) ); hints.ai_socktype = SOCK_STREAM; - if ( getaddrinfo( bt->sh->host, bt->sh->port, &hints, &rp ) != 0 ) - return jabber_bs_abort( bt, "getaddrinfo() failed: %s", strerror( errno ) ); + if ( ( gret = getaddrinfo( bt->sh->host, bt->sh->port, &hints, &rp ) ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo() failed: %s", gai_strerror( gret ) ); ASSERTSOCKOP( bt->tf->fd = fd = socket( rp->ai_family, rp->ai_socktype, 0 ), "Opening socket" ); @@ -922,7 +923,7 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ) { struct jabber_transfer *tf = bt->tf; - int fd; + int fd,gret; char hostname[ HOST_NAME_MAX + 1 ]; struct addrinfo hints, *rp; socklen_t ssize = sizeof( struct sockaddr_storage ); @@ -935,8 +936,8 @@ gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; - if ( getaddrinfo( hostname, "0", &hints, &rp ) != 0 ) - return jabber_bs_abort( bt, "getaddrinfo()" ); + if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) ) != 0 ) + return jabber_bs_abort( bt, "getaddrinfo() failed: %s", gai_strerror( gret ) ); memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); -- cgit v1.2.3 From 4358b10c11410a27af9458c92067549cafbc4c0b Mon Sep 17 00:00:00 2001 From: ulim Date: Sun, 4 May 2008 15:32:15 +0200 Subject: ulibc support, fixes "Invalid SOCKS5 Connect message" problem --- protocols/jabber/iq.c | 5 +++-- protocols/jabber/s5bytestream.c | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 22aa3e7c..eacc85af 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -611,7 +611,7 @@ xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) if( ( bud = jabber_buddy_by_jid( ic, bare_jid , 0 ) ) == NULL ) { /* Who cares about the unknown... */ - imcb_log( ic, "Couldnt find the man: %s", bare_jid); + imcb_log( ic, "Couldn't find buddy: %s", bare_jid); return 0; } @@ -625,6 +625,7 @@ xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) { imcb_log( ic, "WARNING: Couldn't generate feature query" ); xt_free_node( node ); + return 0; } jabber_cache_add( ic, query, jabber_iq_parse_features ); @@ -647,7 +648,7 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no if( ( bud = jabber_buddy_by_jid( ic, xt_find_attr( node, "from") , 0 ) ) == NULL ) { /* Who cares about the unknown... */ - imcb_log( ic, "Couldnt find the man: %s", xt_find_attr( node, "from")); + imcb_log( ic, "Couldn't find buddy: %s", xt_find_attr( node, "from")); return 0; } diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 1008f162..15696501 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -25,6 +25,16 @@ #include "sha1.h" #include +/* Some ifdefs for ulibc (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + struct bs_transfer { struct jabber_transfer *tf; @@ -1047,10 +1057,14 @@ gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition con { struct socks5_message socks5_connect; int msgsize = sizeof( struct socks5_message ); + int ret; - if( !jabber_bs_peek( bt, &socks5_connect, msgsize ) ) + if( !( ret = jabber_bs_peek( bt, &socks5_connect, msgsize ) ) ) return FALSE; + if( ret < msgsize ) + return TRUE; + if( !( socks5_connect.ver == 5) || !( socks5_connect.cmdrep.cmd == 1 ) || !( socks5_connect.atyp == 3 ) || -- cgit v1.2.3 From 29c1456dcadec0d239ffc9d88ea06695b66c435c Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 6 May 2008 02:13:37 +0200 Subject: SOCKS5 bytestream related changes. * allow the SOCKS5 server to not include the pseudo address in its reply(including it is an rfc-style SHOULD in XEP-0065) * ignore if the SOCKS5 server's reply is too short (as is the one from the jabber.cz proxy [apparently using the proxy65 code]) --- protocols/jabber/s5bytestream.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 15696501..996d1f24 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -465,18 +465,28 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con if ( !( ret = jabber_bs_peek( bt, &socks5_reply, sizeof( struct socks5_message ) ) ) ) return FALSE; - if ( ret < sizeof( socks5_reply ) ) + if ( ret < 5 ) /* header up to address length */ return TRUE; + else if( ret < sizeof( struct socks5_message ) ) + { + /* Either a buggy proxy or just one that doesnt regard the SHOULD in XEP-0065 + * saying the reply SHOULD contain the address */ + + ASSERTSOCKOP( ret = recv( fd, &socks5_reply, ret, 0 ), "Dequeuing after MSG_PEEK" ); + } if( !( socks5_reply.ver == 5 ) || - !( socks5_reply.cmdrep.rep == 0 ) || - !( socks5_reply.atyp == 3 ) || - !( socks5_reply.addrlen == 40 ) ) + !( socks5_reply.cmdrep.rep == 0 ) ) return jabber_bs_abort( bt, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", socks5_reply.ver, socks5_reply.cmdrep.rep, socks5_reply.atyp, socks5_reply.addrlen); + + /* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz) + * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect). + * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy + * is sending, it shouldnt matter */ if( bt->tf->ft->sending ) jabber_bs_send_activate( bt ); @@ -742,6 +752,12 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt { /* using a proxy, abort listen */ + if ( tf->watch_in ) + { + b_event_remove( tf->watch_in ); + tf->watch_in = 0; + } + closesocket( tf->fd ); tf->fd = 0; @@ -824,6 +840,8 @@ static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struc return XT_HANDLED; } + imcb_log( tf->ic, "File %s: SOCKS5 handshake and activation successful! Transfer about to start...", tf->ft->file_name ); + /* handshake went through, let's start transferring */ tf->ft->write_request( tf->ft ); -- cgit v1.2.3 From d56ee38d444fb9a4bc0fbf7f699eaf675e019591 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 6 May 2008 02:20:11 +0200 Subject: timeout of transfers after 120 seconds of no progress (bytes received/sent). --- protocols/jabber/si.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index aab71c8b..d47cf7a4 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -54,8 +54,9 @@ void jabber_si_free_transfer( file_transfer_t *ft) void jabber_si_finished( file_transfer_t *ft ) { struct jabber_transfer *tf = ft->data; + time_t diff = time( NULL ) - ft->started; - imcb_log( tf->ic, "File %s transferred successfully!" , ft->file_name ); + imcb_log( tf->ic, "File %s transferred successfully at %d kb/s!" , ft->file_name, (int) ( ft->bytes_transferred / 1024 / diff ) ); } /* file_transfer canceled() callback */ -- cgit v1.2.3 From 44961cb326fde3cb681a79eb28becb74a921a29d Mon Sep 17 00:00:00 2001 From: ulim Date: Wed, 7 May 2008 00:06:22 +0200 Subject: fix bug in new kb/s display for transfers of less than one second. --- protocols/jabber/si.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index d47cf7a4..6e62f684 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -54,7 +54,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) void jabber_si_finished( file_transfer_t *ft ) { struct jabber_transfer *tf = ft->data; - time_t diff = time( NULL ) - ft->started; + time_t diff = time( NULL ) - ft->started ? : 1; imcb_log( tf->ic, "File %s transferred successfully at %d kb/s!" , ft->file_name, (int) ( ft->bytes_transferred / 1024 / diff ) ); } -- cgit v1.2.3 From b5cfc2bdad8a68011bf9215c63293299e4e8dc5c Mon Sep 17 00:00:00 2001 From: ulim Date: Sat, 10 May 2008 12:09:19 +0200 Subject: some changes for sending. * not only query but also respect peer's features (i.e. abort ft if an important feature is not advertised) * wait for proxy discovery to complete before starting the transfer (important for sending to people with auto accept) --- protocols/jabber/jabber.h | 6 +++ protocols/jabber/si.c | 106 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 102 insertions(+), 10 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index fc315710..bc848170 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -153,8 +153,14 @@ struct jabber_transfer /* the stream's private handle */ gpointer streamhandle; + /* timeout for discover queries */ + gint disco_timeout; + gint disco_timeout_fired; + struct im_connection *ic; + struct jabber_buddy *bud; + int watch_in; int watch_out; diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 6e62f684..3afa30ff 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -44,6 +44,9 @@ void jabber_si_free_transfer( file_transfer_t *ft) tf->fd = 0; } + if( tf->disco_timeout ) + b_event_remove( tf->disco_timeout ); + g_free( tf->ini_jid ); g_free( tf->tgt_jid ); g_free( tf->iq_id ); @@ -79,12 +82,90 @@ void jabber_si_canceled( file_transfer_t *ft, char *reason ) } +int jabber_si_check_features( struct jabber_transfer *tf, GSList *features ) { + int foundft = FALSE, foundbt = FALSE, foundsi = FALSE; + + while ( features ) + { + if( !strcmp( features->data, XMLNS_FILETRANSFER ) ) + foundft = TRUE; + if( !strcmp( features->data, XMLNS_BYTESTREAMS ) ) + foundbt = TRUE; + if( !strcmp( features->data, XMLNS_SI ) ) + foundsi = TRUE; + + features = g_slist_next(features); + } + + if( !foundft ) + imcb_file_canceled( tf->ft, "Buddy's client doesn't feature file transfers" ); + else if( !foundbt ) + imcb_file_canceled( tf->ft, "Buddy's client doesn't feature byte streams (required)" ); + else if( !foundsi ) + imcb_file_canceled( tf->ft, "Buddy's client doesn't feature stream initiation (required)" ); + + return foundft && foundbt && foundsi; +} + +void jabber_si_transfer_start( struct jabber_transfer *tf ) { + + if( !jabber_si_check_features( tf, tf->bud->features ) ) + return; + + /* send the request to our buddy */ + jabber_si_send_request( tf->ic, tf->bud->full_jid, tf ); + + /* and start the receive logic */ + imcb_file_recv_start( tf->ft ); + +} + +gboolean jabber_si_waitfor_disco( gpointer data, gint fd, b_input_condition cond ) +{ + struct jabber_transfer *tf = data; + struct jabber_data *jd = tf->ic->proto_data; + + tf->disco_timeout_fired++; + + if( tf->bud->features && jd->have_streamhosts==1 ) { + jabber_si_transfer_start( tf ); + tf->disco_timeout = 0; + return FALSE; + } + + /* 8 seconds should be enough for server and buddy to respond */ + if ( tf->disco_timeout_fired < 16 ) + return TRUE; + + if( !tf->bud->features && jd->have_streamhosts!=1 ) + imcb_file_canceled( tf->ft, "Couldn't get buddy's features or the server's" ); + else if( !tf->bud->features ) + imcb_file_canceled( tf->ft, "Couldn't get buddy's features" ); + else + imcb_file_canceled( tf->ft, "Couldn't get server's features" ); + + tf->disco_timeout = 0; + return FALSE; +} + void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who ) { struct jabber_transfer *tf; struct jabber_data *jd = ic->proto_data; - char *server = jd->server; + struct jabber_buddy *bud; + char *server = jd->server, *s; + + if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) ) + bud = jabber_buddy_by_ext_jid( ic, who, 0 ); + else + bud = jabber_buddy_by_jid( ic, who, 0 ); + if( bud == NULL ) + { + imcb_file_canceled( tf->ft, "Couldn't find buddy (BUG?)" ); + return; + } + 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 ); @@ -94,22 +175,27 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, tf->ft->data = tf; tf->ft->free = jabber_si_free_transfer; tf->ft->finished = jabber_si_finished; + tf->bud = bud; ft->write = jabber_bs_send_write; jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); - /* query the buddy's features */ - jabber_iq_query_features( ic, who ); + /* query buddy's features and server's streaming proxies if neccessary */ - /* query proxies from the server */ - if( !jd->have_streamhosts ) - jabber_iq_query_server( ic, server, XMLNS_DISCO_ITEMS ); + if( !tf->bud->features ) + jabber_iq_query_features( ic, bud->full_jid ); - /* send the request to our buddy */ - jabber_si_send_request( ic, who, tf ); + if( jd->have_streamhosts!=1 ) { + jd->have_streamhosts = 0; + jabber_iq_query_server( ic, server, XMLNS_DISCO_ITEMS ); + } - /* and start the receive logic */ - imcb_file_recv_start( ft ); + /* if we had to do a query, wait for the result. + * Otherwise fire away. */ + if( !tf->bud->features || jd->have_streamhosts!=1 ) + tf->disco_timeout = b_timeout_add( 500, jabber_si_waitfor_disco, tf ); + else + jabber_si_transfer_start( tf ); } /* -- cgit v1.2.3 From cce0450184b4358ef06d91cca985fa3ca389fcd6 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 2 Jun 2008 14:49:33 +0200 Subject: Added textual SOCKS5 error messages. --- protocols/jabber/s5bytestream.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 996d1f24..41bae40a 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -71,6 +71,18 @@ struct socks5_message in_port_t port; } __attribute__ ((packed)); +char *socks5_reply_code[] = { + "succeeded", + "general SOCKS server failure", + "connection not allowed by ruleset", + "Network unreachable", + "Host unreachable", + "Connection refused", + "TTL expired", + "Command not supported", + "Address type not supported", + "unassigned"}; + /* connect() timeout in seconds. */ #define JABBER_BS_CONTIMEOUT 15 /* listen timeout */ @@ -476,12 +488,19 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con } if( !( socks5_reply.ver == 5 ) || - !( socks5_reply.cmdrep.rep == 0 ) ) - return jabber_bs_abort( bt, "SOCKS5 CONNECT failed (reply: ver=%d, rep=%d, atyp=%d, addrlen=%d", + !( socks5_reply.cmdrep.rep == 0 ) ) { + char errstr[128] = ""; + if( ( socks5_reply.ver == 5 ) && ( socks5_reply.cmdrep.rep < + ( sizeof( socks5_reply_code ) / sizeof( socks5_reply_code[0] ) ) ) ) { + sprintf( errstr, "with \"%s\" ", socks5_reply_code[ socks5_reply.cmdrep.rep ] ); + } + return jabber_bs_abort( bt, "SOCKS5 CONNECT failed %s(reply: ver=%d, rep=%d, atyp=%d, addrlen=%d)", + errstr, socks5_reply.ver, socks5_reply.cmdrep.rep, socks5_reply.atyp, socks5_reply.addrlen); + } /* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz) * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect). -- cgit v1.2.3 From 3355a82eae40c89024233ccafc5f0c25dee2dc5c Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 2 Jun 2008 15:26:38 +0200 Subject: added a #define for AI_NUMERICSERV in s5bytestream.c (missing in ulibc). --- protocols/jabber/s5bytestream.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 41bae40a..809fbb22 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -35,6 +35,10 @@ #endif #endif +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +#endif + struct bs_transfer { struct jabber_transfer *tf; -- cgit v1.2.3 From 8a90001ab2cde971abc64b8d5e4174dc4c4b0ae2 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 22 Jul 2008 14:37:49 +0200 Subject: Added an account setting 'proxy'. Note that this is only used for sending. The default ; means let the receiver try a direct connection first and then the proxy discovered from the server (if any). If you know you're firewalled you can remove the . If you want to provide your own proxy try something like ";JID,HOST,PORT". E.g. ";proxy.somewhere.org,123.123.123.123,7777". --- protocols/jabber/jabber.c | 2 + protocols/jabber/s5bytestream.c | 101 ++++++++++++++++++++++++++++++---------- protocols/jabber/si.c | 22 +++++---- 3 files changed, 93 insertions(+), 32 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 04d6f906..8b1d78db 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -61,6 +61,8 @@ static void jabber_init( account_t *acc ) s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; + + s = set_add( &acc->set, "proxy", ";", NULL, acc ); } static void jabber_generate_id_hash( struct jabber_data *jd ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 809fbb22..c492491b 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -361,7 +361,7 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru tf->streamhandle = bt; tf->ft->free = jabber_bs_free_transfer; - jabber_bs_recv_handshake( bt, 0, 0 ); + jabber_bs_recv_handshake( bt, -1, 0 ); return XT_HANDLED; } @@ -380,7 +380,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con short revents; int gret; - if ( ( fd != 0 ) && !jabber_bs_poll( bt, fd, &revents ) ) + if ( ( fd != -1 ) && !jabber_bs_poll( bt, fd, &revents ) ) return FALSE; switch( bt->phase ) @@ -553,7 +553,7 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) if( shlist && shlist->next ) { bt->sh = shlist->next->data; - return jabber_bs_recv_handshake( bt, 0, 0 ); + return jabber_bs_recv_handshake( bt, -1, 0 ); } @@ -619,7 +619,7 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) struct bs_transfer *bt = data; struct jabber_transfer *tf = bt->tf; - if( fd != 0 ) /* called via event thread */ + if( fd != -1 ) /* called via event thread */ { tf->watch_in = 0; ASSERTSOCKOP( ret = recv( fd, tf->ft->buffer, sizeof( tf->ft->buffer ), 0 ) , "Receiving" ); @@ -665,7 +665,7 @@ gboolean jabber_bs_recv_write_request( file_transfer_t *ft ) return FALSE; } - jabber_bs_recv_read( tf->streamhandle, 0 , 0 ); + jabber_bs_recv_read( tf->streamhandle, -1 , 0 ); return TRUE; } @@ -775,14 +775,16 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt { /* using a proxy, abort listen */ - if ( tf->watch_in ) + if( tf->watch_in ) { b_event_remove( tf->watch_in ); tf->watch_in = 0; } - closesocket( tf->fd ); - tf->fd = 0; + if( tf->fd != -1 ) { + closesocket( tf->fd ); + tf->fd = -1; + } if ( bt->connect_timeout ) { @@ -797,7 +799,7 @@ static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt if( strcmp( sh->jid, jid ) == 0 ) { bt->sh = sh; - jabber_bs_recv_handshake( bt, 0, 0 ); + jabber_bs_recv_handshake( bt, -1, 0 ); return XT_HANDLED; } } @@ -871,20 +873,80 @@ static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struc return XT_HANDLED; } +jabber_streamhost_t *jabber_si_parse_proxy( struct im_connection *ic, char *proxy ) +{ + char *host, *port, *jid; + jabber_streamhost_t *sh; + + if( ( ( host = strchr( proxy, ',' ) ) == 0 ) || + ( ( port = strchr( host+1, ',' ) ) == 0 ) ) { + imcb_log( ic, "Error parsing proxy setting: \"%s\" (ignored)", proxy ); + return NULL; + } + + jid = proxy; + *host++ = '\0'; + *port++ = '\0'; + + sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( jid ); + sh->host = g_strdup( host ); + strcpy( sh->port, port ); + + return sh; +} + +void jabber_si_set_proxies( struct bs_transfer *bt ) +{ + struct jabber_transfer *tf = bt->tf; + struct jabber_data *jd = tf->ic->proto_data; + char *proxysetting = g_strdup ( set_getstr( &tf->ic->acc->set, "proxy" ) ); + char *proxy,*next; + char port[6]; + char host[INET6_ADDRSTRLEN]; + jabber_streamhost_t *sh, *sh2; + GSList *streamhosts = jd->streamhosts; + + proxy = proxysetting; + while ( proxy && ( *proxy!='\0' ) ) { + if( ( next = strchr( proxy, ';' ) ) ) + *next++ = '\0'; + + if( ( strcmp( proxy, "" ) == 0 ) && jabber_bs_send_listen( bt, &tf->saddr, host, port ) ) { + sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( tf->ini_jid ); + sh->host = g_strdup( host ); + strcpy( sh->port, port ); + bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + + bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + } else if( strcmp( proxy, "" ) == 0 ) { + while ( streamhosts ) { + sh = g_new0( jabber_streamhost_t, 1 ); + sh2 = streamhosts->data; + sh->jid = g_strdup( sh2->jid ); + sh->host = g_strdup( sh2->host ); + strcpy( sh->port, sh2->port ); + bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + streamhosts = g_slist_next( streamhosts ); + } + } else if( ( sh = jabber_si_parse_proxy( tf->ic, proxy ) ) ) + bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + proxy = next; + } +} + /* * Starts a bytestream. */ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) { - char host[INET6_ADDRSTRLEN]; struct bs_transfer *bt; sha1_state_t sha; char hash_hex[41]; unsigned char hash[20]; int i,ret; - struct jabber_data *jd = tf->ic->proto_data; - jabber_streamhost_t sh; - GSList *streamhosts = jd->streamhosts; /* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value */ sha1_init( &sha ); @@ -904,18 +966,9 @@ gboolean jabber_bs_send_start( struct jabber_transfer *tf ) tf->ft->free = jabber_bs_free_transfer; tf->ft->canceled = jabber_bs_canceled; - if ( !jabber_bs_send_listen( bt, &tf->saddr, sh.host = host, sh.port ) ) - return FALSE; - - bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); - bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); - - sh.jid = tf->ini_jid; + jabber_si_set_proxies( bt ); - /* temporarily add listen address to streamhosts, send the request and remove it */ - streamhosts = g_slist_prepend( streamhosts, &sh ); - ret = jabber_bs_send_request( tf, streamhosts); - streamhosts = g_slist_remove( streamhosts, &sh ); + ret = jabber_bs_send_request( tf, bt->streamhosts); return ret; } diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 3afa30ff..e7cb2a17 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -38,10 +38,10 @@ void jabber_si_free_transfer( file_transfer_t *ft) jd->filetransfers = g_slist_remove( jd->filetransfers, tf ); - if( tf->fd ) + if( tf->fd != -1 ) { close( tf->fd ); - tf->fd = 0; + tf->fd = -1; } if( tf->disco_timeout ) @@ -128,8 +128,8 @@ gboolean jabber_si_waitfor_disco( gpointer data, gint fd, b_input_condition cond tf->disco_timeout_fired++; if( tf->bud->features && jd->have_streamhosts==1 ) { - jabber_si_transfer_start( tf ); tf->disco_timeout = 0; + jabber_si_transfer_start( tf ); return FALSE; } @@ -138,13 +138,14 @@ gboolean jabber_si_waitfor_disco( gpointer data, gint fd, b_input_condition cond return TRUE; if( !tf->bud->features && jd->have_streamhosts!=1 ) - imcb_file_canceled( tf->ft, "Couldn't get buddy's features or the server's" ); + imcb_log( tf->ic, "Couldn't get buddy's features nor discover all services of the server" ); else if( !tf->bud->features ) - imcb_file_canceled( tf->ft, "Couldn't get buddy's features" ); + imcb_log( tf->ic, "Couldn't get buddy's features" ); else - imcb_file_canceled( tf->ft, "Couldn't get server's features" ); + imcb_log( tf->ic, "Couldn't discover some of the server's services" ); tf->disco_timeout = 0; + jabber_si_transfer_start( tf ); return FALSE; } @@ -172,6 +173,7 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, tf->ic = ic; tf->ft = ft; + tf->fd = -1; tf->ft->data = tf; tf->ft->free = jabber_si_free_transfer; tf->ft->finished = jabber_si_finished; @@ -185,10 +187,13 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, if( !tf->bud->features ) jabber_iq_query_features( ic, bud->full_jid ); - if( jd->have_streamhosts!=1 ) { + /* If is not set don't check for proxies */ + if( ( jd->have_streamhosts!=1 ) && ( jd->streamhosts==NULL ) && + ( strstr( set_getstr( &ic->acc->set, "proxy" ), "" ) != NULL ) ) { jd->have_streamhosts = 0; jabber_iq_query_server( ic, server, XMLNS_DISCO_ITEMS ); - } + } else if ( jd->streamhosts!=NULL ) + jd->have_streamhosts = 1; /* if we had to do a query, wait for the result. * Otherwise fire away. */ @@ -308,6 +313,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st tf->sid = g_strdup( sid ); tf->ic = ic; tf->ft = ft; + tf->fd = -1; tf->ft->data = tf; tf->ft->accept = jabber_si_answer_request; tf->ft->free = jabber_si_free_transfer; -- cgit v1.2.3 From 4ac647dbcef152bebde7209f7c9cbbf8a5e0fc37 Mon Sep 17 00:00:00 2001 From: ulim Date: Mon, 4 Aug 2008 16:21:49 +0200 Subject: 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) --- protocols/jabber/jabber.c | 12 ++++++++++++ protocols/jabber/s5bytestream.c | 14 +++++++------- protocols/jabber/si.c | 15 --------------- 3 files changed, 19 insertions(+), 22 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 8b1d78db..987ef96e 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -244,6 +244,18 @@ static void jabber_logout( struct im_connection *ic ) { struct jabber_data *jd = ic->proto_data; + while( jd->filetransfers ) + imcb_file_canceled( ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" ); + + while( jd->streamhosts ) + { + jabber_streamhost_t *sh = jd->streamhosts->data; + jd->streamhosts = g_slist_remove( jd->streamhosts, sh ); + g_free( sh->jid ); + g_free( sh->host ); + g_free( sh ); + } + if( jd->fd >= 0 ) jabber_end_stream( ic ); diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index c492491b..7e4ca2ab 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -642,11 +642,11 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) if( ret == 0 ) return jabber_bs_abort( bt, "Remote end closed connection" ); - if( tf->bytesread == 0 ) - tf->ft->started = time( NULL ); - tf->bytesread += ret; + if( tf->bytesread >= tf->ft->file_size ) + imcb_file_finished( tf->ft ); + tf->ft->write( tf->ft, tf->ft->buffer, ret ); return FALSE; @@ -700,16 +700,16 @@ gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int l /* TODO: catch broken pipe */ ASSERTSOCKOP( ret = send( tf->fd, buffer, len, 0 ), "Sending" ); - if( tf->byteswritten == 0 ) - tf->ft->started = time( NULL ); - tf->byteswritten += ret; /* 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 ); + if( tf->byteswritten >= ft->file_size ) + imcb_file_finished( ft ); + else + bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); return TRUE; } diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index e7cb2a17..e7aeffc9 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -53,15 +53,6 @@ void jabber_si_free_transfer( file_transfer_t *ft) g_free( tf->sid ); } -/* file_transfer finished() callback */ -void jabber_si_finished( file_transfer_t *ft ) -{ - struct jabber_transfer *tf = ft->data; - time_t diff = time( NULL ) - ft->started ? : 1; - - imcb_log( tf->ic, "File %s transferred successfully at %d kb/s!" , ft->file_name, (int) ( ft->bytes_transferred / 1024 / diff ) ); -} - /* file_transfer canceled() callback */ void jabber_si_canceled( file_transfer_t *ft, char *reason ) { @@ -176,7 +167,6 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, tf->fd = -1; tf->ft->data = tf; tf->ft->free = jabber_si_free_transfer; - tf->ft->finished = jabber_si_finished; tf->bud = bud; ft->write = jabber_bs_send_write; @@ -301,10 +291,6 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st /* Request is fine. */ - imcb_log( ic, "File transfer request from %s for %s (%zd kb). ", xt_find_attr( node, "from" ), name, size/1024 ); - - imcb_log( ic, "Accept the file transfer if you'd like the file. If you don't, issue the 'transfers reject' command."); - tf = g_new0( struct jabber_transfer, 1 ); tf->ini_jid = g_strdup( ini_jid ); @@ -317,7 +303,6 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st tf->ft->data = tf; tf->ft->accept = jabber_si_answer_request; tf->ft->free = jabber_si_free_transfer; - tf->ft->finished = jabber_si_finished; tf->ft->canceled = jabber_si_canceled; jd->filetransfers = g_slist_prepend( jd->filetransfers, tf ); -- cgit v1.2.3 From a02f34fb047af728f991ced3688c5e804c130878 Mon Sep 17 00:00:00 2001 From: ulim Date: Tue, 12 Aug 2008 01:07:12 +0200 Subject: Added conf entries and lib/ftutil.[ch]. ft_listen = :;: to specify listening addresses for the bitlbee<->client connection and the bitlbee<->IM peer connection, respectively. ft_max_size should be obvious. ft_max_kbps should limit the kilobits per second per transfer (not implemented yet). --- protocols/jabber/s5bytestream.c | 87 ++++++++--------------------------------- 1 file changed, 16 insertions(+), 71 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 7e4ca2ab..3c5ce503 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -23,22 +23,9 @@ #include "jabber.h" #include "sha1.h" +#include "lib/ftutil.h" #include -/* Some ifdefs for ulibc (Thanks to Whoopie) */ -#ifndef HOST_NAME_MAX -#include -#ifdef MAXHOSTNAMELEN -#define HOST_NAME_MAX MAXHOSTNAMELEN -#else -#define HOST_NAME_MAX 255 -#endif -#endif - -#ifndef AI_NUMERICSERV -#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ -#endif - struct bs_transfer { struct jabber_transfer *tf; @@ -114,7 +101,6 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru gboolean jabber_bs_send_handshake_abort( struct bs_transfer *bt, char *error ); gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts ); gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition cond ); -gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ); static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); void jabber_bs_send_activate( struct bs_transfer *bt ); @@ -901,7 +887,7 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) struct jabber_transfer *tf = bt->tf; struct jabber_data *jd = tf->ic->proto_data; char *proxysetting = g_strdup ( set_getstr( &tf->ic->acc->set, "proxy" ) ); - char *proxy,*next; + char *proxy, *next, *errmsg = NULL; char port[6]; char host[INET6_ADDRSTRLEN]; jabber_streamhost_t *sh, *sh2; @@ -912,15 +898,21 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) if( ( next = strchr( proxy, ';' ) ) ) *next++ = '\0'; - if( ( strcmp( proxy, "" ) == 0 ) && jabber_bs_send_listen( bt, &tf->saddr, host, port ) ) { - sh = g_new0( jabber_streamhost_t, 1 ); - sh->jid = g_strdup( tf->ini_jid ); - sh->host = g_strdup( host ); - strcpy( sh->port, port ); - bt->streamhosts = g_slist_append( bt->streamhosts, sh ); + if( strcmp( proxy, "" ) == 0 ) { + if( ( tf->fd = ft_listen( &tf->saddr, host, port, FALSE, &errmsg ) ) != -1 ) { + sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( tf->ini_jid ); + sh->host = g_strdup( host ); + strcpy( sh->port, port ); + bt->streamhosts = g_slist_append( bt->streamhosts, sh ); - bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); - bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); + } else { + imcb_log( tf->ic, "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s", + tf->ft->file_name, + errmsg ); + } } else if( strcmp( proxy, "" ) == 0 ) { while ( streamhosts ) { sh = g_new0( jabber_streamhost_t, 1 ); @@ -1021,53 +1013,6 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) return FALSE; } -/* - * Creates a listening socket and returns it in saddr_ptr. - */ -gboolean jabber_bs_send_listen( struct bs_transfer *bt, struct sockaddr_storage *saddr, char *host, char *port ) -{ - struct jabber_transfer *tf = bt->tf; - int fd,gret; - char hostname[ HOST_NAME_MAX + 1 ]; - struct addrinfo hints, *rp; - socklen_t ssize = sizeof( struct sockaddr_storage ); - - /* won't be long till someone asks for this to be configurable :) */ - - ASSERTSOCKOP( gethostname( hostname, sizeof( hostname ) ), "gethostname()" ); - - memset( &hints, 0, sizeof( struct addrinfo ) ); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; - - if ( ( gret = getaddrinfo( hostname, "0", &hints, &rp ) ) != 0 ) - return jabber_bs_abort( bt, "getaddrinfo() failed: %s", gai_strerror( gret ) ); - - memcpy( saddr, rp->ai_addr, rp->ai_addrlen ); - - ASSERTSOCKOP( fd = tf->fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); - - ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, rp->ai_addrlen ), "Binding socket" ); - - freeaddrinfo( rp ); - - ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); - - if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? - ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr - , host, INET6_ADDRSTRLEN ) ) - return jabber_bs_abort( bt, "inet_ntop failed on listening socket" ); - - ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); - - if( saddr->ss_family == AF_INET ) - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in *) saddr )->sin_port ) ); - else - sprintf( port, "%d", ntohs( ( ( struct sockaddr_in6 *) saddr )->sin6_port ) ); - - return TRUE; -} - /* * SOCKS5BYTESTREAM protocol for the sender */ -- cgit v1.2.3 From e046390da36e369c94af607fdedfe7b9f99d9e47 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 00:25:54 +0100 Subject: Make purple use BitlBee's event handling API. Since the APIs never really diverged too much this is fairly transparent. I did rename and redefine GAIM_INPUT_* variables to really make it work without adding another stupid layer in between. One problem left, the new libpurple input API doesn't care about return values. Fixing that in the next CL. --- protocols/jabber/io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index 10efad37..3b0ef3ef 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -63,7 +63,7 @@ int jabber_write( struct im_connection *ic, char *buf, int len ) it via the event handler. If not, add the handler. (In most cases it probably won't be necessary.) */ if( ( ret = jabber_write_queue( ic ) ) && jd->tx_len > 0 ) - jd->w_inpa = b_input_add( jd->fd, GAIM_INPUT_WRITE, jabber_write_callback, ic ); + jd->w_inpa = b_input_add( jd->fd, B_EV_IO_WRITE, jabber_write_callback, ic ); } else { @@ -528,7 +528,7 @@ gboolean jabber_start_stream( struct im_connection *ic ) jd->xt = xt_new( jabber_handlers, ic ); if( jd->r_inpa <= 0 ) - jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, ic ); + jd->r_inpa = b_input_add( jd->fd, B_EV_IO_READ, jabber_read_callback, ic ); greet = g_strdup_printf( "" " Date: Sun, 13 Dec 2009 14:48:56 +0000 Subject: No functional changes, just some style "fixes". Although I admit I'm somewhat growing out of my own coding style, I do try to keep things consistent at least within files. Comments are now in reviewboard: http://code.bitlbee.org/rb/r/13/ --- protocols/jabber/Makefile | 2 +- protocols/jabber/iq.c | 38 +++++++++++++++++++++++--------------- protocols/jabber/jabber.c | 4 ++-- protocols/jabber/jabber.h | 2 +- 4 files changed, 27 insertions(+), 19 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index 891df5eb..78a02696 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o si.o s5bytestream.o +objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index f17a7bb5..e0658a9e 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -98,9 +98,9 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) XMLNS_CHATSTATES, XMLNS_MUC, XMLNS_PING, - XMLNS_SI, - XMLNS_BYTESTREAMS, - XMLNS_FILETRANSFER, + XMLNS_SI, + XMLNS_BYTESTREAMS, + XMLNS_FILETRANSFER, NULL }; const char **f; @@ -126,16 +126,18 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) } else if( strcmp( type, "set" ) == 0 ) { - if( ( c = xt_find_node( node->children, "si" ) ) && - ( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) ) + if( ( c = xt_find_node( node->children, "si" ) ) && + ( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) ) { return jabber_si_handle_request( ic, node, c ); - } else if( !( c = xt_find_node( node->children, "query" ) ) || - !( s = xt_find_attr( c, "xmlns" ) ) ) + } + else if( !( c = xt_find_node( node->children, "query" ) ) || + !( s = xt_find_attr( c, "xmlns" ) ) ) { imcb_log( ic, "Warning: Received incomplete IQ-%s packet", type ); return XT_HANDLED; - } else if( strcmp( s, XMLNS_ROSTER ) == 0 ) + } + else if( strcmp( s, XMLNS_ROSTER ) == 0 ) { /* This is a roster push. XMPP servers send this when someone was added to (or removed from) the buddy list. AFAIK they're @@ -159,11 +161,13 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) reply = jabber_make_error_packet( node, "not-allowed", "cancel", NULL ); pack = 0; } - } else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 ) + } + else if( strcmp( s, XMLNS_BYTESTREAMS ) == 0 ) { - /* Bytestream Request (stage 2 of file transfer) */ + /* Bytestream Request (stage 2 of file transfer) */ return jabber_bs_recv_request( ic, node, c ); - } else + } + else { xt_free_node( reply ); reply = jabber_make_error_packet( node, "feature-not-implemented", "cancel", NULL ); @@ -654,9 +658,10 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no } c = c->children; - while( ( c = xt_find_node( c, "feature" ) ) ) { + while( ( c = xt_find_node( c, "feature" ) ) ) + { feature = xt_find_attr( c, "var" ); - bud->features = g_slist_append(bud->features, g_strdup(feature) ); + bud->features = g_slist_append( bud->features, g_strdup( feature ) ); c = c->next; } @@ -718,7 +723,8 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n c = c->next; } - } else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) + } + else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) { char *category, *type; @@ -736,7 +742,8 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n c = c->next; } - } else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_BYTESTREAMS ) == 0 ) + } + else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_BYTESTREAMS ) == 0 ) { char *host, *jid; int port; @@ -760,5 +767,6 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n if( jd->have_streamhosts == 0 ) jd->have_streamhosts++; + return XT_HANDLED; } diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 15341c9b..02461101 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -62,6 +62,8 @@ static void jabber_init( account_t *acc ) s->flags |= ACC_SET_OFFLINE_ONLY; s = set_add( &acc->set, "priority", "0", set_eval_priority, acc ); + + s = set_add( &acc->set, "proxy", ";", NULL, acc ); s = set_add( &acc->set, "resource", "BitlBee", NULL, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; @@ -79,8 +81,6 @@ static void jabber_init( account_t *acc ) s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; - - s = set_add( &acc->set, "proxy", ";", NULL, acc ); } static void jabber_generate_id_hash( struct jabber_data *jd ); diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 9f101f83..898c4978 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -59,7 +59,7 @@ typedef enum have a real JID. */ } jabber_buddy_flags_t; -/* Stores a streamhost's(a.k.a. proxy) data */ +/* Stores a streamhost's (a.k.a. proxy) data */ typedef struct { char *jid; -- cgit v1.2.3 From 60e4df367e5c3af0eb1aada19f9c39ef7079e8e6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 17 Mar 2010 23:23:27 +0000 Subject: Small cleanup. The max_packet_size variable doesn't seem to be read anywhere, and reworked string handling in ft_listen() a little bit. --- protocols/jabber/s5bytestream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 3c5ce503..c292e61e 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -889,7 +889,7 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) char *proxysetting = g_strdup ( set_getstr( &tf->ic->acc->set, "proxy" ) ); char *proxy, *next, *errmsg = NULL; char port[6]; - char host[INET6_ADDRSTRLEN]; + char host[HOST_NAME_MAX+1]; jabber_streamhost_t *sh, *sh2; GSList *streamhosts = jd->streamhosts; -- cgit v1.2.3 From c1a3c27575ac6ae77cbffb1e48d02ebee3f83152 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 17 Mar 2010 23:41:07 +0000 Subject: Avoid some NULL pointer dereferences on malformed XMPP packets. --- protocols/jabber/iq.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index d690d336..22f97b2a 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -657,18 +657,20 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no { struct xt_node *c; struct jabber_buddy *bud; - char *feature; + char *feature, *xmlns, *from; if( !( c = xt_find_node( node->children, "query" ) ) || - !( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) ) + !( from = xt_find_attr( c, "from" ) ) || + !( xmlns = xt_find_attr( c, "xmlns" ) ) || + !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); return XT_HANDLED; } - if( ( bud = jabber_buddy_by_jid( ic, xt_find_attr( node, "from") , 0 ) ) == NULL ) + if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL ) { /* Who cares about the unknown... */ - imcb_log( ic, "Couldn't find buddy: %s", xt_find_attr( node, "from")); + imcb_log( ic, "Couldn't find buddy: %s", from ); return 0; } @@ -676,7 +678,8 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no while( ( c = xt_find_node( c, "feature" ) ) ) { feature = xt_find_attr( c, "var" ); - bud->features = g_slist_append( bud->features, g_strdup( feature ) ); + if( feature ) + bud->features = g_slist_append( bud->features, g_strdup( feature ) ); c = c->next; } @@ -712,9 +715,11 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n { struct xt_node *c; struct jabber_data *jd = ic->proto_data; + char *xmlns, *from; if( !( c = xt_find_node( node->children, "query" ) ) || - !xt_find_attr( node, "from" ) ) + !( from = xt_find_attr( node, "from" ) ) || + !( xmlns = xt_find_attr( c, "xmlns" ) ) ) { imcb_log( ic, "WARNING: Received incomplete IQ-result packet for discover" ); return XT_HANDLED; @@ -722,24 +727,24 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n jd->have_streamhosts++; - if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_ITEMS ) == 0 ) + if( strcmp( xmlns, XMLNS_DISCO_ITEMS ) == 0 ) { - char *item, *itemjid; + char *itemjid; /* answer from server */ c = c->children; while( ( c = xt_find_node( c, "item" ) ) ) { - item = xt_find_attr( c, "name" ); itemjid = xt_find_attr( c, "jid" ); - - jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO ); + + if( itemjid ) + jabber_iq_query_server( ic, itemjid, XMLNS_DISCO_INFO ); c = c->next; } } - else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_DISCO_INFO ) == 0 ) + else if( xmlns, XMLNS_DISCO_INFO ) == 0 ) { char *category, *type; @@ -753,27 +758,29 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n if( type && ( strcmp( type, "bytestreams" ) == 0 ) && category && ( strcmp( category, "proxy" ) == 0 ) ) - jabber_iq_query_server( ic, xt_find_attr( node, "from" ), XMLNS_BYTESTREAMS ); + jabber_iq_query_server( ic, from, XMLNS_BYTESTREAMS ); c = c->next; } } - else if( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_BYTESTREAMS ) == 0 ) + else if( xmlns, XMLNS_BYTESTREAMS ) == 0 ) { - char *host, *jid; + char *host, *jid, *port_s; int port; /* answer from proxy */ if( ( c = xt_find_node( c->children, "streamhost" ) ) && ( host = xt_find_attr( c, "host" ) ) && - ( port = atoi( xt_find_attr( c, "port" ) ) ) && + ( port_s = xt_find_attr( c, "port" ) ) && + ( sscanf( port_s, "%d", &port ) == 1 ) && ( jid = xt_find_attr( c, "jid" ) ) ) { jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); + sh->jid = g_strdup( jid ); sh->host = g_strdup( host ); - sprintf( sh->port, "%u", port ); + g_snprintf( sh->port, sizeof( sh->port ), "%u", port ); imcb_log( ic, "Proxy found: jid %s host %s port %u", jid, host, port ); jd->streamhosts = g_slist_append( jd->streamhosts, sh ); -- cgit v1.2.3 From 42fc5b6cfed51ac011df8877cf5e24f00828e8be Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 17 Mar 2010 23:47:35 +0000 Subject: Ouch, committing stuff without even checking if it compiles is bad style. :-) --- protocols/jabber/iq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 22f97b2a..e3c77d3d 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -744,7 +744,7 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n c = c->next; } } - else if( xmlns, XMLNS_DISCO_INFO ) == 0 ) + else if( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) { char *category, *type; @@ -763,7 +763,7 @@ xt_status jabber_iq_parse_server_features( struct im_connection *ic, struct xt_n c = c->next; } } - else if( xmlns, XMLNS_BYTESTREAMS ) == 0 ) + else if( strcmp( xmlns, XMLNS_BYTESTREAMS ) == 0 ) { char *host, *jid, *port_s; int port; -- cgit v1.2.3 From 78d254f110d47f9e0c3a8f12259f93a4faa2130d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 18 Mar 2010 00:07:16 +0000 Subject: More small fixes. (NULL derefs and s/close/disconnect/) --- protocols/jabber/s5bytestream.c | 5 +++-- protocols/jabber/si.c | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index c292e61e..5fcbef6a 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -281,11 +281,12 @@ int jabber_bs_recv_request( struct im_connection *ic, struct xt_node *node, stru shnode = qnode->children; while( ( shnode = xt_find_node( shnode, "streamhost" ) ) ) { - char *jid, *host; + char *jid, *host, *port_s; int port; if( ( jid = xt_find_attr( shnode, "jid" ) ) && ( host = xt_find_attr( shnode, "host" ) ) && - ( ( port = atoi( xt_find_attr( shnode, "port" ) ) ) ) ) + ( port_s = xt_find_attr( shnode, "port" ) ) && + ( sscanf( port_s, "%d", &port ) == 1 ) ) { jabber_streamhost_t *sh = g_new0( jabber_streamhost_t, 1 ); sh->jid = g_strdup(jid); diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index e7aeffc9..424db8b3 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -40,7 +40,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) if( tf->fd != -1 ) { - close( tf->fd ); + disconnect( tf->fd ); tf->fd = -1; } @@ -51,6 +51,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) g_free( tf->tgt_jid ); g_free( tf->iq_id ); g_free( tf->sid ); + g_free( tf ); } /* file_transfer canceled() callback */ @@ -203,7 +204,7 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, struct xt_node *sinode) { struct xt_node *c, *d, *reply; - char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid; + char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid, *size_s; struct jabber_buddy *bud; int requestok = FALSE; char *name; @@ -229,7 +230,8 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st !( d = xt_find_node( sinode->children, "file" ) ) || !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || !( name = xt_find_attr( d, "name" ) ) || - !( size = (size_t) atoll( xt_find_attr( d, "size" ) ) ) || + !( size_s = xt_find_attr( d, "size" ) ) || + !( 1 == sscanf( size_s, "%lld", &size ) ) || !( d = xt_find_node( sinode->children, "feature" ) ) || !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || !( d = xt_find_node( d->children, "x" ) ) || -- cgit v1.2.3 From b8a491db597ba2d82cc8eddc727509197747f0d5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 18 Mar 2010 00:30:38 +0000 Subject: Some compiler warning fixes and renamed "transfers" command to just "transfer" since all root commands are singular so far. --- protocols/jabber/s5bytestream.c | 2 +- protocols/jabber/si.c | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 5fcbef6a..bb1d9e2e 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -829,7 +829,7 @@ static xt_status jabber_bs_send_handle_activate( struct im_connection *ic, struc { char *sid; GSList *tflist; - struct jabber_transfer *tf; + struct jabber_transfer *tf = NULL; struct xt_node *query; struct jabber_data *jd = ic->proto_data; diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index 424db8b3..a87f7b8b 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -40,7 +40,7 @@ void jabber_si_free_transfer( file_transfer_t *ft) if( tf->fd != -1 ) { - disconnect( tf->fd ); + closesocket( tf->fd ); tf->fd = -1; } @@ -155,10 +155,10 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, if( bud == NULL ) { - imcb_file_canceled( tf->ft, "Couldn't find buddy (BUG?)" ); + imcb_file_canceled( ft, "Couldn't find buddy (BUG?)" ); return; } - + 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 ); @@ -231,7 +231,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || !( name = xt_find_attr( d, "name" ) ) || !( size_s = xt_find_attr( d, "size" ) ) || - !( 1 == sscanf( size_s, "%lld", &size ) ) || + !( 1 == sscanf( size_s, "%zd", &size ) ) || !( d = xt_find_node( sinode->children, "feature" ) ) || !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || !( d = xt_find_node( d->children, "x" ) ) || @@ -241,14 +241,15 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) ) { imcb_log( ic, "WARNING: Received incomplete Stream Initiation request" ); - } else + } + else { /* Check if we support one of the options */ c = d->children; while( ( c = xt_find_node( c, "option" ) ) ) - if( ( d = xt_find_node( c->children, "value" ) ) && - ( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) + if( ( d = xt_find_node( c->children, "value" ) ) && + ( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) { requestok = TRUE; break; @@ -258,10 +259,11 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st imcb_log( ic, "WARNING: Unsupported file transfer request from %s", ini_jid); } - if ( requestok ) + if( requestok ) { /* Figure out who the transfer should come frome... */ + ext_jid = ini_jid; if( ( s = strchr( ini_jid, '/' ) ) ) { if( ( bud = jabber_buddy_by_jid( ic, ini_jid, GET_BUDDY_EXACT ) ) ) @@ -281,8 +283,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st *s = '/'; } - - if ( !requestok ) + else { reply = jabber_make_error_packet( node, "item-not-found", "cancel", NULL ); if (!jabber_write_packet( ic, reply )) -- cgit v1.2.3 From 699376f7c3b3d6aff18af0601fa1f1ac6c5a2892 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 00:39:45 +0000 Subject: Read the from attribute from the main stanza, not the inside query. Also fixing another potential NULL pointer dereference. --- protocols/jabber/iq.c | 4 ++-- protocols/jabber/si.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index e3c77d3d..dfdc6887 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -659,8 +659,8 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no struct jabber_buddy *bud; char *feature, *xmlns, *from; - if( !( c = xt_find_node( node->children, "query" ) ) || - !( from = xt_find_attr( c, "from" ) ) || + if( !( from = xt_find_attr( node, "from" ) ) || + !( c = xt_find_node( node->children, "query" ) ) || !( xmlns = xt_find_attr( c, "xmlns" ) ) || !( strcmp( xmlns, XMLNS_DISCO_INFO ) == 0 ) ) { diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index a87f7b8b..b76fb3e0 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -249,6 +249,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st c = d->children; while( ( c = xt_find_node( c, "option" ) ) ) if( ( d = xt_find_node( c->children, "value" ) ) && + ( d->text != NULL ) && ( strcmp( d->text, XMLNS_BYTESTREAMS ) == 0 ) ) { requestok = TRUE; -- cgit v1.2.3 From 54a20149778028bff730511c6cc8027f61634124 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 00:46:18 +0000 Subject: xt_status functions should always return an XT_ status, it's not a boolean. --- protocols/jabber/iq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index dfdc6887..3e2eb870 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -632,7 +632,7 @@ xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) { /* Who cares about the unknown... */ imcb_log( ic, "Couldn't find buddy: %s", bare_jid); - return 0; + return XT_HANDLED; } if( bud->features ) /* been here already */ @@ -645,12 +645,12 @@ xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ) { imcb_log( ic, "WARNING: Couldn't generate feature query" ); xt_free_node( node ); - return 0; + return XT_HANDLED; } jabber_cache_add( ic, query, jabber_iq_parse_features ); - return jabber_write_packet( ic, query ); + return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT; } xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ) @@ -671,7 +671,7 @@ xt_status jabber_iq_parse_features( struct im_connection *ic, struct xt_node *no { /* Who cares about the unknown... */ imcb_log( ic, "Couldn't find buddy: %s", from ); - return 0; + return XT_HANDLED; } c = c->children; @@ -705,7 +705,7 @@ xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xml jd->have_streamhosts--; jabber_cache_add( ic, query, jabber_iq_parse_server_features ); - return jabber_write_packet( ic, query ); + return jabber_write_packet( ic, query ) ? XT_HANDLED : XT_ABORT; } /* -- cgit v1.2.3 From aed152f005a70e04f7d833dc4fb468e400e54fb3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 01:02:03 +0000 Subject: Killed the last str*cmp(xml_find_attr) combos. --- protocols/jabber/iq.c | 3 ++- protocols/jabber/si.c | 39 ++++++++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 16 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 3e2eb870..f5fbdc13 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -127,7 +127,8 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) else if( strcmp( type, "set" ) == 0 ) { if( ( c = xt_find_node( node->children, "si" ) ) && - ( strcmp( xt_find_attr( c, "xmlns" ), XMLNS_SI ) == 0 ) ) + ( s = xt_find_attr( c, "xmlns" ) ) && + ( strcmp( s, XMLNS_SI ) == 0 ) ) { return jabber_si_handle_request( ic, node, c ); } diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index b76fb3e0..eff4aad7 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -207,7 +207,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid, *size_s; struct jabber_buddy *bud; int requestok = FALSE; - char *name; + char *name, *cmp; size_t size; struct jabber_transfer *tf; struct jabber_data *jd = ic->proto_data; @@ -226,19 +226,25 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st !( tgt_jid = xt_find_attr( node, "to" ) ) || !( iq_id = xt_find_attr( node, "id" ) ) || !( sid = xt_find_attr( sinode, "id" ) ) || - !( strcmp( xt_find_attr( sinode, "profile" ), XMLNS_FILETRANSFER ) == 0 ) || + !( cmp = xt_find_attr( sinode, "profile" ) ) || + !( 0 == strcmp( cmp, XMLNS_FILETRANSFER ) ) || !( d = xt_find_node( sinode->children, "file" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( 0 == strcmp( cmp, XMLNS_FILETRANSFER ) ) || !( name = xt_find_attr( d, "name" ) ) || !( size_s = xt_find_attr( d, "size" ) ) || !( 1 == sscanf( size_s, "%zd", &size ) ) || !( d = xt_find_node( sinode->children, "feature" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( 0 == strcmp( cmp, XMLNS_FEATURE ) ) || !( d = xt_find_node( d->children, "x" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_XDATA ) == 0 ) || - !( strcmp( xt_find_attr( d, "type" ), "form" ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( 0 == strcmp( cmp, XMLNS_XDATA ) ) || + !( cmp = xt_find_attr( d, "type" ) ) || + !( 0 == strcmp( cmp, "form" ) ) || !( d = xt_find_node( d->children, "field" ) ) || - !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) ) + !( cmp = xt_find_attr( d, "var" ) ) || + !( 0 == strcmp( cmp, "stream-method" ) ) ) { imcb_log( ic, "WARNING: Received incomplete Stream Initiation request" ); } @@ -366,7 +372,7 @@ 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, *iq_id; + char *ini_jid, *tgt_jid, *iq_id, *cmp; GSList *tflist; struct jabber_transfer *tf=NULL; struct jabber_data *jd = ic->proto_data; @@ -391,16 +397,19 @@ static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_n !( 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 ) || -/* !( d = xt_find_node( c->children, "file" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FILETRANSFER ) == 0 ) || */ + !( cmp = xt_find_attr( c, "xmlns" ) ) || + !( strcmp( cmp, XMLNS_SI ) == 0 ) || !( d = xt_find_node( c->children, "feature" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_FEATURE ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( strcmp( cmp, XMLNS_FEATURE ) == 0 ) || !( d = xt_find_node( d->children, "x" ) ) || - !( strcmp( xt_find_attr( d, "xmlns" ), XMLNS_XDATA ) == 0 ) || - !( strcmp( xt_find_attr( d, "type" ), "submit" ) == 0 ) || + !( cmp = xt_find_attr( d, "xmlns" ) ) || + !( strcmp( cmp, XMLNS_XDATA ) == 0 ) || + !( cmp = xt_find_attr( d, "type" ) ) || + !( strcmp( cmp, "submit" ) == 0 ) || !( d = xt_find_node( d->children, "field" ) ) || - !( strcmp( xt_find_attr( d, "var" ), "stream-method" ) == 0 ) || + !( cmp = xt_find_attr( d, "var" ) ) || + !( strcmp( cmp, "stream-method" ) == 0 ) || !( d = xt_find_node( d->children, "value" ) ) ) { imcb_log( ic, "WARNING: Received incomplete Stream Initiation response" ); -- cgit v1.2.3 From a81d679654e36055ce9913cd7541885b41380947 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 15:56:59 +0000 Subject: Fixed jabber_bs_peek() to deal with incomplete packets as well. --- protocols/jabber/s5bytestream.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index bb1d9e2e..58a6c2e4 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -45,6 +45,9 @@ struct bs_transfer { char *pseudoadr; gint connect_timeout; + + char peek_buf[64]; + int peek_buf_len; }; struct socks5_message @@ -149,20 +152,26 @@ gboolean jabber_bs_peek( struct bs_transfer *bt, void *buffer, int buflen ) int ret; int fd = bt->tf->fd; - ASSERTSOCKOP( ret = recv( fd, buffer, buflen, MSG_PEEK ), "MSG_PEEK'ing" ); + if( buflen > sizeof( bt->peek_buf ) ) + return jabber_bs_abort( bt, "BUG: %d > sizeof(peek_buf)", buflen ); + + ASSERTSOCKOP( ret = recv( fd, bt->peek_buf + bt->peek_buf_len, + buflen - bt->peek_buf_len, 0 ), "recv() on SOCKS5 connection" ); if( ret == 0 ) return jabber_bs_abort( bt, "Remote end closed connection" ); - - if( ret < buflen ) - return ret; - - ASSERTSOCKOP( ret = recv( fd, buffer, buflen, 0 ), "Dequeuing after MSG_PEEK" ); - - if( ret != buflen ) - return jabber_bs_abort( bt, "recv returned less than previous recv with MSG_PEEK" ); - return ret; + bt->peek_buf_len += ret; + memcpy( buffer, bt->peek_buf, bt->peek_buf_len ); + + if( bt->peek_buf_len == buflen ) + { + /* If we have everything the caller wanted, reset the peek buffer. */ + bt->peek_buf_len = 0; + return buflen; + } + else + return bt->peek_buf_len; } @@ -559,6 +568,7 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); bt->tf->watch_in = 0; + /* MUST always return FALSE! */ return FALSE; } @@ -1011,6 +1021,7 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */ imcb_file_canceled( tf->ft, error ); + /* MUST always return FALSE! */ return FALSE; } -- cgit v1.2.3 From 1cc0df34f742f93f995b68210de3d1f2eac2b5ac Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 16:52:22 +0000 Subject: s/last_act/last_msg/ now. --- protocols/jabber/si.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index eff4aad7..bfb64f11 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -275,7 +275,7 @@ int jabber_si_handle_request( struct im_connection *ic, struct xt_node *node, st { if( ( bud = jabber_buddy_by_jid( ic, ini_jid, GET_BUDDY_EXACT ) ) ) { - bud->last_act = time( NULL ); + bud->last_msg = time( NULL ); ext_jid = bud->ext_jid ? : bud->bare_jid; } else -- cgit v1.2.3 From 17a6ee93f4fbefe8b4356d884fdd95f4e72ce8cc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Apr 2010 16:37:06 +0200 Subject: Including DCC stuff again, with a wonderful extra layer of abstraction. Some hooks are missing so sending files doesn't work yet. Receiving also still seems to have some issues. On the plus side, at least the MSN/Jabber modules work again. --- protocols/jabber/iq.c | 4 ++-- protocols/jabber/jabber.c | 2 +- protocols/jabber/jabber_util.c | 8 ++++---- protocols/jabber/s5bytestream.c | 14 +++++++------- protocols/jabber/si.c | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index f5fbdc13..bdedeb08 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -391,7 +391,7 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node * { if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) ) { - if( initial || imcb_find_buddy( ic, jid ) == NULL ) + if( initial || bee_user_by_handle( ic->bee, ic, jid ) == NULL ) imcb_add_buddy( ic, jid, ( group && group->text_len ) ? group->text : NULL ); @@ -589,7 +589,7 @@ static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct ( s = xt_find_attr( node, "type" ) ) && strcmp( s, "result" ) == 0 ) { - if( imcb_find_buddy( ic, jid ) == NULL ) + if( bee_user_by_handle( ic->bee, ic, jid ) == NULL ) imcb_add_buddy( ic, jid, NULL ); } else diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 956769b7..acad525e 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -266,7 +266,7 @@ static void jabber_logout( struct im_connection *ic ) struct jabber_data *jd = ic->proto_data; while( jd->filetransfers ) - imcb_file_canceled( ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" ); + imcb_file_canceled( ic, ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" ); while( jd->streamhosts ) { diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index bd2fbe8c..6f58f124 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -278,8 +278,7 @@ static void jabber_buddy_ask_yes( void *data ) presence_send_request( bla->ic, bla->handle, "subscribed" ); - if( imcb_find_buddy( bla->ic, bla->handle ) == NULL ) - imcb_ask_add( bla->ic, bla->handle, NULL ); + imcb_ask_add( bla->ic, bla->handle, NULL ); g_free( bla->handle ); g_free( bla ); @@ -461,7 +460,7 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_, } if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && - ( bare_exists || imcb_find_buddy( ic, jid ) ) ) + ( bare_exists || bee_user_by_handle( ic->bee, ic, jid ) ) ) { *s = '/'; bud = jabber_buddy_add( ic, jid ); @@ -482,7 +481,8 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_, if( bud == NULL ) /* No match. Create it now? */ - return ( ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid_ ) ) ? + return ( ( flags & GET_BUDDY_CREAT ) && + bee_user_by_handle( ic->bee, ic, jid_ ) ) ? jabber_buddy_add( ic, jid_ ) : NULL; else if( bud->resource && ( flags & GET_BUDDY_EXACT ) ) /* We want an exact match, so in thise case there shouldn't be a /resource. */ diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 58a6c2e4..4896e380 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -565,7 +565,7 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) imcb_log( tf->ic, "WARNING: Error transmitting bytestream response" ); xt_free_node( reply ); - imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" ); + imcb_file_canceled( tf->ic, tf->ft, "couldn't connect to any streamhosts" ); bt->tf->watch_in = 0; /* MUST always return FALSE! */ @@ -602,7 +602,7 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) xt_add_attr( reply, "id", tf->iq_id ); if( !jabber_write_packet( tf->ic, reply ) ) - imcb_file_canceled( tf->ft, "Error transmitting bytestream response" ); + imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream response" ); xt_free_node( reply ); } @@ -642,7 +642,7 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) tf->bytesread += ret; if( tf->bytesread >= tf->ft->file_size ) - imcb_file_finished( tf->ft ); + imcb_file_finished( tf->ic, tf->ft ); tf->ft->write( tf->ft, tf->ft->buffer, ret ); @@ -658,7 +658,7 @@ gboolean jabber_bs_recv_write_request( file_transfer_t *ft ) if( tf->watch_in ) { - imcb_file_canceled( ft, "BUG in jabber file transfer: write_request called when already watching for input" ); + imcb_file_canceled( tf->ic, ft, "BUG in jabber file transfer: write_request called when already watching for input" ); return FALSE; } @@ -704,7 +704,7 @@ gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int l return jabber_bs_abort( bt, "send() sent %d instead of %d (send buffer too big!)", ret, len ); if( tf->byteswritten >= ft->file_size ) - imcb_file_finished( ft ); + imcb_file_finished( tf->ic, ft ); else bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); @@ -1004,7 +1004,7 @@ gboolean jabber_bs_send_request( struct jabber_transfer *tf, GSList *streamhosts jabber_cache_add( tf->ic, iq, jabber_bs_send_handle_reply ); if( !jabber_write_packet( tf->ic, iq ) ) - imcb_file_canceled( tf->ft, "Error transmitting bytestream request" ); + imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream request" ); return TRUE; } @@ -1019,7 +1019,7 @@ gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error ) error ); if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */ - imcb_file_canceled( tf->ft, error ); + imcb_file_canceled( tf->ic, tf->ft, error ); /* MUST always return FALSE! */ return FALSE; diff --git a/protocols/jabber/si.c b/protocols/jabber/si.c index bfb64f11..58c0e17f 100644 --- a/protocols/jabber/si.c +++ b/protocols/jabber/si.c @@ -90,11 +90,11 @@ int jabber_si_check_features( struct jabber_transfer *tf, GSList *features ) { } if( !foundft ) - imcb_file_canceled( tf->ft, "Buddy's client doesn't feature file transfers" ); + imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature file transfers" ); else if( !foundbt ) - imcb_file_canceled( tf->ft, "Buddy's client doesn't feature byte streams (required)" ); + imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature byte streams (required)" ); else if( !foundsi ) - imcb_file_canceled( tf->ft, "Buddy's client doesn't feature stream initiation (required)" ); + imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature stream initiation (required)" ); return foundft && foundbt && foundsi; } @@ -108,7 +108,7 @@ void jabber_si_transfer_start( struct jabber_transfer *tf ) { jabber_si_send_request( tf->ic, tf->bud->full_jid, tf ); /* and start the receive logic */ - imcb_file_recv_start( tf->ft ); + imcb_file_recv_start( tf->ic, tf->ft ); } @@ -155,7 +155,7 @@ void jabber_si_transfer_request( struct im_connection *ic, file_transfer_t *ft, if( bud == NULL ) { - imcb_file_canceled( ft, "Couldn't find buddy (BUG?)" ); + imcb_file_canceled( ic, ft, "Couldn't find buddy (BUG?)" ); return; } -- cgit v1.2.3 From 3ab1d317831a6c1830bb648a1a8d63a41c92f651 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Apr 2010 20:12:25 +0200 Subject: Fixing a bug in s5bytestream code to deal with incomplete SOCKS5 messages and my rewrite of the bs_peek function. --- protocols/jabber/s5bytestream.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 58a6c2e4..36a2e438 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -481,10 +481,11 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con return TRUE; else if( ret < sizeof( struct socks5_message ) ) { - /* Either a buggy proxy or just one that doesnt regard the SHOULD in XEP-0065 - * saying the reply SHOULD contain the address */ - - ASSERTSOCKOP( ret = recv( fd, &socks5_reply, ret, 0 ), "Dequeuing after MSG_PEEK" ); + /* Either a buggy proxy or just one that doesnt regard + * the SHOULD in XEP-0065 saying the reply SHOULD + * contain the address. We'll take it, so make sure the + * next jabber_bs_peek starts with an empty buffer. */ + bt->peek_buf_len = 0; } if( !( socks5_reply.ver == 5 ) || -- cgit v1.2.3 From aea8b68bd0e057441d671c008200e71dd046a211 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 6 May 2010 01:28:56 +0100 Subject: Starting to restore chatroom stuff. Only enough to create and be joined into a room. More will follow soon. --- protocols/jabber/conference.c | 4 +++- protocols/jabber/jabber.c | 2 +- protocols/jabber/presence.c | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index f434c58a..17108428 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -91,11 +91,13 @@ static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_no struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name ) { char *normalized = jabber_normalize( name ); + GSList *l; struct groupchat *ret; struct jabber_chat *jc; - for( ret = ic->groupchats; ret; ret = ret->next ) + for( l = ic->groupchats; l; l = l->next ) { + ret = l->data; jc = ret->data; if( strcmp( normalized, jc->name ) == 0 ) break; diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index acad525e..75bc44d3 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -281,7 +281,7 @@ static void jabber_logout( struct im_connection *ic ) jabber_end_stream( ic ); while( ic->groupchats ) - jabber_chat_free( ic->groupchats ); + jabber_chat_free( ic->groupchats->data ); if( jd->r_inpa >= 0 ) b_event_remove( jd->r_inpa ); diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index 006eeead..dadccfb9 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -205,6 +205,7 @@ int presence_send_update( struct im_connection *ic ) struct jabber_data *jd = ic->proto_data; struct xt_node *node, *cap; struct groupchat *c; + GSList *l; int st; node = jabber_make_packet( "presence", NULL, NULL, NULL ); @@ -228,8 +229,9 @@ int presence_send_update( struct im_connection *ic ) /* Have to send this update to all groupchats too, the server won't do this automatically. */ - for( c = ic->groupchats; c && st; c = c->next ) + for( l = ic->groupchats; l && st; l = l->next ) { + struct groupchat *c = l->data; struct jabber_chat *jc = c->data; xt_add_attr( node, "to", jc->my_full_jid ); -- cgit v1.2.3 From eaaa9862451175392d6df48c4795b188518bed4b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 15:48:38 +0100 Subject: Misc. cleanup. Also updated the Yahoo! module to deal with struct groupchat in a GSList so that a default config fully compiles again. --- protocols/jabber/presence.c | 1 - 1 file changed, 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index dadccfb9..2875d23e 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -204,7 +204,6 @@ int presence_send_update( struct im_connection *ic ) { struct jabber_data *jd = ic->proto_data; struct xt_node *node, *cap; - struct groupchat *c; GSList *l; int st; -- cgit v1.2.3 From e68565706f0c2ea710e7ea83cd5a69e538eb061c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 8 May 2010 15:58:32 +0100 Subject: Fixed buggy jabber_chat_by_jid() after GSList change. --- protocols/jabber/conference.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index 17108428..0d0e3318 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -104,7 +104,7 @@ struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name } g_free( normalized ); - return ret; + return l && ret; } void jabber_chat_free( struct groupchat *c ) -- cgit v1.2.3 From dcd16c5f8b8788d476bf4193701fc61656dfbf14 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 14:21:24 +0100 Subject: Read group information from Jabber contact lists. The code was already there, but with a simple typo. --- protocols/jabber/iq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index bdedeb08..a5495196 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -382,7 +382,7 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node * c = query->children; while( ( c = xt_find_node( c, "item" ) ) ) { - struct xt_node *group = xt_find_node( node->children, "group" ); + struct xt_node *group = xt_find_node( c->children, "group" ); char *jid = xt_find_attr( c, "jid" ); char *name = xt_find_attr( c, "name" ); char *sub = xt_find_attr( c, "subscription" ); -- cgit v1.2.3 From 58f5ef70c3824b881ad6b35f854a8c8ac59a5d32 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 13 May 2010 01:30:36 +0100 Subject: Use ?, not &&. --- protocols/jabber/conference.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index 0d0e3318..bb3fbcf3 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -104,7 +104,7 @@ struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name } g_free( normalized ); - return l && ret; + return l ? ret : NULL; } void jabber_chat_free( struct groupchat *c ) -- cgit v1.2.3 From d25ebea73aef7d6b2bbc17212af5109383277e6e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:04:55 +0100 Subject: GAIM_INPUT_* were renamed, at last. --- protocols/jabber/s5bytestream.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 58a6c2e4..14611a6f 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -405,7 +405,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_CONNECTED; - bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt ); + bt->tf->watch_out = b_input_add( fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt ); /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); @@ -432,7 +432,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_REQUEST; - bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_recv_handshake, bt ); + bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt ); bt->tf->watch_out = 0; return FALSE; @@ -588,7 +588,7 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) bt->sh->port ); tf->ft->data = tf; - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); + tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); tf->ft->write_request = jabber_bs_recv_write_request; reply = xt_new_node( "streamhost-used", NULL, NULL ); @@ -630,7 +630,7 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) if( ( ret == -1 ) && ( errno == EAGAIN ) ) { - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); + tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); return FALSE; } } @@ -706,7 +706,7 @@ gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int l if( tf->byteswritten >= ft->file_size ) imcb_file_finished( ft ); else - bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); + bt->tf->watch_out = b_input_add( tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt ); return TRUE; } @@ -917,7 +917,7 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) strcpy( sh->port, port ); bt->streamhosts = g_slist_append( bt->streamhosts, sh ); - bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); } else { imcb_log( tf->ic, "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s", @@ -1054,7 +1054,7 @@ gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_CONNECTED; - bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); return FALSE; } case BS_PHASE_CONNECTED: -- cgit v1.2.3 From f60079b74053a53da3720b992c603fddf75ff1dd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:26:54 +0100 Subject: Allow one to run the configure script from a different directory and put all build files in there. I need this to properly make Debian package variants (i.e. libpurple and native). --- protocols/jabber/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index 78a02696..912ea702 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/jabber/ +endif # [SH] Program variables objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ -- cgit v1.2.3 From f5d87ea5e4db1864cc9dd95c295f166af9944014 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 19:26:17 +0100 Subject: Pick up group changes in the middle of a Jabber session, now that we care about that info. --- protocols/jabber/iq.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 55c678aa..5166e322 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -390,9 +390,8 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node * { if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) ) { - if( initial || bee_user_by_handle( ic->bee, ic, jid ) == NULL ) - imcb_add_buddy( ic, jid, ( group && group->text_len ) ? - group->text : NULL ); + imcb_add_buddy( ic, jid, ( group && group->text_len ) ? + group->text : NULL ); if( name ) imcb_rename_buddy( ic, jid, name ); -- cgit v1.2.3 From 46d215d562f8e1aba2b24e2d1feab27337956d50 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 9 Jun 2010 00:43:13 +0100 Subject: Allow moving contacts around between groups. Works with at least Jabber, will check the others now. --- protocols/jabber/iq.c | 4 +++- protocols/jabber/jabber.c | 2 +- protocols/jabber/jabber.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 5166e322..2930e75f 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -554,7 +554,7 @@ static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_no static xt_status jabber_add_to_roster_callback( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); -int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ) +int jabber_add_to_roster( struct im_connection *ic, const char *handle, const char *name, const char *group ) { struct xt_node *node; int st; @@ -564,6 +564,8 @@ int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ) xt_add_attr( node, "jid", handle ); if( name ) xt_add_attr( node, "name", name ); + if( group ) + xt_add_child( node, xt_new_node( "group", group, NULL ) ); /* And pack it into a roster-add packet */ node = xt_new_node( "query", NULL, node ); diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index cf491f81..d5948958 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -412,7 +412,7 @@ static void jabber_add_buddy( struct im_connection *ic, char *who, char *group ) return; } - if( jabber_add_to_roster( ic, who, NULL ) ) + if( jabber_add_to_roster( ic, who, NULL, group ) ) presence_send_request( ic, who, "subscribe" ); } diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index b3638597..45a1c5c1 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -234,7 +234,7 @@ int jabber_init_iq_auth( struct im_connection *ic ); xt_status jabber_pkt_bind_sess( struct im_connection *ic, struct xt_node *node, struct xt_node *orig ); int jabber_get_roster( struct im_connection *ic ); int jabber_get_vcard( struct im_connection *ic, char *bare_jid ); -int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name ); +int jabber_add_to_roster( struct im_connection *ic, const char *handle, const char *name, const char *group ); int jabber_remove_from_roster( struct im_connection *ic, char *handle ); xt_status jabber_iq_query_features( struct im_connection *ic, char *bare_jid ); xt_status jabber_iq_query_server( struct im_connection *ic, char *jid, char *xmlns ); -- cgit v1.2.3 From 938c30512f4dac4f084fd6bb8b7f41655de9bce4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 22 Jul 2010 08:43:21 +0100 Subject: Fixed crash on failed Jabber file transfers. --- protocols/jabber/s5bytestream.c | 1 - 1 file changed, 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index a8137271..6759b78b 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -568,7 +568,6 @@ gboolean jabber_bs_recv_handshake_abort( struct bs_transfer *bt, char *error ) imcb_file_canceled( tf->ic, tf->ft, "couldn't connect to any streamhosts" ); - bt->tf->watch_in = 0; /* MUST always return FALSE! */ return FALSE; } -- cgit v1.2.3 From 03f38289e15c27b93f8fdecf22a03353e4d01096 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Jul 2010 16:06:22 +0200 Subject: Adding protocol-specific chatroom settings. First one to use this: AIM chatrooms to use exchange numbers other than 4. --- protocols/jabber/jabber.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'protocols/jabber') diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 7147a628..01353f8e 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -440,7 +440,7 @@ static void jabber_remove_buddy( struct im_connection *ic, char *who, char *grou presence_send_request( ic, who, "unsubscribe" ); } -static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password ) +static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password, set_t **sets ) { if( strchr( room, '@' ) == NULL ) imcb_error( ic, "Invalid room name: %s", room ); -- cgit v1.2.3