diff options
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; | 
