Index: lib/Makefile
===================================================================
--- lib/Makefile	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/Makefile	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -10,5 +10,5 @@
 
 # [SH] Program variables
-objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o
+objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
 
 CFLAGS += -Wall
Index: lib/arc.c
===================================================================
--- lib/arc.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/arc.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -131,15 +131,37 @@
    
    Both functions return the number of bytes in the result string.
+   
+   Note that if you use the pad_to argument, you will need zero-termi-
+   nation to find back the original string length after decryption. So
+   it shouldn't be used if your string contains \0s by itself!
 */
 
-int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password )
+int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to )
 {
 	struct arc_state *st;
 	unsigned char *key;
-	int key_len, i;
+	char *padded = NULL;
+	int key_len, i, padded_len;
 	
 	key_len = strlen( password ) + ARC_IV_LEN;
 	if( clear_len <= 0 )
 		clear_len = strlen( clear );
+	
+	/* Pad the string to the closest multiple of pad_to. This makes it
+	   impossible to see the exact length of the password. */
+	if( pad_to > 0 && ( clear_len % pad_to ) > 0 )
+	{
+		padded_len = clear_len + pad_to - ( clear_len % pad_to );
+		padded = g_malloc( padded_len );
+		memcpy( padded, clear, clear_len );
+		
+		/* First a \0 and then random data, so we don't have to do
+		   anything special when decrypting. */
+		padded[clear_len] = 0;
+		random_bytes( (unsigned char*) padded + clear_len + 1, padded_len - clear_len - 1 );
+		
+		clear = padded;
+		clear_len = padded_len;
+	}
 	
 	/* Prepare buffers and the key + IV */
@@ -161,4 +183,5 @@
 	
 	g_free( st );
+	g_free( padded );
 	
 	return clear_len + ARC_IV_LEN;
Index: lib/arc.h
===================================================================
--- lib/arc.h	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/arc.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -31,6 +31,10 @@
 };
 
-struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );
+#ifndef G_GNUC_MALLOC
+#define G_GNUC_MALLOC
+#endif
+
+G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );
 unsigned char arc_getbyte( struct arc_state *st );
-int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password );
+int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to );
 int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password );
Index: lib/base64.c
===================================================================
--- lib/base64.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/base64.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -118,5 +118,5 @@
 	int i, outlen = 0;
 	
-	for( i = 0; in[i]; i += 4 )
+	for( i = 0; in[i] && in[i+1] && in[i+2] && in[i+3]; i += 4 )
 	{
 		int sx;
Index: lib/events_glib.c
===================================================================
--- lib/events_glib.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/events_glib.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -51,9 +51,10 @@
 } GaimIOClosure;
 
-static GMainLoop *loop;
+static GMainLoop *loop = NULL;
 
 void b_main_init()
 {
-	loop = g_main_new( FALSE );
+	if( loop == NULL )
+		loop = g_main_new( FALSE );
 }
 
Index: lib/misc.c
===================================================================
--- lib/misc.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/misc.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -33,4 +33,5 @@
 #define BITLBEE_CORE
 #include "nogaim.h"
+#include "base64.h"
 #include <stdio.h>
 #include <stdlib.h>
@@ -59,29 +60,4 @@
 	strcpy(text, text2);
 	g_free(text2);
-}
-
-char *normalize(const char *s)
-{
-	static char buf[BUF_LEN];
-	char *t, *u;
-	int x = 0;
-
-	g_return_val_if_fail((s != NULL), NULL);
-
-	u = t = g_strdup(s);
-
-	strcpy(t, s);
-	g_strdown(t);
-
-	while (*t && (x < BUF_LEN - 1)) {
-		if (*t != ' ') {
-			buf[x] = *t;
-			x++;
-		}
-		t++;
-	}
-	buf[x] = '\0';
-	g_free(u);
-	return buf;
 }
 
@@ -408,4 +384,5 @@
 void random_bytes( unsigned char *buf, int count )
 {
+#ifndef _WIN32
 	static int use_dev = -1;
 	
@@ -457,4 +434,5 @@
 	
 	if( !use_dev )
+#endif
 	{
 		int i;
@@ -608,2 +586,38 @@
 		return sockerr_again();
 }
+
+/* Returns values: -1 == Failure (base64-decoded to something unexpected)
+                    0 == Okay
+                    1 == Password doesn't match the hash. */
+int md5_verify_password( char *password, char *hash )
+{
+	md5_byte_t *pass_dec = NULL;
+	md5_byte_t pass_md5[16];
+	md5_state_t md5_state;
+	int ret = -1, i;
+	
+	if( base64_decode( hash, &pass_dec ) == 21 )
+	{
+		md5_init( &md5_state );
+		md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
+		md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
+		md5_finish( &md5_state, pass_md5 );
+		
+		for( i = 0; i < 16; i ++ )
+		{
+			if( pass_dec[i] != pass_md5[i] )
+			{
+				ret = 1;
+				break;
+			}
+		}
+		
+		/* If we reached the end of the loop, it was a match! */
+		if( i == 16 )
+			ret = 0;
+	}
+	
+	g_free( pass_dec );
+
+	return ret;
+}
Index: lib/misc.h
===================================================================
--- lib/misc.h	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/misc.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -41,5 +41,4 @@
 G_MODULE_EXPORT char *add_cr( char *text );
 G_MODULE_EXPORT char *strip_newlines(char *source);
-G_MODULE_EXPORT char *normalize( const char *s );
 
 G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );
@@ -67,3 +66,5 @@
 G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl );
 
+G_MODULE_EXPORT int md5_verify_password( char *password, char *hash );
+
 #endif
Index: lib/proxy.c
===================================================================
--- lib/proxy.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/proxy.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -114,4 +114,5 @@
 {
 	struct sockaddr_in *sin;
+	struct sockaddr_in me;
 	int fd = -1;
 
@@ -127,4 +128,14 @@
 
 	sock_make_nonblocking(fd);
+	
+	if( global.conf->iface_out )
+	{
+		me.sin_family = AF_INET;
+		me.sin_port = 0;
+		me.sin_addr.s_addr = inet_addr( global.conf->iface_out );
+		
+		if( bind( fd, (struct sockaddr *) &me, sizeof( me ) ) != 0 )
+			event_debug( "bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out );
+	}
 	
 	event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
@@ -530,5 +541,5 @@
 	struct PHB *phb;
 	
-	if (!host || !port || (port == -1) || !func || strlen(host) > 128) {
+	if (!host || port <= 0 || !func || strlen(host) > 128) {
 		return -1;
 	}
@@ -538,5 +549,5 @@
 	phb->data = data;
 	
-	if ((proxytype == PROXY_NONE) || strlen(proxyhost) > 0 || !proxyport || (proxyport == -1))
+	if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0)
 		return proxy_connect_none(host, port, phb);
 	else if (proxytype == PROXY_HTTP)
Index: lib/ssl_bogus.c
===================================================================
--- lib/ssl_bogus.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/ssl_bogus.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -65,2 +65,7 @@
 	return GAIM_INPUT_READ;
 }
+
+int ssl_pending( void *conn )
+{
+	return 0;
+}
Index: lib/ssl_client.h
===================================================================
--- lib/ssl_client.h	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/ssl_client.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -63,4 +63,7 @@
 G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len );
 
+/* See ssl_openssl.c for an explanation. */
+G_MODULE_EXPORT int ssl_pending( void *conn );
+
 /* Abort the SSL connection and disconnect the socket. Do not use close()
    directly, both the SSL library and the peer will be unhappy! */
Index: lib/ssl_gnutls.c
===================================================================
--- lib/ssl_gnutls.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/ssl_gnutls.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -216,4 +216,10 @@
 }
 
+/* See ssl_openssl.c for an explanation. */
+int ssl_pending( void *conn )
+{
+	return 0;
+}
+
 void ssl_disconnect( void *conn_ )
 {
Index: lib/ssl_nss.c
===================================================================
--- lib/ssl_nss.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/ssl_nss.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -175,4 +175,10 @@
 }
 
+/* See ssl_openssl.c for an explanation. */
+int ssl_pending( void *conn )
+{
+	return 0;
+}
+
 void ssl_disconnect( void *conn_ )
 {
Index: lib/ssl_openssl.c
===================================================================
--- lib/ssl_openssl.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/ssl_openssl.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -68,13 +68,13 @@
 	
 	conn->fd = proxy_connect( host, port, ssl_connected, conn );
+	if( conn->fd < 0 )
+	{
+		g_free( conn );
+		return NULL;
+	}
+	
 	conn->func = func;
 	conn->data = data;
 	conn->inpa = -1;
-	
-	if( conn->fd < 0 )
-	{
-		g_free( conn );
-		return NULL;
-	}
 	
 	return conn;
@@ -236,4 +236,19 @@
 }
 
+/* Only OpenSSL *really* needs this (and well, maybe NSS). See for more info:
+   http://www.gnu.org/software/gnutls/manual/gnutls.html#index-gnutls_005frecord_005fcheck_005fpending-209
+   http://www.openssl.org/docs/ssl/SSL_pending.html
+   
+   Required because OpenSSL empties the TCP buffer completely but doesn't
+   necessarily give us all the unencrypted data.
+   
+   Returns 0 if there's nothing left or if we don't have to care (GnuTLS),
+   1 if there's more data. */
+int ssl_pending( void *conn )
+{
+	return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ?
+	       SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0;
+}
+
 void ssl_disconnect( void *conn_ )
 {
Index: lib/ssl_sspi.c
===================================================================
--- lib/ssl_sspi.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ lib/ssl_sspi.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,278 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* SSL module - SSPI backend */
+
+/* Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org> */
+
+/*
+  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 with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "ssl_client.h"
+#include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <sspi.h>
+#include <schannel.h>
+#include "sock.h"
+
+static gboolean initialized = FALSE;
+int ssl_errno;
+
+struct scd
+{
+	int fd;
+	ssl_input_function func;
+	gpointer data;
+	gboolean established;
+  	CredHandle cred;		/* SSL credentials */
+	CtxtHandle context;		/* SSL context */
+	SecPkgContext_StreamSizes sizes;
+
+	char *host;
+
+	char *pending_raw_data;
+	gsize pending_raw_data_len;
+	char *pending_data;
+	gsize pending_data_len;
+};
+
+static void ssl_connected(gpointer, gint, GaimInputCondition);
+
+void sspi_global_init(void)
+{
+	/* FIXME */
+}
+
+void sspi_global_deinit(void)
+{
+	/* FIXME */
+}
+
+void *ssl_connect(char *host, int port, ssl_input_function func, gpointer data)
+{
+	struct scd *conn = g_new0(struct scd, 1);
+		
+	conn->fd = proxy_connect(host, port, ssl_connected, conn);
+	sock_make_nonblocking(conn->fd);
+	conn->func = func;
+	conn->data = data;
+	conn->host = g_strdup(host);
+	
+	if (conn->fd < 0)
+	{
+		g_free(conn);
+		return NULL;
+	}
+	
+	if (!initialized)
+	{
+		sspi_global_init();
+		initialized = TRUE;
+		atexit(sspi_global_deinit);
+	}
+
+	return conn;
+}
+
+static void ssl_connected(gpointer _conn, gint fd, GaimInputCondition cond)
+{
+	struct scd *conn = _conn;
+	SCHANNEL_CRED ssl_cred;
+	TimeStamp timestamp;
+	SecBuffer ibuf[2],obuf[1];
+	SecBufferDesc ibufs,obufs;
+	ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+    	ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+      	ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+		ISC_REQ_MANUAL_CRED_VALIDATION;
+	ULONG a;
+	gsize size = 0;
+	gchar *data = NULL;
+
+	memset(&ssl_cred, 0, sizeof(SCHANNEL_CRED));
+	ssl_cred.dwVersion = SCHANNEL_CRED_VERSION;
+	ssl_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
+
+	SECURITY_STATUS st = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &ssl_cred, NULL, NULL, &conn->cred, &timestamp);
+
+	if (st != SEC_E_OK) {
+		conn->func(conn->data, NULL, cond);
+		return;
+	}
+	
+	do {
+		/* initialize buffers */
+	    ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = data;
+	    ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NULL;
+	    obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NULL;
+    	ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+	    ibuf[1].BufferType = SECBUFFER_EMPTY;
+
+		/* initialize buffer descriptors */
+	    ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+	    ibufs.cBuffers = 2; obufs.cBuffers = 1;
+	    ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+
+		st = InitializeSecurityContext(&conn->cred, size?&conn->context:NULL, conn->host, req, 0, SECURITY_NETWORK_DREP, size?&ibufs:NULL, 0, &conn->context, &obufs, &a, &timestamp);  
+    	if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+			/* FIXME: Check return value */
+			send(conn->fd, obuf[0].pvBuffer, obuf[0].cbBuffer, 0);
+		}
+
+		switch (st) {
+		case SEC_I_INCOMPLETE_CREDENTIALS:
+			break;
+		case SEC_I_CONTINUE_NEEDED:
+			break;
+		case SEC_E_INCOMPLETE_MESSAGE:
+			break;
+		case SEC_E_OK:
+			break;
+		}
+	
+		QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->sizes);
+	} while (1);
+
+	conn->func(conn->data, conn, cond);
+}
+
+int ssl_read(void *conn, char *retdata, int len)
+{
+	struct scd *scd = conn;
+	SecBufferDesc msg;
+	SecBuffer buf[4];
+	int ret = -1, i;
+	char *data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer);
+
+	/* FIXME: Try to read some data */
+
+  	msg.ulVersion = SECBUFFER_VERSION;
+	msg.cBuffers = 4;
+	msg.pBuffers = buf;
+	
+	buf[0].BufferType = SECBUFFER_DATA;
+	buf[0].cbBuffer = len;
+	buf[0].pvBuffer = data;
+
+	buf[1].BufferType = SECBUFFER_EMPTY;
+	buf[2].BufferType = SECBUFFER_EMPTY;
+	buf[3].BufferType = SECBUFFER_EMPTY;
+
+	SECURITY_STATUS st = DecryptMessage(&scd->context, &msg, 0, NULL);
+
+	if (st != SEC_E_OK) {
+		/* FIXME */
+		return -1;
+	}
+
+	for (i = 0; i < 4; i++) {
+		if (buf[i].BufferType == SECBUFFER_DATA) {
+			memcpy(retdata, buf[i].pvBuffer, len);
+			ret = len;
+		}	
+	}
+
+	g_free(data);
+	return -1;
+}
+
+int ssl_write(void *conn, const char *userdata, int len)
+{
+	struct scd *scd = conn;
+	SecBuffer buf[4];
+	SecBufferDesc msg;
+	char *data;
+	int ret;
+
+	msg.ulVersion = SECBUFFER_VERSION;
+	msg.cBuffers = 4;
+	msg.pBuffers = buf;
+
+	data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer);
+	memcpy(data + scd->sizes.cbHeader, userdata, len);
+
+	buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+	buf[0].cbBuffer = scd->sizes.cbHeader;
+	buf[0].pvBuffer = data;
+
+	buf[1].BufferType = SECBUFFER_DATA;
+	buf[1].cbBuffer = len;
+	buf[1].pvBuffer = data + scd->sizes.cbHeader;
+
+	buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+	buf[2].cbBuffer = scd->sizes.cbTrailer;
+	buf[2].pvBuffer = data + scd->sizes.cbHeader + len;
+	buf[3].BufferType = SECBUFFER_EMPTY;
+
+	SECURITY_STATUS st = EncryptMessage(&scd->context, 0, &msg, 0);
+
+	ret = send(scd->fd, data, 
+				buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer, 0);
+
+	g_free(data);
+
+	return ret;
+}
+
+void ssl_disconnect(void *conn)
+{
+	struct scd *scd = conn;
+
+	SecBufferDesc msg;
+	SecBuffer buf;
+	DWORD dw;
+
+	dw = SCHANNEL_SHUTDOWN;
+	buf.cbBuffer = sizeof(dw);
+	buf.BufferType = SECBUFFER_TOKEN;
+	buf.pvBuffer = &dw;
+	
+	msg.ulVersion = SECBUFFER_VERSION;
+	msg.cBuffers = 1;
+	msg.pBuffers = &buf;
+
+	SECURITY_STATUS st = ApplyControlToken(&scd->context, &msg);
+
+	if (st != SEC_E_OK) {
+		/* FIXME */
+	}
+	
+	/* FIXME: call InitializeSecurityContext(Schannel), passing 
+	 * in empty buffers*/
+
+	DeleteSecurityContext(&scd->context);
+
+	FreeCredentialsHandle(&scd->cred);
+
+	closesocket(scd->fd);
+	g_free(scd->host);
+	g_free(scd);
+}
+
+int ssl_getfd(void *conn)
+{
+	return ((struct scd*)conn)->fd;
+}
+
+GaimInputCondition ssl_getdirection( void *conn )
+{
+	return GAIM_INPUT_WRITE; /* FIXME: or GAIM_INPUT_READ */
+}
Index: lib/url.c
===================================================================
--- lib/url.c	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/url.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -26,11 +26,14 @@
 #include "url.h"
 
-/* Convert an URL to a url_t structure					*/
+/* Convert an URL to a url_t structure */
 int url_set( url_t *url, char *set_url )
 {
-	char s[MAX_STRING];
+	char s[MAX_STRING+1];
 	char *i;
 	
-	/* protocol://							*/
+	memset( url, 0, sizeof( url_t ) );
+	memset( s, 0, sizeof( s ) );
+	
+	/* protocol:// */
 	if( ( i = strstr( set_url, "://" ) ) == NULL )
 	{
@@ -49,11 +52,10 @@
 			url->proto = PROTO_SOCKS5;
 		else
-		{
-			return( 0 );
-		}
+			return 0;
+		
 		strncpy( s, i + 3, MAX_STRING );
 	}
 	
-	/* Split							*/
+	/* Split */
 	if( ( i = strchr( s, '/' ) ) == NULL )
 	{
@@ -67,5 +69,5 @@
 	strncpy( url->host, s, MAX_STRING );
 	
-	/* Check for username in host field				*/
+	/* Check for username in host field */
 	if( strrchr( url->host, '@' ) != NULL )
 	{
@@ -76,5 +78,5 @@
 		*url->pass = 0;
 	}
-	/* If not: Fill in defaults					*/
+	/* If not: Fill in defaults */
 	else
 	{
@@ -82,5 +84,5 @@
 	}
 	
-	/* Password?							*/
+	/* Password? */
 	if( ( i = strchr( url->user, ':' ) ) != NULL )
 	{
@@ -88,5 +90,5 @@
 		strcpy( url->pass, i + 1 );
 	}
-	/* Port number?							*/
+	/* Port number? */
 	if( ( i = strchr( url->host, ':' ) ) != NULL )
 	{
Index: lib/url.h
===================================================================
--- lib/url.h	(revision 9730d7250bb9e938ca00b72efdd8e8b3c03b2753)
+++ lib/url.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -26,9 +26,9 @@
 #include "bitlbee.h"
 
-#define PROTO_HTTP		2
-#define PROTO_HTTPS		5
-#define PROTO_SOCKS4	3
-#define PROTO_SOCKS5	4
-#define PROTO_DEFAULT	PROTO_HTTP
+#define PROTO_HTTP      2
+#define PROTO_HTTPS     5
+#define PROTO_SOCKS4    3
+#define PROTO_SOCKS5    4
+#define PROTO_DEFAULT   PROTO_HTTP
 
 typedef struct url
@@ -36,8 +36,8 @@
 	int proto;
 	int port;
-	char host[MAX_STRING];
-	char file[MAX_STRING];
-	char user[MAX_STRING];
-	char pass[MAX_STRING];
+	char host[MAX_STRING+1];
+	char file[MAX_STRING+1];
+	char user[MAX_STRING+1];
+	char pass[MAX_STRING+1];
 } url_t;
 
Index: lib/xmltree.c
===================================================================
--- lib/xmltree.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ lib/xmltree.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,590 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
+*                                                                           *
+*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "xmltree.h"
+
+static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
+{
+	struct xt_parser *xt = data;
+	struct xt_node *node = g_new0( struct xt_node, 1 ), *nt;
+	int i;
+	
+	node->parent = xt->cur;
+	node->name = g_strdup( element_name );
+	
+	/* First count the number of attributes */
+	for( i = 0; attr_names[i]; i ++ );
+	
+	/* Then allocate a NULL-terminated array. */
+	node->attr = g_new0( struct xt_attr, i + 1 );
+	
+	/* And fill it, saving one variable by starting at the end. */
+	for( i --; i >= 0; i -- )
+	{
+		node->attr[i].key = g_strdup( attr_names[i] );
+		node->attr[i].value = g_strdup( attr_values[i] );
+	}
+	
+	/* Add it to the linked list of children nodes, if we have a current
+	   node yet. */
+	if( xt->cur )
+	{
+		if( xt->cur->children )
+		{
+			for( nt = xt->cur->children; nt->next; nt = nt->next );
+			nt->next = node;
+		}
+		else
+		{
+			xt->cur->children = node;
+		}
+	}
+	else if( xt->root )
+	{
+		/* ERROR situation: A second root-element??? */
+	}
+	
+	/* Now this node will be the new current node. */
+	xt->cur = node;
+	/* And maybe this is the root? */
+	if( xt->root == NULL )
+		xt->root = node;
+}
+
+static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
+{
+	struct xt_parser *xt = data;
+	struct xt_node *node = xt->cur;
+	
+	if( node == NULL )
+		return;
+	
+	/* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */
+	node->text = g_renew( char, node->text, node->text_len + text_len + 1 );
+	memcpy( node->text + node->text_len, text, text_len );
+	node->text_len += text_len;
+	/* Zero termination is always nice to have. */
+	node->text[node->text_len] = 0;
+}
+
+static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
+{
+	struct xt_parser *xt = data;
+	
+	xt->cur->flags |= XT_COMPLETE;
+	xt->cur = xt->cur->parent;
+}
+
+GMarkupParser xt_parser_funcs =
+{
+	xt_start_element,
+	xt_end_element,
+	xt_text,
+	NULL,
+	NULL
+};
+
+struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data )
+{
+	struct xt_parser *xt = g_new0( struct xt_parser, 1 );
+	
+	xt->data = data;
+	xt->handlers = handlers;
+	xt_reset( xt );
+	
+	return xt;
+}
+
+/* Reset the parser, flush everything we have so far. For example, we need
+   this for XMPP when doing TLS/SASL to restart the stream. */
+void xt_reset( struct xt_parser *xt )
+{
+	if( xt->parser )
+		g_markup_parse_context_free( xt->parser );
+	
+	xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL );
+	
+	if( xt->root )
+	{
+		xt_free_node( xt->root );
+		xt->root = NULL;
+		xt->cur = NULL;
+	}
+}
+
+/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
+   end-of-stream and 1 otherwise. */
+int xt_feed( struct xt_parser *xt, char *text, int text_len )
+{
+	if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
+	{
+		return -1;
+	}
+	
+	return !( xt->root && xt->root->flags & XT_COMPLETE );
+}
+
+/* Find completed nodes and see if a handler has to be called. Passing
+   a node isn't necessary if you want to start at the root, just pass
+   NULL. This second argument is needed for recursive calls. */
+int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
+{
+	struct xt_node *c;
+	xt_status st;
+	int i;
+	
+	/* Just in case someone likes infinite loops... */
+	if( xt->root == NULL )
+		return 0;
+	
+	if( node == NULL )
+		return xt_handle( xt, xt->root, depth );
+	
+	if( depth != 0 )
+		for( c = node->children; c; c = c->next )
+			if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) )
+				return 0;
+	
+	if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
+	{
+		for( i = 0; xt->handlers[i].func; i ++ )
+		{
+			/* This one is fun! \o/ */
+			
+						/* If handler.name == NULL it means it should always match. */
+			if( ( xt->handlers[i].name == NULL || 
+						/* If it's not, compare. There should always be a name. */
+			      g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
+						/* If handler.parent == NULL, it's a match. */
+			    ( xt->handlers[i].parent == NULL ||
+						/* If there's a parent node, see if the name matches. */
+			      ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
+						/* If there's no parent, the handler should mention <root> as a parent. */
+			                       g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
+			{
+				st = xt->handlers[i].func( node, xt->data );
+				
+				if( st == XT_ABORT )
+					return 0;
+				else if( st != XT_NEXT )
+					break;
+			}
+		}
+		
+		node->flags |= XT_SEEN;
+	}
+	
+	return 1;
+}
+
+/* Garbage collection: Cleans up all nodes that are handled. Useful for
+   streams because there's no reason to keep a complete packet history
+   in memory. */
+void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
+{
+	struct xt_node *c, *prev;
+	
+	if( !xt || !xt->root )
+		return;
+	
+	if( node == NULL )
+		return xt_cleanup( xt, xt->root, depth );
+	
+	if( node->flags & XT_SEEN && node == xt->root )
+	{
+		xt_free_node( xt->root );
+		xt->root = xt->cur = NULL;
+		/* xt->cur should be NULL already, BTW... */
+		
+		return;
+	}
+	
+	/* c contains the current node, prev the previous node (or NULL).
+	   I admit, this one's pretty horrible. */
+	for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
+	{
+		if( c->flags & XT_SEEN )
+		{
+			/* Remove the node from the linked list. */
+			if( prev )
+				prev->next = c->next;
+			else
+				node->children = c->next;
+			
+			xt_free_node( c );
+			
+			/* Since the for loop wants to get c->next, make sure
+			   c points at something that exists (and that c->next
+			   will actually be the next item we should check). c
+			   can be NULL now, if we just removed the first item.
+			   That explains the ? thing in for(). */
+			c = prev;
+		}
+		else
+		{
+			/* This node can't be cleaned up yet, but maybe a
+			   subnode can. */
+			if( depth != 0 )
+				xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
+		}
+	}
+}
+
+static void xt_to_string_real( struct xt_node *node, GString *str )
+{
+	char *buf;
+	struct xt_node *c;
+	int i;
+	
+	g_string_append_printf( str, "<%s", node->name );
+	
+	for( i = 0; node->attr[i].key; i ++ )
+	{
+		buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
+		g_string_append( str, buf );
+		g_free( buf );
+	}
+	
+	if( node->text == NULL && node->children == NULL )
+	{
+		g_string_append( str, "/>" );
+		return;
+	}
+	
+	g_string_append( str, ">" );
+	if( node->text_len > 0 )
+	{
+		buf = g_markup_escape_text( node->text, node->text_len );
+		g_string_append( str, buf );
+		g_free( buf );
+	}
+	
+	for( c = node->children; c; c = c->next )
+		xt_to_string_real( c, str );
+	
+	g_string_append_printf( str, "</%s>", node->name );
+}
+
+char *xt_to_string( struct xt_node *node )
+{
+	GString *ret;
+	char *real;
+	
+	ret = g_string_new( "" );
+	xt_to_string_real( node, ret );
+	
+	real = ret->str;
+	g_string_free( ret, FALSE );
+	
+	return real;
+}
+
+#ifdef DEBUG
+void xt_print( struct xt_node *node )
+{
+	int i;
+	struct xt_node *c;
+	
+	/* Indentation */
+	for( c = node; c->parent; c = c->parent )
+		printf( "\t" );
+	
+	/* Start the tag */
+	printf( "<%s", node->name );
+	
+	/* Print the attributes */
+	for( i = 0; node->attr[i].key; i ++ )
+		printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) );
+	
+	/* /> in case there's really *nothing* inside this tag, otherwise
+	   just >. */
+	/* If this tag doesn't have any content at all... */
+	if( node->text == NULL && node->children == NULL )
+	{
+		printf( "/>\n" );
+		return;
+		/* Then we're finished! */
+	}
+	
+	/* Otherwise... */
+	printf( ">" );
+	
+	/* Only print the text if it contains more than whitespace (TEST). */
+	if( node->text_len > 0 )
+	{
+		for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
+		if( node->text[i] )
+			printf( "%s", g_markup_escape_text( node->text, -1 ) );
+	}
+	
+	if( node->children )
+		printf( "\n" );
+	
+	for( c = node->children; c; c = c->next )
+		xt_print( c );
+	
+	if( node->children )
+		for( c = node; c->parent; c = c->parent )
+			printf( "\t" );
+	
+	/* Non-empty tag is now finished. */
+	printf( "</%s>\n", node->name );
+}
+#endif
+
+struct xt_node *xt_dup( struct xt_node *node )
+{
+	struct xt_node *dup = g_new0( struct xt_node, 1 );
+	struct xt_node *c, *dc = NULL;
+	int i;
+	
+	/* Let's NOT copy the parent element here BTW! Only do it for children. */
+	
+	dup->name = g_strdup( node->name );
+	dup->flags = node->flags;
+	if( node->text )
+	{
+		dup->text = g_memdup( node->text, node->text_len + 1 );
+		dup->text_len = node->text_len;
+	}
+	
+	/* Count the number of attributes and allocate the new array. */
+	for( i = 0; node->attr[i].key; i ++ );
+	dup->attr = g_new0( struct xt_attr, i + 1 );
+	
+	/* Copy them all! */
+	for( i --; i >= 0; i -- )
+	{
+		dup->attr[i].key = g_strdup( node->attr[i].key );
+		dup->attr[i].value = g_strdup( node->attr[i].value );
+	}
+	
+	/* This nice mysterious loop takes care of the children. */
+	for( c = node->children; c; c = c->next )
+	{
+		if( dc == NULL )
+			dc = dup->children = xt_dup( c );
+		else
+			dc = ( dc->next = xt_dup( c ) );
+		
+		dc->parent = dup;
+	}
+	
+	return dup;
+}
+
+/* Frees a node. This doesn't clean up references to itself from parents! */
+void xt_free_node( struct xt_node *node )
+{
+	int i;
+	
+	if( !node )
+		return;
+	
+	g_free( node->name );
+	g_free( node->text );
+	
+	for( i = 0; node->attr[i].key; i ++ )
+	{
+		g_free( node->attr[i].key );
+		g_free( node->attr[i].value );
+	}
+	g_free( node->attr );
+	
+	while( node->children )
+	{
+		struct xt_node *next = node->children->next;
+		
+		xt_free_node( node->children );
+		node->children = next;
+	}
+	
+	g_free( node );
+}
+
+void xt_free( struct xt_parser *xt )
+{
+	if( !xt )
+		return;
+	
+	if( xt->root )
+		xt_free_node( xt->root );
+	
+	g_markup_parse_context_free( xt->parser );
+	
+	g_free( xt );
+}
+
+/* To find a node's child with a specific name, pass the node's children
+   list, not the node itself! The reason you have to do this by hand: So
+   that you can also use this function as a find-next. */
+struct xt_node *xt_find_node( struct xt_node *node, const char *name )
+{
+	while( node )
+	{
+		if( g_strcasecmp( node->name, name ) == 0 )
+			break;
+		
+		node = node->next;
+	}
+	
+	return node;
+}
+
+char *xt_find_attr( struct xt_node *node, const 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;
+	
+	return node->attr[i].value;
+}
+
+struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children )
+{
+	struct xt_node *node, *c;
+	
+	node = g_new0( struct xt_node, 1 );
+	node->name = g_strdup( name );
+	node->children = children;
+	node->attr = g_new0( struct xt_attr, 1 );
+	
+	if( text )
+	{
+		node->text_len = strlen( text );
+		node->text = g_memdup( text, node->text_len + 1 );
+	}
+	
+	for( c = children; c; c = c->next )
+	{
+		if( c->parent != NULL )
+		{
+			/* ERROR CONDITION: They seem to have a parent already??? */
+		}
+		
+		c->parent = node;
+	}
+	
+	return node;
+}
+
+void xt_add_child( struct xt_node *parent, struct xt_node *child )
+{
+	struct xt_node *node;
+	
+	/* This function can actually be used to add more than one child, so
+	   do handle this properly. */
+	for( node = child; node; node = node->next )
+	{
+		if( node->parent != NULL )
+		{
+			/* ERROR CONDITION: They seem to have a parent already??? */
+		}
+		
+		node->parent = parent;
+	}
+	
+	if( parent->children == NULL )
+	{
+		parent->children = child;
+	}
+	else
+	{
+		for( node = parent->children; node->next; node = node->next );
+		node->next = child;
+	}
+}
+
+void xt_add_attr( struct xt_node *node, const char *key, const char *value )
+{
+	int i;
+	
+	/* Now actually it'd be nice if we can also change existing attributes
+	   (which actually means this function doesn't have the right name).
+	   So let's find out if we have this attribute already... */
+	for( i = 0; node->attr[i].key; i ++ )
+		if( strcmp( node->attr[i].key, key ) == 0 )
+			break;
+	
+	if( node->attr[i].key == NULL )
+	{
+		/* If not, allocate space for a new attribute. */
+		node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
+		node->attr[i].key = g_strdup( key );
+		node->attr[i+1].key = NULL;
+	}
+	else
+	{
+		/* Otherwise, free the old value before setting the new one. */
+		g_free( node->attr[i].value );
+	}
+	
+	node->attr[i].value = g_strdup( value );
+}
+
+int xt_remove_attr( struct xt_node *node, const char *key )
+{
+	int i, last;
+	
+	for( i = 0; node->attr[i].key; i ++ )
+		if( strcmp( node->attr[i].key, key ) == 0 )
+			break;
+	
+	/* If we didn't find the attribute... */
+	if( node->attr[i].key == NULL )
+		return 0;
+	
+	g_free( node->attr[i].key );
+	g_free( node->attr[i].value );
+	
+	/* If it's the last, this is easy: */
+	if( node->attr[i+1].key == NULL )
+	{
+		node->attr[i].key = node->attr[i].value = NULL;
+	}
+	else /* It's also pretty easy, actually. */
+	{
+		/* Find the last item. */
+		for( last = i + 1; node->attr[last+1].key; last ++ );
+		
+		node->attr[i] = node->attr[last];
+		node->attr[last].key = NULL;
+		node->attr[last].value = NULL;
+	}
+	
+	/* Let's not bother with reallocating memory here. It takes time and
+	   most packets don't stay in memory for long anyway. */
+	
+	return 1;
+}
Index: lib/xmltree.h
===================================================================
--- lib/xmltree.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ lib/xmltree.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,97 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
+*                                                                           *
+*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#ifndef _XMLTREE_H
+#define _XMLTREE_H
+
+typedef enum
+{
+	XT_COMPLETE	= 1,	/* </tag> reached */
+	XT_SEEN		= 2,	/* Handler called (or not defined) */
+} xt_flags;
+
+typedef enum
+{
+	XT_ABORT,		/* Abort, don't handle the rest anymore */
+	XT_HANDLED,		/* Handled this tag properly, go to the next one */
+	XT_NEXT			/* Try if there's another matching handler */
+} xt_status;
+
+struct xt_attr
+{
+	char *key, *value;
+};
+
+struct xt_node
+{
+	struct xt_node *parent;
+	struct xt_node *children;
+	
+	char *name;
+	struct xt_attr *attr;
+	char *text;
+	int text_len;
+	
+	struct xt_node *next;
+	xt_flags flags;
+};
+
+typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data );
+
+struct xt_handler_entry
+{
+	char *name, *parent;
+	xt_handler_func func;
+};
+
+struct xt_parser
+{
+	GMarkupParseContext *parser;
+	struct xt_node *root;
+	struct xt_node *cur;
+	
+	const struct xt_handler_entry *handlers;
+	gpointer data;
+	
+	GError *gerr;
+};
+
+struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data );
+void xt_reset( struct xt_parser *xt );
+int xt_feed( struct xt_parser *xt, char *text, int text_len );
+int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth );
+void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth );
+char *xt_to_string( struct xt_node *node );
+void xt_print( struct xt_node *node );
+struct xt_node *xt_dup( struct xt_node *node );
+void xt_free_node( struct xt_node *node );
+void xt_free( struct xt_parser *xt );
+struct xt_node *xt_find_node( struct xt_node *node, const char *name );
+char *xt_find_attr( struct xt_node *node, const char *key );
+
+struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children );
+void xt_add_child( struct xt_node *parent, struct xt_node *child );
+void xt_add_attr( struct xt_node *node, const char *key, const char *value );
+int xt_remove_attr( struct xt_node *node, const char *key );
+
+#endif
