Index: protocols/jabber/Makefile
===================================================================
--- protocols/jabber/Makefile	(revision f06894d8f55b50b632c1d81ad878f8581273ba66)
+++ protocols/jabber/Makefile	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
@@ -10,5 +10,5 @@
 
 # [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
Index: protocols/jabber/io.c
===================================================================
--- protocols/jabber/io.c	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
+++ protocols/jabber/io.c	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
@@ -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;
+}
Index: protocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision f06894d8f55b50b632c1d81ad878f8581273ba66)
+++ protocols/jabber/iq.c	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
@@ -24,12 +24,87 @@
 #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;
 	
-	printf( "Received IQ from %s:\n", from );
-	xt_print( node );
+	query = xt_find_node( node->children, "query" );
+	
+	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;
+}
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision f06894d8f55b50b632c1d81ad878f8581273ba66)
+++ protocols/jabber/jabber.c	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
@@ -28,4 +28,5 @@
 #include <stdio.h>
 
+#include "ssl_client.h"
 #include "xmltree.h"
 #include "bitlbee.h"
@@ -55,12 +56,56 @@
 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;
 }
 
@@ -96,26 +141,4 @@
 }
 
-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[] )
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision f06894d8f55b50b632c1d81ad878f8581273ba66)
+++ protocols/jabber/jabber.h	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
@@ -30,10 +30,43 @@
 #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
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision f06894d8f55b50b632c1d81ad878f8581273ba66)
+++ protocols/jabber/jabber_util.c	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
@@ -2,5 +2,5 @@
 *                                                                           *
 *  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Main file                                                *
+*  Jabber module - Misc. stuff                                              *
 *                                                                           *
 *  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
@@ -23,4 +23,6 @@
 
 #include "jabber.h"
+
+static int next_id = 1;
 
 char *set_eval_resprio( set_t *set, char *value )
@@ -47,2 +49,20 @@
 		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;
+}
Index: protocols/jabber/message.c
===================================================================
--- protocols/jabber/message.c	(revision f06894d8f55b50b632c1d81ad878f8581273ba66)
+++ protocols/jabber/message.c	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
@@ -2,5 +2,5 @@
 *                                                                           *
 *  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>                   *
Index: protocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision f06894d8f55b50b632c1d81ad878f8581273ba66)
+++ protocols/jabber/presence.c	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
@@ -2,5 +2,5 @@
 *                                                                           *
 *  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>                   *
Index: protocols/jabber/xmltree.c
===================================================================
--- protocols/jabber/xmltree.c	(revision f06894d8f55b50b632c1d81ad878f8581273ba66)
+++ protocols/jabber/xmltree.c	(revision 21167d2d14c333d67445546bb69dd52dd295287d)
@@ -206,5 +206,7 @@
 	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 );
@@ -357,4 +359,7 @@
 	int i;
 	
+	if( !node )
+		return;
+	
 	g_free( node->name );
 	g_free( node->text );
@@ -380,4 +385,7 @@
 void xt_free( struct xt_parser *xt )
 {
+	if( !xt )
+		return;
+	
 	if( xt->root )
 		xt_free_node( xt->root );
@@ -407,4 +415,7 @@
 {
 	int i;
+	
+	if( !node )
+		return NULL;
 	
 	for( i = 0; node->attr[i].key; i ++ )
