Index: lib/Makefile
===================================================================
--- lib/Makefile	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/Makefile	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -8,9 +8,11 @@
 
 -include ../Makefile.settings
+ifdef SRCDIR
+SRCDIR := $(SRCDIR)lib/
+endif
 
 # [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 xmltree.o
+objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
 
-CFLAGS += -Wall
 LFLAGS += -r
 
@@ -37,5 +39,5 @@
 $(objects): ../Makefile.settings Makefile
 
-$(objects): %.o: %.c
+$(objects): %.o: $(SRCDIR)%.c
 	@echo '*' Compiling $<
 	@$(CC) -c $(CFLAGS) $< -o $@
Index: lib/events.h
===================================================================
--- lib/events.h	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/events.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -48,6 +48,8 @@
    the given callback function. */
 typedef enum {
-	GAIM_INPUT_READ = 1 << 1,
-	GAIM_INPUT_WRITE = 1 << 2
+	B_EV_IO_READ = 1 << 0,
+	B_EV_IO_WRITE = 1 << 1,
+	B_EV_FLAG_FORCE_ONCE = 1 << 16,
+	B_EV_FLAG_FORCE_REPEAT = 1 << 17,
 } b_input_condition;
 typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond);
Index: lib/events_glib.c
===================================================================
--- lib/events_glib.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/events_glib.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -49,4 +49,5 @@
 	b_event_handler function;
 	gpointer data;
+	guint flags;
 } GaimIOClosure;
 
@@ -76,7 +77,7 @@
 
 	if (condition & GAIM_READ_COND)
-		gaim_cond |= GAIM_INPUT_READ;
+		gaim_cond |= B_EV_IO_READ;
 	if (condition & GAIM_WRITE_COND)
-		gaim_cond |= GAIM_INPUT_WRITE;
+		gaim_cond |= B_EV_IO_WRITE;
 	
 	event_debug( "gaim_io_invoke( %d, %d, 0x%x )\n", g_io_channel_unix_get_fd(source), condition, data );
@@ -87,5 +88,10 @@
 		event_debug( "Returned FALSE, cancelling.\n" );
 	
-	return st;
+	if (closure->flags & B_EV_FLAG_FORCE_ONCE)
+		return FALSE;
+	else if (closure->flags & B_EV_FLAG_FORCE_REPEAT)
+		return TRUE;
+	else
+		return st;
 }
 
@@ -105,8 +111,9 @@
 	closure->function = function;
 	closure->data = data;
+	closure->flags = condition;
 	
-	if (condition & GAIM_INPUT_READ)
+	if (condition & B_EV_IO_READ)
 		cond |= GAIM_READ_COND;
-	if (condition & GAIM_INPUT_WRITE)
+	if (condition & B_EV_IO_WRITE)
 		cond |= GAIM_WRITE_COND;
 	
Index: lib/events_libevent.c
===================================================================
--- lib/events_libevent.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/events_libevent.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -60,4 +60,5 @@
 	b_event_handler function;
 	void *data;
+	guint flags;
 };
 
@@ -126,7 +127,7 @@
 	{
 		if( event & EV_READ )
-			cond |= GAIM_INPUT_READ;
+			cond |= B_EV_IO_READ;
 		if( event & EV_WRITE )
-			cond |= GAIM_INPUT_WRITE;
+			cond |= B_EV_IO_WRITE;
 	}
 	
@@ -150,5 +151,5 @@
 		return;
 	}
-	else if( !st )
+	else if( !st && !( b_ev->flags & B_EV_FLAG_FORCE_REPEAT ) )
 	{
 		event_debug( "Handler returned FALSE: " );
@@ -174,6 +175,6 @@
 	event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data );
 	
-	if( ( condition & GAIM_INPUT_READ  && ( b_ev = g_hash_table_lookup( read_hash,  &fd ) ) ) ||
-	    ( condition & GAIM_INPUT_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) )
+	if( ( condition & B_EV_IO_READ  && ( b_ev = g_hash_table_lookup( read_hash,  &fd ) ) ) ||
+	    ( condition & B_EV_IO_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) )
 	{
 		/* We'll stick with this libevent entry, but give it a new BitlBee id. */
@@ -198,7 +199,7 @@
 		
 		out_cond = EV_PERSIST;
-		if( condition & GAIM_INPUT_READ )
+		if( condition & B_EV_IO_READ )
 			out_cond |= EV_READ;
-		if( condition & GAIM_INPUT_WRITE )
+		if( condition & B_EV_IO_WRITE )
 			out_cond |= EV_WRITE;
 		
@@ -212,4 +213,5 @@
 	}
 	
+	b_ev->flags = condition;
 	g_hash_table_insert( id_hash, &b_ev->id, b_ev );
 	return b_ev->id;
Index: lib/ftutil.c
===================================================================
--- lib/ftutil.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
+++ lib/ftutil.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -0,0 +1,144 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Utility functions for file transfer                                      *
+*                                                                           *
+*  Copyright 2008 Uli Meis <a.sporto+bee@gmail.com>                         *
+*                                                                           *
+*  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.              *
+*                                                                           *
+\***************************************************************************/
+
+#define BITLBEE_CORE
+#include "bitlbee.h"
+#include <poll.h>
+#include <netinet/tcp.h>
+#include "lib/ftutil.h"
+
+#define ASSERTSOCKOP(op, msg) \
+	if( (op) == -1 ) {\
+		g_snprintf( errmsg, sizeof( errmsg ), msg ": %s", strerror( errno ) ); \
+		return -1; }
+
+/*
+ * Creates a listening socket and returns it in saddr_ptr.
+ */
+int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client, char **errptr )
+{
+	int fd, gret, saddrlen;
+	struct addrinfo hints, *rp;
+	socklen_t ssize = sizeof( struct sockaddr_storage );
+	struct sockaddr_storage saddrs, *saddr = &saddrs;
+	static char errmsg[1024];
+	char *ftlisten = global.conf->ft_listen;
+
+	if( errptr )
+		*errptr = errmsg;
+
+	strcpy( port, "0" );
+
+	/* Format is <IP-A>[:<Port-A>];<IP-B>[:<Port-B>] where
+	 * A is for connections with the bitlbee client (DCC)
+	 * and B is for connections with IM peers.
+	 */
+	if( ftlisten )
+	{
+		char *scolon = strchr( ftlisten, ';' );
+		char *colon;
+
+		if( scolon )
+		{
+			if( for_bitlbee_client )
+			{
+				*scolon = '\0';
+				strncpy( host, ftlisten, HOST_NAME_MAX );
+				*scolon = ';';
+			}
+			else
+			{
+				strncpy( host, scolon + 1, HOST_NAME_MAX );
+			}
+		}
+		else
+		{
+			strncpy( host, ftlisten, HOST_NAME_MAX );
+		}
+
+		if( ( colon = strchr( host, ':' ) ) )
+		{
+			*colon = '\0';
+			strncpy( port, colon + 1, 5 );
+		}
+	}
+	else if( copy_fd >= 0 && getsockname( copy_fd, (struct sockaddr*) &saddrs, &ssize ) == 0 &&
+	         ( saddrs.ss_family == AF_INET || saddrs.ss_family == AF_INET6 ) &&
+	         getnameinfo( (struct sockaddr*) &saddrs, ssize, host, HOST_NAME_MAX,
+	                      NULL, 0, NI_NUMERICHOST ) == 0 )
+	{
+		/* We just took our local address on copy_fd, which is likely to be a
+		   sensible address from which we can do a file transfer now - the
+		   most sensible we can get easily. */
+	}
+	else
+	{
+		ASSERTSOCKOP( gethostname( host, HOST_NAME_MAX + 1 ), "gethostname()" );
+	}
+
+	memset( &hints, 0, sizeof( struct addrinfo ) );
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_NUMERICSERV;
+
+	if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) != 0 )
+	{
+		sprintf( errmsg, "getaddrinfo() failed: %s", gai_strerror( gret ) );
+		return -1;
+	}
+
+	saddrlen = rp->ai_addrlen;
+
+	memcpy( saddr, rp->ai_addr, saddrlen );
+
+	freeaddrinfo( rp );
+
+	ASSERTSOCKOP( fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" );
+	ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, saddrlen ), "Binding socket" );
+	ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" );
+
+	if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ?
+			( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr :
+			( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr,
+			host, HOST_NAME_MAX ) )
+	{
+		strcpy( errmsg, "inet_ntop failed on listening socket" );
+		return -1;
+	}
+
+	ssize = sizeof( struct sockaddr_storage );
+	ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" );
+
+	if( saddr->ss_family == AF_INET )
+		g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in *) saddr )->sin_port ) );
+	else
+		g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in6 *) saddr )->sin6_port ) );
+
+	if( saddr_ptr )
+		memcpy( saddr_ptr, saddr, saddrlen );
+
+	/* I hate static-length strings.. */
+	host[HOST_NAME_MAX] = '\0';
+	port[5] = '\0';
+	
+	return fd;
+}
Index: lib/ftutil.h
===================================================================
--- lib/ftutil.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
+++ lib/ftutil.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -0,0 +1,40 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Utility functions for file transfer                                      *
+*                                                                           *
+*  Copyright 2008 Uli Meis <a.sporto+bee@gmail.com>                         *
+*                                                                           *
+*  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.              *
+*                                                                           *
+\***************************************************************************/
+
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0x0400   /* Don't use name resolution.  */
+#endif
+
+/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */
+#ifndef HOST_NAME_MAX
+#include <sys/param.h>
+#ifdef MAXHOSTNAMELEN
+#define HOST_NAME_MAX MAXHOSTNAMELEN
+#else
+#define HOST_NAME_MAX 255
+#endif
+#endif
+
+/* This function should be used with care. host should be AT LEAST a
+   char[HOST_NAME_MAX+1] and port AT LEAST a char[6]. */
+int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client, char **errptr );
Index: lib/http_client.c
===================================================================
--- lib/http_client.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/http_client.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -35,7 +35,8 @@
 static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond );
 static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond );
-
-
-void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data )
+static void http_free( struct http_request *req );
+
+
+struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data )
 {
 	struct http_request *req;
@@ -67,9 +68,10 @@
 	req->request = g_strdup( request );
 	req->request_length = strlen( request );
+	req->redir_ttl = 3;
 	
 	return( req );
 }
 
-void *http_dorequest_url( char *url_string, http_input_function func, gpointer data )
+struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data )
 {
 	url_t *url = g_new0( url_t, 1 );
@@ -149,8 +151,8 @@
 	if( req->bytes_written < req->request_length )
 		req->inpa = b_input_add( source,
-		                         req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE,
+		                         req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_WRITE,
 	        	                 http_connected, req );
 	else
-		req->inpa = b_input_add( source, GAIM_INPUT_READ, http_incoming_data, req );
+		req->inpa = b_input_add( source, B_EV_IO_READ, http_incoming_data, req );
 	
 	return FALSE;
@@ -234,5 +236,5 @@
 	/* There will be more! */
 	req->inpa = b_input_add( req->fd,
-	                         req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_READ,
+	                         req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_READ,
 	                         http_incoming_data, req );
 	
@@ -311,5 +313,5 @@
 	}
 	
-	if( req->status_code == 301 || req->status_code == 302 )
+	if( ( req->status_code == 301 || req->status_code == 302 ) && req->redir_ttl-- > 0 )
 	{
 		char *loc, *new_request, *new_host;
@@ -445,5 +447,5 @@
 }
 
-void http_free( struct http_request *req )
+static void http_free( struct http_request *req )
 {
 	g_free( req->request );
Index: lib/http_client.h
===================================================================
--- lib/http_client.h	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/http_client.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -61,4 +61,6 @@
 	int finished;           /* Set to non-0 if the request was completed
 	                           successfully. */
+	int redir_ttl;          /* You can set it to 0 if you don't want
+	                           http_client to follow them. */
 	
 	http_input_function func;
@@ -79,6 +81,4 @@
    you want to add some extra headers. As you can see, HTTPS connections
    are also supported (using ssl_client). */
-void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data );
-void *http_dorequest_url( char *url_string, http_input_function func, gpointer data );
-
-void http_free( struct http_request *req );
+struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data );
+struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data );
Index: lib/md5.h
===================================================================
--- lib/md5.h	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/md5.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -27,5 +27,9 @@
 #include <sys/types.h>
 #include <gmodule.h>
+#if(__sun)
+#include <inttypes.h>
+#else
 #include <stdint.h>
+#endif
 
 typedef uint8_t md5_byte_t;
Index: lib/misc.c
===================================================================
--- lib/misc.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/misc.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -77,4 +77,39 @@
 	
 	return mktime(&tm);
+}
+
+time_t mktime_utc( struct tm *tp )
+{
+	struct tm utc;
+	time_t res, tres;
+	
+	tp->tm_isdst = -1;
+	res = mktime( tp );
+	/* Problem is, mktime() just gave us the GMT timestamp for the
+	   given local time... While the given time WAS NOT local. So
+	   we should fix this now.
+	   
+	   Now I could choose between messing with environment variables
+	   (kludgy) or using timegm() (not portable)... Or doing the
+	   following, which I actually prefer...
+	   
+	   tzset() may also work but in other places I actually want to
+	   use local time.
+	   
+	   FFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUU!! */
+	gmtime_r( &res, &utc );
+	utc.tm_isdst = -1;
+	if( utc.tm_hour == tp->tm_hour && utc.tm_min == tp->tm_min )
+		/* Sweet! We're in UTC right now... */
+		return res;
+	
+	tres = mktime( &utc );
+	res += res - tres;
+	
+	/* Yes, this is a hack. And it will go wrong around DST changes.
+	   BUT this is more likely to be threadsafe than messing with
+	   environment variables, and possibly more portable... */
+	
+	return res;
 }
 
@@ -119,9 +154,9 @@
 {
 	char *start = in;
-	char *out = g_malloc( strlen( in ) + 1 );
+	char out[strlen(in)+1];
 	char *s = out, *cs;
 	int i, matched;
 	
-	memset( out, 0, strlen( in ) + 1 );
+	memset( out, 0, sizeof( out ) );
 	
 	while( *in )
@@ -185,5 +220,4 @@
 	
 	strcpy( start, out );
-	g_free( out );
 }
 
@@ -270,13 +304,16 @@
 	for( i = j = 0; t[i]; i ++, j ++ )
 	{
-		/* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */
-		if( !isalnum( t[i] ) )
+		/* Warning: isalnum() is locale-aware, so don't use it here! */
+		if( ( t[i] >= 'A' && t[i] <= 'Z' ) ||
+		    ( t[i] >= 'a' && t[i] <= 'z' ) ||
+		    ( t[i] >= '0' && t[i] <= '9' ) ||
+		    strchr( "._-~", t[i] ) )
+		{
+			s[j] = t[i];
+		}
+		else
 		{
 			sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
 			j += 2;
-		}
-		else
-		{
-			s[j] = t[i];
 		}
 	}
@@ -611,2 +648,53 @@
 	return ret;
 }
+
+/* Split commands (root-style, *not* IRC-style). Handles "quoting of"
+   white\ space in 'various ways'. Returns a NULL-terminated static
+   char** so watch out with nested use! Definitely not thread-safe. */
+char **split_command_parts( char *command )
+{
+	static char *cmd[IRC_MAX_ARGS+1];
+	char *s, q = 0;
+	int k;
+	
+	memset( cmd, 0, sizeof( cmd ) );
+	cmd[0] = command;
+	k = 1;
+	for( s = command; *s && k < IRC_MAX_ARGS; s ++ )
+		if( *s == ' ' && !q )
+		{
+			*s = 0;
+			while( *++s == ' ' );
+			if( *s == '"' || *s == '\'' )
+			{
+				q = *s;
+				s ++;
+			}
+			if( *s )
+			{
+				cmd[k++] = s;
+				s --;
+			}
+			else
+			{
+				break;
+			}
+		}
+		else if( *s == '\\' && ( ( !q && s[1] ) || ( q && q == s[1] ) ) )
+		{
+			char *cpy;
+			
+			for( cpy = s; *cpy; cpy ++ )
+				cpy[0] = cpy[1];
+		}
+		else if( *s == q )
+		{
+			q = *s = 0;
+		}
+	
+	/* Full zero-padding for easier argc checking. */
+	while( k <= IRC_MAX_ARGS )
+		cmd[k++] = NULL;
+	
+	return cmd;
+}
Index: lib/misc.h
===================================================================
--- lib/misc.h	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/misc.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -43,4 +43,5 @@
 
 G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );
+G_MODULE_EXPORT time_t mktime_utc( struct tm *tp );
 double gettime( void );
 
@@ -68,3 +69,5 @@
 G_MODULE_EXPORT int md5_verify_password( char *password, char *hash );
 
+G_MODULE_EXPORT char **split_command_parts( char *command );
+
 #endif
Index: lib/oauth.c
===================================================================
--- lib/oauth.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
+++ lib/oauth.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -0,0 +1,453 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple OAuth client (consumer) implementation.                           *
+*                                                                           *
+*  Copyright 2010 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 <gmodule.h>
+#include <stdlib.h>
+#include <string.h>
+#include "http_client.h"
+#include "base64.h"
+#include "misc.h"
+#include "sha1.h"
+#include "url.h"
+#include "oauth.h"
+
+#define HMAC_BLOCK_SIZE 64
+
+static char *oauth_sign( const char *method, const char *url,
+                         const char *params, struct oauth_info *oi )
+{
+	sha1_state_t sha1;
+	uint8_t hash[sha1_hash_size];
+	uint8_t key[HMAC_BLOCK_SIZE+1];
+	char *s;
+	int i;
+	
+	/* Create K. If our current key is >64 chars we have to hash it,
+	   otherwise just pad. */
+	memset( key, 0, HMAC_BLOCK_SIZE );
+	i = strlen( oi->sp->consumer_secret ) + 1 + ( oi->token_secret ? strlen( oi->token_secret ) : 0 );
+	if( i > HMAC_BLOCK_SIZE )
+	{
+		sha1_init( &sha1 );
+		sha1_append( &sha1, (uint8_t*) oi->sp->consumer_secret, strlen( oi->sp->consumer_secret ) );
+		sha1_append( &sha1, (uint8_t*) "&", 1 );
+		if( oi->token_secret )
+			sha1_append( &sha1, (uint8_t*) oi->token_secret, strlen( oi->token_secret ) );
+		sha1_finish( &sha1, key );
+	}
+	else
+	{
+		g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s",
+		            oi->sp->consumer_secret, oi->token_secret ? oi->token_secret : "" );
+	}
+	
+	/* Inner part: H(K XOR 0x36, text) */
+	sha1_init( &sha1 );
+	
+	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
+		key[i] ^= 0x36;
+	sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
+	
+	/* OAuth: text = method&url&params, all http_encoded. */
+	sha1_append( &sha1, (const uint8_t*) method, strlen( method ) );
+	sha1_append( &sha1, (const uint8_t*) "&", 1 );
+	
+	s = g_new0( char, strlen( url ) * 3 + 1 );
+	strcpy( s, url );
+	http_encode( s );
+	sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
+	sha1_append( &sha1, (const uint8_t*) "&", 1 );
+	g_free( s );
+	
+	s = g_new0( char, strlen( params ) * 3 + 1 );
+	strcpy( s, params );
+	http_encode( s );
+	sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
+	g_free( s );
+	
+	sha1_finish( &sha1, hash );
+	
+	/* Final result: H(K XOR 0x5C, inner stuff) */
+	sha1_init( &sha1 );
+	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
+		key[i] ^= 0x36 ^ 0x5c;
+	sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
+	sha1_append( &sha1, hash, sha1_hash_size );
+	sha1_finish( &sha1, hash );
+	
+	/* base64_encode + HTTP escape it (both consumers 
+	   need it that away) and we're done. */
+	s = base64_encode( hash, sha1_hash_size );
+	s = g_realloc( s, strlen( s ) * 3 + 1 );
+	http_encode( s );
+	
+	return s;
+}
+
+static char *oauth_nonce()
+{
+	unsigned char bytes[9];
+	
+	random_bytes( bytes, sizeof( bytes ) );
+	return base64_encode( bytes, sizeof( bytes ) );
+}
+
+void oauth_params_add( GSList **params, const char *key, const char *value )
+{
+	char *item;
+	
+	item = g_strdup_printf( "%s=%s", key, value );
+	*params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp );
+}
+
+void oauth_params_del( GSList **params, const char *key )
+{
+	int key_len = strlen( key );
+	GSList *l, *n;
+	
+	for( l = *params; l; l = n )
+	{
+		n = l->next;
+		
+		if( strncmp( (char*) l->data, key, key_len ) == 0 &&
+		    ((char*)l->data)[key_len] == '=' )
+		{
+			g_free( l->data );
+			*params = g_slist_remove( *params, l->data );
+		}
+	}
+}
+
+void oauth_params_set( GSList **params, const char *key, const char *value )
+{
+	oauth_params_del( params, key );
+	oauth_params_add( params, key, value );
+}
+
+const char *oauth_params_get( GSList **params, const char *key )
+{
+	int key_len = strlen( key );
+	GSList *l;
+	
+	for( l = *params; l; l = l->next )
+	{
+		if( strncmp( (char*) l->data, key, key_len ) == 0 &&
+		    ((char*)l->data)[key_len] == '=' )
+			return (const char*) l->data + key_len + 1;
+	}
+	
+	return NULL;
+}
+
+static void oauth_params_parse( GSList **params, char *in )
+{
+	char *amp, *eq, *s;
+	
+	while( in && *in )
+	{
+		eq = strchr( in, '=' );
+		if( !eq )
+			break;
+		
+		*eq = '\0';
+		if( ( amp = strchr( eq + 1, '&' ) ) )
+			*amp = '\0';
+		
+		s = g_strdup( eq + 1 );
+		http_decode( s );
+		oauth_params_add( params, in, s );
+		g_free( s );
+		
+		*eq = '=';
+		if( amp == NULL )
+			break;
+		
+		*amp = '&';
+		in = amp + 1;
+	}
+}
+
+void oauth_params_free( GSList **params )
+{
+	while( params && *params )
+	{
+		g_free( (*params)->data );
+		*params = g_slist_remove( *params, (*params)->data );
+	}
+}
+
+char *oauth_params_string( GSList *params )
+{
+	GSList *l;
+	GString *str = g_string_new( "" );
+	
+	for( l = params; l; l = l->next )
+	{
+		char *s, *eq;
+		
+		s = g_malloc( strlen( l->data ) * 3 + 1 );
+		strcpy( s, l->data );
+		if( ( eq = strchr( s, '=' ) ) )
+			http_encode( eq + 1 );
+		g_string_append( str, s );
+		g_free( s );
+		
+		if( l->next )
+			g_string_append_c( str, '&' );
+	}
+	
+	return g_string_free( str, FALSE );
+}
+
+void oauth_info_free( struct oauth_info *info )
+{
+	if( info )
+	{
+		g_free( info->auth_url );
+		g_free( info->request_token );
+		g_free( info->token );
+		g_free( info->token_secret );
+		g_free( info );
+	}
+}
+
+static void oauth_add_default_params( GSList **params, const struct oauth_service *sp )
+{
+	char *s;
+	
+	oauth_params_set( params, "oauth_consumer_key", sp->consumer_key );
+	oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" );
+	
+	s = g_strdup_printf( "%d", (int) time( NULL ) );
+	oauth_params_set( params, "oauth_timestamp", s );
+	g_free( s );
+	
+	s = oauth_nonce();
+	oauth_params_set( params, "oauth_nonce", s );
+	g_free( s );
+	
+	oauth_params_set( params, "oauth_version", "1.0" );
+}
+
+static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, struct oauth_info *oi )
+{
+	GSList *params = NULL;
+	char *s, *params_s, *post;
+	void *req;
+	url_t url_p;
+	
+	if( !url_set( &url_p, url ) )
+	{
+		oauth_params_free( params_ );
+		return NULL;
+	}
+	
+	if( params_ )
+		params = *params_;
+	
+	oauth_add_default_params( &params, oi->sp );
+	
+	params_s = oauth_params_string( params );
+	oauth_params_free( &params );
+	
+	s = oauth_sign( "POST", url, params_s, oi );
+	post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s );
+	g_free( params_s );
+	g_free( s );
+	
+	s = g_strdup_printf( "POST %s HTTP/1.0\r\n"
+	                     "Host: %s\r\n"
+	                     "Content-Type: application/x-www-form-urlencoded\r\n"
+	                     "Content-Length: %zd\r\n"
+	                     "\r\n"
+	                     "%s", url_p.file, url_p.host, strlen( post ), post );
+	g_free( post );
+	
+	req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
+	                      s, func, oi );
+	g_free( s );
+	
+	return req;
+}
+
+static void oauth_request_token_done( struct http_request *req );
+
+struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data )
+{
+	struct oauth_info *st = g_new0( struct oauth_info, 1 );
+	GSList *params = NULL;
+	
+	st->func = func;
+	st->data = data;
+	st->sp = sp;
+	
+	oauth_params_add( &params, "oauth_callback", "oob" );
+	
+	if( !oauth_post_request( sp->url_request_token, &params, oauth_request_token_done, st ) )
+	{
+		oauth_info_free( st );
+		return NULL;
+	}
+	
+	return st;
+}
+
+static void oauth_request_token_done( struct http_request *req )
+{
+	struct oauth_info *st = req->data;
+	
+	st->http = req;
+	
+	if( req->status_code == 200 )
+	{
+		GSList *params = NULL;
+		
+		st->auth_url = g_strdup_printf( "%s?%s", st->sp->url_authorize, req->reply_body );
+		oauth_params_parse( &params, req->reply_body );
+		st->request_token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
+		oauth_params_free( &params );
+	}
+	
+	st->stage = OAUTH_REQUEST_TOKEN;
+	st->func( st );
+}
+
+static void oauth_access_token_done( struct http_request *req );
+
+gboolean oauth_access_token( const char *pin, struct oauth_info *st )
+{
+	GSList *params = NULL;
+	
+	oauth_params_add( &params, "oauth_token", st->request_token );
+	oauth_params_add( &params, "oauth_verifier", pin );
+	
+	return oauth_post_request( st->sp->url_access_token, &params, oauth_access_token_done, st ) != NULL;
+}
+
+static void oauth_access_token_done( struct http_request *req )
+{
+	struct oauth_info *st = req->data;
+	
+	st->http = req;
+	
+	if( req->status_code == 200 )
+	{
+		GSList *params = NULL;
+		
+		oauth_params_parse( &params, req->reply_body );
+		st->token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
+		st->token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
+		oauth_params_free( &params );
+	}
+	
+	st->stage = OAUTH_ACCESS_TOKEN;
+	if( st->func( st ) )
+	{
+		/* Don't need these anymore, but keep the rest. */
+		g_free( st->auth_url );
+		st->auth_url = NULL;
+		g_free( st->request_token );
+		st->request_token = NULL;
+	}
+}
+
+char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args )
+{
+	GSList *params = NULL, *l;
+	char *sig = NULL, *params_s, *s;
+	GString *ret = NULL;
+	
+	oauth_params_add( &params, "oauth_token", oi->token );
+	oauth_add_default_params( &params, oi->sp );
+	
+	/* Start building the OAuth header. 'key="value", '... */
+	ret = g_string_new( "OAuth " );
+	for( l = params; l; l = l->next )
+	{
+		char *kv = l->data;
+		char *eq = strchr( kv, '=' );
+		char esc[strlen(kv)*3+1];
+		
+		if( eq == NULL )
+			break; /* WTF */
+		
+		strcpy( esc, eq + 1 );
+		http_encode( esc );
+		
+		g_string_append_len( ret, kv, eq - kv + 1 );
+		g_string_append_c( ret, '"' );
+		g_string_append( ret, esc );
+		g_string_append( ret, "\", " );
+	}
+	
+	/* Now, before generating the signature, add GET/POST arguments to params
+	   since they should be included in the base signature string (but not in
+	   the HTTP header). */
+	if( args )
+		oauth_params_parse( &params, args );
+	if( ( s = strchr( url, '?' ) ) )
+	{
+		s = g_strdup( s + 1 );
+		oauth_params_parse( &params, s );
+		g_free( s );
+	}
+	
+	/* Append the signature and we're done! */
+	params_s = oauth_params_string( params );
+	sig = oauth_sign( method, url, params_s, oi );
+	g_string_append_printf( ret, "oauth_signature=\"%s\"", sig );
+	g_free( params_s );
+	
+	oauth_params_free( &params );
+	g_free( sig );
+	
+	return ret ? g_string_free( ret, FALSE ) : NULL;
+}
+
+char *oauth_to_string( struct oauth_info *oi )
+{
+	GSList *params = NULL;
+	char *ret;
+	
+	oauth_params_add( &params, "oauth_token", oi->token );
+	oauth_params_add( &params, "oauth_token_secret", oi->token_secret );
+	ret = oauth_params_string( params );
+	oauth_params_free( &params );
+	
+	return ret;
+}
+
+struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp )
+{
+	struct oauth_info *oi = g_new0( struct oauth_info, 1 );
+	GSList *params = NULL;
+	
+	oauth_params_parse( &params, in );
+	oi->token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
+	oi->token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
+	oauth_params_free( &params );
+	oi->sp = sp;
+	
+	return oi;
+}
Index: lib/oauth.h
===================================================================
--- lib/oauth.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
+++ lib/oauth.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -0,0 +1,90 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple OAuth client (consumer) implementation.                           *
+*                                                                           *
+*  Copyright 2010 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.              *
+*                                                                           *
+\***************************************************************************/
+
+/* http://oauth.net/core/1.0a/ */
+
+struct oauth_info;
+
+/* Callback function called twice during the access token request process.
+   Return FALSE if something broke and the process must be aborted. */
+typedef gboolean (*oauth_cb)( struct oauth_info * );
+
+typedef enum
+{
+	OAUTH_INIT,
+	OAUTH_REQUEST_TOKEN,
+	OAUTH_ACCESS_TOKEN,
+} oauth_stage_t;
+
+struct oauth_info
+{
+	oauth_stage_t stage;
+	const struct oauth_service *sp;
+	
+	oauth_cb func;
+	void *data;
+	
+	struct http_request *http;
+	
+	char *auth_url;
+	char *request_token;
+	
+	char *token;
+	char *token_secret;
+};
+
+struct oauth_service
+{
+	char *url_request_token;
+	char *url_access_token;
+	char *url_authorize;
+	
+	char *consumer_key;
+	char *consumer_secret;
+};
+
+/* http://oauth.net/core/1.0a/#auth_step1 (section 6.1) 
+   Request an initial anonymous token which can be used to construct an
+   authorization URL for the user. This is passed to the callback function
+   in a struct oauth_info. */
+struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data );
+
+/* http://oauth.net/core/1.0a/#auth_step3 (section 6.3)
+   The user gets a PIN or so which we now exchange for the final access
+   token. This is passed to the callback function in the same
+   struct oauth_info. */
+gboolean oauth_access_token( const char *pin, struct oauth_info *st );
+
+/* http://oauth.net/core/1.0a/#anchor12 (section 7)
+   Generate an OAuth Authorization: HTTP header. access_token should be
+   saved/fetched using the functions above. args can be a string with
+   whatever's going to be in the POST body of the request. GET args will
+   automatically be grabbed from url. */
+char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args );
+
+/* Shouldn't normally be required unless the process is aborted by the user. */
+void oauth_info_free( struct oauth_info *info );
+
+/* Convert to and back from strings, for easier saving. */
+char *oauth_to_string( struct oauth_info *oi );
+struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp );
Index: lib/proxy.c
===================================================================
--- lib/proxy.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/proxy.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -58,25 +58,4 @@
 };
 
-
-
-static struct sockaddr_in *gaim_gethostbyname(const char *host, int port)
-{
-	static struct sockaddr_in sin;
-
-	if (!inet_aton(host, &sin.sin_addr)) {
-		struct hostent *hp;
-		if (!(hp = gethostbyname(host))) {
-			return NULL;
-		}
-		memset(&sin, 0, sizeof(struct sockaddr_in));
-		memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
-		sin.sin_family = hp->h_addrtype;
-	} else
-		sin.sin_family = AF_INET;
-	sin.sin_port = htons(port);
-
-	return &sin;
-}
-
 static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond)
 {
@@ -91,7 +70,7 @@
 		b_event_remove(phb->inpa);
 		if( phb->proxy_func )
-			phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ);
+			phb->proxy_func(phb->proxy_data, -1, B_EV_IO_READ);
 		else {
-			phb->func(phb->data, -1, GAIM_INPUT_READ);
+			phb->func(phb->data, -1, B_EV_IO_READ);
 			g_free(phb);
 		}
@@ -102,54 +81,78 @@
 	b_event_remove(phb->inpa);
 	if( phb->proxy_func )
-		phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ);
+		phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ);
 	else {
-		phb->func(phb->data, source, GAIM_INPUT_READ);
-		g_free(phb);
-	}
-	
-	return FALSE;
-}
-
-static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb)
-{
-	struct sockaddr_in *sin;
+		phb->func(phb->data, source, B_EV_IO_READ);
+		g_free(phb);
+	}
+	
+	return FALSE;
+}
+
+static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb)
+{
 	struct sockaddr_in me;
 	int fd = -1;
-
-	if (!(sin = gaim_gethostbyname(host, port))) {
-		g_free(phb);
-		return -1;
-	}
-
-	if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
-		g_free(phb);
-		return -1;
-	}
-
-	sock_make_nonblocking(fd);
-	
-	if( global.conf->iface_out )
+	int ret;
+	char port[6];
+	struct addrinfo hints;
+	struct addrinfo* result;
+
+	g_snprintf(port, sizeof(port), "%d", port_);
+
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
+
+	if (!(ret = getaddrinfo(host, port, &hints, &result)))
 	{
-		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);
-	
-	if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0 && !sockerr_again()) {
-		closesocket(fd);
-		g_free(phb);
-		
-		return -1;
-	} else {
-		phb->inpa = b_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb);
-		phb->fd = fd;
-		
-		return fd;
-	}
+		struct addrinfo* rp;
+
+		for (rp = result; rp; rp = rp->ai_next)
+		{
+			if ((fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) {
+				event_debug( "socket failed: %d\n", errno);
+				continue;
+			}
+
+			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);
+	
+			if (connect(fd, rp->ai_addr, rp->ai_addrlen) < 0 && !sockerr_again()) {
+				event_debug( "connect failed: %s\n", strerror(errno));
+				closesocket(fd);
+				fd = -1;
+				continue;
+			} else {
+				phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb);
+				phb->fd = fd;
+				
+				break;
+			}
+		}
+
+		freeaddrinfo(result);
+	}
+	else
+	{
+		event_debug("gai(): %s\n", gai_strerror(ret));
+	}
+	
+	if(fd < 0)
+		g_free(phb);
+
+	return fd;
 }
 
@@ -179,5 +182,5 @@
 	if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) ||
 	    (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
-		phb->func(phb->data, source, GAIM_INPUT_READ);
+		phb->func(phb->data, source, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -186,5 +189,5 @@
 
 	close(source);
-	phb->func(phb->data, -1, GAIM_INPUT_READ);
+	phb->func(phb->data, -1, B_EV_IO_READ);
 	g_free(phb->host);
 	g_free(phb);
@@ -204,5 +207,5 @@
 	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -215,5 +218,5 @@
 	if (send(source, cmd, strlen(cmd), 0) < 0) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -230,5 +233,5 @@
 		if (send(source, cmd, strlen(cmd), 0) < 0) {
 			close(source);
-			phb->func(phb->data, -1, GAIM_INPUT_READ);
+			phb->func(phb->data, -1, B_EV_IO_READ);
 			g_free(phb->host);
 			g_free(phb);
@@ -240,11 +243,11 @@
 	if (send(source, cmd, strlen(cmd), 0) < 0) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
-		g_free(phb->host);
-		g_free(phb);
-		return FALSE;
-	}
-
-	phb->inpa = b_input_add(source, GAIM_INPUT_READ, http_canread, phb);
+		phb->func(phb->data, -1, B_EV_IO_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return FALSE;
+	}
+
+	phb->inpa = b_input_add(source, B_EV_IO_READ, http_canread, phb);
 	
 	return FALSE;
@@ -273,5 +276,5 @@
 	memset(packet, 0, sizeof(packet));
 	if (read(source, packet, 9) >= 4 && packet[1] == 90) {
-		phb->func(phb->data, source, GAIM_INPUT_READ);
+		phb->func(phb->data, source, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -280,5 +283,5 @@
 
 	close(source);
-	phb->func(phb->data, -1, GAIM_INPUT_READ);
+	phb->func(phb->data, -1, B_EV_IO_READ);
 	g_free(phb->host);
 	g_free(phb);
@@ -299,5 +302,5 @@
 	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -309,5 +312,5 @@
 	if (!(hp = gethostbyname(phb->host))) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -326,11 +329,11 @@
 	if (write(source, packet, 9) != 9) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
-		g_free(phb->host);
-		g_free(phb);
-		return FALSE;
-	}
-
-	phb->inpa = b_input_add(source, GAIM_INPUT_READ, s4_canread, phb);
+		phb->func(phb->data, -1, B_EV_IO_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return FALSE;
+	}
+
+	phb->inpa = b_input_add(source, B_EV_IO_READ, s4_canread, phb);
 	
 	return FALSE;
@@ -359,5 +362,5 @@
 	if (read(source, buf, 10) < 10) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -366,11 +369,11 @@
 	if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
-		g_free(phb->host);
-		g_free(phb);
-		return FALSE;
-	}
-
-	phb->func(phb->data, source, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return FALSE;
+	}
+
+	phb->func(phb->data, source, B_EV_IO_READ);
 	g_free(phb->host);
 	g_free(phb);
@@ -396,5 +399,5 @@
 	if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -402,5 +405,5 @@
 	}
 
-	phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb);
+	phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread_again, phb);
 }
 
@@ -414,5 +417,5 @@
 	if (read(source, buf, 2) < 2) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -422,5 +425,5 @@
 	if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -442,5 +445,5 @@
 	if (read(source, buf, 2) < 2) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -450,5 +453,5 @@
 	if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -465,5 +468,5 @@
 		if (write(source, buf, 3 + i + j) < 3 + i + j) {
 			close(source);
-			phb->func(phb->data, -1, GAIM_INPUT_READ);
+			phb->func(phb->data, -1, B_EV_IO_READ);
 			g_free(phb->host);
 			g_free(phb);
@@ -471,5 +474,5 @@
 		}
 
-		phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_readauth, phb);
+		phb->inpa = b_input_add(source, B_EV_IO_READ, s5_readauth, phb);
 	} else {
 		s5_sendconnect(phb, source);
@@ -491,5 +494,5 @@
 	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		phb->func(phb->data, -1, B_EV_IO_READ);
 		g_free(phb->host);
 		g_free(phb);
@@ -513,11 +516,11 @@
 	if (write(source, buf, i) < i) {
 		close(source);
-		phb->func(phb->data, -1, GAIM_INPUT_READ);
-		g_free(phb->host);
-		g_free(phb);
-		return FALSE;
-	}
-
-	phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread, phb);
+		phb->func(phb->data, -1, B_EV_IO_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return FALSE;
+	}
+
+	phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread, phb);
 	
 	return FALSE;
Index: lib/sha1.h
===================================================================
--- lib/sha1.h	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/sha1.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -24,5 +24,9 @@
 #define _SHA1_H_
 
+#if(__sun)
+#include <inttypes.h>
+#else
 #include <stdint.h>
+#endif
 #include <gmodule.h>
 
Index: lib/ssl_bogus.c
===================================================================
--- lib/ssl_bogus.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/ssl_bogus.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -59,5 +59,5 @@
 b_input_condition ssl_getdirection( void *conn )
 {
-	return GAIM_INPUT_READ;
+	return B_EV_IO_READ;
 }
 
Index: lib/ssl_client.h
===================================================================
--- lib/ssl_client.h	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/ssl_client.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -71,5 +71,5 @@
 G_MODULE_EXPORT int ssl_getfd( void *conn );
 
-/* This function returns GAIM_INPUT_READ/WRITE. With SSL connections it's
+/* This function returns B_EV_IO_READ/WRITE. With SSL connections it's
    possible that something has to be read while actually were trying to
    write something (think about key exchange/refresh/etc). So when an
Index: lib/ssl_gnutls.c
===================================================================
--- lib/ssl_gnutls.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/ssl_gnutls.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -106,5 +106,5 @@
 	struct scd *conn = data;
 	
-	return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE );
+	return ssl_connected( conn, conn->fd, B_EV_IO_WRITE );
 }
 
@@ -244,4 +244,4 @@
 {
 	return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ?
-	        GAIM_INPUT_WRITE : GAIM_INPUT_READ );
-}
+	        B_EV_IO_WRITE : B_EV_IO_READ );
+}
Index: lib/ssl_nss.c
===================================================================
--- lib/ssl_nss.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/ssl_nss.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -193,4 +193,4 @@
 {
 	/* Just in case someone calls us, let's return the most likely case: */
-	return GAIM_INPUT_READ;
+	return B_EV_IO_READ;
 }
Index: lib/ssl_openssl.c
===================================================================
--- lib/ssl_openssl.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/ssl_openssl.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -102,5 +102,5 @@
 	struct scd *conn = data;
 	
-	return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE );
+	return ssl_connected( conn, conn->fd, B_EV_IO_WRITE );
 }
 
@@ -270,4 +270,4 @@
 b_input_condition ssl_getdirection( void *conn )
 {
-	return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? GAIM_INPUT_WRITE : GAIM_INPUT_READ );
-}
+	return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ );
+}
Index: lib/ssl_sspi.c
===================================================================
--- lib/ssl_sspi.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/ssl_sspi.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -275,4 +275,4 @@
 GaimInputCondition ssl_getdirection( void *conn )
 {
-	return GAIM_INPUT_WRITE; /* FIXME: or GAIM_INPUT_READ */
-}
+	return B_EV_IO_WRITE; /* FIXME: or B_EV_IO_READ */
+}
Index: lib/url.c
===================================================================
--- lib/url.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/url.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -27,5 +27,5 @@
 
 /* Convert an URL to a url_t structure */
-int url_set( url_t *url, char *set_url )
+int url_set( url_t *url, const char *set_url )
 {
 	char s[MAX_STRING+1];
Index: lib/url.h
===================================================================
--- lib/url.h	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/url.h	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -42,3 +42,3 @@
 } url_t;
 
-int url_set( url_t *url, char *set_url );
+int url_set( url_t *url, const char *set_url );
Index: lib/xmltree.c
===================================================================
--- lib/xmltree.c	(revision ee6cc946dc4ee82cb641df94a6ba101e99253af2)
+++ lib/xmltree.c	(revision 5fecede7db56a41cf9d6ecb2b821f5a833ce0f59)
@@ -215,5 +215,8 @@
 	
 	if( node == NULL )
-		return xt_cleanup( xt, xt->root, depth );
+	{
+		xt_cleanup( xt, xt->root, depth );
+		return;
+	}
 	
 	if( node->flags & XT_SEEN && node == xt->root )
@@ -449,5 +452,9 @@
 	while( node )
 	{
-		if( g_strcasecmp( node->name, name ) == 0 )
+		char *colon;
+		
+		if( g_strcasecmp( node->name, name ) == 0 ||
+		    ( ( colon = strchr( node->name, ':' ) ) &&
+		      g_strcasecmp( colon + 1, name ) == 0 ) )
 			break;
 		
@@ -461,4 +468,5 @@
 {
 	int i;
+	char *colon;
 	
 	if( !node )
@@ -468,4 +476,19 @@
 		if( g_strcasecmp( node->attr[i].key, key ) == 0 )
 			break;
+	
+	/* This is an awful hack that only takes care of namespace prefixes
+	   inside a tag. Since IMHO excessive namespace usage in XMPP is
+	   massive overkill anyway (this code exists for almost four years
+	   now and never really missed it): Meh. */
+	if( !node->attr[i].key && strcmp( key, "xmlns" ) == 0 &&
+	    ( colon = strchr( node->name, ':' ) ) )
+	{
+		*colon = '\0';
+		for( i = 0; node->attr[i].key; i ++ )
+			if( strncmp( node->attr[i].key, "xmlns:", 6 ) == 0 &&
+			    strcmp( node->attr[i].key + 6, node->name ) == 0 )
+				break;
+		*colon = ':';
+	}
 	
 	return node->attr[i].value;
