aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkenobi <kenobi@rhea>2007-12-18 03:07:59 +0100
committerkenobi <kenobi@rhea>2007-12-18 03:07:59 +0100
commitdc0ba9c85539533349353713162f94077fb27be3 (patch)
tree8afa151d8e4c348443e9d1c82aea316ba8bbb5f8
parent8076ec04b1b5699f8266fa0e2e17456974ced554 (diff)
sending via proxy
-rw-r--r--dcc.c1
-rw-r--r--protocols/jabber/iq.c162
-rw-r--r--protocols/jabber/jabber.h16
-rw-r--r--protocols/jabber/s5bytestream.c298
-rw-r--r--protocols/jabber/si.c10
5 files changed, 410 insertions, 77 deletions
diff --git a/dcc.c b/dcc.c
index 24673085..2fa3f745 100644
--- a/dcc.c
+++ b/dcc.c
@@ -124,7 +124,6 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha
df = dcc_alloc_transfer( file_name, file_size, ic );
file = df->ft;
file->write = dccs_send_write;
- file->sending = TRUE;
/* listen and request */
if( !dcc_listen( df, &saddr ) ||
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;
- /* <query> element and <streamhost> 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:
+ *
+ * <iq type=set>
+ * <query xmlns=bs sid=sid>
+ * <activate>tgt_jid</activate>
+ * </query>
+ * </iq>
+ */
+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 );
}