Index: bitlbee.c
===================================================================
--- bitlbee.c	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ bitlbee.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -36,4 +36,41 @@
 static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition condition );
 
+static gboolean try_listen( struct addrinfo *res )
+{
+	int i;
+	
+	global.listen_socket = socket( res->ai_family, res->ai_socktype, res->ai_protocol );
+	if( global.listen_socket < 0 )
+	{
+		log_error( "socket" );
+		return FALSE;
+	}
+
+#ifdef IPV6_V6ONLY		
+	if( res->ai_family == AF_INET6 )
+	{
+		i = 0;
+		setsockopt( global.listen_socket, IPPROTO_IPV6, IPV6_V6ONLY,
+		            (char *) &i, sizeof( i ) );
+	}
+#endif
+
+	/* TIME_WAIT (?) sucks.. */
+	i = 1;
+	setsockopt( global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
+
+	i = bind( global.listen_socket, res->ai_addr, res->ai_addrlen );
+	if( i == -1 )
+	{
+		closesocket( global.listen_socket );
+		global.listen_socket = -1;
+		
+		log_error( "bind" );
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
 int bitlbee_daemon_init()
 {
@@ -63,33 +100,16 @@
 
 	global.listen_socket = -1;
-
+	
+	/* Try IPv6 first (which will become an IPv6+IPv4 socket). */
 	for( res = addrinfo_bind; res; res = res->ai_next )
-	{
-		global.listen_socket = socket( res->ai_family, res->ai_socktype, res->ai_protocol );
-		if( global.listen_socket < 0 )
-			continue;
-
-#ifdef IPV6_V6ONLY		
-		if( res->ai_family == AF_INET6 )
-		{
-			i = 0;
-			setsockopt( global.listen_socket, IPPROTO_IPV6, IPV6_V6ONLY,
-			            (char *) &i, sizeof( i ) );
-		}
-#endif
-
-		/* TIME_WAIT (?) sucks.. */
-		i = 1;
-		setsockopt( global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
-
-		i = bind( global.listen_socket, res->ai_addr, res->ai_addrlen );
-		if( i == -1 )
-		{
-			log_error( "bind" );
-			return( -1 );
-		}
-		break;
-	}
-
+		if( res->ai_family == AF_INET6 && try_listen( res ) )
+			break;
+	
+	/* The rest (so IPv4, I guess). */
+	if( res == NULL )
+		for( res = addrinfo_bind; res; res = res->ai_next )
+			if( res->ai_family != AF_INET6 && try_listen( res ) )
+				break;
+	
 	freeaddrinfo( addrinfo_bind );
 
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ bitlbee.h	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -35,5 +35,5 @@
 
 #define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "1.2.6"
+#define BITLBEE_VERSION "1.2.6a"
 #define VERSION BITLBEE_VERSION
 #define BITLBEE_VER(a,b,c) (((a) << 16) + ((b) << 8) + (c))
Index: debian/bitlbee.init
===================================================================
--- debian/bitlbee.init	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ debian/bitlbee.init	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -38,6 +38,6 @@
 d_start() {
 	# Make sure BitlBee can actually write its PID...
-	touch /var/run/bitlbee.pid
-	chown bitlbee: /var/run/bitlbee.pid
+	touch $PIDFILE
+	chown bitlbee: $PIDFILE
 	
 	start-stop-daemon --start --quiet --pidfile $PIDFILE \
Index: debian/changelog
===================================================================
--- debian/changelog	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ debian/changelog	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -1,2 +1,11 @@
+bitlbee (1.2.6a-1) unstable; urgency=low
+
+  * New upstream version.
+  * Native support for Twitter.
+  * Fixed /WHOIS response format. (Closes: #576120)
+  * Problems with bitlbee-skype are solved by now. (Closes: #575572)
+
+ -- Wilmer van der Gaast <wilmer@peer.gaast.net>  Tue, 20 Apr 2010 00:34:51 +0200
+
 bitlbee (1.2.5-1) unstable; urgency=low
 
Index: debian/rules
===================================================================
--- debian/rules	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ debian/rules	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -99,5 +99,5 @@
 		find usr -type f -exec md5sum {} \; > DEBIAN/md5sums
 
-	dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev
+	dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev -v1:$(BITLBEE_VERSION)-0
 
 	dpkg --build debian/bitlbee-dev ..
Index: doc/CHANGES
===================================================================
--- doc/CHANGES	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ doc/CHANGES	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -4,6 +4,14 @@
 http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on
 
+Version 1.2.6a:
+- Fixed a typo that renders the Twitter groupchat mode unusable. A last-
+  minute change that came a few minutes late.
+
+Finished 19 Apr 2010
+
 Version 1.2.6:
-- Native (very basic) support for Twitter.
+- Native (very basic) support for Twitter, implemented by Geert Mulders.
+  Currently supported are posting tweets, reading the ones of people you
+  follow, and sending (not yet receiving!) direct messages.
 - Fixed format of status messages in /WHOIS to improve IRC client
   compatibility.
@@ -16,7 +24,10 @@
   behaviour now is to write to wherever the last message came from, or to
   the bare JID (usually becomes a broadcast) if there wasn't any recent msg.
+- Added a switchboard_keepalives setting which should solve some issues with
+  talking to offline MSN contacts. (Although full support for offline
+  messages is not ready yet!)
 - The usual misc. bug fixes.
 
-Finished ...
+Finished 19 Apr 2010
 
 Version 1.2.5:
Index: doc/user-guide/commands.xml
===================================================================
--- doc/user-guide/commands.xml	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ doc/user-guide/commands.xml	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -21,5 +21,5 @@
 			<description>
 				<para>
-					Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ) and Yahoo. For more information about adding an account, see <emphasis>help account add &lt;protocol&gt;</emphasis>.
+					Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ), Yahoo and Twitter. For more information about adding an account, see <emphasis>help account add &lt;protocol&gt;</emphasis>.
 				</para>
 			</description>
@@ -73,9 +73,13 @@
 					
 					<para>
-						By default all your Twitter contacts will show up in your contact list and their tweets will show up as private messages or in &amp;bitlbee depending on your settings. If you want them in a separate channel, use the use_groupchat setting (see <emphasis>help set use_groupchat</emphasis>).
+						By default all your Twitter contacts will come from a contact called twitter_(yourusername). You can change this behaviour using the <emphasis>mode</emphasis> setting (see <emphasis>help set mode</emphasis>).
 					</para>
 					
 					<para>
-						To send tweets yourself, send them to any of your Twitter contacts via /query (doesn't matter who), or just write in the groupchat channel if you enabled that option.
+						To send tweets yourself, send them to the twitter_(yourusername) contact, or just write in the groupchat channel if you enabled that option.
+					</para>
+
+					<para>
+						Since Twitter now requires OAuth authentication, you should not enter your Twitter password into BitlBee. Just type a bogus password. The first time you log in, BitlBee will start OAuth authentication. (See <emphasis>help set oauth</emphasis>.)
 					</para>
 				</description>
@@ -659,4 +663,24 @@
 	</bitlbee-setting>
 
+	<bitlbee-setting name="mode" type="string" scope="account">
+		<possible-values>one, many, chat</possible-values>
+		<default>one</default>
+
+		<description>
+			<para>
+				By default, everything from the Twitter module will come from one nick, twitter_(yourusername). If you prefer to have individual nicks for everyone, you can set this setting to "many" instead.
+			</para>
+			
+			<para>
+				If you prefer to have all your Twitter things in a separate channel, you can set this setting to "chat".
+			</para>
+			
+			<para>
+				In the last two modes, you can send direct messages by /msg'ing your contacts directly. Note, however, that incoming DMs are not fetched yet.
+			</para>
+		</description>
+
+	</bitlbee-setting>
+
 	<bitlbee-setting name="nick" type="string" scope="chat">
 
@@ -683,4 +707,23 @@
 	</bitlbee-setting>
 
+	<bitlbee-setting name="oauth" type="boolean" scope="account">
+		<default>true</default>
+
+		<description>
+			<para>
+				This enables OAuth authentication for Twitter accounts. From June 2010 this will be mandatory.
+			</para>
+
+			<para>
+				With OAuth enabled, you shouldn't tell BitlBee your Twitter password. Just add your account with a bogus password and type <emphasis>account on</emphasis>. BitlBee will then give you a URL to authenticate with Twitter. If this succeeds, Twitter will return a PIN code which you can give back to BitlBee to finish the process.
+			</para>
+
+			<para>
+				The resulting access token will be saved permanently, so you have to do this only once.
+			</para>
+		</description>
+
+	</bitlbee-setting>
+
 	<bitlbee-setting name="ops" type="string" scope="global">
 		<default>both</default>
@@ -818,4 +861,14 @@
 	</bitlbee-setting>
 
+	<bitlbee-setting name="show_offline" type="boolean" scope="global">
+		<default>false</default>
+
+		<description>
+			<para>
+				If enabled causes BitlBee to also show offline users in Channel. Online-users will get op, away-users voice and offline users none of both. This option takes effect as soon as you reconnect.
+			</para>
+		</description>
+	</bitlbee-setting>
+
 	<bitlbee-setting name="simulate_netsplit" type="boolean" scope="global">
 		<default>true</default>
@@ -863,4 +916,22 @@
 			<para>
 				If BitlBee fails to detect this sometimes (most likely in AIM messages over an ICQ connection), you can set this setting to <emphasis>always</emphasis>, but this might sometimes accidentally strip non-HTML things too.
+			</para>
+		</description>
+	</bitlbee-setting>
+
+	<bitlbee-setting name="switchboard_keepalives" type="boolean" scope="account">
+		<default>false</default>
+
+		<description>
+			<para>
+				Turn on this flag if you have difficulties talking to offline/invisible contacts.
+			</para>
+			
+			<para>
+				With this setting enabled, BitlBee will send keepalives to MSN switchboards with offline/invisible contacts every twenty seconds. This should keep the server and client on the other side from shutting it down.
+			</para>
+			
+			<para>
+				This is useful because BitlBee doesn't support MSN offline messages yet and the MSN servers won't let the user reopen switchboards to offline users. Once offline messaging is supported, this flag might be removed.
 			</para>
 		</description>
@@ -916,14 +987,4 @@
 			<para>
 				Sends you a /notice when a user starts typing a message (if supported by the IM protocol and the user's client). To use this, you most likely want to use a script in your IRC client to show this information in a more sensible way.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="use_groupchat" type="boolean" scope="account">
-		<default>false</default>
-
-		<description>
-			<para>
-				By default the Twitter module shows all Twitter contacts and their Tweet in &amp;bitlbee and/or private messages. With this setting enabled the module will show all contacts and their Tweets in a separate channel.
 			</para>
 		</description>
Index: lib/Makefile
===================================================================
--- lib/Makefile	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ lib/Makefile	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -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 xmltree.o ftutil.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
Index: lib/misc.c
===================================================================
--- lib/misc.c	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ lib/misc.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -306,6 +306,5 @@
 	for( i = j = 0; t[i]; i ++, j ++ )
 	{
-		/* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */
-		if( !isalnum( t[i] ) )
+		if( !isalnum( t[i] ) && !strchr( "._-~", t[i] ) )
 		{
 			sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
Index: lib/oauth.c
===================================================================
--- lib/oauth.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
+++ lib/oauth.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -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 ? : "" );
+	}
+	
+	/* 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 f92456381b2a1487036848fc4e0105b8a439facd)
+++ lib/oauth.h	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -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/url.c
===================================================================
--- lib/url.c	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ lib/url.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -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 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ lib/url.h	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -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: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ protocols/msn/msn.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -38,4 +38,5 @@
 	set_add( &acc->set, "local_display_name", "false", set_eval_bool, acc );
 	set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
+	set_add( &acc->set, "switchboard_keepalives", "false", set_eval_bool, acc );
 }
 
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ protocols/msn/msn.h	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -31,4 +31,5 @@
 #define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r"
 #define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r"
+#define SB_KEEPALIVE_MESSAGE "\r\r\rDONT HANG UP ON ME!\r\r\r"
 
 #ifdef DEBUG_MSN
@@ -53,4 +54,8 @@
                            "TypingUser: %s\r\n" \
                            "\r\n\r\n"
+
+#define SB_KEEPALIVE_HEADERS "MIME-Version: 1.0\r\n" \
+                             "Content-Type: text/x-ping\r\n" \
+                             "\r\n\r\n"
 
 #define PROFILE_URL "http://members.msn.com/"
@@ -84,4 +89,5 @@
 	gint inp;
 	struct msn_handler_data *handler;
+	gint keepalive;
 	
 	int trId;
@@ -181,4 +187,6 @@
 gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond );
 int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m );
+void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial );
+void msn_sb_stop_keepalives( struct msn_switchboard *sb );
 
 /* invitation.c */
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ protocols/msn/ns.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -422,6 +422,10 @@
 	else if( strcmp( cmd[0], "FLN" ) == 0 )
 	{
-		if( cmd[1] )
-			imcb_buddy_status( ic, cmd[1], 0, NULL, NULL );
+		if( cmd[1] == NULL )
+			return 1;
+		
+		imcb_buddy_status( ic, cmd[1], 0, NULL, NULL );
+		
+		msn_sb_start_keepalives( msn_sb_by_handle( ic, cmd[1] ), TRUE );
 	}
 	else if( strcmp( cmd[0], "NLN" ) == 0 )
@@ -449,4 +453,6 @@
 		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),
 		                   st->name, NULL );
+		
+		msn_sb_stop_keepalives( msn_sb_by_handle( ic, cmd[2] ) );
 	}
 	else if( strcmp( cmd[0], "RNG" ) == 0 )
Index: protocols/msn/sb.c
===================================================================
--- protocols/msn/sb.c	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ protocols/msn/sb.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -175,4 +175,9 @@
 			i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user );
 		}
+		else if( strcmp( text, SB_KEEPALIVE_MESSAGE ) == 0 )
+		{
+			buf = g_strdup( SB_KEEPALIVE_HEADERS );
+			i = strlen( buf );
+		}
 		else if( strncmp( text, MSN_INVITE_HEADERS, sizeof( MSN_INVITE_HEADERS ) - 1 ) == 0 ) 
 		{
@@ -256,4 +261,5 @@
 	
 	msn_msgq_purge( ic, &sb->msgq );
+	msn_sb_stop_keepalives( sb );
 	
 	if( sb->key ) g_free( sb->key );
@@ -477,4 +483,6 @@
 		
 		sb->ready = 1;
+		
+		msn_sb_start_keepalives( sb, FALSE );
 	}
 	else if( strcmp( cmd[0], "CAL" ) == 0 )
@@ -526,4 +534,6 @@
 			}
 			
+			msn_sb_start_keepalives( sb, FALSE );
+			
 			return( st );
 		}
@@ -587,4 +597,6 @@
 		if( sb->who )
 		{
+			msn_sb_stop_keepalives( sb );
+			
 			/* This is a single-person chat, and the other person is leaving. */
 			g_free( sb->who );
@@ -752,2 +764,33 @@
 	return( 1 );
 }
+
+static gboolean msn_sb_keepalive( gpointer data, gint source, b_input_condition cond )
+{
+	struct msn_switchboard *sb = data;
+	return sb->ready && msn_sb_sendmessage( sb, SB_KEEPALIVE_MESSAGE );
+}
+
+void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial )
+{
+	bee_user_t *bu;
+	
+	if( sb && sb->who && sb->keepalive == 0 &&
+	    ( bu = bee_user_by_handle( sb->ic->bee, sb->ic, sb->who ) ) &&
+	    !( bu->flags & BEE_USER_ONLINE ) &&
+	    set_getbool( &sb->ic->acc->set, "switchboard_keepalives" ) )
+	{
+		if( initial )
+			msn_sb_keepalive( sb, 0, 0 );
+		
+		sb->keepalive = b_timeout_add( 20000, msn_sb_keepalive, sb );
+	}
+}
+
+void msn_sb_stop_keepalives( struct msn_switchboard *sb )
+{
+	if( sb && sb->keepalive > 0 )
+	{
+		b_event_remove( sb->keepalive );
+		sb->keepalive = 0;
+	}
+}
Index: protocols/twitter/twitter.c
===================================================================
--- protocols/twitter/twitter.c	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ protocols/twitter/twitter.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -23,9 +23,9 @@
 
 #include "nogaim.h"
+#include "oauth.h"
 #include "twitter.h"
 #include "twitter_http.h"
 #include "twitter_lib.h"
 
-
 /**
  * Main loop function
@@ -41,5 +41,5 @@
 	// If the user uses multiple private message windows we need to get the 
 	// users buddies.
-	if (!set_getbool( &ic->acc->set, "use_groupchat" ))
+	if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "many") == 0)
 		twitter_get_statuses_friends(ic, -1);
 
@@ -51,29 +51,8 @@
 }
 
-
-static void twitter_init( account_t *acc )
-{
-	set_t *s;
-	s = set_add( &acc->set, "use_groupchat", "false", set_eval_bool, acc );
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-}
-
-/**
- * Login method. Since the twitter API works with seperate HTTP request we 
- * only save the user and pass to the twitter_data object.
- */
-static void twitter_login( account_t *acc )
-{
-	struct im_connection *ic = imcb_new( acc );
-	struct twitter_data *td = g_new0( struct twitter_data, 1 );
-
-	twitter_connections = g_slist_append( twitter_connections, ic );
-
-	td->user = acc->user;
-	td->pass = acc->pass;
-	td->home_timeline_id = 0;
-
-	ic->proto_data = td;
-
+static void twitter_main_loop_start( struct im_connection *ic )
+{
+	struct twitter_data *td = ic->proto_data;
+	
 	imcb_log( ic, "Connecting to Twitter" );
 
@@ -86,4 +65,124 @@
 }
 
+
+static const struct oauth_service twitter_oauth =
+{
+	"http://api.twitter.com/oauth/request_token",
+	"http://api.twitter.com/oauth/access_token",
+	"http://api.twitter.com/oauth/authorize",
+	.consumer_key = "xsDNKJuNZYkZyMcu914uEA",
+	.consumer_secret = "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo",
+};
+
+static gboolean twitter_oauth_callback( struct oauth_info *info );
+
+static void twitter_oauth_start( struct im_connection *ic )
+{
+	struct twitter_data *td = ic->proto_data;
+	
+	imcb_log( ic, "Requesting OAuth request token" );
+
+	td->oauth_info = oauth_request_token( &twitter_oauth, twitter_oauth_callback, ic );
+}
+
+static gboolean twitter_oauth_callback( struct oauth_info *info )
+{
+	struct im_connection *ic = info->data;
+	struct twitter_data *td;
+	
+	if( !g_slist_find( twitter_connections, ic ) )
+		return FALSE;
+	
+	td = ic->proto_data;
+	if( info->stage == OAUTH_REQUEST_TOKEN )
+	{
+		char name[strlen(ic->acc->user)+9], *msg;
+		
+		if( info->request_token == NULL )
+		{
+			imcb_error( ic, "OAuth error: %s", info->http->status_string );
+			imc_logout( ic, TRUE );
+			return FALSE;
+		}
+		
+		sprintf( name, "twitter_%s", ic->acc->user );
+		msg = g_strdup_printf( "To finish OAuth authentication, please visit "
+		                       "%s and respond with the resulting PIN code.",
+		                       info->auth_url );
+		imcb_buddy_msg( ic, name, msg, 0, 0 );
+		g_free( msg );
+	}
+	else if( info->stage == OAUTH_ACCESS_TOKEN )
+	{
+		if( info->token == NULL || info->token_secret == NULL )
+		{
+			imcb_error( ic, "OAuth error: %s", info->http->status_string );
+			imc_logout( ic, TRUE );
+			return FALSE;
+		}
+		
+		/* IM mods didn't do this so far and it's ugly but I should
+		   be able to get away with it... */
+		g_free( ic->acc->pass );
+		ic->acc->pass = oauth_to_string( info );
+		
+		twitter_main_loop_start( ic );
+	}
+	
+	return TRUE;
+}
+
+
+static char *set_eval_mode( set_t *set, char *value )
+{
+	if( g_strcasecmp( value, "one" ) == 0 ||
+	    g_strcasecmp( value, "many" ) == 0 ||
+	    g_strcasecmp( value, "chat" ) == 0 )
+		return value;
+	else
+		return NULL;
+}
+
+static void twitter_init( account_t *acc )
+{
+	set_t *s;
+	
+	s = set_add( &acc->set, "mode", "one", set_eval_mode, acc );
+	s->flags |= ACC_SET_OFFLINE_ONLY;
+	
+	s = set_add( &acc->set, "oauth", "true", set_eval_bool, acc );
+}
+
+/**
+ * Login method. Since the twitter API works with seperate HTTP request we 
+ * only save the user and pass to the twitter_data object.
+ */
+static void twitter_login( account_t *acc )
+{
+	struct im_connection *ic = imcb_new( acc );
+	struct twitter_data *td = g_new0( struct twitter_data, 1 );
+	char name[strlen(acc->user)+9];
+
+	twitter_connections = g_slist_append( twitter_connections, ic );
+	ic->proto_data = td;
+	ic->flags |= OPT_DOES_HTML;
+	
+	td->user = acc->user;
+	if( !set_getbool( &acc->set, "oauth" ) )
+		td->pass = g_strdup( acc->pass );
+	else if( strstr( acc->pass, "oauth_token=" ) )
+		td->oauth_info = oauth_from_string( acc->pass, &twitter_oauth );
+	td->home_timeline_id = 0;
+	
+	sprintf( name, "twitter_%s", acc->user );
+	imcb_add_buddy( ic, name, NULL );
+	imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
+	
+	if( td->pass || td->oauth_info )
+		twitter_main_loop_start( ic );
+	else
+		twitter_oauth_start( ic );
+}
+
 /**
  * Logout method. Just free the twitter_data.
@@ -104,4 +203,6 @@
 	if( td )
 	{
+		oauth_info_free( td->oauth_info );
+		g_free( td->pass );
 		g_free( td );
 	}
@@ -115,9 +216,26 @@
 static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
 {
-	// Let's just update the status.
-//	if ( g_strcasecmp(who, ic->acc->user) == 0 )
-		twitter_post_status(ic, message);
-//	else
-//		twitter_direct_messages_new(ic, who, message);
+	struct twitter_data *td = ic->proto_data;
+	
+	if (g_strncasecmp(who, "twitter_", 8) == 0 &&
+	    g_strcasecmp(who + 8, ic->acc->user) == 0)
+	{
+		if( set_getbool( &ic->acc->set, "oauth" ) &&
+		    td->oauth_info && td->oauth_info->token == NULL )
+		{
+			if( !oauth_access_token( message, td->oauth_info ) )
+			{
+				imcb_error( ic, "OAuth error: %s", "Failed to send access token request" );
+				imc_logout( ic, TRUE );
+				return FALSE;
+			}
+		}
+		else
+			twitter_post_status(ic, message);
+	}
+	else
+	{
+		twitter_direct_messages_new(ic, who, message);
+	}
 	return( 0 );
 }
Index: protocols/twitter/twitter.h
===================================================================
--- protocols/twitter/twitter.h	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ protocols/twitter/twitter.h	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -37,4 +37,5 @@
 	char* user;
 	char* pass;
+	struct oauth_info *oauth_info;
 	guint64 home_timeline_id;
 	gint main_loop_id;
Index: protocols/twitter/twitter_http.c
===================================================================
--- protocols/twitter/twitter_http.c	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ protocols/twitter/twitter_http.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -29,5 +29,4 @@
 ****************************************************************************/ 
 
-#include "twitter_http.h"
 #include "twitter.h"
 #include "bitlbee.h"
@@ -35,6 +34,9 @@
 #include "misc.h"
 #include "base64.h"
+#include "oauth.h"
 #include <ctype.h>
 #include <errno.h>
+
+#include "twitter_http.h"
 
 
@@ -45,5 +47,5 @@
  * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c
  */
-void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, char** arguments, int arguments_len)
+void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, struct oauth_info* oi, char** arguments, int arguments_len)
 {
 	url_t *url = g_new0( url_t, 1 );
@@ -110,5 +112,17 @@
 
 	// If a pass and user are given we append them to the request.
-	if (userpass_base64)
+	if (oi)
+	{
+		char *full_header;
+		
+		full_header = oauth_http_header(oi, is_post ? "POST" : "GET",
+		                                url_string, url_arguments);
+		
+		tmp = g_strdup_printf("%sAuthorization: %s\r\n", request, full_header);
+		g_free(request);
+		g_free(full_header);
+		request = tmp;
+	}
+	else if (userpass_base64)
 	{
 		tmp = g_strdup_printf("%sAuthorization: Basic %s\r\n", request, userpass_base64);
Index: protocols/twitter/twitter_http.h
===================================================================
--- protocols/twitter/twitter_http.h	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ protocols/twitter/twitter_http.h	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -28,6 +28,8 @@
 #include "http_client.h"
 
+struct oauth_info;
+
 void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, 
-					char* user, char* pass, char** arguments, int arguments_len);
+                   char* user, char* pass, struct oauth_info *oi, char** arguments, int arguments_len);
 
 #endif //_TWITTER_HTTP_H
Index: protocols/twitter/twitter_lib.c
===================================================================
--- protocols/twitter/twitter_lib.c	(revision 6a9d068e73b6d08056302fdc85dd706a2dd647a5)
+++ protocols/twitter/twitter_lib.c	(revision f92456381b2a1487036848fc4e0105b8a439facd)
@@ -105,10 +105,12 @@
 	if (!bee_user_by_handle( ic->bee, ic, name ))
 	{
+		char *mode = set_getstr(&ic->acc->set, "mode");
+		
 		// The buddy is not in the list, add the buddy and set the status to logged in.
 		imcb_add_buddy( ic, name, NULL );
 		imcb_rename_buddy( ic, name, fullname );
-		if (set_getbool( &ic->acc->set, "use_groupchat" ))
+		if (g_strcasecmp(mode, "chat") == 0)
 			imcb_chat_add_buddy( td->home_timeline_gc, name );
-		else
+		else if (g_strcasecmp(mode, "many") == 0)
 			imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
 	}
@@ -128,5 +130,5 @@
 	args[0] = "cursor";
 	args[1] = g_strdup_printf ("%d", next_cursor);
-	twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, args, 2);
+	twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, td->oauth_info, args, 2);
 
 	g_free(args[1]);
@@ -394,5 +396,5 @@
 	}
 
-	twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, args, td->home_timeline_id ? 4 : 2);
+	twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, td->oauth_info, args, td->home_timeline_id ? 4 : 2);
 
 	g_free(args[1]);
@@ -452,12 +454,37 @@
 	GSList *l = NULL;
 	struct twitter_xml_status *status;
-
+	char from[MAX_STRING];
+	gboolean mode_one;
+	
+	mode_one = g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) == 0;
+
+	if( mode_one )
+	{
+		g_snprintf( from, sizeof( from ) - 1, "twitter_%s", ic->acc->user );
+		from[MAX_STRING-1] = '\0';
+	}
+	
 	for ( l = list; l ; l = g_slist_next(l) )
 	{
+		char *text = NULL;
+		
 		status = l->data;
-		imcb_buddy_msg( ic, status->user->screen_name, status->text, 0, status->created_at );
+		
+		if( mode_one )
+			text = g_strdup_printf( "\002<\002%s\002>\002 %s",
+			                        status->user->screen_name, status->text );
+		else
+			twitter_add_buddy(ic, status->user->screen_name, status->user->name);
+		
+		imcb_buddy_msg( ic,
+		                mode_one ? from : status->user->screen_name,
+		                mode_one ? text : status->text,
+		                0, status->created_at );
+		
 		// Update the home_timeline_id to hold the highest id, so that by the next request
 		// we won't pick up the updates allready in the list.
 		td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;
+		
+		g_free( text );
 	}
 }
@@ -483,5 +510,5 @@
 	{
 		td->http_fails = 0;
-		if (!ic->flags & OPT_LOGGED_IN)
+		if (!(ic->flags & OPT_LOGGED_IN))
 			imcb_connected(ic);
 	}
@@ -512,5 +539,5 @@
 
 	// See if the user wants to see the messages in a groupchat window or as private messages.
-	if (set_getbool( &ic->acc->set, "use_groupchat" ))
+	if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
 		twitter_groupchat(ic, txl->list);
 	else
@@ -593,5 +620,5 @@
 	args[1] = g_strdup_printf ("%d", next_cursor);
 
-	twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, args, 2);
+	twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, td->oauth_info, args, 2);
 
 	g_free(args[1]);
@@ -612,5 +639,5 @@
 	if (req->status_code != 200) {
 		// It didn't go well, output the error and return.
-		imcb_error(ic, "Could not post tweet... HTTP STATUS: %d", req->status_code);
+		imcb_error(ic, "Could not post message... HTTP STATUS: %d", req->status_code);
 		return;
 	}
@@ -627,5 +654,5 @@
 	args[0] = "status";
 	args[1] = msg;
-	twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 2);
+	twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth_info, args, 2);
 //	g_free(args[1]);
 }
@@ -645,5 +672,5 @@
 	args[3] = msg;
 	// Use the same callback as for twitter_post_status, since it does basically the same.
-	twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 4);
+	twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth_info, args, 4);
 //	g_free(args[1]);
 //	g_free(args[3]);
