diff options
author | Wilmer van der Gaast <wilmer@gaast.net> | 2006-09-20 21:42:27 +0200 |
---|---|---|
committer | Wilmer van der Gaast <wilmer@gaast.net> | 2006-09-20 21:42:27 +0200 |
commit | 21167d2d14c333d67445546bb69dd52dd295287d (patch) | |
tree | 50c010702a3487c666ad8e614b25879de258d9cf /protocols/jabber | |
parent | f06894d8f55b50b632c1d81ad878f8581273ba66 (diff) |
It can send a valid (pre-XMPP) login packet. Lots of work to do, still...
Diffstat (limited to 'protocols/jabber')
-rw-r--r-- | protocols/jabber/Makefile | 2 | ||||
-rw-r--r-- | protocols/jabber/io.c | 237 | ||||
-rw-r--r-- | protocols/jabber/iq.c | 81 | ||||
-rw-r--r-- | protocols/jabber/jabber.c | 69 | ||||
-rw-r--r-- | protocols/jabber/jabber.h | 33 | ||||
-rw-r--r-- | protocols/jabber/jabber_util.c | 22 | ||||
-rw-r--r-- | protocols/jabber/message.c | 2 | ||||
-rw-r--r-- | protocols/jabber/presence.c | 2 | ||||
-rw-r--r-- | protocols/jabber/xmltree.c | 13 |
9 files changed, 430 insertions, 31 deletions
diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index bf2424ba..d4dcc652 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = iq.o jabber.o jabber_util.o message.o presence.o xmltree.o +objects = io.o iq.o jabber.o jabber_util.o message.o presence.o xmltree.o CFLAGS += -Wall LFLAGS += -r diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c new file mode 100644 index 00000000..db869714 --- /dev/null +++ b/protocols/jabber/io.c @@ -0,0 +1,237 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Jabber module - I/O stuff (plain, SSL), queues, etc * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* 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" + +static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond ); + +int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node ) +{ + char *buf; + int st; + + buf = xt_to_string( node ); + st = jabber_write( gc, buf, strlen( buf ) ); + g_free( buf ); + + return st; +} + +int jabber_write( struct gaim_connection *gc, char *buf, int len ) +{ + struct jabber_data *jd = gc->proto_data; + + if( jd->tx_len == 0 ) + { + /* If the queue is empty, allocate a new buffer. */ + jd->tx_len = len; + jd->txq = g_memdup( buf, len ); + + /* Try if we can write it immediately so we don't have to do + it via the event handler. If not, add the handler. (In + most cases it probably won't be necessary.) */ + if( jabber_write_callback( gc, jd->fd, GAIM_INPUT_WRITE ) ) + jd->w_inpa = b_input_add( jd->fd, GAIM_INPUT_WRITE, jabber_write_callback, gc ); + } + else + { + /* Just add it to the buffer if it's already filled. The + event handler is already set. */ + jd->txq = g_renew( char, jd->txq, jd->tx_len + len ); + memcpy( jd->txq + jd->tx_len, buf, len ); + jd->tx_len += len; + } + + /* FIXME: write_callback could've generated a real error! */ + return 1; +} + +static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond ) +{ + struct gaim_connection *gc = data; + struct jabber_data *jd = gc->proto_data; + int st; + + st = write( jd->fd, jd->txq, jd->tx_len ); + + if( st == jd->tx_len ) + { + /* We wrote everything, clear the buffer. */ + g_free( jd->txq ); + jd->txq = NULL; + jd->tx_len = 0; + + return FALSE; + } + else if( st == 0 || ( st < 0 && !sockerr_again() ) ) + { + hide_login_progress_error( gc, "Short write() to server" ); + signoff( gc ); + return FALSE; + } + else if( st > 0 ) + { + char *s; + + s = g_memdup( jd->txq + st, jd->tx_len - st ); + jd->tx_len -= st; + g_free( jd->txq ); + jd->txq = s; + + return FALSE; + } + else + { + /* Just in case we had EINPROGRESS/EAGAIN: */ + + return TRUE; + } +} + +static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition cond ) +{ + struct gaim_connection *gc = data; + struct jabber_data *jd = gc->proto_data; + char buf[512]; + int st; + + st = read( fd, buf, sizeof( buf ) ); + + if( st > 0 ) + { + /* Parse. */ + if( !xt_feed( jd->xt, buf, st ) ) + { + hide_login_progress_error( gc, "XML stream error" ); + signoff( gc ); + return FALSE; + } + + /* Execute all handlers. */ + if( !xt_handle( jd->xt, NULL ) ) + { + /* Don't do anything, the handlers should have + aborted the connection already... Or not? FIXME */ + return FALSE; + } + + /* Garbage collection. */ + xt_cleanup( jd->xt, NULL ); + + /* This is a bit hackish, unfortunately. Although xmltree + has nifty event handler stuff, it only calls handlers + when nodes are complete. Since the server should only + send an opening <stream:stream> tag, we have to check + this by hand. :-( */ + if( !( jd->flags & JFLAG_STREAM_STARTED ) && jd->xt && jd->xt->root ) + { + if( g_strcasecmp( jd->xt->root->name, "stream:stream" ) == 0 ) + { + jd->flags |= JFLAG_STREAM_STARTED; + return jabber_start_auth( gc ); + } + else + { + hide_login_progress_error( gc, "XML stream error" ); + signoff( gc ); + return FALSE; + } + } + } + else if( st == 0 || ( st < 0 && !sockerr_again() ) ) + { + hide_login_progress_error( gc, "Error while reading from server" ); + signoff( gc ); + return FALSE; + } + + /* EAGAIN/etc or a successful read. */ + return TRUE; +} + +static gboolean jabber_start_stream( struct gaim_connection *gc ); + +gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond ) +{ + struct gaim_connection *gc = data; + + if( source == -1 ) + { + hide_login_progress( gc, "Could not connect to server" ); + signoff( gc ); + return FALSE; + } + + set_login_progress( gc, 1, "Connected to server, logging in" ); + + return jabber_start_stream( gc ); +} + +static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data ) +{ + return XT_ABORT; +} + +static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data ) +{ + printf( "Received unknown packet:\n" ); + xt_print( node ); + + return XT_HANDLED; +} + +static const struct xt_handler_entry jabber_handlers[] = { + { "stream:stream", "<root>", jabber_end_of_stream }, + { "iq", "stream:stream", jabber_pkt_iq }, + { "message", "stream:stream", jabber_pkt_message }, + { "presence", "stream:stream", jabber_pkt_presence }, + { NULL, "stream:stream", jabber_pkt_misc }, + { NULL, NULL, NULL } +}; + +static gboolean jabber_start_stream( struct gaim_connection *gc ) +{ + struct jabber_data *jd = gc->proto_data; + int st; + char *greet; + + /* We'll start our stream now, so prepare everything to receive one + from the server too. */ + xt_free( jd->xt ); /* In case we're RE-starting. */ + jd->xt = xt_new( gc ); + jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers; + + jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, gc ); + + greet = g_strdup_printf( "<?xml version='1.0' ?>" + "<stream:stream to=\"%s\" xmlns=\"jabber:client\" " + "xmlns:stream=\"http://etherx.jabber.org/streams\">", jd->server ); + /* Add this when TLS and SASL are supported? */ + // version=\"1.0\">" + + st = jabber_write( gc, greet, strlen( greet ) ); + + g_free( greet ); + + return st; +} diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index e3553637..58e3f33c 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -23,13 +23,88 @@ #include "jabber.h" +/* +<iq xmlns="jabber:client" id="BeeX00000001" type="result"><query +xmlns="jabber:iq:auth"><username>wilmer</username><resource/><password/><digest/> +<sequence>499</sequence><token>450D1FFD</token></query></iq> +*/ + xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ) { - char *from = xt_find_attr( node, "from" ); + struct gaim_connection *gc = data; + struct jabber_data *jd = gc->proto_data; + struct xt_node *query, *reply = NULL; + char *s; + int st; + + query = xt_find_node( node->children, "query" ); - printf( "Received IQ from %s:\n", from ); - xt_print( node ); + if( !query ) + return XT_HANDLED; /* Ignore it for now, don't know what's best... */ + + if( ( s = xt_find_attr( query, "xmlns" ) ) && strcmp( s, "jabber:iq:auth" ) == 0 ) + { + /* Time to authenticate ourselves! */ + reply = xt_new_node( "query", NULL, NULL ); + xt_add_attr( reply, "xmlns", "jabber:iq:auth" ); + xt_add_child( reply, xt_new_node( "username", jd->username, NULL ) ); + xt_add_child( reply, xt_new_node( "resource", set_getstr( &gc->acc->set, "resource" ), NULL ) ); + + if( xt_find_node( query->children, "digest" ) && ( s = xt_find_attr( jd->xt->root, "id" ) ) ) + { + /* We can do digest authentication, it seems, and of + course we prefer that. */ + SHA_CTX sha; + char hash_hex[40]; + unsigned char hash[20]; + int i; + + shaInit( &sha ); + shaUpdate( &sha, (unsigned char*) s, strlen( s ) ); + shaUpdate( &sha, (unsigned char*) gc->acc->pass, strlen( gc->acc->pass ) ); + shaFinal( &sha, hash ); + + for( i = 0; i < 20; i ++ ) + sprintf( hash_hex + i * 2, "%02x", hash[i] ); + + xt_add_child( reply, xt_new_node( "digest", hash_hex, NULL ) ); + } + else if( xt_find_node( query->children, "password" ) ) + { + /* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */ + xt_add_child( reply, xt_new_node( "password", gc->acc->pass, NULL ) ); + } + else + { + xt_free_node( reply ); + + hide_login_progress_error( gc, "Can't find suitable authentication method" ); + signoff( gc ); + return XT_ABORT; + } + + reply = jabber_make_packet( "iq", "set", NULL, reply ); + st = jabber_write_packet( gc, reply ); + xt_free_node( reply ); + + return st ? XT_HANDLED : XT_ABORT; + } return XT_HANDLED; } +int jabber_start_auth( struct gaim_connection *gc ) +{ + struct jabber_data *jd = gc->proto_data; + struct xt_node *node; + int st; + + node = xt_new_node( "query", NULL, xt_new_node( "username", jd->username, NULL ) ); + xt_add_attr( node, "xmlns", "jabber:iq:auth" ); + node = jabber_make_packet( "iq", "get", NULL, node ); + + st = jabber_write_packet( gc, node ); + + xt_free_node( node ); + return st; +} diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index 0a091b06..9732d6eb 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -27,6 +27,7 @@ #include <ctype.h> #include <stdio.h> +#include "ssl_client.h" #include "xmltree.h" #include "bitlbee.h" #include "jabber.h" @@ -54,14 +55,58 @@ static void jabber_acc_init( account_t *acc ) static void jabber_login( account_t *acc ) { + struct gaim_connection *gc = new_gaim_conn( acc ); + struct jabber_data *jd = g_new0( struct jabber_data, 1 ); + + jd->gc = gc; + gc->proto_data = jd; + + jd->username = g_strdup( acc->user ); + jd->server = strchr( jd->username, '@' ); + + if( jd->server == NULL ) + { + hide_login_progress( gc, "Incomplete account name (format it like <username@jabberserver.name>)" ); + signoff( gc ); + return; + } + + /* So don't think of free()ing jd->server.. :-) */ + *jd->server = 0; + jd->server ++; + + if( set_getbool( &acc->set, "ssl" ) ) + { + signoff( gc ); + /* TODO! */ + } + else + { + jd->fd = proxy_connect( jd->server, set_getint( &acc->set, "port" ), jabber_connected_plain, gc ); + } } static void jabber_close( struct gaim_connection *gc ) { + struct jabber_data *jd = gc->proto_data; + + if( jd->r_inpa >= 0 ) + b_event_remove( jd->r_inpa ); + if( jd->w_inpa >= 0 ) + b_event_remove( jd->w_inpa ); + + if( jd->ssl ) + ssl_disconnect( jd->ssl ); + if( jd->fd >= 0 ) + closesocket( jd->fd ); + + g_free( jd->username ); + g_free( jd ); } static int jabber_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away ) { + return 0; } void jabber_init() @@ -95,28 +140,6 @@ void jabber_init() register_protocol(ret); } -static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data ) -{ - return XT_ABORT; -} - -static xt_status jabber_pkt_misc( struct xt_node *node, gpointer data ) -{ - printf( "Received unknown packet:\n" ); - xt_print( node ); - - return XT_HANDLED; -} - -static const struct xt_handler_entry jabber_handlers[] = { - { "stream:stream", "<root>", jabber_end_of_stream }, - { "iq", "stream:stream", jabber_pkt_iq }, - { "message", "stream:stream", jabber_pkt_message }, - { "presence", "stream:stream", jabber_pkt_presence }, - { NULL, "stream:stream", jabber_pkt_misc }, - { NULL, NULL, NULL } -}; - #if 0 int main( int argc, char *argv[] ) { @@ -144,4 +167,4 @@ int main( int argc, char *argv[] ) xt_free( xt ); } -#endif
\ No newline at end of file +#endif diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 72fae75d..d812ec54 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -29,11 +29,44 @@ #include "xmltree.h" #include "bitlbee.h" +typedef enum +{ + JFLAG_STREAM_STARTED = 1, + JFLAG_AUTHENTICATED = 2, +} jabber_flags_t; + +/* iq.c */ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ); +int jabber_start_auth( struct gaim_connection *gc ); + xt_status jabber_pkt_message( struct xt_node *node, gpointer data ); xt_status jabber_pkt_presence( struct xt_node *node, gpointer data ); +/* jabber_util.c */ char *set_eval_resprio( 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 ); + +/* io.c */ +int jabber_write_packet( struct gaim_connection *gc, struct xt_node *node ); +int jabber_write( struct gaim_connection *gc, char *buf, int len ); +gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond ); + +struct jabber_data +{ + struct gaim_connection *gc; + + int fd; + void *ssl; + char *txq; + int tx_len; + int r_inpa, w_inpa; + + struct xt_parser *xt; + jabber_flags_t flags; + + char *username; /* USERNAME@server */ + char *server; /* username@SERVER -=> server/domain, not hostname */ +}; #endif diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index ff79cb16..46811d05 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -1,7 +1,7 @@ /***************************************************************************\ * * * BitlBee - An IRC to IM gateway * -* Jabber module - Main file * +* Jabber module - Misc. stuff * * * * Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * * * @@ -23,6 +23,8 @@ #include "jabber.h" +static int next_id = 1; + char *set_eval_resprio( set_t *set, char *value ) { account_t *acc = set->data; @@ -46,3 +48,21 @@ char *set_eval_tls( set_t *set, char *value ) else return set_eval_bool( set, value ); } + +struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children ) +{ + char *id = g_strdup_printf( "BeeX%04x", next_id++ ); + struct xt_node *node; + + node = xt_new_node( name, NULL, children ); + + xt_add_attr( node, "id", id ); + if( type ) + xt_add_attr( node, "type", type ); + if( to ) + xt_add_attr( node, "to", to ); + + g_free( id ); + + return node; +} diff --git a/protocols/jabber/message.c b/protocols/jabber/message.c index d4326063..e5f75464 100644 --- a/protocols/jabber/message.c +++ b/protocols/jabber/message.c @@ -1,7 +1,7 @@ /***************************************************************************\ * * * BitlBee - An IRC to IM gateway * -* Jabber module - Main file * +* Jabber module - Handling of message(s) (tags), etc * * * * Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * * * diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index 5383d7aa..83b69013 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -1,7 +1,7 @@ /***************************************************************************\ * * * BitlBee - An IRC to IM gateway * -* Jabber module - Main file * +* Jabber module - Handling of presence (tags), etc * * * * Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * * * diff --git a/protocols/jabber/xmltree.c b/protocols/jabber/xmltree.c index 68c9560d..3d0a2919 100644 --- a/protocols/jabber/xmltree.c +++ b/protocols/jabber/xmltree.c @@ -205,7 +205,9 @@ void xt_cleanup( struct xt_parser *xt, struct xt_node *node ) { struct xt_node *c, *prev; - /* Let's just hope xt->root isn't NULL! */ + if( !xt || !xt->root ) + return; + if( node == NULL ) return xt_cleanup( xt, xt->root ); @@ -356,6 +358,9 @@ void xt_free_node( struct xt_node *node ) { int i; + if( !node ) + return; + g_free( node->name ); g_free( node->text ); @@ -379,6 +384,9 @@ void xt_free_node( struct xt_node *node ) void xt_free( struct xt_parser *xt ) { + if( !xt ) + return; + if( xt->root ) xt_free_node( xt->root ); @@ -407,6 +415,9 @@ char *xt_find_attr( struct xt_node *node, char *key ) { int i; + if( !node ) + return NULL; + for( i = 0; node->attr[i].key; i ++ ) if( g_strcasecmp( node->attr[i].key, key ) == 0 ) break; |