Index: Makefile
===================================================================
--- Makefile	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ Makefile	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -49,5 +49,5 @@
 
 clean: $(subdirs)
-	rm -f *.o $(OUTFILE) core utils/bitlbeed encode decode
+	rm -f *.o $(OUTFILE) core utils/bitlbeed
 	$(MAKE) -C tests clean
 
@@ -126,10 +126,4 @@
 endif
 
-encode: crypting.c
-	$(CC) crypting.c lib/md5.c $(CFLAGS) -o encode -DCRYPTING_MAIN $(CFLAGS) $(EFLAGS) $(LFLAGS)
-
-decode: encode
-	cp encode decode
-
 ctags: 
 	ctags `find . -name "*.c"` `find . -name "*.h"`
Index: account.c
===================================================================
--- account.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ account.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -27,4 +27,5 @@
 #include "bitlbee.h"
 #include "account.h"
+#include "chat.h"
 
 account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass )
@@ -54,4 +55,6 @@
 	s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a );
 	
+	s = set_add( &a->set, "nick_source", "handle", NULL, a );
+	
 	s = set_add( &a->set, "password", NULL, set_eval_account, a );
 	s->flags |= ACC_SET_NOSAVE | SET_NULL_OK;
@@ -68,5 +71,14 @@
 		prpl->init( a );
 	
-	return( a );
+	s = set_add( &a->set, "away", NULL, set_eval_account, a );
+	s->flags |= SET_NULL_OK;
+	
+	if( a->flags & ACC_FLAG_STATUS_MESSAGE )
+	{
+		s = set_add( &a->set, "status", NULL, set_eval_account, a );
+		s->flags |= SET_NULL_OK;
+	}
+	
+	return a;
 }
 
@@ -120,4 +132,19 @@
 		
 		acc->auto_connect = bool2int( value );
+		return value;
+	}
+	else if( strcmp( set->key, "away" ) == 0 ||
+	         strcmp( set->key, "status" ) == 0 )
+	{
+		if( acc->ic && acc->ic->flags & OPT_LOGGED_IN )
+		{
+			/* If we're currently on-line, set the var now already
+			   (bit of a hack) and send an update. */
+			g_free( set->value );
+			set->value = g_strdup( value );
+			
+			imc_away_send_update( acc->ic );
+		}
+		
 		return value;
 	}
@@ -266,5 +293,5 @@
 	p->max = 86400;
 	
-	/* Format: /[0-9]+([*+][0-9]+(<[0-9+]))/ */
+	/* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
 	while( *value && isdigit( *value ) )
 		p->start = p->start * 10 + *value++ - '0';
Index: account.h
===================================================================
--- account.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ account.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -37,4 +37,5 @@
 	int auto_reconnect_delay;
 	int reconnect;
+	int flags;
 	
 	set_t *set;
@@ -56,7 +57,16 @@
 int account_reconnect_delay( account_t *a );
 
-#define ACC_SET_NOSAVE		0x01
-#define ACC_SET_OFFLINE_ONLY	0x02
-#define ACC_SET_ONLINE_ONLY	0x04
+typedef enum
+{
+	ACC_SET_NOSAVE = 0x01,          /* Don't save this setting (i.e. stored elsewhere). */
+	ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
+	ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */
+} account_set_flag_t;
+
+typedef enum
+{
+	ACC_FLAG_AWAY_MESSAGE = 0x01,   /* Supports away messages instead of just states. */
+	ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */
+} account_flag_t;
 
 #endif
Index: bitlbee.c
===================================================================
--- bitlbee.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ bitlbee.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -109,9 +109,11 @@
 		chdir( "/" );
 		
-		/* Sometimes std* are already closed (for example when we're in a RESTARTed
-		   BitlBee process. So let's only close TTY-fds. */
-		if( isatty( 0 ) ) close( 0 );
-		if( isatty( 1 ) ) close( 1 );
-		if( isatty( 2 ) ) close( 2 );
+		if( getenv( "_BITLBEE_RESTART_STATE" ) == NULL )
+			for( i = 0; i < 3; i ++ )
+				if( close( i ) == 0 )
+				{
+					/* Keep something bogus on those fd's just in case. */
+					open( "/dev/null", O_WRONLY );
+				}
 	}
 #endif
Index: bitlbee.conf
===================================================================
--- bitlbee.conf	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ bitlbee.conf	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -55,7 +55,6 @@
 ##
 ## Password the user should enter when logging into a closed BitlBee server.
-## You can also have an MD5-encrypted password here. Format: "md5:", followed
-## by a hash as generated for the <user password=""> attribute in a BitlBee
-## XML file (for now there's no easier way to generate the hash).
+## You can also have a BitlBee-style MD5 hash here. Format: "md5:", followed
+## by a hash as generated by "bitlbee -x hash <password>".
 ##
 # AuthPassword = ItllBeBitlBee   ## Heh.. Our slogan. ;-)
@@ -128,4 +127,12 @@
 ## Proxy = socks5://socksproxy.localnet.com
 
+## Protocols offered by bitlbee
+## 
+## As recompiling may be quite unpractical for some people, this option
+## allows to remove the support of protocol, even if compiled in. If
+## nothing is given, there are no restrictions.
+## 
+## Protocols = jabber yahoo
+
 
 [defaults]
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ bitlbee.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -27,5 +27,7 @@
 #define _BITLBEE_H
 
+#ifndef _GNU_SOURCE
 #define _GNU_SOURCE /* Stupid GNU :-P */
+#endif
 
 /* Depend on Windows 2000 for now since we need getaddrinfo() */
@@ -36,5 +38,5 @@
 
 #define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "1.2.4"
+#define BITLBEE_VERSION "1.2.5"
 #define VERSION BITLBEE_VERSION
 
@@ -132,5 +134,4 @@
 #include "account.h"
 #include "nick.h"
-#include "chat.h"
 #include "conf.h"
 #include "log.h"
Index: chat.c
===================================================================
--- chat.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ chat.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -25,4 +25,5 @@
 
 #include "bitlbee.h"
+#include "chat.h"
 
 struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel )
Index: chat.h
===================================================================
--- chat.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ chat.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -24,4 +24,7 @@
 */
 
+#ifndef _CHAT_H
+#define _CHAT_H
+
 struct chat
 {
@@ -45,2 +48,4 @@
 
 int chat_join( irc_t *irc, struct chat *c, const char *password );
+
+#endif
Index: conf.c
===================================================================
--- conf.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ conf.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -64,4 +64,5 @@
 	conf->ping_timeout = 300;
 	conf->user = NULL;
+	conf->protocols = NULL;
 	proxytype = 0;
 	
@@ -128,5 +129,5 @@
 		{
 			printf( "Usage: bitlbee [-D/-F [-i <interface>] [-p <port>] [-n] [-v]] [-I]\n"
-			        "               [-c <file>] [-d <dir>] [-h]\n"
+			        "               [-c <file>] [-d <dir>] [-x] [-h]\n"
 			        "\n"
 			        "An IRC-to-other-chat-networks gateway\n"
@@ -144,4 +145,5 @@
 			        "  -c  Load alternative configuration file\n"
 			        "  -d  Specify alternative user configuration directory\n"
+			        "  -x  Command-line interface to password encryption/hashing\n"
 			        "  -h  Show this help page.\n" );
 			return NULL;
@@ -312,4 +314,9 @@
 				conf->user = g_strdup( ini->value );
 			}
+			else if( g_strcasecmp( ini->key, "protocols" ) == 0 )
+			{
+				g_strfreev( conf->protocols );
+				conf->protocols = g_strsplit_set( ini->value, " \t,;", -1 );
+			}
 			else
 			{
Index: conf.h
===================================================================
--- conf.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ conf.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -51,4 +51,5 @@
 	int ping_timeout;
 	char *user;
+	char **protocols;
 } conf_t;
 
Index: configure
===================================================================
--- configure	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ configure	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -158,5 +158,5 @@
 
 echo CFLAGS=$CFLAGS >> Makefile.settings
-echo CFLAGS+=-I`pwd` -I`pwd`/lib -I`pwd`/protocols -I. >> Makefile.settings
+echo CFLAGS+=-I`pwd` -iquote`pwd`/lib -iquote`pwd`/protocols -I. >> Makefile.settings
 
 echo CFLAGS+=-DHAVE_CONFIG_H >> Makefile.settings
@@ -269,5 +269,5 @@
 detect_ldap()
 {
-	TMPFILE=`mktemp`
+	TMPFILE=$(mktemp)
 	if $CC -o $TMPFILE -shared -lldap 2>/dev/null >/dev/null; then
 		cat<<EOF>>Makefile.settings
@@ -282,4 +282,41 @@
 		ret=0
 	fi
+}
+
+RESOLV_TESTCODE='
+#include <arpa/nameser.h>
+#include <resolv.h> 
+
+int main()
+{
+	ns_initparse( NULL, 0, NULL );
+	ns_parserr( NULL, ns_s_an, 0, NULL );
+}
+'
+
+detect_resolv_dynamic()
+{
+	echo "$RESOLV_TESTCODE" | $CC -o /dev/null -x c - -lresolv >/dev/null 2>/dev/null
+	if [ "$?" = "0" ]; then
+		echo 'EFLAGS+=-lresolv' >> Makefile.settings
+		return 0
+	fi
+
+	return 1
+}
+
+detect_resolv_static()
+{
+	for i in $systemlibdirs; do
+		if [ -f $i/libresolv.a ]; then
+			echo "$RESOLV_TESTCODE" | $CC -o /dev/null -x c - -Wl,$i/libresolv.a >/dev/null 2>/dev/null
+			if [ "$?" = "0" ]; then
+				echo 'EFLAGS+='$i'/libresolv.a' >> Makefile.settings
+				return 0
+			fi
+		fi
+	done
+
+	return 1
 }
 
@@ -325,9 +362,9 @@
 	
 	## Yes, you, at the console! How can you authenticate if you don't have any SSL!?
-	if [ "$msn" = "1" ]; then
+	if [ "$msn" = "1" -o "$yahoo" = "1" ]; then
 		echo
-		echo 'Real SSL support is necessary for MSN authentication, will build without'
-		echo 'MSN protocol support.'
+		echo 'WARNING: The MSN and Yahoo! modules will not work without SSL. Disabling.'
 		msn=0
+		yahoo=0
 	fi
 	
@@ -351,11 +388,7 @@
 echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings
 
-for i in $systemlibdirs; do
-	if [ -f $i/libresolv.a ]; then
-		echo '#define HAVE_RESOLV_A' >> config.h
-		echo 'EFLAGS+='$i'/libresolv.a' >> Makefile.settings
-		break
-	fi
-done
+if detect_resolv_dynamic || detect_resolv_static; then
+	echo '#define HAVE_RESOLV_A' >> config.h
+fi
 
 STORAGES="text xml"
@@ -435,4 +468,10 @@
 fi
 
+if [ ! -e doc/user-guide/help.txt ] && ! type xmlto > /dev/null 2> /dev/null; then
+	echo
+	echo 'WARNING: Building from an unreleased source tree without prebuilt helpfile.'
+	echo 'Install xmlto if you want online help to work.'
+fi
+
 echo
 if [ -z "$BITLBEE_VERSION" -a -d .bzr ] && type bzr > /dev/null 2> /dev/null; then
@@ -520,4 +559,5 @@
 ;;
 Darwin )
+	echo 'STRIP=\# skip strip' >> Makefile.settings
 ;;
 IRIX )
Index: crypting.c
===================================================================
--- crypting.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ crypting.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -132,62 +132,2 @@
 	return (rv);
 }
-
-#ifdef CRYPTING_MAIN
-
-/* A little main() function for people who want a stand-alone program to
-   encode/decode BitlCrypted files. */
-
-int main( int argc, char *argv[] )
-{
-	char *hash, *action, line[256];
-	char* (*func)( char *, const char * );
-	
-	if( argc < 2 )
-	{
-		fprintf( stderr, "Usage: %s <password>\n\n"
-		                 "Reads from stdin, writes to stdout.\n"
-		                 "Call as \"encode\" to encode, \"decode\" to decode.\n", argv[0] );
-		return( 1 );
-	}
-	
-	hash = hashpass( argv[1] );
-	action = argv[0] + strlen( argv[0] ) - strlen( "encode" );
-	
-	if( strcmp( action, "encode" ) == 0 )
-	{
-		fwrite( hash, 32, 1, stdout );
-		func = obfucrypt;
-	}
-	else if( strcmp( action, "decode" ) == 0 )
-	{
-		char hash2[32];
-		
-		fread( hash2, 32, 1, stdin );
-		if( memcmp( hash, hash2, 32 ) != 0 )
-		{
-			fprintf( stderr, "Passwords don't match. Can't decode.\n" );
-			return( 1 );
-		}
-		func = deobfucrypt;
-	}
-	else
-	{
-		return( main( 0, NULL ) );
-	}
-	
-	while( fscanf( stdin, "%[^\n]255s", line ) > 0 )
-	{
-		char *out;
-		
-		/* Flush the newline */
-		fgetc( stdin );
-		
-		out = func( line, argv[1] );
-		printf( "%s\n", out );
-		g_free( out );
-	}
-	
-	return( 0 );
-}
-
-#endif
Index: doc/CHANGES
===================================================================
--- doc/CHANGES	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ doc/CHANGES	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -3,4 +3,25 @@
 
 http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on
+
+Version 1.2.5:
+- Many bug fixes, including a fix for MSN login issues, Jabber login timing
+  issues, Yahoo! crashes at login time with huge contact lists,
+- Avoid linking in a static version of libresolv now that glibc has all
+  relevant functions available in the dynamic version.
+- Improved away state code and added the ability to set (non-away) status
+  messages using "set status" (also possible per account) and see them in
+  blist and /whois output.
+- Added a post-1.2 equivalent of encode/decode to quickly encrypt/decrypt
+  passwords in a way that BitlBee can read them.
+- Allow using the full name for generating nicknames, instead of just the
+  handle. This is especially useful when using the Facebook XMPP server.
+- Auto reconnect is now enabled by default since all protocols can properly
+  detect cases where auto reconnect should be avoided (i.e. concurrent
+  logins).
+- Changed the default resource_select setting which should reduce message
+  routing issues on Jabber (i.e. messages going someone's phone instead of
+  the main client).
+
+Fixed 17 Mar 2010
 
 Version 1.2.4:
Index: doc/user-guide/commands.xml
===================================================================
--- doc/user-guide/commands.xml	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ doc/user-guide/commands.xml	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -585,4 +585,20 @@
 	</bitlbee-setting>
 
+	<bitlbee-setting name="away" type="string" scope="both">
+		<description>
+			<para>
+				To mark yourself as away, it is recommended to just use <emphasis>/away</emphasis>, like on normal IRC networks. If you want to mark yourself as away on only one IM network, you can use this per-account setting.
+			</para>
+
+			<para>
+				You can set it to any value and BitlBee will try to map it to the most appropriate away state for every open IM connection, or set it as a free-form away message where possible.
+			</para>
+
+			<para>
+				Any per-account away setting will override globally set away states. To un-set the setting, use <emphasis>set -del away</emphasis>.
+			</para>
+		</description>
+	</bitlbee-setting>
+
 	<bitlbee-setting name="away_devoice" type="boolean" scope="global">
 		<default>true</default>
@@ -818,4 +834,34 @@
 	</bitlbee-setting>
 
+	<bitlbee-setting name="nick_source" type="string" scope="account">
+		<default>handle</default>
+		<possible-values>handle, full_name, first_name</possible-values>
+
+		<description>
+			<para>
+				By default, BitlBee generates a nickname for every contact by taking its handle and chopping off everything after the @. In some cases, this gives very inconvenient nicknames. The Facebook XMPP server is a good example, as all Facebook XMPP handles are numeric.
+			</para>
+
+			<para>
+				With this setting set to <emphasis>full_name</emphasis>, the person's full name is used to generate a nickname. Or if you don't like long nicknames, set this setting to <emphasis>first_name</emphasis> instead and only the first word will be used. Note that the full name can be full of non-ASCII characters which will be stripped off.
+			</para>
+		</description>
+	</bitlbee-setting>
+
+	<bitlbee-setting name="ops" type="string" scope="global">
+		<default>both</default>
+		<possible-values>both, root, user, none</possible-values>
+
+		<description>
+			<para>
+				Some people prefer themself and root to have operator status in &amp;bitlbee, other people don't. You can change these states using this setting.
+			</para>
+
+			<para>
+				The value "both" means both user and root get ops. "root" means, well, just root. "user" means just the user. "none" means nobody will get operator status.
+			</para>
+		</description>
+	</bitlbee-setting>
+
 	<bitlbee-setting name="password" type="string" scope="both">
 		<description>
@@ -896,5 +942,5 @@
 
 	<bitlbee-setting name="resource_select" type="string" scope="account">
-		<default>priority</default>
+		<default>activity</default>
 		<possible-values>priority, activity</possible-values>
 
@@ -954,4 +1000,20 @@
 			<para>
 				Currently only available for Jabber connections. Set this to true if the server accepts SSL connections.
+			</para>
+		</description>
+	</bitlbee-setting>
+
+	<bitlbee-setting name="status" type="string" scope="both">
+		<description>
+			<para>
+				Certain protocols (like Jabber/XMPP) support status messages, similar to away messages. They can be used to indicate things like your location or activity, without showing up as away/busy.
+			</para>
+
+			<para>
+				This setting can be used to set such a message. It will be available as a per-account setting for protocols that support it, and also as a global setting (which will then automatically be used for all protocols that support it).
+			</para>
+
+			<para>
+				Away states set using <emphasis>/away</emphasis> or the <emphasis>away</emphasis> setting will override this setting. To un-set the setting, use <emphasis>set -del status</emphasis>.
 			</para>
 		</description>
Index: doc/user-guide/misc.xml
===================================================================
--- doc/user-guide/misc.xml	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ doc/user-guide/misc.xml	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -77,5 +77,5 @@
 
 <para>
-Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>chat add</emphasis> command to join them. See <emphasis>help chat_add</emphasis> for more information.
+Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>chat add</emphasis> command to join them. See <emphasis>help chat add</emphasis> for more information.
 </para>
 
@@ -86,23 +86,23 @@
 
 <para>
-As you might've expected, you can just use the <emphasis>/away</emphasis> command in your IRC client to set an away-state. BitlBee supports most away-states supported by the protocols.
+To mark yourself as away, you can just use the <emphasis>/away</emphasis> command in your IRC client. BitlBee supports most away-states supported by the protocols.
 </para>
 
 <para>
-Not all away states are supported by all protocols, and some protocols have different names for them. BitlBee will try to pick the best available alias from this list for every connection:
+Away states have different names accross different protocols. BitlBee will try to pick the best available option for every connection:
 </para>
 
 <simplelist>
-	<member>Away from computer, Away, Extended away</member>
-	<member>NA, N/A, Not available</member>
-	<member>Busy, Do not disturb, DND, Occupied</member>
-	<member>Be right back, BRB</member>
-	<member>On the phone, Phone, On phone</member>
-	<member>Out to lunch, Lunch, Food</member>
+	<member>Away</member>
+	<member>NA</member>
+	<member>Busy, DND</member>
+	<member>BRB</member>
+	<member>Phone</member>
+	<member>Lunch, Food</member>
 	<member>Invisible, Hidden</member>
 </simplelist>
 
 <para>
-So <emphasis>/away Food</emphasis> will set your state to "Out to lunch" on your MSN connection, and for most other connections the default, "Away" or "Away from computer" will be chosen.
+So <emphasis>/away Food</emphasis> will set your state to "Out to lunch" on your MSN connection, and for most other connections the default, "Away" will be chosen.
 </para>
 
@@ -111,4 +111,8 @@
 </para>
 
+<para>
+If you want to set an away state for only one of your connections, you can use the per-account <emphasis>away</emphasis> setting. See <emphasis>help set away</emphasis>.
+</para>
+
 </sect1>
 
Index: irc.c
===================================================================
--- irc.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ irc.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -81,4 +81,23 @@
 }
 
+static char *set_eval_away_status( set_t *set, char *value )
+{
+	irc_t *irc = set->data;
+	account_t *a;
+	
+	g_free( set->value );
+	set->value = g_strdup( value );
+	
+	for( a = irc->accounts; a; a = a->next )
+	{
+		struct im_connection *ic = a->ic;
+		
+		if( ic && ic->flags & OPT_LOGGED_IN )
+			imc_away_send_update( ic );
+	}
+	
+	return value;
+}
+
 irc_t *irc_new( int fd )
 {
@@ -147,6 +166,8 @@
 
 	
+	s = set_add( &irc->set, "away", NULL,  set_eval_away_status, irc );
+	s->flags |= SET_NULL_OK;
 	s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
-	s = set_add( &irc->set, "auto_reconnect", "false", set_eval_bool, irc );
+	s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc );
 	s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc );
 	s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ irc_commands.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -27,4 +27,5 @@
 #include "bitlbee.h"
 #include "ipc.h"
+#include "chat.h"
 
 static void irc_cmd_pass( irc_t *irc, char **cmd )
@@ -447,5 +448,4 @@
 	user_t *u = user_find( irc, irc->nick );
 	char *away = cmd[1];
-	account_t *a;
 	
 	if( !u ) return;
@@ -474,11 +474,5 @@
 	}
 	
-	for( a = irc->accounts; a; a = a->next )
-	{
-		struct im_connection *ic = a->ic;
-		
-		if( ic && ic->flags & OPT_LOGGED_IN )
-			imc_set_away( ic, u->away );
-	}
+	set_setstr( &irc->set, "away", u->away );
 }
 
@@ -503,4 +497,6 @@
 		else if( u->away )
 			irc_reply( irc, 301, "%s :%s", u->nick, u->away );
+		if( u->status_msg )
+			irc_reply( irc, 333, "%s :Status: %s", u->nick, u->status_msg );
 		
 		irc_reply( irc, 318, "%s :End of /WHOIS list", nick );
Index: lib/misc.c
===================================================================
--- lib/misc.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ lib/misc.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -34,4 +34,5 @@
 #include "nogaim.h"
 #include "base64.h"
+#include "md5.h"
 #include <stdio.h>
 #include <stdlib.h>
@@ -46,4 +47,5 @@
 #endif
 
+#include "md5.h"
 #include "ssl_client.h"
 
@@ -532,5 +534,5 @@
 
 /* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
-char *word_wrap( char *msg, int line_len )
+char *word_wrap( const char *msg, int line_len )
 {
 	GString *ret = g_string_sized_new( strlen( msg ) + 16 );
Index: lib/misc.h
===================================================================
--- lib/misc.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ lib/misc.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -62,5 +62,5 @@
 G_MODULE_EXPORT struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain );
 
-G_MODULE_EXPORT char *word_wrap( char *msg, int line_len );
+G_MODULE_EXPORT char *word_wrap( const char *msg, int line_len );
 
 G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl );
Index: protocols/jabber/io.c
===================================================================
--- protocols/jabber/io.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/jabber/io.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -375,37 +375,11 @@
 	
 	if( ( c = xt_find_node( node->children, "bind" ) ) )
-	{
-		reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
-		xt_add_attr( reply, "xmlns", XMLNS_BIND );
-		reply = jabber_make_packet( "iq", "set", NULL, reply );
-		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
-		
-		if( !jabber_write_packet( ic, reply ) )
-			return XT_ABORT;
-		
-		jd->flags |= JFLAG_WAIT_BIND;
-	}
+		jd->flags |= JFLAG_WANT_BIND;
 	
 	if( ( c = xt_find_node( node->children, "session" ) ) )
-	{
-		reply = xt_new_node( "session", NULL, NULL );
-		xt_add_attr( reply, "xmlns", XMLNS_SESSION );
-		reply = jabber_make_packet( "iq", "set", NULL, reply );
-		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
-		
-		if( !jabber_write_packet( ic, reply ) )
-			return XT_ABORT;
-		
-		jd->flags |= JFLAG_WAIT_SESSION;
-	}
-	
-	/* This flag is already set if we authenticated via SASL, so now
-	   we can resume the session in the new stream, if we don't have
-	   to bind/initialize the session. */
-	if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
-	{
-		if( !jabber_get_roster( ic ) )
-			return XT_ABORT;
-	}
+		jd->flags |= JFLAG_WANT_SESSION;
+	
+	if( jd->flags & JFLAG_AUTHENTICATED )
+		return jabber_pkt_bind_sess( ic, NULL, NULL );
 	
 	return XT_HANDLED;
@@ -441,4 +415,5 @@
 	imcb_log( ic, "Converting stream to TLS" );
 	
+	jd->flags |= JFLAG_STARTTLS_DONE;
 	jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, ic );
 	
@@ -531,7 +506,8 @@
 		jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, ic );
 	
-	greet = g_strdup_printf( "<?xml version='1.0' ?>"
-	                         "<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
-	                          "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", jd->server );
+	greet = g_strdup_printf( "%s<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
+	                          "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", 
+	                          ( jd->flags & JFLAG_STARTTLS_DONE ) ? "" : "<?xml version='1.0' ?>",
+	                          jd->server );
 	
 	st = jabber_write( ic, greet, strlen( greet ) );
Index: protocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/jabber/iq.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -298,8 +298,8 @@
 {
 	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c;
+	struct xt_node *c, *reply = NULL;
 	char *s;
 	
-	if( ( c = xt_find_node( node->children, "bind" ) ) )
+	if( node && ( c = xt_find_node( node->children, "bind" ) ) )
 	{
 		c = xt_find_node( c->children, "jid" );
@@ -307,13 +307,28 @@
 		    strcmp( s + 1, set_getstr( &ic->acc->set, "resource" ) ) != 0 )
 			imcb_log( ic, "Server changed session resource string to `%s'", s + 1 );
-		
-		jd->flags &= ~JFLAG_WAIT_BIND;
-	}
-	else
-	{
-		jd->flags &= ~JFLAG_WAIT_SESSION;
-	}
-	
-	if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
+	}
+	
+	if( jd->flags & JFLAG_WANT_BIND )
+	{
+		reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
+		xt_add_attr( reply, "xmlns", XMLNS_BIND );
+		jd->flags &= ~JFLAG_WANT_BIND;
+	}
+	else if( jd->flags & JFLAG_WANT_SESSION )
+	{
+		reply = xt_new_node( "session", NULL, NULL );
+		xt_add_attr( reply, "xmlns", XMLNS_SESSION );
+		jd->flags &= ~JFLAG_WANT_SESSION;
+	}
+	
+	if( reply != NULL )
+	{
+		reply = jabber_make_packet( "iq", "set", NULL, reply );
+		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
+		
+		if( !jabber_write_packet( ic, reply ) )
+			return XT_ABORT;
+	}
+	else if( ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )
 	{
 		if( !jabber_get_roster( ic ) )
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/jabber/jabber.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -67,5 +67,5 @@
 	s->flags |= ACC_SET_OFFLINE_ONLY;
 	
-	s = set_add( &acc->set, "resource_select", "priority", NULL, acc );
+	s = set_add( &acc->set, "resource_select", "activity", NULL, acc );
 	
 	s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
@@ -80,4 +80,6 @@
 	s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc );
 	s->flags |= ACC_SET_OFFLINE_ONLY;
+	
+	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE;
 }
 
@@ -364,8 +366,9 @@
 	while( bud )
 	{
-		imcb_log( ic, "Buddy %s (%d) information:\nAway state: %s\nAway message: %s",
-		                   bud->full_jid, bud->priority,
-		                   bud->away_state ? bud->away_state->full_name : "(none)",
-		                   bud->away_message ? : "(none)" );
+		imcb_log( ic, "Buddy %s (%d) information:", bud->full_jid, bud->priority );
+		if( bud->away_state )
+			imcb_log( ic, "Away state: %s", bud->away_state->full_name );
+		imcb_log( ic, "Status message: %s", bud->away_message ? : "(none)" );
+		
 		bud = bud->next;
 	}
@@ -377,9 +380,10 @@
 {
 	struct jabber_data *jd = ic->proto_data;
-	struct jabber_away_state *state;
-	
-	/* Save all this info. We need it, for example, when changing the priority setting. */
-	state = (void *) jabber_away_state_by_name( state_txt );
-	jd->away_state = state ? state : (void *) jabber_away_state_list; /* Fall back to "Away" if necessary. */
+	
+	/* state_txt == NULL -> Not away.
+	   Unknown state -> fall back to the first defined away state. */
+	jd->away_state = state_txt ? jabber_away_state_by_name( state_txt )
+	                 ? : jabber_away_state_list : NULL;
+	
 	g_free( jd->away_message );
 	jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL;
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/jabber/jabber.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -27,6 +27,7 @@
 #include <glib.h>
 
+#include "bitlbee.h"
+#include "md5.h"
 #include "xmltree.h"
-#include "bitlbee.h"
 
 extern GSList *jabber_connections;
@@ -39,10 +40,11 @@
 	JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after
 	                                   SASL or TLS). */
-	JFLAG_WAIT_SESSION = 8,	        /* Set if we sent a <session> tag and need a reply
+	JFLAG_WANT_SESSION = 8,	        /* Set if the server wants a <session/> tag
 	                                   before we continue. */
-	JFLAG_WAIT_BIND = 16,           /* ... for <bind> tag. */
+	JFLAG_WANT_BIND = 16,           /* ... for <bind> tag. */
 	JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this
 	                                   activates all XEP-85 related code. */
 	JFLAG_XMLCONSOLE = 64,          /* If the user added an xmlconsole buddy. */
+	JFLAG_STARTTLS_DONE = 128,      /* If a plaintext session was converted to TLS. */
 } jabber_flags_t;
 
@@ -83,5 +85,5 @@
 	/* After changing one of these two (or the priority setting), call
 	   presence_send_update() to inform the server about the changes. */
-	struct jabber_away_state *away_state;
+	const struct jabber_away_state *away_state;
 	char *away_message;
 	
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/jabber/jabber_util.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -228,8 +228,7 @@
 {
 	{ "away",  "Away" },
-	{ "chat",  "Free for Chat" },
+	{ "chat",  "Free for Chat" },   /* WTF actually uses this? */
 	{ "dnd",   "Do not Disturb" },
 	{ "xa",    "Extended Away" },
-	{ "",      "Online" },
 	{ "",      NULL }
 };
@@ -238,4 +237,7 @@
 {
 	int i;
+	
+	if( code == NULL )
+		return NULL;
 	
 	for( i = 0; jabber_away_state_list[i].full_name; i ++ )
@@ -249,4 +251,7 @@
 {
 	int i;
+	
+	if( name == NULL )
+		return NULL;
 	
 	for( i = 0; jabber_away_state_list[i].full_name; i ++ )
Index: protocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/jabber/presence.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -190,11 +190,10 @@
 		int is_away = 0;
 
-		if( send_presence->away_state && !( *send_presence->away_state->code == 0 ||
-		    strcmp( send_presence->away_state->code, "chat" ) == 0 ) )
+		if( send_presence->away_state &&
+		    strcmp( send_presence->away_state->code, "chat" ) != 0 )
 			is_away = OPT_AWAY;
 
 		imcb_buddy_status( ic, send_presence->bare_jid, OPT_LOGGED_IN | is_away,
-		                   ( is_away && send_presence->away_state ) ?
-		                   send_presence->away_state->full_name : NULL,
+		                   is_away ? send_presence->away_state->full_name : NULL,
 		                   send_presence->away_message );
 	}
@@ -209,6 +208,4 @@
 	struct jabber_data *jd = ic->proto_data;
 	struct xt_node *node, *cap;
-	char *show = jd->away_state->code;
-	char *status = jd->away_message;
 	struct groupchat *c;
 	int st;
@@ -216,8 +213,8 @@
 	node = jabber_make_packet( "presence", NULL, NULL, NULL );
 	xt_add_child( node, xt_new_node( "priority", set_getstr( &ic->acc->set, "priority" ), NULL ) );
-	if( show && *show )
-		xt_add_child( node, xt_new_node( "show", show, NULL ) );
-	if( status )
-		xt_add_child( node, xt_new_node( "status", status, NULL ) );
+	if( jd->away_state )
+		xt_add_child( node, xt_new_node( "show", jd->away_state->code, NULL ) );
+	if( jd->away_message )
+		xt_add_child( node, xt_new_node( "status", jd->away_message, NULL ) );
 	
 	/* This makes the packet slightly bigger, but clients interested in
Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/msn/msn.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -26,4 +26,8 @@
 #include "nogaim.h"
 #include "msn.h"
+
+int msn_chat_id;
+GSList *msn_connections;
+GSList *msn_switchboards;
 
 static char *msn_set_display_name( set_t *set, char *value );
@@ -139,6 +143,7 @@
 	
 	if( l == NULL )
-		for( i = 0; msn_away_state_list[i].number > -1; i ++ )
-			l = g_list_append( l, (void*) msn_away_state_list[i].name );
+		for( i = 0; *msn_away_state_list[i].code; i ++ )
+			if( *msn_away_state_list[i].name )
+				l = g_list_append( l, (void*) msn_away_state_list[i].name );
 	
 	return l;
@@ -149,15 +154,12 @@
 	char buf[1024];
 	struct msn_data *md = ic->proto_data;
-	const struct msn_away_state *st;
-	
-	if( strcmp( state, GAIM_AWAY_CUSTOM ) == 0 )
-		st = msn_away_state_by_name( "Away" );
+	
+	if( state )
+		md->away_state = msn_away_state_by_name( state ) ? :
+		                 msn_away_state_list + 1;
 	else
-		st = msn_away_state_by_name( state );
-	
-	if( !st ) st = msn_away_state_list;
-	md->away_state = st;
-	
-	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code );
+		md->away_state = msn_away_state_list;
+	
+	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, md->away_state->code );
 	msn_write( ic, buf, strlen( buf ) );
 }
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/msn/msn.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -97,5 +97,4 @@
 struct msn_away_state
 {
-	int number;
 	char code[4];
 	char name[16];
@@ -136,5 +135,5 @@
 #define STATUS_SB_CHAT_SPARE	8	/* Same, but also for groupchats (not used yet). */
 
-int msn_chat_id;
+extern int msn_chat_id;
 extern const struct msn_away_state msn_away_state_list[];
 extern const struct msn_status_code msn_status_code_list[];
@@ -145,6 +144,6 @@
    connection), the callback should check whether it's still listed here
    before doing *anything* else. */
-GSList *msn_connections;
-GSList *msn_switchboards;
+extern GSList *msn_connections;
+extern GSList *msn_switchboards;
 
 /* ns.c */
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/msn/msn_util.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -171,7 +171,7 @@
 		
 		/* End of headers? */
-		if( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||
-		    strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ||
-		    strncmp( text + i - 2, "\r\r", 2 ) == 0 )
+		if( ( i >= 4 && strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ) ||
+		    ( i >= 2 && ( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||   
+		                  strncmp( text + i - 2, "\r\r", 2 ) == 0 ) ) )
 		{
 			break;
@@ -374,5 +374,5 @@
 	*list = NULL;
 	
-	imcb_log( ic, ret->str );
+	imcb_log( ic, "%s", ret->str );
 	g_string_free( ret, TRUE );
 }
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/msn/ns.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -229,17 +229,24 @@
 			}
 		}
-		else if( num_parts == 7 && strcmp( cmd[2], "OK" ) == 0 )
+		else if( num_parts >= 7 && strcmp( cmd[2], "OK" ) == 0 )
 		{
 			set_t *s;
 			
-			http_decode( cmd[4] );
-			
-			strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) );
-			ic->displayname[sizeof(ic->displayname)-1] = 0;
-			
-			if( ( s = set_find( &ic->acc->set, "display_name" ) ) )
-			{
-				g_free( s->value );
-				s->value = g_strdup( cmd[4] );
+			if( num_parts == 7 )
+			{
+				http_decode( cmd[4] );
+				
+				strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) );
+				ic->displayname[sizeof(ic->displayname)-1] = 0;
+				
+				if( ( s = set_find( &ic->acc->set, "display_name" ) ) )
+				{
+					g_free( s->value );
+					s->value = g_strdup( cmd[4] );
+				}
+			}
+			else
+			{
+				imcb_log( ic, "Warning: Friendly name in server response was corrupted" );
 			}
 			
@@ -406,9 +413,10 @@
 		{
 			/* FIXME: Warn/Bomb about unknown away state? */
-			st = msn_away_state_list;
-		}
-		
-		imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN |
-		                   ( st->number ? OPT_AWAY : 0 ), st->name, NULL );
+			st = msn_away_state_list + 1;
+		}
+		
+		imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN | 
+		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),
+		                   st->name, NULL );
 	}
 	else if( strcmp( cmd[0], "FLN" ) == 0 )
@@ -435,9 +443,10 @@
 		{
 			/* FIXME: Warn/Bomb about unknown away state? */
-			st = msn_away_state_list;
-		}
-		
-		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN |
-		                   ( st->number ? OPT_AWAY : 0 ), st->name, NULL );
+			st = msn_away_state_list + 1;
+		}
+		
+		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN | 
+		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),
+		                   st->name, NULL );
 	}
 	else if( strcmp( cmd[0], "RNG" ) == 0 )
@@ -649,6 +658,6 @@
 				}
 				
-				if( arg1 ) g_free( arg1 );
-				if( mtype ) g_free( mtype );
+				g_free( arg1 );
+				g_free( mtype );
 			}
 			else if( g_strncasecmp( ct, "text/x-msmsgsprofile", 20 ) == 0 )
@@ -658,20 +667,28 @@
 			else if( g_strncasecmp( ct, "text/x-msmsgsinitialemailnotification", 37 ) == 0 )
 			{
-				char *inbox = msn_findheader( body, "Inbox-Unread:", blen );
-				char *folders = msn_findheader( body, "Folders-Unread:", blen );
-				
-				if( inbox && folders && set_getbool( &ic->acc->set, "mail_notifications" ) )
+				if( set_getbool( &ic->acc->set, "mail_notifications" ) )
 				{
-					imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
+					char *inbox = msn_findheader( body, "Inbox-Unread:", blen );
+					char *folders = msn_findheader( body, "Folders-Unread:", blen );
+
+					if( inbox && folders )
+						imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
+					
+					g_free( inbox );
+					g_free( folders );
 				}
 			}
 			else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 )
 			{
-				char *from = msn_findheader( body, "From-Addr:", blen );
-				char *fromname = msn_findheader( body, "From:", blen );
-				
-				if( from && fromname && set_getbool( &ic->acc->set, "mail_notifications" ) )
+				if( set_getbool( &ic->acc->set, "mail_notifications" ) )
 				{
-					imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from );
+					char *from = msn_findheader( body, "From-Addr:", blen );
+					char *fromname = msn_findheader( body, "From:", blen );
+					
+					if( from && fromname )
+						imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from );
+
+					g_free( from );
+					g_free( fromname );
 				}
 			}
Index: protocols/msn/tables.c
===================================================================
--- protocols/msn/tables.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/msn/tables.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -29,25 +29,14 @@
 const struct msn_away_state msn_away_state_list[] =
 {
-	{  0, "NLN", "Available" },
-	{  1, "BSY", "Busy" },
-	{  3, "IDL", "Idle" },
-	{  5, "BRB", "Be Right Back" },
-	{  7, "AWY", "Away" },
-	{  9, "PHN", "On the Phone" },
-	{ 11, "LUN", "Out to Lunch" },
-	{ 13, "HDN", "Hidden" },
-	{ -1, "",    "" }
+	{ "NLN", "" },
+	{ "AWY", "Away" },
+	{ "BSY", "Busy" },
+	{ "IDL", "Idle" },
+	{ "BRB", "Be Right Back" },
+	{ "PHN", "On the Phone" },
+	{ "LUN", "Out to Lunch" },
+	{ "HDN", "Hidden" },
+	{ "",    "" }
 };
-
-const struct msn_away_state *msn_away_state_by_number( int number )
-{
-	int i;
-	
-	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
-		if( msn_away_state_list[i].number == number )
-			return( msn_away_state_list + i );
-	
-	return( NULL );
-}
 
 const struct msn_away_state *msn_away_state_by_code( char *code )
@@ -55,9 +44,9 @@
 	int i;
 	
-	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+	for( i = 0; *msn_away_state_list[i].code; i ++ )
 		if( g_strcasecmp( msn_away_state_list[i].code, code ) == 0 )
 			return( msn_away_state_list + i );
 	
-	return( NULL );
+	return NULL;
 }
 
@@ -66,9 +55,9 @@
 	int i;
 	
-	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+	for( i = 0; *msn_away_state_list[i].code; i ++ )
 		if( g_strcasecmp( msn_away_state_list[i].name, name ) == 0 )
 			return( msn_away_state_list + i );
 	
-	return( NULL );
+	return NULL;
 }
 
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/nogaim.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2006 Wilmer van der Gaast and others                *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -33,6 +33,8 @@
 
 #define BITLBEE_CORE
+#include <ctype.h>
+
 #include "nogaim.h"
-#include <ctype.h>
+#include "chat.h"
 
 static int remove_chat_buddy_silent( struct groupchat *b, const char *handle );
@@ -96,5 +98,17 @@
 void register_protocol (struct prpl *p)
 {
-	protocols = g_list_append(protocols, p);
+	int i;
+	gboolean refused = global.conf->protocols != NULL;
+ 
+	for (i = 0; global.conf->protocols && global.conf->protocols[i]; i++)
+ 	{
+ 		if (g_strcasecmp(p->name, global.conf->protocols[i]) == 0)
+			refused = FALSE;
+ 	}
+
+	if (refused)
+		log_message(LOGLVL_WARNING, "Protocol %s disabled\n", p->name);
+	else
+		protocols = g_list_append(protocols, p);
 }
 
@@ -266,7 +280,6 @@
 	ic->flags |= OPT_LOGGED_IN;
 	
-	/* Also necessary when we're not away, at least for some of the
-	   protocols. */
-	imc_set_away( ic, u->away );
+	/* Necessary to send initial presence status, even if we're not away. */
+	imc_away_send_update( ic );
 	
 	/* Apparently we're connected successfully, so reset the
@@ -370,5 +383,5 @@
 /* list.c */
 
-void imcb_add_buddy( struct im_connection *ic, char *handle, char *group )
+void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group )
 {
 	user_t *u;
@@ -444,7 +457,8 @@
 }
 
-void imcb_rename_buddy( struct im_connection *ic, char *handle, char *realname )
+void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname )
 {
 	user_t *u = user_findhandle( ic, handle );
+	char *set;
 	
 	if( !u || !realname ) return;
@@ -459,7 +473,24 @@
 			imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname );
 	}
-}
-
-void imcb_remove_buddy( struct im_connection *ic, char *handle, char *group )
+	
+	set = set_getstr( &ic->acc->set, "nick_source" );
+	if( strcmp( set, "handle" ) != 0 )
+	{
+		char *name = g_strdup( realname );
+		
+		if( strcmp( set, "first_name" ) == 0 )
+		{
+			int i;
+			for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {}
+			name[i] = '\0';
+		}
+		
+		imcb_buddy_nick_hint( ic, handle, name );
+		
+		g_free( name );
+	}
+}
+
+void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group )
 {
 	user_t *u;
@@ -471,5 +502,5 @@
 /* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM
    modules to suggest a nickname for a handle. */
-void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick )
+void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick )
 {
 	user_t *u = user_findhandle( ic, handle );
@@ -616,9 +647,7 @@
 	oo = u->online;
 	
-	if( u->away )
-	{
-		g_free( u->away );
-		u->away = NULL;
-	}
+	g_free( u->away );
+	g_free( u->status_msg );
+	u->away = u->status_msg = NULL;
 	
 	if( ( flags & OPT_LOGGED_IN ) && !u->online )
@@ -658,5 +687,8 @@
 		}
 	}
-	/* else waste_any_state_information_for_now(); */
+	else
+	{
+		u->status_msg = g_strdup( message );
+	}
 	
 	/* LISPy... */
@@ -692,5 +724,5 @@
 }
 
-void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t flags, time_t sent_at )
+void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at )
 {
 	irc_t *irc = ic->irc;
@@ -833,5 +865,5 @@
 }
 
-void imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at )
+void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at )
 {
 	struct im_connection *ic = c->ic;
@@ -905,5 +937,5 @@
 /* buddy_chat.c */
 
-void imcb_chat_add_buddy( struct groupchat *b, char *handle )
+void imcb_chat_add_buddy( struct groupchat *b, const char *handle )
 {
 	user_t *u = user_findhandle( b->ic, handle );
@@ -940,5 +972,5 @@
 
 /* This function is one BIG hack... :-( EREWRITE */
-void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason )
+void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason )
 {
 	user_t *u;
@@ -1026,49 +1058,28 @@
 }
 
-static char *imc_away_alias_find( GList *gcm, char *away );
-
-int imc_set_away( struct im_connection *ic, char *away )
-{
-	GList *m, *ms;
-	char *s;
-	
-	if( !away ) away = "";
-	ms = m = ic->acc->prpl->away_states( ic );
-	
-	while( m )
-	{
-		if( *away )
-		{
-			if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
-				break;
-		}
-		else
-		{
-			if( g_strcasecmp( m->data, "Available" ) == 0 )
-				break;
-			if( g_strcasecmp( m->data, "Online" ) == 0 )
-				break;
-		}
-		m = m->next;
-	}
-	
-	if( m )
-	{
-		ic->acc->prpl->set_away( ic, m->data, *away ? away : NULL );
-	}
-	else
-	{
-		s = imc_away_alias_find( ms, away );
-		if( s )
-		{
-			ic->acc->prpl->set_away( ic, s, away );
-			if( set_getbool( &ic->irc->set, "debug" ) )
-				imcb_log( ic, "Setting away state to %s", s );
-		}
-		else
-			ic->acc->prpl->set_away( ic, GAIM_AWAY_CUSTOM, away );
-	}
-	
-	return( 1 );
+static char *imc_away_state_find( GList *gcm, char *away, char **message );
+
+int imc_away_send_update( struct im_connection *ic )
+{
+	char *away, *msg = NULL;
+	
+	away = set_getstr( &ic->acc->set, "away" ) ?
+	     : set_getstr( &ic->irc->set, "away" );
+	if( away && *away )
+	{
+		GList *m = ic->acc->prpl->away_states( ic );
+		msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
+		away = imc_away_state_find( m, away, &msg ) ? : m->data;
+	}
+	else if( ic->acc->flags & ACC_FLAG_STATUS_MESSAGE )
+	{
+		away = NULL;
+		msg = set_getstr( &ic->acc->set, "status" ) ?
+		    : set_getstr( &ic->irc->set, "status" );
+	}
+	
+	ic->acc->prpl->set_away( ic, away, msg );
+	
+	return 1;
 }
 
@@ -1085,14 +1096,31 @@
 };
 
-static char *imc_away_alias_find( GList *gcm, char *away )
+static char *imc_away_state_find( GList *gcm, char *away, char **message )
 {
 	GList *m;
 	int i, j;
 	
+	for( m = gcm; m; m = m->next )
+		if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
+		{
+			/* At least the Yahoo! module works better if message
+			   contains no data unless it adds something to what
+			   we have in state already. */
+			if( strlen( m->data ) == strlen( away ) )
+				*message = NULL;
+			
+			return m->data;
+		}
+	
 	for( i = 0; *imc_away_alias_list[i]; i ++ )
 	{
+		int keep_message;
+		
 		for( j = 0; imc_away_alias_list[i][j]; j ++ )
 			if( g_strncasecmp( away, imc_away_alias_list[i][j], strlen( imc_away_alias_list[i][j] ) ) == 0 )
+			{
+				keep_message = strlen( away ) != strlen( imc_away_alias_list[i][j] );
 				break;
+			}
 		
 		if( !imc_away_alias_list[i][j] )	/* If we reach the end, this row */
@@ -1102,15 +1130,20 @@
 		for( j = 0; imc_away_alias_list[i][j]; j ++ )
 		{
-			m = gcm;
-			while( m )
-			{
+			for( m = gcm; m; m = m->next )
 				if( g_strcasecmp( imc_away_alias_list[i][j], m->data ) == 0 )
-					return( imc_away_alias_list[i][j] );
-				m = m->next;
-			}
-		}
-	}
-	
-	return( NULL );
+				{
+					if( !keep_message )
+						*message = NULL;
+					
+					return imc_away_alias_list[i][j];
+				}
+		}
+		
+		/* No need to look further, apparently this state doesn't
+		   have any good alias for this protocol. */
+		break;
+	}
+	
+	return NULL;
 }
 
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/nogaim.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -39,14 +39,14 @@
 #define _NOGAIM_H
 
+#include <stdint.h>
+
 #include "bitlbee.h"
 #include "account.h"
 #include "proxy.h"
 #include "query.h"
-#include "md5.h"
 
 #define BUDDY_ALIAS_MAXLEN 388   /* because MSN names can be 387 characters */
 
 #define WEBSITE "http://www.bitlbee.org/"
-#define GAIM_AWAY_CUSTOM "Custom"
 
 /* Sharing flags between all kinds of things. I just hope I won't hit any
@@ -221,6 +221,6 @@
 	
 	/* You can tell what away states your protocol supports, so that
-	 * BitlBee will try to map the IRC away reasons to them, or use
-	 * GAIM_AWAY_CUSTOM when calling skype_set_away(). */
+	 * BitlBee will try to map the IRC away reasons to them. If your
+	 * protocol doesn't have any, just return one generic "Away". */
 	GList *(* away_states)(struct im_connection *ic);
 	
@@ -279,9 +279,9 @@
  * user, usually after a login, or if the user added a buddy and the IM
  * server confirms that the add was successful. Don't forget to do this! */
-G_MODULE_EXPORT void imcb_add_buddy( struct im_connection *ic, char *handle, char *group );
-G_MODULE_EXPORT void imcb_remove_buddy( struct im_connection *ic, char *handle, char *group );
+G_MODULE_EXPORT void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group );
+G_MODULE_EXPORT void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group );
 G_MODULE_EXPORT struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle );
-G_MODULE_EXPORT void imcb_rename_buddy( struct im_connection *ic, char *handle, char *realname );
-G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick );
+G_MODULE_EXPORT void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname );
+G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick );
 
 /* Buddy activity */
@@ -293,5 +293,5 @@
 /* Not implemented yet! */ G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle );
 /* Call when a handle says something. 'flags' and 'sent_at may be just 0. */
-G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t flags, time_t sent_at );
+G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at );
 G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags );
 G_MODULE_EXPORT void imcb_clean_handle( struct im_connection *ic, char *handle );
@@ -306,9 +306,9 @@
  *   user, too. */
 G_MODULE_EXPORT struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle );
-G_MODULE_EXPORT void imcb_chat_add_buddy( struct groupchat *b, char *handle );
+G_MODULE_EXPORT void imcb_chat_add_buddy( struct groupchat *b, const char *handle );
 /* To remove a handle from a group chat. Reason can be NULL. */
-G_MODULE_EXPORT void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason );
+G_MODULE_EXPORT void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason );
 /* To tell BitlBee 'who' said 'msg' in 'c'. 'flags' and 'sent_at' can be 0. */
-G_MODULE_EXPORT void imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at );
+G_MODULE_EXPORT void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at );
 /* System messages specific to a groupchat, so they can be displayed in the right context. */
 G_MODULE_EXPORT void imcb_chat_log( struct groupchat *c, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
@@ -318,5 +318,5 @@
 
 /* Actions, or whatever. */
-int imc_set_away( struct im_connection *ic, char *away );
+int imc_away_send_update( struct im_connection *ic );
 int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags );
 int imc_chat_msg( struct groupchat *c, char *msg, int flags );
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/oscar/oscar.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -380,4 +380,6 @@
 		s->flags |= ACC_SET_OFFLINE_ONLY;
 	}
+	
+	acc->flags |= ACC_FLAG_AWAY_MESSAGE;
 }
 
@@ -1952,4 +1954,6 @@
 static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
 {
+	if (state == NULL)
+		state = "";
 
 	if (!g_strcasecmp(state, _("Visible"))) {
@@ -1959,5 +1963,7 @@
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
 		return;
-	} /* else... */
+	} else if (message == NULL) {
+		message = state;
+	}
 
 	if (od->rights.maxawaymsglen == 0)
@@ -2002,5 +2008,5 @@
 	}
 
-	if (!g_strcasecmp(state, "Online")) {
+	if (state == NULL) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
 	} else if (!g_strcasecmp(state, "Away")) {
@@ -2027,5 +2033,5 @@
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
 		ic->away = g_strdup(msg);
-	} else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
+	} else {
 	 	if (no_message) {
 			aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
@@ -2276,18 +2282,19 @@
 {
 	struct oscar_data *od = ic->proto_data;
-	GList *m = NULL;
-
-	if (!od->icq)
-		return g_list_append(m, GAIM_AWAY_CUSTOM);
-
-	m = g_list_append(m, "Online");
-	m = g_list_append(m, "Away");
-	m = g_list_append(m, "Do Not Disturb");
-	m = g_list_append(m, "Not Available");
-	m = g_list_append(m, "Occupied");
-	m = g_list_append(m, "Free For Chat");
-	m = g_list_append(m, "Invisible");
-
-	return m;
+
+	if (od->icq) {
+		static GList *m = NULL;
+		m = g_list_append(m, "Away");
+		m = g_list_append(m, "Do Not Disturb");
+		m = g_list_append(m, "Not Available");
+		m = g_list_append(m, "Occupied");
+		m = g_list_append(m, "Free For Chat");
+		m = g_list_append(m, "Invisible");
+		return m;
+	} else {
+		static GList *m = NULL;
+		m = g_list_append(m, "Away");
+		return m;
+	}
 }
 
Index: protocols/yahoo/libyahoo2.c
===================================================================
--- protocols/yahoo/libyahoo2.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/yahoo/libyahoo2.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -857,53 +857,4 @@
 }
 
-static YList * bud_str2list(char *rawlist)
-{
-	YList * l = NULL;
-
-	char **lines;
-	char **split;
-	char **buddies;
-	char **tmp, **bud;
-
-	lines = y_strsplit(rawlist, "\n", -1);
-	for (tmp = lines; *tmp; tmp++) {
-		struct yahoo_buddy *newbud;
-
-		split = y_strsplit(*tmp, ":", 2);
-		if (!split)
-			continue;
-		if (!split[0] || !split[1]) {
-			y_strfreev(split);
-			continue;
-		}
-		buddies = y_strsplit(split[1], ",", -1);
-
-		for (bud = buddies; bud && *bud; bud++) {
-			newbud = y_new0(struct yahoo_buddy, 1);
-			newbud->id = strdup(*bud);
-			newbud->group = strdup(split[0]);
-
-			if(y_list_find_custom(l, newbud, is_same_bud)) {
-				FREE(newbud->id);
-				FREE(newbud->group);
-				FREE(newbud);
-				continue;
-			}
-
-			newbud->real_name = NULL;
-
-			l = y_list_append(l, newbud);
-
-			NOTICE(("Added buddy %s to group %s", newbud->id, newbud->group));
-		}
-
-		y_strfreev(buddies);
-		y_strfreev(split);
-	}
-	y_strfreev(lines);
-
-	return l;
-}
-
 static char * getcookie(char *rawcookie)
 {
@@ -1362,38 +1313,25 @@
 }
 
-
-static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+static void yahoo_process_status(struct yahoo_input_data *yid,
+	struct yahoo_packet *pkt)
 {
 	YList *l;
 	struct yahoo_data *yd = yid->yd;
 
-	struct user
-	{
-		char *name;	/* 7	name */
-		int   state;	/* 10	state */
-		int   flags;	/* 13	flags, bit 0 = pager, bit 1 = chat, bit 2 = game */
-		int   mobile;	/* 60	mobile */
-		char *msg;	/* 19	custom status message */
-		int   away;	/* 47	away (or invisible)*/
-		int   buddy_session;	/* 11	state */
-		int   f17;	/* 17	in chat? then what about flags? */
-		int   idle;	/* 137	seconds idle */
-		int   f138;	/* 138	state */
-		char *f184;	/* 184	state */
-		int   f192;	/* 192	state */
-		int   f10001;	/* 10001	state */
-		int   f10002;	/* 10002	state */
-		int   f198;	/* 198	state */
-		char *f197;	/* 197	state */
-		char *f205;	/* 205	state */
-		int   f213;	/* 213	state */
-	} *u;
+	struct yahoo_process_status_entry *u;
 
 	YList *users = 0;
-	
+
 	if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
-		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_DUPL, NULL);
-		return;
-	}
+		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
+			YAHOO_LOGIN_DUPL, NULL);
+		return;
+	}
+
+	/* Status updates may be spread accross multiple packets and not
+	   even on buddy boundaries, so keeping some state is important.
+	   So, continue where we left off, and only add a user entry to
+	   the list once it's complete (301-315 End buddy). */
+	u = yd->half_user;
 
 	for (l = pkt->hash; l; l = l->next) {
@@ -1401,99 +1339,126 @@
 
 		switch (pair->key) {
-		case 0: /* we won't actually do anything with this */
+		case 300:	/* Begin buddy */
+			if (!strcmp(pair->value, "315") && !u) {
+				u = yd->half_user = y_new0(struct yahoo_process_status_entry, 1);
+			}
+			break;
+		case 301:	/* End buddy */
+			if (!strcmp(pair->value, "315") && u) {
+				/* Sometimes user info comes in an odd format with no
+				   "begin buddy" but *with* an "end buddy". Don't add
+				   it twice. */
+				if (!y_list_find(users, u))
+					users = y_list_prepend(users, u);
+				u = yd->half_user = NULL;
+			}
+			break;
+		case 0:	/* we won't actually do anything with this */
 			NOTICE(("key %d:%s", pair->key, pair->value));
 			break;
-		case 1: /* we don't get the full buddy list here. */
+		case 1:	/* we don't get the full buddy list here. */
 			if (!yd->logged_in) {
-				yd->logged_in = TRUE;
-				if(yd->current_status < 0)
+				yd->logged_in = 1;
+				if (yd->current_status < 0)
 					yd->current_status = yd->initial_status;
-				YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
+				YAHOO_CALLBACK(ext_yahoo_login_response) (yd->
+					client_id, YAHOO_LOGIN_OK, NULL);
 			}
 			break;
-		case 8: /* how many online buddies we have */
+		case 8:	/* how many online buddies we have */
 			NOTICE(("key %d:%s", pair->key, pair->value));
 			break;
-		case 7: /* the current buddy */
-			u = y_new0(struct user, 1);
+		case 7:	/* the current buddy */
+			if (!u) {
+				/* This will only happen in case of a single level message */
+				u = y_new0(struct yahoo_process_status_entry, 1);
+				users = y_list_prepend(users, u);
+			}
 			u->name = pair->value;
-			users = y_list_prepend(users, u);
-			break;
-		case 10: /* state */
-			((struct user*)users->data)->state = strtol(pair->value, NULL, 10);
-			break;
-		case 19: /* custom status message */
-			((struct user*)users->data)->msg = pair->value;
-			break;
-		case 47: /* is it an away message or not */
-			((struct user*)users->data)->away = atoi(pair->value);
-			break;
-		case 137: /* seconds idle */
-			((struct user*)users->data)->idle = atoi(pair->value);
-			break;
-		case 11: /* this is the buddy's session id */
-			((struct user*)users->data)->buddy_session = atoi(pair->value);
-			break;
-		case 17: /* in chat? */
-			((struct user*)users->data)->f17 = atoi(pair->value);
-			break;
-		case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
-			((struct user*)users->data)->flags = atoi(pair->value);
-			break;
-		case 60: /* SMS -> 1 MOBILE USER */
+			break;
+		case 10:	/* state */
+			u->state = strtol(pair->value, NULL, 10);
+			break;
+		case 19:	/* custom status message */
+			u->msg = pair->value;
+			break;
+		case 47:	/* is it an away message or not. Not applicable for YMSG16 anymore */
+			u->away = atoi(pair->value);
+			break;
+		case 137:	/* seconds idle */
+			u->idle = atoi(pair->value);
+			break;
+		case 11:	/* this is the buddy's session id */
+			u->buddy_session = atoi(pair->value);
+			break;
+		case 17:	/* in chat? */
+			u->f17 = atoi(pair->value);
+			break;
+		case 13:	/* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
+			u->flags = atoi(pair->value);
+			break;
+		case 60:	/* SMS -> 1 MOBILE USER */
 			/* sometimes going offline makes this 2, but invisible never sends it */
-			((struct user*)users->data)->mobile = atoi(pair->value);
+			u->mobile = atoi(pair->value);
 			break;
 		case 138:
-			((struct user*)users->data)->f138 = atoi(pair->value);
+			u->f138 = atoi(pair->value);
 			break;
 		case 184:
-			((struct user*)users->data)->f184 = pair->value;
+			u->f184 = pair->value;
 			break;
 		case 192:
-			((struct user*)users->data)->f192 = atoi(pair->value);
+			u->f192 = atoi(pair->value);
 			break;
 		case 10001:
-			((struct user*)users->data)->f10001 = atoi(pair->value);
+			u->f10001 = atoi(pair->value);
 			break;
 		case 10002:
-			((struct user*)users->data)->f10002 = atoi(pair->value);
+			u->f10002 = atoi(pair->value);
 			break;
 		case 198:
-			((struct user*)users->data)->f198 = atoi(pair->value);
+			u->f198 = atoi(pair->value);
 			break;
 		case 197:
-			((struct user*)users->data)->f197 = pair->value;
+			u->f197 = pair->value;
 			break;
 		case 205:
-			((struct user*)users->data)->f205 = pair->value;
+			u->f205 = pair->value;
 			break;
 		case 213:
-			((struct user*)users->data)->f213 = atoi(pair->value);
-			break;
-		case 16: /* Custom error message */
-			YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, pair->value, 0, E_CUSTOM);
+			u->f213 = atoi(pair->value);
+			break;
+		case 16:	/* Custom error message */
+			YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id,
+				pair->value, 0, E_CUSTOM);
 			break;
 		default:
-			WARNING(("unknown status key %d:%s", pair->key, pair->value));
-			break;
-		}
-	}
-	
+			WARNING(("unknown status key %d:%s", pair->key,
+					pair->value));
+			break;
+		}
+	}
+
 	while (users) {
 		YList *t = users;
-		struct user *u = users->data;
+		struct yahoo_process_status_entry *u = users->data;
 
 		if (u->name != NULL) {
-			if (pkt->service == YAHOO_SERVICE_LOGOFF) { /* || u->flags == 0) { Not in YMSG16 */
-				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
+			if (pkt->service ==
+				YAHOO_SERVICE_LOGOFF
+				/*|| u->flags == 0 No flags for YMSG16 */ ) {
+				YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->
+					client_id, u->name,
+					YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
 			} else {
 				/* Key 47 always seems to be 1 for YMSG16 */
-				if(!u->state)
+				if (!u->state)
 					u->away = 0;
 				else
 					u->away = 1;
 
-				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, u->state, u->msg, u->away, u->idle, u->mobile);
+				YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->
+					client_id, u->name, u->state, u->msg,
+					u->away, u->idle, u->mobile);
 			}
 		}
@@ -1505,5 +1470,6 @@
 }
 
-static void yahoo_process_buddy_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+static void yahoo_process_buddy_list(struct yahoo_input_data *yid,
+	struct yahoo_packet *pkt)
 {
 	struct yahoo_data *yd = yid->yd;
@@ -1517,14 +1483,14 @@
 		struct yahoo_pair *pair = l->data;
 
-		switch(pair->key) {
+		switch (pair->key) {
 		case 300:
 		case 301:
 		case 302:
+			break;	/* Separators. Our logic does not need them */
 		case 303:
-			if ( 315 == atoi(pair->value) )
+			if (318 == atoi(pair->value))
 				last_packet = 1;
 			break;
 		case 65:
-			g_free(cur_group);
 			cur_group = strdup(pair->value);
 			break;
@@ -1532,11 +1498,14 @@
 			newbud = y_new0(struct yahoo_buddy, 1);
 			newbud->id = strdup(pair->value);
-			if(cur_group)
+			if (cur_group)
 				newbud->group = strdup(cur_group);
-			else {
-				struct yahoo_buddy *lastbud = (struct yahoo_buddy *)y_list_nth(
-								yd->buddies, y_list_length(yd->buddies)-1)->data;
+			else if (yd->buddies) {
+				struct yahoo_buddy *lastbud =
+					(struct yahoo_buddy *)y_list_nth(yd->
+					buddies,
+					y_list_length(yd->buddies) - 1)->data;
 				newbud->group = strdup(lastbud->group);
-			}
+			} else
+				newbud->group = strdup("Buddies");
 
 			yd->buddies = y_list_append(yd->buddies, newbud);
@@ -1545,76 +1514,56 @@
 		}
 	}
-	
-	g_free(cur_group);
 
 	/* we could be getting multiple packets here */
-	if (last_packet)
-		return;
-
-	YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
-
-	/*** We login at the very end of the packet communication */
+	if (pkt->hash && !last_packet)
+		return;
+
+	YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, yd->buddies);
+
+	/* Logged in */
 	if (!yd->logged_in) {
-		yd->logged_in = TRUE;
-		if(yd->current_status < 0)
+		yd->logged_in = 1;
+		if (yd->current_status < 0)
 			yd->current_status = yd->initial_status;
-		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
-	}
-}
-
-static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
+			YAHOO_LOGIN_OK, NULL);
+
+		/*
+		yahoo_set_away(yd->client_id, yd->initial_status, NULL,
+			(yd->initial_status == YAHOO_STATUS_AVAILABLE) ? 0 : 1);
+
+		yahoo_get_yab(yd->client_id);
+		*/
+	}
+
+}
+
+static void yahoo_process_list(struct yahoo_input_data *yid,
+	struct yahoo_packet *pkt)
 {
 	struct yahoo_data *yd = yid->yd;
 	YList *l;
 
-	if (!yd->logged_in) {
-		yd->logged_in = TRUE;
-		if(yd->current_status < 0)
-			yd->current_status = yd->initial_status;
-		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
-	}
-
+	/* we could be getting multiple packets here */
 	for (l = pkt->hash; l; l = l->next) {
 		struct yahoo_pair *pair = l->data;
 
-		switch(pair->key) {
-		case 87: /* buddies */
-			if(!yd->rawbuddylist)
-				yd->rawbuddylist = strdup(pair->value);
-			else {
-				yd->rawbuddylist = y_string_append(yd->rawbuddylist, pair->value);
+		switch (pair->key) {
+		case 89:	/* identities */
+			{
+				char **identities =
+					y_strsplit(pair->value, ",", -1);
+				int i;
+				for (i = 0; identities[i]; i++)
+					yd->identities =
+						y_list_append(yd->identities,
+						strdup(identities[i]));
+				y_strfreev(identities);
 			}
-			break;
-
-		case 88: /* ignore list */
-			if(!yd->ignorelist)
-				yd->ignorelist = strdup("Ignore:");
-			yd->ignorelist = y_string_append(yd->ignorelist, pair->value);
-			break;
-
-		case 89: /* identities */
-			{
-			char **identities = y_strsplit(pair->value, ",", -1);
-			int i;
-			for(i=0; identities[i]; i++)
-				yd->identities = y_list_append(yd->identities, 
-						strdup(identities[i]));
-			y_strfreev(identities);
-			}
-			YAHOO_CALLBACK(ext_yahoo_got_identities)(yd->client_id, yd->identities);
-			break;
-		case 59: /* cookies */
-			if(yd->ignorelist) {
-				yd->ignore = bud_str2list(yd->ignorelist);
-				FREE(yd->ignorelist);
-				YAHOO_CALLBACK(ext_yahoo_got_ignore)(yd->client_id, yd->ignore);
-			}
-			if(yd->rawbuddylist) {
-				yd->buddies = bud_str2list(yd->rawbuddylist);
-				FREE(yd->rawbuddylist);
-				YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
-			}
-
-			if(pair->value[0]=='Y') {
+			YAHOO_CALLBACK(ext_yahoo_got_identities) (yd->client_id,
+				yd->identities);
+			break;
+		case 59:	/* cookies */
+			if (pair->value[0] == 'Y') {
 				FREE(yd->cookie_y);
 				FREE(yd->login_cookie);
@@ -1623,26 +1572,26 @@
 				yd->login_cookie = getlcookie(yd->cookie_y);
 
-			} else if(pair->value[0]=='T') {
+			} else if (pair->value[0] == 'T') {
 				FREE(yd->cookie_t);
 				yd->cookie_t = getcookie(pair->value);
 
-			} else if(pair->value[0]=='C') {
+			} else if (pair->value[0] == 'C') {
 				FREE(yd->cookie_c);
 				yd->cookie_c = getcookie(pair->value);
-			} 
-
-			if(yd->cookie_y && yd->cookie_t)
-				YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id);
-
-			break;
-		case 3: /* my id */
-		case 90: /* 1 */
-		case 100: /* 0 */
-		case 101: /* NULL */
-		case 102: /* NULL */
-		case 93: /* 86400/1440 */
-			break;
-		}
-	}
+			}
+
+			break;
+		case 3:	/* my id */
+		case 90:	/* 1 */
+		case 100:	/* 0 */
+		case 101:	/* NULL */
+		case 102:	/* NULL */
+		case 93:	/* 86400/1440 */
+			break;
+		}
+	}
+
+	if (yd->cookie_y && yd->cookie_t)	/* We don't get cookie_c anymore */
+		YAHOO_CALLBACK(ext_yahoo_got_cookies) (yd->client_id);
 }
 
@@ -2395,7 +2344,13 @@
 {
 	struct yahoo_https_auth_data *had = req->data;
-	struct yahoo_input_data *yid = had->yid;
-	struct yahoo_data *yd = yid->yd;
+	struct yahoo_input_data *yid;
+	struct yahoo_data *yd;
 	int st;
+	
+	if (y_list_find(inputs, had->yid) == NULL)
+		return;
+	
+	yid = had->yid;
+	yd = yid->yd;
 	
 	if (req->status_code != 200) {
@@ -2438,9 +2393,15 @@
 {
 	struct yahoo_https_auth_data *had = req->data;
-	struct yahoo_input_data *yid = had->yid;
-	struct yahoo_data *yd = yid->yd;
+	struct yahoo_input_data *yid;
+	struct yahoo_data *yd;
 	struct yahoo_packet *pack;
-	char *crumb;
+	char *crumb = NULL;
 	int st;
+	
+	if (y_list_find(inputs, had->yid) == NULL)
+		return;
+	
+	yid = had->yid;
+	yd = yid->yd;
 	
 	md5_byte_t result[16];
@@ -4082,12 +4043,6 @@
 
 	yd = yid->yd;
-
 	old_status = yd->current_status;
-
-	if (msg && strncmp(msg,"Invisible",9)) {
-		yd->current_status = YAHOO_STATUS_CUSTOM;
-	} else {
-		yd->current_status = state;
-	}
+	yd->current_status = state;
 
 	/* Thank you libpurple :) */
@@ -4104,13 +4059,6 @@
 	snprintf(s, sizeof(s), "%d", yd->current_status);
 	yahoo_packet_hash(pkt, 10, s);
-	 
-	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
-		yahoo_packet_hash(pkt, 19, msg);
-	} else {
-		yahoo_packet_hash(pkt, 19, "");
-	}
-	
+	yahoo_packet_hash(pkt, 19, msg && state == YAHOO_STATUS_CUSTOM ? msg : "");
 	yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");
-
 	yahoo_send_packet(yid, pkt, 0);
 	yahoo_packet_free(pkt);
Index: protocols/yahoo/yahoo.c
===================================================================
--- protocols/yahoo/yahoo.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/yahoo/yahoo.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -130,4 +130,6 @@
 {
 	set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
+	
+	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE;
 }
 
@@ -197,27 +199,10 @@
 {
 	struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data;
-	char *away;
-	
-	away = NULL;
-	
-	if( state && msg && g_strcasecmp( state, msg ) != 0 )
-	{
-		yd->current_status = YAHOO_STATUS_CUSTOM;
-		away = "";
-	}
-	else if( state )
-	{
-		/* Set msg to NULL since (if it isn't NULL already) it's equal
-		   to state. msg must be empty if we want to use an existing
-		   away state. */
-		msg = NULL;
-		
-		away = "";
-		if( g_strcasecmp( state, "Available" ) == 0 )
-		{
-			yd->current_status = YAHOO_STATUS_AVAILABLE;
-			away = NULL;
-		}
-		else if( g_strcasecmp( state, "Be Right Back" ) == 0 )
+	
+	if( state && msg == NULL )
+	{
+		/* Use these states only if msg doesn't contain additional
+		   info since away messages are only supported with CUSTOM. */
+		if( g_strcasecmp( state, "Be Right Back" ) == 0 )
 			yd->current_status = YAHOO_STATUS_BRB;
 		else if( g_strcasecmp( state, "Busy" ) == 0 )
@@ -239,15 +224,13 @@
 		else if( g_strcasecmp( state, "Invisible" ) == 0 )
 			yd->current_status = YAHOO_STATUS_INVISIBLE;
-		else if( g_strcasecmp( state, GAIM_AWAY_CUSTOM ) == 0 )
-		{
-			yd->current_status = YAHOO_STATUS_AVAILABLE;
-			
-			away = NULL;
-		}
-	}
+		else
+			yd->current_status = YAHOO_STATUS_CUSTOM;
+	}
+	else if( msg )
+		yd->current_status = YAHOO_STATUS_CUSTOM;
 	else
 		yd->current_status = YAHOO_STATUS_AVAILABLE;
 	
-	yahoo_set_away( yd->y2_id, yd->current_status, msg, away != NULL ? 2 : 0 );
+	yahoo_set_away( yd->y2_id, yd->current_status, msg, state ? 2 : 0 );
 }
 
@@ -258,5 +241,4 @@
 	if( m == NULL )
 	{
-		m = g_list_append( m, "Available" );
 		m = g_list_append( m, "Be Right Back" );
 		m = g_list_append( m, "Busy" );
@@ -269,5 +251,4 @@
 		m = g_list_append( m, "Stepped Out" );
 		m = g_list_append( m, "Invisible" );
-		m = g_list_append( m, GAIM_AWAY_CUSTOM );
 	}
 	
Index: protocols/yahoo/yahoo2_types.h
===================================================================
--- protocols/yahoo/yahoo2_types.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ protocols/yahoo/yahoo2_types.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -196,4 +196,6 @@
 
 	void  *server_settings;
+	
+	struct yahoo_process_status_entry *half_user;
 };
 
@@ -261,4 +263,25 @@
 };
 
+struct yahoo_process_status_entry {
+	char *name;	/* 7      name */
+	int state;	/* 10     state */
+	int flags;	/* 13     flags, bit 0 = pager, bit 1 = chat, bit 2 = game */
+	int mobile;	/* 60     mobile */
+	char *msg;	/* 19     custom status message */
+	int away;	/* 47     away (or invisible) */
+	int buddy_session; /* 11  state */
+	int f17;	/* 17     in chat? then what about flags? */
+	int idle;	/* 137    seconds idle */
+	int f138;	/* 138    state */
+	char *f184;	/* 184    state */
+	int f192;	/* 192    state */
+	int f10001;	/* 10001  state */
+	int f10002;	/* 10002  state */
+	int f198;	/* 198    state */
+	char *f197;	/* 197    state */
+	char *f205;	/* 205    state */
+	int f213;	/* 213    state */
+};
+
 #ifdef __cplusplus
 }
Index: root_commands.c
===================================================================
--- root_commands.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ root_commands.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -30,4 +30,5 @@
 #include "help.h"
 #include "otr.h"
+#include "chat.h"
 
 #include <string.h>
@@ -143,4 +144,10 @@
 	storage_status_t status = storage_load( irc, cmd[1] );
 	char *account_on[] = { "account", "on", NULL };
+	
+	if( strchr( irc->umode, 'R' ) != NULL )
+	{
+		irc_usermsg( irc, "You're already logged in." );
+		return;
+	}
 	
 	switch (status) {
@@ -912,5 +919,5 @@
 		online = 1;
 	else
-		online =  away = 1;
+		online = away = 1;
 	
 	if( strchr( irc->umode, 'b' ) != NULL )
@@ -925,6 +932,11 @@
 		if( online == 1 )
 		{
+			char st[256] = "Online";
+			
+			if( u->status_msg )
+				g_snprintf( st, sizeof( st ) - 1, "Online (%s)", u->status_msg );
+			
 			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );
-			irc_usermsg( irc, format, u->nick, s, "Online" );
+			irc_usermsg( irc, format, u->nick, s, st );
 		}
 		
@@ -1119,77 +1131,4 @@
 		irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] );
 	}
-
-
-
-#if 0
-	account_t *a;
-	struct im_connection *ic;
-	char *chat, *channel, *nick = NULL, *password = NULL;
-	struct groupchat *c;
-	
-	if( !( a = account_get( irc, cmd[1] ) ) )
-	{
-		irc_usermsg( irc, "Invalid account" );
-		return;
-	}
-	else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
-	{
-		irc_usermsg( irc, "That account is not on-line" );
-		return;
-	}
-	else if( a->prpl->chat_join == NULL )
-	{
-		irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
-		return;
-	}
-	ic = a->ic;
-	
-	chat = cmd[2];
-	if( cmd[3] )
-	{
-		if( strchr( CTYPES, cmd[3][0] ) == NULL )
-			channel = g_strdup_printf( "&%s", cmd[3] );
-		else
-			channel = g_strdup( cmd[3] );
-	}
-	else
-	{
-		char *s;
-		
-		channel = g_strdup_printf( "&%s", chat );
-		if( ( s = strchr( channel, '@' ) ) )
-			*s = 0;
-	}
-	if( cmd[3] && cmd[4] )
-		nick = cmd[4];
-	else
-		nick = irc->nick;
-	if( cmd[3] && cmd[4] && cmd[5] )
-		password = cmd[5];
-	
-	if( !nick_ok( channel + 1 ) )
-	{
-		irc_usermsg( irc, "Invalid channel name: %s", channel );
-		g_free( channel );
-		return;
-	}
-	else if( g_strcasecmp( channel, irc->channel ) == 0 || irc_chat_by_channel( irc, channel ) )
-	{
-		irc_usermsg( irc, "Channel already exists: %s", channel );
-		g_free( channel );
-		return;
-	}
-	
-	if( ( c = a->prpl->chat_join( ic, chat, nick, password ) ) )
-	{
-		g_free( c->channel );
-		c->channel = channel;
-	}
-	else
-	{
-		irc_usermsg( irc, "Tried to join chat, not sure if this was successful" );
-		g_free( channel );
-	}
-#endif
 }
 
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ storage_xml.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -29,4 +29,5 @@
 #include "arc.h"
 #include "md5.h"
+#include "chat.h"
 
 #if GLIB_CHECK_VERSION(2,8,0)
Index: unix.c
===================================================================
--- unix.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ unix.c	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -25,4 +25,7 @@
 
 #include "bitlbee.h"
+
+#include "arc.h"
+#include "base64.h"
 #include "commands.h"
 #include "crypting.h"
@@ -32,4 +35,6 @@
 #include "ipc.h"
 #include "lib/ssl_client.h"
+#include "md5.h"
+#include "misc.h"
 #include <signal.h>
 #include <unistd.h>
@@ -42,4 +47,6 @@
 static void sighandler( int signal );
 
+static int crypt_main( int argc, char *argv[] );
+
 int main( int argc, char *argv[] )
 {
@@ -47,4 +54,7 @@
 	char *old_cwd = NULL;
 	struct sigaction sig, old;
+	
+	if( argc > 1 && strcmp( argv[1], "-x" ) == 0 )
+		return crypt_main( argc, argv );
 	
 	log_init();
@@ -168,4 +178,62 @@
 }
 
+static int crypt_main( int argc, char *argv[] )
+{
+	int pass_len;
+	unsigned char *pass_cr, *pass_cl;
+	
+	if( argc < 4 || ( strcmp( argv[2], "hash" ) != 0 &&
+	                  strcmp( argv[2], "unhash" ) != 0 && argc < 5 ) )
+	{
+		printf( "Supported:\n"
+		        "  %s -x enc <key> <cleartext password>\n"
+		        "  %s -x dec <key> <encrypted password>\n"
+		        "  %s -x hash <cleartext password>\n"
+		        "  %s -x unhash <hashed password>\n"
+		        "  %s -x chkhash <hashed password> <cleartext password>\n",
+		        argv[0], argv[0], argv[0], argv[0], argv[0] );
+	}
+	else if( strcmp( argv[2], "enc" ) == 0 )
+	{
+		pass_len = arc_encode( argv[4], strlen( argv[4] ), (unsigned char**) &pass_cr, argv[3], 12 );
+		printf( "%s\n", base64_encode( pass_cr, pass_len ) );
+	}
+	else if( strcmp( argv[2], "dec" ) == 0 )
+	{
+		pass_len = base64_decode( argv[4], (unsigned char**) &pass_cr );
+		arc_decode( pass_cr, pass_len, (char**) &pass_cl, argv[3] );
+		printf( "%s\n", pass_cl );
+	}
+	else if( strcmp( argv[2], "hash" ) == 0 )
+	{
+		md5_byte_t pass_md5[21];
+		md5_state_t md5_state;
+		
+		random_bytes( pass_md5 + 16, 5 );
+		md5_init( &md5_state );
+		md5_append( &md5_state, (md5_byte_t*) argv[3], strlen( argv[3] ) );
+		md5_append( &md5_state, pass_md5 + 16, 5 ); /* Add the salt. */
+		md5_finish( &md5_state, pass_md5 );
+		
+		printf( "%s\n", base64_encode( pass_md5, 21 ) );
+	}
+	else if( strcmp( argv[2], "unhash" ) == 0 )
+	{
+		printf( "Hash %s submitted to a massive Beowulf cluster of\n"
+		        "overclocked 486s. Expect your answer next year somewhere around this time. :-)\n", argv[3] );
+	}
+	else if( strcmp( argv[2], "chkhash" ) == 0 )
+	{
+		char *hash = strncmp( argv[3], "md5:", 4 ) == 0 ? argv[3] + 4 : argv[3];
+		int st = md5_verify_password( argv[4], hash );
+		
+		printf( "Hash %s given password.\n", st == 0 ? "matches" : "does not match" );
+		
+		return st;
+	}
+	
+	return 0;
+}
+
 static void sighandler( int signal )
 {
Index: user.h
===================================================================
--- user.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
+++ user.h	(revision 5f8ab6a9adf09ea7c07f728227bdb6d3953588f1)
@@ -34,4 +34,5 @@
 	
 	char *away;
+	char *status_msg; /* Non-IRC extension, but nice on IM. */
 	
 	char is_private;
