Index: account.c
===================================================================
--- account.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ account.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -182,17 +182,15 @@
 	account_t *a, *l = NULL;
 	
+	if( acc->ic )
+		/* Caller should have checked, accounts still in use can't be deleted. */
+		return;
+	
 	for( a = irc->accounts; a; a = (l=a)->next )
 		if( a == acc )
 		{
-			if( a->ic ) return; /* Caller should have checked, accounts still in use can't be deleted. */
-			
 			if( l )
-			{
 				l->next = a->next;
-			}
 			else
-			{
 				irc->accounts = a->next;
-			}
 			
 			while( a->set )
@@ -203,5 +201,5 @@
 			g_free( a->user );
 			g_free( a->pass );
-			if( a->server ) g_free( a->server );
+			g_free( a->server );
 			if( a->reconnect )	/* This prevents any reconnect still queued to happen */
 				cancel_auto_reconnect( a );
Index: bitlbee.conf
===================================================================
--- bitlbee.conf	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ bitlbee.conf	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -49,6 +49,11 @@
 ##
 ## 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).
 ##
 # AuthPassword = ItllBeBitlBee   ## Heh.. Our slogan. ;-)
+## or
+# AuthPassword = md5:gzkK0Ox/1xh+1XTsQjXxBJ571Vgl
 
 ## OperPassword
@@ -57,4 +62,6 @@
 ##
 # OperPassword = ChangeMe!
+## or
+# OperPassword = md5:I0mnZbn1t4R731zzRdDN2/pK7lRX
 
 ## HostName
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ bitlbee.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -30,8 +30,8 @@
 
 #define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "1.1.1dev"
+#define BITLBEE_VERSION "1.2"
 #define VERSION BITLBEE_VERSION
 
-#define MAX_STRING 128
+#define MAX_STRING 511
 
 #if HAVE_CONFIG_H
Index: conf.c
===================================================================
--- conf.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ conf.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -252,5 +252,5 @@
 			{
 				g_strfreev( conf->migrate_storage );
-				conf->migrate_storage = g_strsplit( ini->value, " \t,;", -1 );
+				conf->migrate_storage = g_strsplit_set( ini->value, " \t,;", -1 );
 			}
 			else if( g_strcasecmp( ini->key, "pinginterval" ) == 0 )
Index: debian/README.Debian
===================================================================
--- debian/README.Debian	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ debian/README.Debian	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -1,5 +1,5 @@
-                   *** NEWS (Version 1.1 and later) ***
+                   *** NEWS (Version 1.2 and later) ***
 
-Starting from version 1.1, BitlBee has a forking daemon mode. The Debian
+Starting from version 1.2, BitlBee has a forking daemon mode. The Debian
 package now uses this mode by default, instead of inetd mode. If you don't
 want to use this, you can disable the init scripts (best way to do this is
@@ -7,5 +7,19 @@
 should be necessary only once, it won't be touched during upgrades.
 
---------------------------------------------------------------------------
+Another important change in BitlBee 1.2 is the file format used for your
+personal settings. Everything's now saved in a single .xml (per account,
+of course) file instead of $nick.accounts and $nick.nicks. One advantage
+of this new format is that the passwords are actually encrypted instead of
+just vaguely obfuscated. BitlBee can still read the old files, and will
+save things in the new format when you save/disconnect. After that, you
+can safely remove the old-style files (this is recommended).
+
+I tried making this transition (the new file format but especially, in this
+case, the inetd->forkdaemon mode change) as smooth as possible, but I'm
+aware that many BitlBee users will have their own hacks already to run the
+program. I hope the package won't break any of this for anyone. 1.2-2
+should fix at least some of the issues.
+
+---------------------------------------------------------------------------
 
 Debconf should have asked you on what port you want BitlBee to run. If it
Index: debian/bitlbee.init
===================================================================
--- debian/bitlbee.init	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ debian/bitlbee.init	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -1,3 +1,10 @@
 #! /bin/sh
+### BEGIN INIT INFO
+# Provides:          bitlbee
+# Required-Start:    $remote_fs $syslog
+# Required-Stop:     $remote_fs $syslog
+# Default-Start:     2 3 4 5
+# Default-Stop:      1
+### END INIT INFO
 #
 # Init script for BitlBee Debian package. Based on skeleton init script:
@@ -18,5 +25,5 @@
 # Default value
 BITLBEE_PORT=6667
-DAEMON_OPT=-F
+BITLBEE_OPTS=-F
 
 # Read config file if it is present.
@@ -37,6 +44,6 @@
 	
 	start-stop-daemon --start --quiet --pidfile $PIDFILE \
-		-c bitlbee -g nogroup \
-		--exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $DAEMON_OPT
+		-c bitlbee: \
+		--exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $BITLBEE_OPTS
 }
 
Index: debian/changelog
===================================================================
--- debian/changelog	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ debian/changelog	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -1,11 +1,60 @@
-bitlbee (1.1.1dev-0pre) unstable; urgency=low
-
+bitlbee (1.2-4) unstable; urgency=low
+
+  * Not a real release, just a placeholder for the changelog.
+  * Fixed init script to use the BITLBEE_OPTS variable, not an undefined
+    DAEMON_OPT.
+  * Added dependency information to the init script. (Closes: #472567)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 29 Mar 2008 21:10:33 +0000
+
+bitlbee (1.2-3) unstable; urgency=low
+
+  * Removed DEB_BUILD_OPTIONS again (forgot to apply that change to the 1.2
+    branch when I finished 1.0.4-2, things diverged too much anyway.)
+    Closes: #472540.
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 24 Mar 2008 21:10:14 +0000
+
+bitlbee (1.2-2) unstable; urgency=low
+
+  * Fixed some packaging issues reported by IRC and e-mail. (Closes: #472373)
+  * Fixed proxy support. (Closes: #472395)
+  * Added a BitlBee group so only root can edit the configs and BitlBee can
+    just *read* it.
+  * Manually deleting /var/lib/bitlbee/ when purging, deluser doesn't want to
+    do it.
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 24 Mar 2008 19:48:24 +0000
+
+bitlbee (1.2-1) unstable; urgency=low
+
+  * New upstream release. (Closes: #325017, #386914, #437515)
+  * With hopefully completely sane charset handling (Closes: #296145)
   * Switched to the new forking daemon mode. Added /etc/default/bitlbee
     file, an init script. People who want to stick with inetd can do so, see
     the defaults file.
+    (Closes: #460741, #466171, #294585, #345038, #306452, #392682)
   * Got rid of debconf Woody compatibility stuff.
   * No more MPL code in BitlBee, thanks to the Jabber module rewrite!
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Fri, 06 Jul 2007 09:09:36 +0100
+  * Added Italian translation, sorry for taking so long! (Closes: #448238)
+  * Added libevent dependency (more reliable event handling).
+  * Removed GLib 1.x dependency because BitlBee really requires GLib >=2.4.
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Tue, 18 Mar 2008 23:44:19 +0000
+
+bitlbee (1.0.4-2) unstable; urgency=low
+
+  * Removed $DEB_BUILD_OPTIONS because apparently buildds fill it with crap.
+    (Closes: #458717)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 11 Feb 2008 19:15:33 +0000
+
+bitlbee (1.0.4-1) unstable; urgency=low
+
+  * New upstream release.
+  * Changed libnss-dev dependency. (Closes: #370442)
+  * Added build-indep rule to debian/rules. (Closes: #395673)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Wed, 29 Aug 2007 20:24:28 +0100
 
 bitlbee (1.0.3-1.3) unstable; urgency=low
Index: debian/conffiles
===================================================================
--- debian/conffiles	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ debian/conffiles	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -1,2 +1,3 @@
 /etc/bitlbee/motd.txt
 /etc/bitlbee/bitlbee.conf
+/etc/init.d/bitlbee
Index: debian/control
===================================================================
--- debian/control	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ debian/control	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -4,5 +4,5 @@
 Maintainer: Wilmer van der Gaast <wilmer@gaast.net>
 Standards-Version: 3.5.9
-Build-Depends: libglib2.0-dev | libglib-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf
+Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf
 
 Package: bitlbee
Index: debian/po/it.po
===================================================================
--- debian/po/it.po	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
+++ debian/po/it.po	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -0,0 +1,36 @@
+# Italian translation of the bitlbee debconf template
+# This file is distributed under the same license as the bitlbee package
+# Copyright (C) 2007 Free Software Foundation, Inc.
+# Luca Monducci <luca.mo@tiscali.it>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: bitlbee\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-08-30 04:31+0200\n"
+"PO-Revision-Date: 2007-10-27 11:52+0200\n"
+"Last-Translator: Luca Monducci <luca.mo@tiscali.it>\n"
+"Language-Team: Italian <debian-l10n-italian@lists.debian.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: string
+#. Description
+#: ../bitlbee.templates.master:1001
+msgid "On what TCP port should BitlBee listen for connections?"
+msgstr "Su quale porta TCP si deve mettere in ascolto BitlBee?"
+
+#. Type: string
+#. Description
+#: ../bitlbee.templates.master:1001
+msgid ""
+"BitlBee normally listens on the regular IRC port, 6667. This might not be a "
+"very good idea when you're running a real IRC daemon as well. 6668 might be "
+"a good alternative. Leaving this value blank means that BitlBee will not be "
+"run automatically."
+msgstr ""
+"Normalmente BitlBee si mette in ascolto sulla consueta porta IRC, la 6667. "
+"Questa potrebbe non essere una buona idea se è in esecuzione anche un reale "
+"demone IRC. La porta 6668 potrebbe essere una valida alternativa. Lasciando "
+"vuoto questo valore BitlBee non viene avviato automaticamente."
Index: debian/postinst
===================================================================
--- debian/postinst	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ debian/postinst	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -14,9 +14,18 @@
 BITLBEE_DISABLED=0
 BITLBEE_UPGRADE_DONT_RESTART=0
-[ -r /etc/default/bitlbee ] && source /etc/default/bitlbee
+[ -r /etc/default/bitlbee ] && . /etc/default/bitlbee
 
-if [ "$BITLBEE_DISABLED" = "0" ]; then
-	## In case it's still there (if we're upgrading right now)
+if [ "$BITLBEE_DISABLED" = "0" ] && type update-inetd > /dev/null 2> /dev/null &&
+   ( expr "$2" : '0\..*' > /dev/null || expr "$2" : '1\.0\..*' > /dev/null ); then
+	## Make sure the inetd entry is gone (can still be there from a
+	## previous version.
 	update-inetd --remove '.*/usr/sbin/bitlbee'
+	if grep -q /usr/sbin/bitlbee /etc/inetd.conf 2> /dev/null; then
+		# Thanks for breaking update-inetd! (bugs.debian.org/311111)
+		# I hope that it works at least with xinetd, because this
+		# emergency hack doesn't:
+		perl -pi -e 's:^[^#].*/usr/sbin/bitlbee$:## Now using daemon mode\: # $&:' /etc/inetd.conf
+		killall -HUP inetd || true
+	fi
 fi
 
@@ -65,11 +74,19 @@
 fi
 
-adduser --system --home /var/lib/bitlbee/ --disabled-login --disabled-password bitlbee
+adduser --system --group --disabled-login --disabled-password --home /var/lib/bitlbee/ bitlbee
 chmod 700 /var/lib/bitlbee/
 
 ## Can't do this in packaging phase: Don't know the UID yet. Access to
-## the file should be limited, now that it stores passwords.
-chmod 600 /etc/bitlbee/bitlbee.conf
-chown bitlbee /etc/bitlbee/bitlbee.conf
+## the file should be limited, now that it stores passwords. Added
+## --group later for a little more security, but have to see if I can
+## apply this change to existing installations on upgrades. Will think
+## about that later.
+if getent group bitlbee > /dev/null; then
+	chmod 640 /etc/bitlbee/bitlbee.conf
+	chown root:bitlbee /etc/bitlbee/bitlbee.conf
+else
+	chmod 600 /etc/bitlbee/bitlbee.conf
+	chown bitlbee /etc/bitlbee/bitlbee.conf
+fi
 
 if [ -z "$2" ]; then
Index: debian/postrm
===================================================================
--- debian/postrm	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ debian/postrm	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -9,3 +9,6 @@
 
 update-rc.d bitlbee remove > /dev/null 2>&1 || true
-deluser --remove-home bitlbee || true
+rm -f /etc/default/bitlbee
+
+deluser --system --remove-home bitlbee || true
+rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var
Index: debian/rules
===================================================================
--- debian/rules	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ debian/rules	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -3,13 +3,16 @@
 DEBUG ?= 0
 
+ifdef BITLBEE_VERSION
+BITLBEE_FORCE_VERSION=1
+else
 # Want to use the full package version number instead of just the release.
 BITLBEE_VERSION ?= "$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')"
 export BITLBEE_VERSION
-
+endif
 
 build-arch: build-arch-stamp
 build-arch-stamp:
 	if [ ! -d debian ]; then exit 1; fi
-	./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee $(DEB_BUILD_OPTIONS)
+	./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent
 	$(MAKE)
 #	$(MAKE) -C doc/ all
@@ -66,5 +69,5 @@
 		find usr -type f -exec md5sum {} \; > DEBIAN/md5sums
 	dpkg-shlibdeps -Tdebian/bitlbee.substvars -dDepends debian/bitlbee/usr/sbin/bitlbee
-ifdef BITLBEE_VERSION
+ifdef BITLBEE_FORCE_VERSION
 	dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -v1:$(BITLBEE_VERSION)-0 -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0'
 else
Index: doc/CHANGES
===================================================================
--- doc/CHANGES	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ doc/CHANGES	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -1,6 +1,17 @@
+Version 1.2.1:
+- Fixed proxy support.
+- Fixed stalling issues while connecting to Jabber when using the OpenSSL
+  module.
+- Fixed problem with GLib and ForkDaemon where processes didn't die when
+  the client disconnects.
+- Fixed handling of "set charset none". (Which pretty much breaks the account
+  completely in 1.2.)
+- You can now automatically identify yourself to BitlBee by setting a server
+  password in your IRC client.
+- Compatible with all crazy kinds of line endings that clients can send.
+
+Finished ...
+
 Version 1.2:
-- First BitlBee development/testing RELEASE. This should be quite stable
-  though (and for most people more stable than 1.0.x). It just has a couple
-  of rough edges and needs a bit more testing.
 - Added ForkDaemon mode next to the existing Daemon- and inetd modes. With
   ForkDaemon you can run BitlBee as a stand-alone daemon and every connection
@@ -21,7 +32,16 @@
   1.x is so old that supporting it really isn't necessary anymore.
 - Many, many, MANY little changes, improvements, fixes. Using non-blocking
-  I/O as much as possible, fixed lots of little bugs (including bugs that
-  affected daemon mode stability). See the bzr logs for more information.
-- Added units tests, will have to add some more before the real release.
+  I/O as much as possible, replaced the Gaim (0.59, IOW heavily outdated)
+  API, fixed lots of little bugs (including bugs that affected daemon mode
+  stability). See the bzr logs for more information.
+- One of the user-visible changes from the API change: You can finally see
+  all away states/messages properly.
+- Added units tests. Test coverage is very minimal for now.
+- Better charset handling: Everything is just converted from/to UTF-8 right
+  in the IRC core, and charset mismatches are detected (if possible) and the
+  user is asked to resolve this before continuing. Also, UTF-8 is the default
+  setting now, since that's how the world seems to work these days.
+- One can now keep hashed passwords in bitlbee.conf instead of the cleartext
+  version.
 - Most important change: New file format for user data (accounts, nicks and
   settings). Migration to the new format should happen transparently,
@@ -69,6 +89,11 @@
   * An XML console (add xmlconsole to your contact list or see "help set
     xmlconsole" if you want it permanently).
-
-Finished ???
+- The Yahoo! module now says it supports YMSG protocol version 12, which will
+  hopefully keep the Yahoo module working after 2008-04-02 (when Yahoo! is
+  dropping support for version 6.x of their client).
+- MSN switchboard handling changes. Hopefully less messages will get lost now,
+  although things are still not perfect.
+
+Finished 17 Mar 2008
 
 Version 1.0.4:
Index: doc/README
===================================================================
--- doc/README	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ doc/README	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -41,4 +41,18 @@
 by default) and chown it to the UID BitlBee is running as. Make sure this
 directory is read-/writable by this user only.
+
+--- (Fork)Daemon mode
+
+If you don't want to run any inetd daemon, you can run BitlBee in Daemon
+mode. Right now, daemon mode may be a bad idea on servers with multiple
+users, since possible fatal BitlBee bugs will crash the BitlBee process and
+disconnect all connected users at once. Instead, you can use ForkDaemon
+mode, which serves every user from a separate process, without depending on
+an inetd daemon.
+
+To use BitlBee in daemon mode, just start it with the right flags or enable
+it in bitlbee.conf. You probably want to write an init script to start
+BitlBee automatically after a reboot. (This is where you realise using
+a package from your distro would've been a better idea. :-P)
 
 
Index: doc/user-guide/Support.xml
===================================================================
--- doc/user-guide/Support.xml	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ doc/user-guide/Support.xml	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -4,10 +4,11 @@
 
 <sect1>
-<title>BitlBee is beta software</title>
+<title>Disclaimer</title>
 
 <para>
-Although BitlBee has quite some functionality it is still beta. That means it 
-can crash at any time, corrupt your data or whatever. Don't use it in 
-any production environment and don't rely on it.
+BitlBee doesn't come with a warranty and is still (and will probably always
+be) under development. That means it can crash at any time, corrupt your
+data or whatever. Don't use it in any production environment and don't rely
+on it, or at least don't blame us if things blow up. :-)
 </para>
 
Index: doc/user-guide/commands.xml
===================================================================
--- doc/user-guide/commands.xml	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ doc/user-guide/commands.xml	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -163,9 +163,5 @@
 
 			<para>
-				If you want, you can also tell BitlBee what nick to give the new contact. Of course you can also use the <emphasis>rename</emphasis> command for that, but sometimes this might be more convenient.
-			</para>
-			
-			<para>
-				Adding -tmp adds the buddy to the internal BitlBee structures only, not to the real contact list (like done by <emphasis>set handle_unknown add</emphasis>). This allows you to talk to people who are not in your contact list.
+				If you want, you can also tell BitlBee what nick to give the new contact. The -tmp option adds the buddy to the internal BitlBee structures only, not to the real contact list (like done by <emphasis>set handle_unknown add</emphasis>). This allows you to talk to people who are not in your contact list. This normally won't show you any presence notifications.
 			</para>
 		</description>
@@ -383,14 +379,14 @@
 
 	<bitlbee-setting name="charset" type="string" scope="global">
-		<default>iso8859-1</default>
+		<default>utf-8</default>
 		<possible-values>you can get a list of all possible values by doing 'iconv -l' in a shell</possible-values>
 
 		<description>
 			<para>
-				The charset setting enables you to use different character sets in BitlBee. These get converted to UTF-8 before sending and from UTF-8 when receiving.
-			</para>
-
-			<para>
-				If you don't know what's the best value for this, at least iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://czyborra.com/charsets/iso8859.html
+				This setting tells BitlBee what your IRC client sends and expects. It should be equal to the charset setting of your IRC client if you want to be able to send and receive non-ASCII text properly.
+			</para>
+
+			<para>
+				Most systems use UTF-8 these days. On older systems, an iso8859 charset may work better. For example, iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://www.unicodecharacter.com/charsets/iso8859.html
 			</para>
 		</description>
@@ -677,5 +673,5 @@
 		<description>
 			<para>
-				Sends you a /notice when a user starts typing a message (if the protocol supports it, MSN for example). This is a bug, not a feature. (But please don't report it.. ;-) You don't want to use it. Really. In fact the typing-notification is just one of the least useful 'innovations' ever. It's just there because some guy will probably ask me about it anyway. ;-)
+				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>
@@ -830,19 +826,15 @@
 		<short-description>Change friendly name, nick</short-description>
 		<syntax>nick &lt;connection&gt; [&lt;new nick&gt;]</syntax>
-		<syntax>nick</syntax>
-
-		<description>
-			<para>
-				This command allows to set the friendly name of an im account. If no new name is specified the command will report the current name. When the name contains spaces, don't forget to quote the whole nick in double quotes. Currently this command is only supported by the MSN protocol.
-			</para>
-
-			<para>
-				It is recommended to use the per-account <emphasis>display_name</emphasis> setting to read and change this information. The <emphasis>nick</emphasis> command is deprecated.
+		<syntax>nick &lt;connection&gt;</syntax>
+
+		<description>
+			<para>
+				Deprecated: Use the per-account <emphasis>display_name</emphasis> setting to read and change this information.
 			</para>
 		</description>
 
 		<ircexample>
-			<ircline nick="wouter">nick 1 "Wouter Paesen"</ircline>
-			<ircline nick="root">Setting your name on connection 1 to `Wouter Paesen'</ircline>
+			<ircline nick="wouter">account set 1/display_name "The majestik møøse"</ircline>
+			<ircline nick="root">display_name = `The majestik møøse'</ircline>
 		</ircexample>
 
Index: doc/user-guide/misc.xml
===================================================================
--- doc/user-guide/misc.xml	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ doc/user-guide/misc.xml	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -47,8 +47,4 @@
 </variablelist>
 
-<para>
-This list was extracted from <ulink url="http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=">http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=</ulink>.
-</para>
-
 </sect1>
 
@@ -56,5 +52,5 @@
 <title>Groupchats</title>
 <para>
-Since version 0.8x, BitlBee supports groupchats on the MSN and Yahoo! networks. This text will try to explain you how they work.
+BitlBee now supports groupchats on all IM networks. This text will try to explain you how they work.
 </para>
 
@@ -73,5 +69,5 @@
 
 <para>
-If you want to start a groupchat with the person <emphasis>jim_msn</emphasis> in it, just join the channel <emphasis>#jim_msn</emphasis>. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and jim_msn in it.
+If you want to start a groupchat with the person <emphasis>lisa_msn</emphasis> in it, just join the channel <emphasis>#lisa_msn</emphasis>. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and lisa_msn in it.
 </para>
 
@@ -82,21 +78,4 @@
 <para>
 Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>join_chat</emphasis> command to join them. See <emphasis>help join_chat</emphasis> for more information.
-</para>
-
-<para>
-This is all you'll probably need to know. If you have any problems, please read <emphasis>help groupchats3</emphasis>.
-</para>
-
-</sect1>
-
-<sect1 id="groupchats3">
-<title>Groupchat channel names</title>
-
-<para>
-Obviously the (numbered) channel names don't make a lot of sense. Problem is that groupchats usually don't have names at all in the IM-world, while IRC insists on a name. So BitlBee just generates something random, just don't pay attention to it. :-)
-</para>
-
-<para>
-Please also note that BitlBee doesn't support groupchats for all protocols yet. BitlBee will tell you so. Support for other protocols will hopefully come later.
 </para>
 
@@ -121,4 +100,5 @@
 	<member>On the phone, Phone, On phone</member>
 	<member>Out to lunch, Lunch, Food</member>
+	<member>Invisible, Hidden</member>
 </simplelist>
 
@@ -128,5 +108,5 @@
 
 <para>
-You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Protocols like Yahoo! and Jabber will also show this complete away message to your buddies.
+You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Most IM-protocols can also show this additional information to your buddies.
 </para>
 
Index: doc/user-guide/quickstart.xml
===================================================================
--- doc/user-guide/quickstart.xml	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ doc/user-guide/quickstart.xml	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -61,9 +61,9 @@
 
 <para>
-For most protocols (currently MSN, Jabber, Yahoo and AOL) BitlBee can download the contact list automatically from the IM server and all the on-line users should appear in the control channel when you log in.
+Now BitlBee logs in and downloads the contact list from the IM server. In a few seconds, all your on-line buddies should show up in the control channel.
 </para>
 
 <para>
-BitlBee will convert names into irc-friendly form (for instance: tux@example.com will be given the nickname tux). If you have more than one person who would have the same name by this logic (for instance: tux@example.com and tux@bitlbee.org) the second one to log on will be tux_. The same is true if you have a tux log on to AOL and a tux log on from Yahoo.
+BitlBee will convert names into IRC-friendly form (for instance: tux@example.com will be given the nickname tux). If you have more than one person who would have the same name by this logic (for instance: tux@example.com and tux@bitlbee.org) the second one to log on will be tux_. The same is true if you have a tux log on to AOL and a tux log on from Yahoo.
 </para>
 
@@ -127,9 +127,13 @@
 <ircexample>
 	<ircline nick="you">tux: hey, how's the weather down there?</ircline>
-	<ircline nick="tux"> you: a bit chilly!</ircline>
+	<ircline nick="tux">you: a bit chilly!</ircline>
 </ircexample>
 
 <para>
-If you'd rather chat with them in a separate window use the <emphasis>/msg</emphasis> or <emphasis>/query</emphasis> command, just like you would for a private message in IRC.  If you want to have messages automatically come up in private messages rather than in the &amp;bitlbee channel, use the <emphasis>set private</emphasis> command: <emphasis>set private true</emphasis> (<emphasis>set private false</emphasis> to change back).
+Note that, although all contacts are in the &amp;bitlbee channel, only tux will actually receive this message. The &amp;bitlbee channel shouldn't be confused with a real IRC channel.
+</para>
+
+<para>
+If you prefer chatting in a separate window, use the <emphasis>/msg</emphasis> or <emphasis>/query</emphasis> command, just like on real IRC. BitlBee will remember how you talk to someone and show his/her responses the same way. If you want to change the default behaviour (for people you haven't talked to yet), see <emphasis>help set private</emphasis>.
 </para>
 
Index: ipc.c
===================================================================
--- ipc.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ ipc.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -258,128 +258,121 @@
 	else
 	{
+		ipc_master_free_fd( source );
+	}
+	
+	return TRUE;
+}
+
+gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond )
+{
+	char *buf, **cmd;
+	
+	if( ( buf = ipc_readline( source ) ) )
+	{
+		cmd = irc_parse_line( buf );
+		if( cmd )
+			ipc_command_exec( data, cmd, ipc_child_commands );
+	}
+	else
+	{
+		ipc_child_disable();
+	}
+	
+	return TRUE;
+}
+
+void ipc_to_master( char **cmd )
+{
+	if( global.conf->runmode == RUNMODE_FORKDAEMON )
+	{
+		char *s = irc_build_line( cmd );
+		ipc_to_master_str( "%s", s );
+		g_free( s );
+	}
+	else if( global.conf->runmode == RUNMODE_DAEMON )
+	{
+		ipc_command_exec( NULL, cmd, ipc_master_commands );
+	}
+}
+
+void ipc_to_master_str( char *format, ... )
+{
+	char *msg_buf;
+	va_list params;
+
+	va_start( params, format );
+	msg_buf = g_strdup_vprintf( format, params );
+	va_end( params );
+	
+	if( strlen( msg_buf ) > 512 )
+	{
+		/* Don't send it, it's too long... */
+	}
+	else if( global.conf->runmode == RUNMODE_FORKDAEMON )
+	{
+		if( global.listen_socket >= 0 )
+			if( write( global.listen_socket, msg_buf, strlen( msg_buf ) ) <= 0 )
+				ipc_child_disable();
+	}
+	else if( global.conf->runmode == RUNMODE_DAEMON )
+	{
+		char **cmd, *s;
+		
+		if( ( s = strchr( msg_buf, '\r' ) ) )
+			*s = 0;
+		
+		cmd = irc_parse_line( msg_buf );
+		ipc_command_exec( NULL, cmd, ipc_master_commands );
+		g_free( cmd );
+	}
+	
+	g_free( msg_buf );
+}
+
+void ipc_to_children( char **cmd )
+{
+	if( global.conf->runmode == RUNMODE_FORKDAEMON )
+	{
+		char *msg_buf = irc_build_line( cmd );
+		ipc_to_children_str( "%s", msg_buf );
+		g_free( msg_buf );
+	}
+	else if( global.conf->runmode == RUNMODE_DAEMON )
+	{
 		GSList *l;
-		struct bitlbee_child *c;
-		
-		for( l = child_list; l; l = l->next )
+		
+		for( l = irc_connection_list; l; l = l->next )
+			ipc_command_exec( l->data, cmd, ipc_child_commands );
+	}
+}
+
+void ipc_to_children_str( char *format, ... )
+{
+	char *msg_buf;
+	va_list params;
+
+	va_start( params, format );
+	msg_buf = g_strdup_vprintf( format, params );
+	va_end( params );
+	
+	if( strlen( msg_buf ) > 512 )
+	{
+		/* Don't send it, it's too long... */
+	}
+	else if( global.conf->runmode == RUNMODE_FORKDAEMON )
+	{
+		int msg_len = strlen( msg_buf );
+		GSList *l, *next;
+		
+		for( l = child_list; l; l = next )
 		{
-			c = l->data;
-			if( c->ipc_fd == source )
+			struct bitlbee_child *c = l->data;
+			
+			next = l->next;
+			if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 )
 			{
 				ipc_master_free_one( c );
 				child_list = g_slist_remove( child_list, c );
-				break;
 			}
-		}
-	}
-	
-	return TRUE;
-}
-
-gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond )
-{
-	char *buf, **cmd;
-	
-	if( ( buf = ipc_readline( source ) ) )
-	{
-		cmd = irc_parse_line( buf );
-		if( cmd )
-			ipc_command_exec( data, cmd, ipc_child_commands );
-	}
-	else
-	{
-		b_event_remove( global.listen_watch_source_id );
-		close( global.listen_socket );
-		
-		global.listen_socket = -1;
-	}
-	
-	return TRUE;
-}
-
-void ipc_to_master( char **cmd )
-{
-	if( global.conf->runmode == RUNMODE_FORKDAEMON )
-	{
-		char *s = irc_build_line( cmd );
-		ipc_to_master_str( "%s", s );
-		g_free( s );
-	}
-	else if( global.conf->runmode == RUNMODE_DAEMON )
-	{
-		ipc_command_exec( NULL, cmd, ipc_master_commands );
-	}
-}
-
-void ipc_to_master_str( char *format, ... )
-{
-	char *msg_buf;
-	va_list params;
-
-	va_start( params, format );
-	msg_buf = g_strdup_vprintf( format, params );
-	va_end( params );
-	
-	if( strlen( msg_buf ) > 512 )
-	{
-		/* Don't send it, it's too long... */
-	}
-	else if( global.conf->runmode == RUNMODE_FORKDAEMON )
-	{
-		write( global.listen_socket, msg_buf, strlen( msg_buf ) );
-	}
-	else if( global.conf->runmode == RUNMODE_DAEMON )
-	{
-		char **cmd, *s;
-		
-		if( ( s = strchr( msg_buf, '\r' ) ) )
-			*s = 0;
-		
-		cmd = irc_parse_line( msg_buf );
-		ipc_command_exec( NULL, cmd, ipc_master_commands );
-		g_free( cmd );
-	}
-	
-	g_free( msg_buf );
-}
-
-void ipc_to_children( char **cmd )
-{
-	if( global.conf->runmode == RUNMODE_FORKDAEMON )
-	{
-		char *msg_buf = irc_build_line( cmd );
-		ipc_to_children_str( "%s", msg_buf );
-		g_free( msg_buf );
-	}
-	else if( global.conf->runmode == RUNMODE_DAEMON )
-	{
-		GSList *l;
-		
-		for( l = irc_connection_list; l; l = l->next )
-			ipc_command_exec( l->data, cmd, ipc_child_commands );
-	}
-}
-
-void ipc_to_children_str( char *format, ... )
-{
-	char *msg_buf;
-	va_list params;
-
-	va_start( params, format );
-	msg_buf = g_strdup_vprintf( format, params );
-	va_end( params );
-	
-	if( strlen( msg_buf ) > 512 )
-	{
-		/* Don't send it, it's too long... */
-	}
-	else if( global.conf->runmode == RUNMODE_FORKDAEMON )
-	{
-		int msg_len = strlen( msg_buf );
-		GSList *l;
-		
-		for( l = child_list; l; l = l->next )
-		{
-			struct bitlbee_child *c = l->data;
-			write( c->ipc_fd, msg_buf, msg_len );
 		}
 	}
@@ -410,4 +403,21 @@
 }
 
+void ipc_master_free_fd( int fd )
+{
+	GSList *l;
+	struct bitlbee_child *c;
+	
+	for( l = child_list; l; l = l->next )
+	{
+		c = l->data;
+		if( c->ipc_fd == fd )
+		{
+			ipc_master_free_one( c );
+			child_list = g_slist_remove( child_list, c );
+			break;
+		}
+	}
+}
+
 void ipc_master_free_all()
 {
@@ -419,4 +429,12 @@
 	g_slist_free( child_list );
 	child_list = NULL;
+}
+
+void ipc_child_disable()
+{
+	b_event_remove( global.listen_watch_source_id );
+	close( global.listen_socket );
+	
+	global.listen_socket = -1;
 }
 
Index: ipc.h
===================================================================
--- ipc.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ ipc.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -44,5 +44,8 @@
 
 void ipc_master_free_one( struct bitlbee_child *child );
+void ipc_master_free_fd( int fd );
 void ipc_master_free_all();
+
+void ipc_child_disable();
 
 void ipc_to_master( char **cmd );
Index: irc.c
===================================================================
--- irc.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ irc.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -42,4 +42,33 @@
 }
 
+static char *set_eval_charset( set_t *set, char *value )
+{
+	irc_t *irc = set->data;
+	GIConv ic, oc;
+
+	if( g_strcasecmp( value, "none" ) == 0 )
+		value = g_strdup( "utf-8" );
+
+	if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
+	{
+		return NULL;
+	}
+	if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
+	{
+		g_iconv_close( ic );
+		return NULL;
+	}
+	
+	if( irc->iconv != (GIConv) -1 )
+		g_iconv_close( irc->iconv );
+	if( irc->oconv != (GIConv) -1 )
+		g_iconv_close( irc->oconv );
+	
+	irc->iconv = ic;
+	irc->oconv = oc;
+
+	return value;
+}
+
 irc_t *irc_new( int fd )
 {
@@ -64,4 +93,7 @@
 	irc->mynick = g_strdup( ROOT_NICK );
 	irc->channel = g_strdup( ROOT_CHAN );
+	
+	irc->iconv = (GIConv) -1;
+	irc->oconv = (GIConv) -1;
 	
 	if( global.conf->hostname )
@@ -127,4 +159,7 @@
 	conf_loaddefaults( irc );
 	
+	/* Evaluator sets the iconv/oconv structures. */
+	set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
+	
 	return( irc );
 }
@@ -185,7 +220,6 @@
 
 /* Because we have no garbage collection, this is quite annoying */
-void irc_free(irc_t * irc)
-{
-	account_t *account;
+void irc_free( irc_t * irc )
+{
 	user_t *user, *usertmp;
 	
@@ -196,5 +230,45 @@
 			irc_usermsg( irc, "Error while saving settings!" );
 	
-	closesocket( irc->fd );
+	irc_connection_list = g_slist_remove( irc_connection_list, irc );
+	
+	while( irc->accounts )
+	{
+		if( irc->accounts->ic )
+			imc_logout( irc->accounts->ic, FALSE );
+		else if( irc->accounts->reconnect )
+			cancel_auto_reconnect( irc->accounts );
+		
+		if( irc->accounts->ic == NULL )
+			account_del( irc, irc->accounts );
+		else
+			/* Nasty hack, but account_del() doesn't work in this
+			   case and we don't want infinite loops, do we? ;-) */
+			irc->accounts = irc->accounts->next;
+	}
+	
+	while( irc->queries != NULL )
+		query_del( irc, irc->queries );
+	
+	while( irc->set )
+		set_del( &irc->set, irc->set->key );
+	
+	if (irc->users != NULL)
+	{
+		user = irc->users;
+		while( user != NULL )
+		{
+			g_free( user->nick );
+			g_free( user->away );
+			g_free( user->handle );
+			if( user->user != user->nick ) g_free( user->user );
+			if( user->host != user->nick ) g_free( user->host );
+			if( user->realname != user->nick ) g_free( user->realname );
+			b_event_remove( user->sendbuf_timer );
+					
+			usertmp = user;
+			user = user->next;
+			g_free( usertmp );
+		}
+	}
 	
 	if( irc->ping_source_id > 0 )
@@ -204,66 +278,35 @@
 		b_event_remove( irc->w_watch_source_id );
 	
-	irc_connection_list = g_slist_remove( irc_connection_list, irc );
-	
-	for (account = irc->accounts; account; account = account->next) {
-		if (account->ic) {
-			imc_logout(account->ic, TRUE);
-		} else if (account->reconnect) {
-			cancel_auto_reconnect(account);
-		}
-	}
-	
-	g_free(irc->sendbuffer);
-	g_free(irc->readbuffer);
-	
-	g_free(irc->nick);
-	g_free(irc->user);
-	g_free(irc->host);
-	g_free(irc->realname);
-	g_free(irc->password);
-	
-	g_free(irc->myhost);
-	g_free(irc->mynick);
-	
-	g_free(irc->channel);
-	
-	while (irc->queries != NULL)
-		query_del(irc, irc->queries);
-	
-	while (irc->accounts)
-		if (irc->accounts->ic == NULL)
-			account_del(irc, irc->accounts);
-		else
-			/* Nasty hack, but account_del() doesn't work in this
-			   case and we don't want infinite loops, do we? ;-) */
-			irc->accounts = irc->accounts->next;
-	
-	while (irc->set)
-		set_del(&irc->set, irc->set->key);
-	
-	if (irc->users != NULL) {
-		user = irc->users;
-		while (user != NULL) {
-			g_free(user->nick);
-			g_free(user->away);
-			g_free(user->handle);
-			if(user->user!=user->nick) g_free(user->user);
-			if(user->host!=user->nick) g_free(user->host);
-			if(user->realname!=user->nick) g_free(user->realname);
-			b_event_remove(user->sendbuf_timer);
-					
-			usertmp = user;
-			user = user->next;
-			g_free(usertmp);
-		}
-	}
-	
-	g_hash_table_foreach_remove(irc->userhash, irc_free_hashkey, NULL);
-	g_hash_table_destroy(irc->userhash);
-	
-	g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);
-	g_hash_table_destroy(irc->watches);
-	
-	g_free(irc);
+	closesocket( irc->fd );
+	irc->fd = -1;
+	
+	g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
+	g_hash_table_destroy( irc->userhash );
+	
+	g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
+	g_hash_table_destroy( irc->watches );
+	
+	if( irc->iconv != (GIConv) -1 )
+		g_iconv_close( irc->iconv );
+	if( irc->oconv != (GIConv) -1 )
+		g_iconv_close( irc->oconv );
+	
+	g_free( irc->sendbuffer );
+	g_free( irc->readbuffer );
+	
+	g_free( irc->nick );
+	g_free( irc->user );
+	g_free( irc->host );
+	g_free( irc->realname );
+	g_free( irc->password );
+	
+	g_free( irc->myhost );
+	g_free( irc->mynick );
+	
+	g_free( irc->channel );
+	
+	g_free( irc->last_target );
+	
+	g_free( irc );
 	
 	if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON )
@@ -286,5 +329,5 @@
 void irc_process( irc_t *irc )
 {
-	char **lines, *temp, **cmd, *cs;
+	char **lines, *temp, **cmd;
 	int i;
 
@@ -295,9 +338,8 @@
 		for( i = 0; *lines[i] != '\0'; i ++ )
 		{
-			char conv[IRC_MAX_LINE+1];
+			char *conv = NULL;
 			
-			/* [WvG] Because irc_tokenize splits at every newline, the lines[] list
-			    should end with an empty string. This is why this actually works.
-			    Took me a while to figure out, Maurits. :-P */
+			/* [WvG] If the last line isn't empty, it's an incomplete line and we
+			   should wait for the rest to come in before processing it. */
 			if( lines[i+1] == NULL )
 			{
@@ -309,8 +351,12 @@
 			}
 			
-			if( ( cs = set_getstr( &irc->set, "charset" ) ) )
-			{
-				conv[IRC_MAX_LINE] = 0;
-				if( do_iconv( cs, "UTF-8", lines[i], conv, 0, IRC_MAX_LINE - 2 ) == -1 )
+			if( irc->iconv != (GIConv) -1 )
+			{
+				gsize bytes_read, bytes_written;
+				
+				conv = g_convert_with_iconv( lines[i], -1, irc->iconv,
+				                             &bytes_read, &bytes_written, NULL );
+				
+				if( conv == NULL || bytes_read != strlen( lines[i] ) )
 				{
 					/* GLib can do strange things if things are not in the expected charset,
@@ -324,13 +370,16 @@
 						                  "expect by changing the charset setting. See "
 						                  "`help set charset' for more information. Your "
-						                  "message was ignored.", cs );
-						*conv = 0;
+						                  "message was ignored.",
+						                  set_getstr( &irc->set, "charset" ) );
+						
+						g_free( conv );
+						conv = NULL;
 					}
 					else
 					{
 						irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
-						           "Warning: invalid (non-UTF8) characters received at login time." );
+						           "Warning: invalid characters received at login time." );
 						
-						strncpy( conv, lines[i], IRC_MAX_LINE );
+						conv = g_strdup( lines[i] );
 						for( temp = conv; *temp; temp ++ )
 							if( *temp & 0x80 )
@@ -341,9 +390,13 @@
 			}
 			
-			if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
-				continue;
-			irc_exec( irc, cmd );
+			if( lines[i] )
+			{
+				if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
+					continue;
+				irc_exec( irc, cmd );
+				g_free( cmd );
+			}
 			
-			g_free( cmd );
+			g_free( conv );
 			
 			/* Shouldn't really happen, but just in case... */
@@ -369,40 +422,39 @@
 char **irc_tokenize( char *buffer )
 {
-	int i, j;
+	int i, j, n = 3;
 	char **lines;
 
-	/* Count the number of elements we're gonna need. */
-	for( i = 0, j = 1; buffer[i] != '\0'; i ++ )
-	{
-		if( buffer[i] == '\n' )
-			if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
-				j ++;
-	}
-	
-	/* Allocate j+1 elements. */
-	lines = g_new( char *, j + 1 );
+	/* Allocate n+1 elements. */
+	lines = g_new( char *, n + 1 );
+	
+	lines[0] = buffer;
+	
+	/* Split the buffer in several strings, and accept any kind of line endings,
+	 * knowing that ERC on Windows may send something interesting like \r\r\n,
+	 * and surely there must be clients that think just \n is enough... */
+	for( i = 0, j = 0; buffer[i] != '\0'; i ++ )
+	{
+		if( buffer[i] == '\r' || buffer[i] == '\n' )
+		{
+			while( buffer[i] == '\r' || buffer[i] == '\n' )
+				buffer[i++] = '\0';
+			
+			lines[++j] = buffer + i;
+			
+			if( j >= n )
+			{
+				n *= 2;
+				lines = g_renew( char *, lines, n + 1 );
+			}
+
+			if( buffer[i] == '\0' )
+				break;
+		}
+	}
 	
 	/* NULL terminate our list. */ 
-	lines[j] = NULL;
-	
-	lines[0] = buffer;
-	
-	/* Split the buffer in several strings, using \r\n as our seperator, where \r is optional.
-	 * Although this is not in the RFC, some braindead ircds (newnet's) use this, so some clients might too. 
-	 */
-	for( i = 0, j = 0; buffer[i] != '\0'; i ++)
-	{
-		if( buffer[i] == '\n' )
-		{
-			buffer[i] = '\0';
-			
-			if( i > 0 && buffer[i-1] == '\r' )
-				buffer[i-1] = '\0';
-			if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
-				lines[++j] = buffer + i + 1;
-		}
-	}
-	
-	return( lines );
+	lines[++j] = NULL;
+	
+	return lines;
 }
 
@@ -538,5 +590,4 @@
 
 	return;
-
 }
 
@@ -544,5 +595,5 @@
 {
 	int size;
-	char line[IRC_MAX_LINE+1], *cs;
+	char line[IRC_MAX_LINE+1];
 		
 	/* Don't try to write anything new anymore when shutting down. */
@@ -550,17 +601,22 @@
 		return;
 	
-	line[IRC_MAX_LINE] = 0;
+	memset( line, 0, sizeof( line ) );
 	g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
-	
 	strip_newlines( line );
-	if( ( cs = set_getstr( &irc->set, "charset" ) ) && ( g_strcasecmp( cs, "utf-8" ) != 0 ) )
-	{
-		char conv[IRC_MAX_LINE+1];
-		
-		conv[IRC_MAX_LINE] = 0;
-		if( do_iconv( "UTF-8", cs, line, conv, 0, IRC_MAX_LINE - 2 ) != -1 )
-			strcpy( line, conv );
-	}
-	strcat( line, "\r\n" );
+	
+	if( irc->oconv != (GIConv) -1 )
+	{
+		gsize bytes_read, bytes_written;
+		char *conv;
+		
+		conv = g_convert_with_iconv( line, -1, irc->oconv,
+		                             &bytes_read, &bytes_written, NULL );
+
+		if( bytes_read == strlen( line ) )
+			strncpy( line, conv, IRC_MAX_LINE - 2 );
+		
+		g_free( conv );
+	}
+	g_strlcat( line, "\r\n", IRC_MAX_LINE + 1 );
 	
 	if( irc->sendbuffer != NULL )
@@ -735,5 +791,10 @@
 	irc_spawn( irc, u );
 	
-	irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\nIf you've never used BitlBee before, please do read the help information using the \x02help\x02 command. Lots of FAQs are answered there." );
+	irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
+	                  "If you've never used BitlBee before, please do read the help "
+	                  "information using the \x02help\x02 command. Lots of FAQs are "
+	                  "answered there.\n"
+	                  "If you already have an account on this server, just use the "
+	                  "\x02identify\x02 command to identify yourself." );
 	
 	if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
@@ -741,4 +802,14 @@
 	
 	irc->status |= USTATUS_LOGGED_IN;
+	
+	/* This is for bug #209 (use PASS to identify to NickServ). */
+	if( irc->password != NULL )
+	{
+		char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
+		
+		irc_setpass( irc, NULL );
+		root_command( irc, send_cmd );
+		g_free( send_cmd[1] );
+	}
 }
 
Index: irc.h
===================================================================
--- irc.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ irc.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -61,4 +61,5 @@
 	char *sendbuffer;
 	char *readbuffer;
+	GIConv iconv, oconv;
 
 	int sentbytes;
@@ -69,5 +70,7 @@
 	char *host;
 	char *realname;
-	char *password;
+	char *password; /* HACK: Used to save the user's password, but before
+	                   logging in, this may contain a password we should
+	                   send to identify after USER/NICK are received. */
 
 	char umode[8];
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ irc_commands.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -30,12 +30,33 @@
 static void irc_cmd_pass( irc_t *irc, char **cmd )
 {
-	if( global.conf->auth_pass && strcmp( cmd[1], global.conf->auth_pass ) == 0 )
+	if( irc->status & USTATUS_LOGGED_IN )
+	{
+		char *send_cmd[] = { "identify", cmd[1], NULL };
+		
+		/* We're already logged in, this client seems to send the PASS
+		   command last. (Possibly it won't send it at all if it turns
+		   out we don't require it, which will break this feature.)
+		   Try to identify using the given password. */
+		return root_command( irc, send_cmd );
+	}
+	/* Handling in pre-logged-in state, first see if this server is
+	   password-protected: */
+	else if( global.conf->auth_pass &&
+	    ( strncmp( global.conf->auth_pass, "md5:", 4 ) == 0 ?
+	        md5_verify_password( cmd[1], global.conf->auth_pass + 4 ) == 0 :
+	        strcmp( cmd[1], global.conf->auth_pass ) == 0 ) )
 	{
 		irc->status |= USTATUS_AUTHORIZED;
 		irc_check_login( irc );
 	}
-	else
+	else if( global.conf->auth_pass )
 	{
 		irc_reply( irc, 464, ":Incorrect password" );
+	}
+	else
+	{
+		/* Remember the password and try to identify after USER/NICK. */
+		irc_setpass( irc, cmd[1] );
+		irc_check_login( irc );
 	}
 }
@@ -88,5 +109,8 @@
 static void irc_cmd_oper( irc_t *irc, char **cmd )
 {
-	if( global.conf->oper_pass && strcmp( cmd[2], global.conf->oper_pass ) == 0 )
+	if( global.conf->oper_pass &&
+	    ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
+	        md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
+	        strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
 	{
 		irc_umode_set( irc, "+o", 1 );
@@ -254,6 +278,5 @@
 			if( cmd[1] != irc->last_target )
 			{
-				if( irc->last_target )
-					g_free( irc->last_target );
+				g_free( irc->last_target );
 				irc->last_target = g_strdup( cmd[1] );
 			}
@@ -575,5 +598,5 @@
 
 static const command_t irc_commands[] = {
-	{ "pass",        1, irc_cmd_pass,        IRC_CMD_PRE_LOGIN },
+	{ "pass",        1, irc_cmd_pass,        0 },
 	{ "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
 	{ "nick",        1, irc_cmd_nick,        0 },
Index: lib/Makefile
===================================================================
--- lib/Makefile	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/Makefile	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -10,5 +10,5 @@
 
 # [SH] Program variables
-objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o
+objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
 
 CFLAGS += -Wall
Index: lib/arc.c
===================================================================
--- lib/arc.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/arc.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -131,15 +131,37 @@
    
    Both functions return the number of bytes in the result string.
+   
+   Note that if you use the pad_to argument, you will need zero-termi-
+   nation to find back the original string length after decryption. So
+   it shouldn't be used if your string contains \0s by itself!
 */
 
-int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password )
+int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to )
 {
 	struct arc_state *st;
 	unsigned char *key;
-	int key_len, i;
+	char *padded = NULL;
+	int key_len, i, padded_len;
 	
 	key_len = strlen( password ) + ARC_IV_LEN;
 	if( clear_len <= 0 )
 		clear_len = strlen( clear );
+	
+	/* Pad the string to the closest multiple of pad_to. This makes it
+	   impossible to see the exact length of the password. */
+	if( pad_to > 0 && ( clear_len % pad_to ) > 0 )
+	{
+		padded_len = clear_len + pad_to - ( clear_len % pad_to );
+		padded = g_malloc( padded_len );
+		memcpy( padded, clear, clear_len );
+		
+		/* First a \0 and then random data, so we don't have to do
+		   anything special when decrypting. */
+		padded[clear_len] = 0;
+		random_bytes( (unsigned char*) padded + clear_len + 1, padded_len - clear_len - 1 );
+		
+		clear = padded;
+		clear_len = padded_len;
+	}
 	
 	/* Prepare buffers and the key + IV */
@@ -161,4 +183,5 @@
 	
 	g_free( st );
+	g_free( padded );
 	
 	return clear_len + ARC_IV_LEN;
Index: lib/arc.h
===================================================================
--- lib/arc.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/arc.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -31,6 +31,6 @@
 };
 
-struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );
+G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );
 unsigned char arc_getbyte( struct arc_state *st );
-int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password );
+int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to );
 int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password );
Index: lib/base64.c
===================================================================
--- lib/base64.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/base64.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -118,5 +118,5 @@
 	int i, outlen = 0;
 	
-	for( i = 0; in[i]; i += 4 )
+	for( i = 0; in[i] && in[i+1] && in[i+2] && in[i+3]; i += 4 )
 	{
 		int sx;
Index: lib/events_glib.c
===================================================================
--- lib/events_glib.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/events_glib.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -51,9 +51,10 @@
 } GaimIOClosure;
 
-static GMainLoop *loop;
+static GMainLoop *loop = NULL;
 
 void b_main_init()
 {
-	loop = g_main_new( FALSE );
+	if( loop == NULL )
+		loop = g_main_new( FALSE );
 }
 
Index: lib/misc.c
===================================================================
--- lib/misc.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/misc.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -33,4 +33,5 @@
 #define BITLBEE_CORE
 #include "nogaim.h"
+#include "base64.h"
 #include <stdio.h>
 #include <stdlib.h>
@@ -597,2 +598,42 @@
 		return sockerr_again();
 }
+
+/* Returns values: -1 == Failure (base64-decoded to something unexpected)
+                    0 == Okay
+                    1 == Password doesn't match the hash. */
+int md5_verify_password( char *password, char *hash )
+{
+	md5_byte_t *pass_dec = NULL;
+	md5_byte_t pass_md5[16];
+	md5_state_t md5_state;
+	int ret, i;
+	
+	if( base64_decode( hash, &pass_dec ) != 21 )
+	{
+		ret = -1;
+	}
+	else
+	{
+		md5_init( &md5_state );
+		md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
+		md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
+		md5_finish( &md5_state, pass_md5 );
+		
+		for( i = 0; i < 16; i ++ )
+		{
+			if( pass_dec[i] != pass_md5[i] )
+			{
+				ret = 1;
+				break;
+			}
+		}
+		
+		/* If we reached the end of the loop, it was a match! */
+		if( i == 16 )
+			ret = 0;
+	}
+	
+	g_free( pass_dec );
+
+	return ret;
+}
Index: lib/misc.h
===================================================================
--- lib/misc.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/misc.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -67,3 +67,5 @@
 G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl );
 
+G_MODULE_EXPORT int md5_verify_password( char *password, char *hash );
+
 #endif
Index: lib/proxy.c
===================================================================
--- lib/proxy.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/proxy.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -530,5 +530,5 @@
 	struct PHB *phb;
 	
-	if (!host || !port || (port == -1) || !func || strlen(host) > 128) {
+	if (!host || port <= 0 || !func || strlen(host) > 128) {
 		return -1;
 	}
@@ -538,5 +538,5 @@
 	phb->data = data;
 	
-	if ((proxytype == PROXY_NONE) || strlen(proxyhost) > 0 || !proxyport || (proxyport == -1))
+	if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0)
 		return proxy_connect_none(host, port, phb);
 	else if (proxytype == PROXY_HTTP)
Index: lib/ssl_client.h
===================================================================
--- lib/ssl_client.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/ssl_client.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -60,4 +60,7 @@
 G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len );
 
+/* See ssl_openssl.c for an explanation. */
+G_MODULE_EXPORT int ssl_pending( void *conn );
+
 /* Abort the SSL connection and disconnect the socket. Do not use close()
    directly, both the SSL library and the peer will be unhappy! */
Index: lib/ssl_gnutls.c
===================================================================
--- lib/ssl_gnutls.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/ssl_gnutls.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -211,4 +211,10 @@
 }
 
+/* See ssl_openssl.c for an explanation. */
+int ssl_pending( void *conn )
+{
+	return 0;
+}
+
 void ssl_disconnect( void *conn_ )
 {
Index: lib/ssl_nss.c
===================================================================
--- lib/ssl_nss.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/ssl_nss.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -169,4 +169,10 @@
 }
 
+/* See ssl_openssl.c for an explanation. */
+int ssl_pending( void *conn )
+{
+	return 0;
+}
+
 void ssl_disconnect( void *conn_ )
 {
Index: lib/ssl_openssl.c
===================================================================
--- lib/ssl_openssl.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/ssl_openssl.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -62,13 +62,13 @@
 	
 	conn->fd = proxy_connect( host, port, ssl_connected, conn );
+	if( conn->fd < 0 )
+	{
+		g_free( conn );
+		return NULL;
+	}
+	
 	conn->func = func;
 	conn->data = data;
 	conn->inpa = -1;
-	
-	if( conn->fd < 0 )
-	{
-		g_free( conn );
-		return NULL;
-	}
 	
 	return conn;
@@ -231,4 +231,19 @@
 }
 
+/* Only OpenSSL *really* needs this (and well, maybe NSS). See for more info:
+   http://www.gnu.org/software/gnutls/manual/gnutls.html#index-gnutls_005frecord_005fcheck_005fpending-209
+   http://www.openssl.org/docs/ssl/SSL_pending.html
+   
+   Required because OpenSSL empties the TCP buffer completely but doesn't
+   necessarily give us all the unencrypted data.
+   
+   Returns 0 if there's nothing left or if we don't have to care (GnuTLS),
+   1 if there's more data. */
+int ssl_pending( void *conn )
+{
+	return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ?
+	       SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0;
+}
+
 void ssl_disconnect( void *conn_ )
 {
Index: lib/url.c
===================================================================
--- lib/url.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/url.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -26,11 +26,14 @@
 #include "url.h"
 
-/* Convert an URL to a url_t structure					*/
+/* Convert an URL to a url_t structure */
 int url_set( url_t *url, char *set_url )
 {
-	char s[MAX_STRING];
+	char s[MAX_STRING+1];
 	char *i;
 	
-	/* protocol://							*/
+	memset( url, 0, sizeof( url_t ) );
+	memset( s, 0, sizeof( s ) );
+	
+	/* protocol:// */
 	if( ( i = strstr( set_url, "://" ) ) == NULL )
 	{
@@ -49,11 +52,10 @@
 			url->proto = PROTO_SOCKS5;
 		else
-		{
-			return( 0 );
-		}
+			return 0;
+		
 		strncpy( s, i + 3, MAX_STRING );
 	}
 	
-	/* Split							*/
+	/* Split */
 	if( ( i = strchr( s, '/' ) ) == NULL )
 	{
@@ -67,5 +69,5 @@
 	strncpy( url->host, s, MAX_STRING );
 	
-	/* Check for username in host field				*/
+	/* Check for username in host field */
 	if( strrchr( url->host, '@' ) != NULL )
 	{
@@ -76,5 +78,5 @@
 		*url->pass = 0;
 	}
-	/* If not: Fill in defaults					*/
+	/* If not: Fill in defaults */
 	else
 	{
@@ -82,5 +84,5 @@
 	}
 	
-	/* Password?							*/
+	/* Password? */
 	if( ( i = strchr( url->user, ':' ) ) != NULL )
 	{
@@ -88,5 +90,5 @@
 		strcpy( url->pass, i + 1 );
 	}
-	/* Port number?							*/
+	/* Port number? */
 	if( ( i = strchr( url->host, ':' ) ) != NULL )
 	{
Index: lib/url.h
===================================================================
--- lib/url.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ lib/url.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -26,9 +26,9 @@
 #include "bitlbee.h"
 
-#define PROTO_HTTP		2
-#define PROTO_HTTPS		5
-#define PROTO_SOCKS4	3
-#define PROTO_SOCKS5	4
-#define PROTO_DEFAULT	PROTO_HTTP
+#define PROTO_HTTP      2
+#define PROTO_HTTPS     5
+#define PROTO_SOCKS4    3
+#define PROTO_SOCKS5    4
+#define PROTO_DEFAULT   PROTO_HTTP
 
 typedef struct url
@@ -36,8 +36,8 @@
 	int proto;
 	int port;
-	char host[MAX_STRING];
-	char file[MAX_STRING];
-	char user[MAX_STRING];
-	char pass[MAX_STRING];
+	char host[MAX_STRING+1];
+	char file[MAX_STRING+1];
+	char user[MAX_STRING+1];
+	char pass[MAX_STRING+1];
 } url_t;
 
Index: lib/xmltree.c
===================================================================
--- lib/xmltree.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
+++ lib/xmltree.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -0,0 +1,590 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
+*                                                                           *
+*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library is distributed in the hope that it will be useful,          *
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "xmltree.h"
+
+static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
+{
+	struct xt_parser *xt = data;
+	struct xt_node *node = g_new0( struct xt_node, 1 ), *nt;
+	int i;
+	
+	node->parent = xt->cur;
+	node->name = g_strdup( element_name );
+	
+	/* First count the number of attributes */
+	for( i = 0; attr_names[i]; i ++ );
+	
+	/* Then allocate a NULL-terminated array. */
+	node->attr = g_new0( struct xt_attr, i + 1 );
+	
+	/* And fill it, saving one variable by starting at the end. */
+	for( i --; i >= 0; i -- )
+	{
+		node->attr[i].key = g_strdup( attr_names[i] );
+		node->attr[i].value = g_strdup( attr_values[i] );
+	}
+	
+	/* Add it to the linked list of children nodes, if we have a current
+	   node yet. */
+	if( xt->cur )
+	{
+		if( xt->cur->children )
+		{
+			for( nt = xt->cur->children; nt->next; nt = nt->next );
+			nt->next = node;
+		}
+		else
+		{
+			xt->cur->children = node;
+		}
+	}
+	else if( xt->root )
+	{
+		/* ERROR situation: A second root-element??? */
+	}
+	
+	/* Now this node will be the new current node. */
+	xt->cur = node;
+	/* And maybe this is the root? */
+	if( xt->root == NULL )
+		xt->root = node;
+}
+
+static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
+{
+	struct xt_parser *xt = data;
+	struct xt_node *node = xt->cur;
+	
+	if( node == NULL )
+		return;
+	
+	/* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */
+	node->text = g_renew( char, node->text, node->text_len + text_len + 1 );
+	memcpy( node->text + node->text_len, text, text_len );
+	node->text_len += text_len;
+	/* Zero termination is always nice to have. */
+	node->text[node->text_len] = 0;
+}
+
+static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
+{
+	struct xt_parser *xt = data;
+	
+	xt->cur->flags |= XT_COMPLETE;
+	xt->cur = xt->cur->parent;
+}
+
+GMarkupParser xt_parser_funcs =
+{
+	xt_start_element,
+	xt_end_element,
+	xt_text,
+	NULL,
+	NULL
+};
+
+struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data )
+{
+	struct xt_parser *xt = g_new0( struct xt_parser, 1 );
+	
+	xt->data = data;
+	xt->handlers = handlers;
+	xt_reset( xt );
+	
+	return xt;
+}
+
+/* Reset the parser, flush everything we have so far. For example, we need
+   this for XMPP when doing TLS/SASL to restart the stream. */
+void xt_reset( struct xt_parser *xt )
+{
+	if( xt->parser )
+		g_markup_parse_context_free( xt->parser );
+	
+	xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL );
+	
+	if( xt->root )
+	{
+		xt_free_node( xt->root );
+		xt->root = NULL;
+		xt->cur = NULL;
+	}
+}
+
+/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
+   end-of-stream and 1 otherwise. */
+int xt_feed( struct xt_parser *xt, char *text, int text_len )
+{
+	if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
+	{
+		return -1;
+	}
+	
+	return !( xt->root && xt->root->flags & XT_COMPLETE );
+}
+
+/* Find completed nodes and see if a handler has to be called. Passing
+   a node isn't necessary if you want to start at the root, just pass
+   NULL. This second argument is needed for recursive calls. */
+int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
+{
+	struct xt_node *c;
+	xt_status st;
+	int i;
+	
+	/* Just in case someone likes infinite loops... */
+	if( xt->root == NULL )
+		return 0;
+	
+	if( node == NULL )
+		return xt_handle( xt, xt->root, depth );
+	
+	if( depth != 0 )
+		for( c = node->children; c; c = c->next )
+			if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) )
+				return 0;
+	
+	if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
+	{
+		for( i = 0; xt->handlers[i].func; i ++ )
+		{
+			/* This one is fun! \o/ */
+			
+						/* If handler.name == NULL it means it should always match. */
+			if( ( xt->handlers[i].name == NULL || 
+						/* If it's not, compare. There should always be a name. */
+			      g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
+						/* If handler.parent == NULL, it's a match. */
+			    ( xt->handlers[i].parent == NULL ||
+						/* If there's a parent node, see if the name matches. */
+			      ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
+						/* If there's no parent, the handler should mention <root> as a parent. */
+			                       g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
+			{
+				st = xt->handlers[i].func( node, xt->data );
+				
+				if( st == XT_ABORT )
+					return 0;
+				else if( st != XT_NEXT )
+					break;
+			}
+		}
+		
+		node->flags |= XT_SEEN;
+	}
+	
+	return 1;
+}
+
+/* Garbage collection: Cleans up all nodes that are handled. Useful for
+   streams because there's no reason to keep a complete packet history
+   in memory. */
+void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
+{
+	struct xt_node *c, *prev;
+	
+	if( !xt || !xt->root )
+		return;
+	
+	if( node == NULL )
+		return xt_cleanup( xt, xt->root, depth );
+	
+	if( node->flags & XT_SEEN && node == xt->root )
+	{
+		xt_free_node( xt->root );
+		xt->root = xt->cur = NULL;
+		/* xt->cur should be NULL already, BTW... */
+		
+		return;
+	}
+	
+	/* c contains the current node, prev the previous node (or NULL).
+	   I admit, this one's pretty horrible. */
+	for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
+	{
+		if( c->flags & XT_SEEN )
+		{
+			/* Remove the node from the linked list. */
+			if( prev )
+				prev->next = c->next;
+			else
+				node->children = c->next;
+			
+			xt_free_node( c );
+			
+			/* Since the for loop wants to get c->next, make sure
+			   c points at something that exists (and that c->next
+			   will actually be the next item we should check). c
+			   can be NULL now, if we just removed the first item.
+			   That explains the ? thing in for(). */
+			c = prev;
+		}
+		else
+		{
+			/* This node can't be cleaned up yet, but maybe a
+			   subnode can. */
+			if( depth != 0 )
+				xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
+		}
+	}
+}
+
+static void xt_to_string_real( struct xt_node *node, GString *str )
+{
+	char *buf;
+	struct xt_node *c;
+	int i;
+	
+	g_string_append_printf( str, "<%s", node->name );
+	
+	for( i = 0; node->attr[i].key; i ++ )
+	{
+		buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
+		g_string_append( str, buf );
+		g_free( buf );
+	}
+	
+	if( node->text == NULL && node->children == NULL )
+	{
+		g_string_append( str, "/>" );
+		return;
+	}
+	
+	g_string_append( str, ">" );
+	if( node->text_len > 0 )
+	{
+		buf = g_markup_escape_text( node->text, node->text_len );
+		g_string_append( str, buf );
+		g_free( buf );
+	}
+	
+	for( c = node->children; c; c = c->next )
+		xt_to_string_real( c, str );
+	
+	g_string_append_printf( str, "</%s>", node->name );
+}
+
+char *xt_to_string( struct xt_node *node )
+{
+	GString *ret;
+	char *real;
+	
+	ret = g_string_new( "" );
+	xt_to_string_real( node, ret );
+	
+	real = ret->str;
+	g_string_free( ret, FALSE );
+	
+	return real;
+}
+
+#ifdef DEBUG
+void xt_print( struct xt_node *node )
+{
+	int i;
+	struct xt_node *c;
+	
+	/* Indentation */
+	for( c = node; c->parent; c = c->parent )
+		printf( "\t" );
+	
+	/* Start the tag */
+	printf( "<%s", node->name );
+	
+	/* Print the attributes */
+	for( i = 0; node->attr[i].key; i ++ )
+		printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) );
+	
+	/* /> in case there's really *nothing* inside this tag, otherwise
+	   just >. */
+	/* If this tag doesn't have any content at all... */
+	if( node->text == NULL && node->children == NULL )
+	{
+		printf( "/>\n" );
+		return;
+		/* Then we're finished! */
+	}
+	
+	/* Otherwise... */
+	printf( ">" );
+	
+	/* Only print the text if it contains more than whitespace (TEST). */
+	if( node->text_len > 0 )
+	{
+		for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
+		if( node->text[i] )
+			printf( "%s", g_markup_escape_text( node->text, -1 ) );
+	}
+	
+	if( node->children )
+		printf( "\n" );
+	
+	for( c = node->children; c; c = c->next )
+		xt_print( c );
+	
+	if( node->children )
+		for( c = node; c->parent; c = c->parent )
+			printf( "\t" );
+	
+	/* Non-empty tag is now finished. */
+	printf( "</%s>\n", node->name );
+}
+#endif
+
+struct xt_node *xt_dup( struct xt_node *node )
+{
+	struct xt_node *dup = g_new0( struct xt_node, 1 );
+	struct xt_node *c, *dc = NULL;
+	int i;
+	
+	/* Let's NOT copy the parent element here BTW! Only do it for children. */
+	
+	dup->name = g_strdup( node->name );
+	dup->flags = node->flags;
+	if( node->text )
+	{
+		dup->text = g_memdup( node->text, node->text_len + 1 );
+		dup->text_len = node->text_len;
+	}
+	
+	/* Count the number of attributes and allocate the new array. */
+	for( i = 0; node->attr[i].key; i ++ );
+	dup->attr = g_new0( struct xt_attr, i + 1 );
+	
+	/* Copy them all! */
+	for( i --; i >= 0; i -- )
+	{
+		dup->attr[i].key = g_strdup( node->attr[i].key );
+		dup->attr[i].value = g_strdup( node->attr[i].value );
+	}
+	
+	/* This nice mysterious loop takes care of the children. */
+	for( c = node->children; c; c = c->next )
+	{
+		if( dc == NULL )
+			dc = dup->children = xt_dup( c );
+		else
+			dc = ( dc->next = xt_dup( c ) );
+		
+		dc->parent = dup;
+	}
+	
+	return dup;
+}
+
+/* Frees a node. This doesn't clean up references to itself from parents! */
+void xt_free_node( struct xt_node *node )
+{
+	int i;
+	
+	if( !node )
+		return;
+	
+	g_free( node->name );
+	g_free( node->text );
+	
+	for( i = 0; node->attr[i].key; i ++ )
+	{
+		g_free( node->attr[i].key );
+		g_free( node->attr[i].value );
+	}
+	g_free( node->attr );
+	
+	while( node->children )
+	{
+		struct xt_node *next = node->children->next;
+		
+		xt_free_node( node->children );
+		node->children = next;
+	}
+	
+	g_free( node );
+}
+
+void xt_free( struct xt_parser *xt )
+{
+	if( !xt )
+		return;
+	
+	if( xt->root )
+		xt_free_node( xt->root );
+	
+	g_markup_parse_context_free( xt->parser );
+	
+	g_free( xt );
+}
+
+/* To find a node's child with a specific name, pass the node's children
+   list, not the node itself! The reason you have to do this by hand: So
+   that you can also use this function as a find-next. */
+struct xt_node *xt_find_node( struct xt_node *node, const char *name )
+{
+	while( node )
+	{
+		if( g_strcasecmp( node->name, name ) == 0 )
+			break;
+		
+		node = node->next;
+	}
+	
+	return node;
+}
+
+char *xt_find_attr( struct xt_node *node, const char *key )
+{
+	int i;
+	
+	if( !node )
+		return NULL;
+	
+	for( i = 0; node->attr[i].key; i ++ )
+		if( g_strcasecmp( node->attr[i].key, key ) == 0 )
+			break;
+	
+	return node->attr[i].value;
+}
+
+struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children )
+{
+	struct xt_node *node, *c;
+	
+	node = g_new0( struct xt_node, 1 );
+	node->name = g_strdup( name );
+	node->children = children;
+	node->attr = g_new0( struct xt_attr, 1 );
+	
+	if( text )
+	{
+		node->text_len = strlen( text );
+		node->text = g_memdup( text, node->text_len + 1 );
+	}
+	
+	for( c = children; c; c = c->next )
+	{
+		if( c->parent != NULL )
+		{
+			/* ERROR CONDITION: They seem to have a parent already??? */
+		}
+		
+		c->parent = node;
+	}
+	
+	return node;
+}
+
+void xt_add_child( struct xt_node *parent, struct xt_node *child )
+{
+	struct xt_node *node;
+	
+	/* This function can actually be used to add more than one child, so
+	   do handle this properly. */
+	for( node = child; node; node = node->next )
+	{
+		if( node->parent != NULL )
+		{
+			/* ERROR CONDITION: They seem to have a parent already??? */
+		}
+		
+		node->parent = parent;
+	}
+	
+	if( parent->children == NULL )
+	{
+		parent->children = child;
+	}
+	else
+	{
+		for( node = parent->children; node->next; node = node->next );
+		node->next = child;
+	}
+}
+
+void xt_add_attr( struct xt_node *node, const char *key, const char *value )
+{
+	int i;
+	
+	/* Now actually it'd be nice if we can also change existing attributes
+	   (which actually means this function doesn't have the right name).
+	   So let's find out if we have this attribute already... */
+	for( i = 0; node->attr[i].key; i ++ )
+		if( strcmp( node->attr[i].key, key ) == 0 )
+			break;
+	
+	if( node->attr[i].key == NULL )
+	{
+		/* If not, allocate space for a new attribute. */
+		node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
+		node->attr[i].key = g_strdup( key );
+		node->attr[i+1].key = NULL;
+	}
+	else
+	{
+		/* Otherwise, free the old value before setting the new one. */
+		g_free( node->attr[i].value );
+	}
+	
+	node->attr[i].value = g_strdup( value );
+}
+
+int xt_remove_attr( struct xt_node *node, const char *key )
+{
+	int i, last;
+	
+	for( i = 0; node->attr[i].key; i ++ )
+		if( strcmp( node->attr[i].key, key ) == 0 )
+			break;
+	
+	/* If we didn't find the attribute... */
+	if( node->attr[i].key == NULL )
+		return 0;
+	
+	g_free( node->attr[i].key );
+	g_free( node->attr[i].value );
+	
+	/* If it's the last, this is easy: */
+	if( node->attr[i+1].key == NULL )
+	{
+		node->attr[i].key = node->attr[i].value = NULL;
+	}
+	else /* It's also pretty easy, actually. */
+	{
+		/* Find the last item. */
+		for( last = i + 1; node->attr[last+1].key; last ++ );
+		
+		node->attr[i] = node->attr[last];
+		node->attr[last].key = NULL;
+		node->attr[last].value = NULL;
+	}
+	
+	/* Let's not bother with reallocating memory here. It takes time and
+	   most packets don't stay in memory for long anyway. */
+	
+	return 1;
+}
Index: lib/xmltree.h
===================================================================
--- lib/xmltree.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
+++ lib/xmltree.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -0,0 +1,97 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
+*                                                                           *
+*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library is distributed in the hope that it will be useful,          *
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#ifndef _XMLTREE_H
+#define _XMLTREE_H
+
+typedef enum
+{
+	XT_COMPLETE	= 1,	/* </tag> reached */
+	XT_SEEN		= 2,	/* Handler called (or not defined) */
+} xt_flags;
+
+typedef enum
+{
+	XT_ABORT,		/* Abort, don't handle the rest anymore */
+	XT_HANDLED,		/* Handled this tag properly, go to the next one */
+	XT_NEXT			/* Try if there's another matching handler */
+} xt_status;
+
+struct xt_attr
+{
+	char *key, *value;
+};
+
+struct xt_node
+{
+	struct xt_node *parent;
+	struct xt_node *children;
+	
+	char *name;
+	struct xt_attr *attr;
+	char *text;
+	int text_len;
+	
+	struct xt_node *next;
+	xt_flags flags;
+};
+
+typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data );
+
+struct xt_handler_entry
+{
+	char *name, *parent;
+	xt_handler_func func;
+};
+
+struct xt_parser
+{
+	GMarkupParseContext *parser;
+	struct xt_node *root;
+	struct xt_node *cur;
+	
+	const struct xt_handler_entry *handlers;
+	gpointer data;
+	
+	GError *gerr;
+};
+
+struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data );
+void xt_reset( struct xt_parser *xt );
+int xt_feed( struct xt_parser *xt, char *text, int text_len );
+int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth );
+void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth );
+char *xt_to_string( struct xt_node *node );
+void xt_print( struct xt_node *node );
+struct xt_node *xt_dup( struct xt_node *node );
+void xt_free_node( struct xt_node *node );
+void xt_free( struct xt_parser *xt );
+struct xt_node *xt_find_node( struct xt_node *node, const char *name );
+char *xt_find_attr( struct xt_node *node, const char *key );
+
+struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children );
+void xt_add_child( struct xt_node *parent, struct xt_node *child );
+void xt_add_attr( struct xt_node *node, const char *key, const char *value );
+int xt_remove_attr( struct xt_node *node, const char *key );
+
+#endif
Index: protocols/jabber/Makefile
===================================================================
--- protocols/jabber/Makefile	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ protocols/jabber/Makefile	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -10,5 +10,5 @@
 
 # [SH] Program variables
-objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
+objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o
 
 CFLAGS += -Wall
Index: protocols/jabber/io.c
===================================================================
--- protocols/jabber/io.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ protocols/jabber/io.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -241,6 +241,11 @@
 	}
 	
-	/* EAGAIN/etc or a successful read. */
-	return TRUE;
+	if( ssl_pending( jd->ssl ) )
+		/* OpenSSL empties the TCP buffers completely but may keep some
+		   data in its internap buffers. select() won't see that, but
+		   ssl_pending() does. */
+		return jabber_read_callback( data, fd, cond );
+	else
+		return TRUE;
 }
 
@@ -521,6 +526,5 @@
 	   from the server too. */
 	xt_free( jd->xt );	/* In case we're RE-starting. */
-	jd->xt = xt_new( ic );
-	jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
+	jd->xt = xt_new( jabber_handlers, ic );
 	
 	if( jd->r_inpa <= 0 )
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ protocols/jabber/jabber.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -267,4 +267,5 @@
 	xt_free( jd->xt );
 	
+	g_free( jd->cached_id_prefix );
 	g_free( jd->away_message );
 	g_free( jd->username );
Index: otocols/jabber/xmltree.c
===================================================================
--- protocols/jabber/xmltree.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ 	(revision )
@@ -1,589 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
-*                                                                           *
-*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#include <glib.h>
-#include <string.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <stdio.h>
-
-#include "xmltree.h"
-
-static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
-{
-	struct xt_parser *xt = data;
-	struct xt_node *node = g_new0( struct xt_node, 1 ), *nt;
-	int i;
-	
-	node->parent = xt->cur;
-	node->name = g_strdup( element_name );
-	
-	/* First count the number of attributes */
-	for( i = 0; attr_names[i]; i ++ );
-	
-	/* Then allocate a NULL-terminated array. */
-	node->attr = g_new0( struct xt_attr, i + 1 );
-	
-	/* And fill it, saving one variable by starting at the end. */
-	for( i --; i >= 0; i -- )
-	{
-		node->attr[i].key = g_strdup( attr_names[i] );
-		node->attr[i].value = g_strdup( attr_values[i] );
-	}
-	
-	/* Add it to the linked list of children nodes, if we have a current
-	   node yet. */
-	if( xt->cur )
-	{
-		if( xt->cur->children )
-		{
-			for( nt = xt->cur->children; nt->next; nt = nt->next );
-			nt->next = node;
-		}
-		else
-		{
-			xt->cur->children = node;
-		}
-	}
-	else if( xt->root )
-	{
-		/* ERROR situation: A second root-element??? */
-	}
-	
-	/* Now this node will be the new current node. */
-	xt->cur = node;
-	/* And maybe this is the root? */
-	if( xt->root == NULL )
-		xt->root = node;
-}
-
-static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
-{
-	struct xt_parser *xt = data;
-	struct xt_node *node = xt->cur;
-	
-	if( node == NULL )
-		return;
-	
-	/* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */
-	node->text = g_renew( char, node->text, node->text_len + text_len + 1 );
-	memcpy( node->text + node->text_len, text, text_len );
-	node->text_len += text_len;
-	/* Zero termination is always nice to have. */
-	node->text[node->text_len] = 0;
-}
-
-static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
-{
-	struct xt_parser *xt = data;
-	
-	xt->cur->flags |= XT_COMPLETE;
-	xt->cur = xt->cur->parent;
-}
-
-GMarkupParser xt_parser_funcs =
-{
-	xt_start_element,
-	xt_end_element,
-	xt_text,
-	NULL,
-	NULL
-};
-
-struct xt_parser *xt_new( gpointer data )
-{
-	struct xt_parser *xt = g_new0( struct xt_parser, 1 );
-	
-	xt->data = data;
-	xt_reset( xt );
-	
-	return xt;
-}
-
-/* Reset the parser, flush everything we have so far. For example, we need
-   this for XMPP when doing TLS/SASL to restart the stream. */
-void xt_reset( struct xt_parser *xt )
-{
-	if( xt->parser )
-		g_markup_parse_context_free( xt->parser );
-	
-	xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL );
-	
-	if( xt->root )
-	{
-		xt_free_node( xt->root );
-		xt->root = NULL;
-		xt->cur = NULL;
-	}
-}
-
-/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
-   end-of-stream and 1 otherwise. */
-int xt_feed( struct xt_parser *xt, char *text, int text_len )
-{
-	if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
-	{
-		return -1;
-	}
-	
-	return !( xt->root && xt->root->flags & XT_COMPLETE );
-}
-
-/* Find completed nodes and see if a handler has to be called. Passing
-   a node isn't necessary if you want to start at the root, just pass
-   NULL. This second argument is needed for recursive calls. */
-int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
-{
-	struct xt_node *c;
-	xt_status st;
-	int i;
-	
-	/* Just in case someone likes infinite loops... */
-	if( xt->root == NULL )
-		return 0;
-	
-	if( node == NULL )
-		return xt_handle( xt, xt->root, depth );
-	
-	if( depth != 0 )
-		for( c = node->children; c; c = c->next )
-			if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) )
-				return 0;
-	
-	if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
-	{
-		for( i = 0; xt->handlers[i].func; i ++ )
-		{
-			/* This one is fun! \o/ */
-			
-						/* If handler.name == NULL it means it should always match. */
-			if( ( xt->handlers[i].name == NULL || 
-						/* If it's not, compare. There should always be a name. */
-			      g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
-						/* If handler.parent == NULL, it's a match. */
-			    ( xt->handlers[i].parent == NULL ||
-						/* If there's a parent node, see if the name matches. */
-			      ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
-						/* If there's no parent, the handler should mention <root> as a parent. */
-			                       g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
-			{
-				st = xt->handlers[i].func( node, xt->data );
-				
-				if( st == XT_ABORT )
-					return 0;
-				else if( st != XT_NEXT )
-					break;
-			}
-		}
-		
-		node->flags |= XT_SEEN;
-	}
-	
-	return 1;
-}
-
-/* Garbage collection: Cleans up all nodes that are handled. Useful for
-   streams because there's no reason to keep a complete packet history
-   in memory. */
-void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
-{
-	struct xt_node *c, *prev;
-	
-	if( !xt || !xt->root )
-		return;
-	
-	if( node == NULL )
-		return xt_cleanup( xt, xt->root, depth );
-	
-	if( node->flags & XT_SEEN && node == xt->root )
-	{
-		xt_free_node( xt->root );
-		xt->root = xt->cur = NULL;
-		/* xt->cur should be NULL already, BTW... */
-		
-		return;
-	}
-	
-	/* c contains the current node, prev the previous node (or NULL).
-	   I admit, this one's pretty horrible. */
-	for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
-	{
-		if( c->flags & XT_SEEN )
-		{
-			/* Remove the node from the linked list. */
-			if( prev )
-				prev->next = c->next;
-			else
-				node->children = c->next;
-			
-			xt_free_node( c );
-			
-			/* Since the for loop wants to get c->next, make sure
-			   c points at something that exists (and that c->next
-			   will actually be the next item we should check). c
-			   can be NULL now, if we just removed the first item.
-			   That explains the ? thing in for(). */
-			c = prev;
-		}
-		else
-		{
-			/* This node can't be cleaned up yet, but maybe a
-			   subnode can. */
-			if( depth != 0 )
-				xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
-		}
-	}
-}
-
-static void xt_to_string_real( struct xt_node *node, GString *str )
-{
-	char *buf;
-	struct xt_node *c;
-	int i;
-	
-	g_string_append_printf( str, "<%s", node->name );
-	
-	for( i = 0; node->attr[i].key; i ++ )
-	{
-		buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
-		g_string_append( str, buf );
-		g_free( buf );
-	}
-	
-	if( node->text == NULL && node->children == NULL )
-	{
-		g_string_append( str, "/>" );
-		return;
-	}
-	
-	g_string_append( str, ">" );
-	if( node->text_len > 0 )
-	{
-		buf = g_markup_escape_text( node->text, node->text_len );
-		g_string_append( str, buf );
-		g_free( buf );
-	}
-	
-	for( c = node->children; c; c = c->next )
-		xt_to_string_real( c, str );
-	
-	g_string_append_printf( str, "</%s>", node->name );
-}
-
-char *xt_to_string( struct xt_node *node )
-{
-	GString *ret;
-	char *real;
-	
-	ret = g_string_new( "" );
-	xt_to_string_real( node, ret );
-	
-	real = ret->str;
-	g_string_free( ret, FALSE );
-	
-	return real;
-}
-
-#ifdef DEBUG
-void xt_print( struct xt_node *node )
-{
-	int i;
-	struct xt_node *c;
-	
-	/* Indentation */
-	for( c = node; c->parent; c = c->parent )
-		printf( "\t" );
-	
-	/* Start the tag */
-	printf( "<%s", node->name );
-	
-	/* Print the attributes */
-	for( i = 0; node->attr[i].key; i ++ )
-		printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) );
-	
-	/* /> in case there's really *nothing* inside this tag, otherwise
-	   just >. */
-	/* If this tag doesn't have any content at all... */
-	if( node->text == NULL && node->children == NULL )
-	{
-		printf( "/>\n" );
-		return;
-		/* Then we're finished! */
-	}
-	
-	/* Otherwise... */
-	printf( ">" );
-	
-	/* Only print the text if it contains more than whitespace (TEST). */
-	if( node->text_len > 0 )
-	{
-		for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
-		if( node->text[i] )
-			printf( "%s", g_markup_escape_text( node->text, -1 ) );
-	}
-	
-	if( node->children )
-		printf( "\n" );
-	
-	for( c = node->children; c; c = c->next )
-		xt_print( c );
-	
-	if( node->children )
-		for( c = node; c->parent; c = c->parent )
-			printf( "\t" );
-	
-	/* Non-empty tag is now finished. */
-	printf( "</%s>\n", node->name );
-}
-#endif
-
-struct xt_node *xt_dup( struct xt_node *node )
-{
-	struct xt_node *dup = g_new0( struct xt_node, 1 );
-	struct xt_node *c, *dc = NULL;
-	int i;
-	
-	/* Let's NOT copy the parent element here BTW! Only do it for children. */
-	
-	dup->name = g_strdup( node->name );
-	dup->flags = node->flags;
-	if( node->text )
-	{
-		dup->text = g_memdup( node->text, node->text_len + 1 );
-		dup->text_len = node->text_len;
-	}
-	
-	/* Count the number of attributes and allocate the new array. */
-	for( i = 0; node->attr[i].key; i ++ );
-	dup->attr = g_new0( struct xt_attr, i + 1 );
-	
-	/* Copy them all! */
-	for( i --; i >= 0; i -- )
-	{
-		dup->attr[i].key = g_strdup( node->attr[i].key );
-		dup->attr[i].value = g_strdup( node->attr[i].value );
-	}
-	
-	/* This nice mysterious loop takes care of the children. */
-	for( c = node->children; c; c = c->next )
-	{
-		if( dc == NULL )
-			dc = dup->children = xt_dup( c );
-		else
-			dc = ( dc->next = xt_dup( c ) );
-		
-		dc->parent = dup;
-	}
-	
-	return dup;
-}
-
-/* Frees a node. This doesn't clean up references to itself from parents! */
-void xt_free_node( struct xt_node *node )
-{
-	int i;
-	
-	if( !node )
-		return;
-	
-	g_free( node->name );
-	g_free( node->text );
-	
-	for( i = 0; node->attr[i].key; i ++ )
-	{
-		g_free( node->attr[i].key );
-		g_free( node->attr[i].value );
-	}
-	g_free( node->attr );
-	
-	while( node->children )
-	{
-		struct xt_node *next = node->children->next;
-		
-		xt_free_node( node->children );
-		node->children = next;
-	}
-	
-	g_free( node );
-}
-
-void xt_free( struct xt_parser *xt )
-{
-	if( !xt )
-		return;
-	
-	if( xt->root )
-		xt_free_node( xt->root );
-	
-	g_markup_parse_context_free( xt->parser );
-	
-	g_free( xt );
-}
-
-/* To find a node's child with a specific name, pass the node's children
-   list, not the node itself! The reason you have to do this by hand: So
-   that you can also use this function as a find-next. */
-struct xt_node *xt_find_node( struct xt_node *node, const char *name )
-{
-	while( node )
-	{
-		if( g_strcasecmp( node->name, name ) == 0 )
-			break;
-		
-		node = node->next;
-	}
-	
-	return node;
-}
-
-char *xt_find_attr( struct xt_node *node, const char *key )
-{
-	int i;
-	
-	if( !node )
-		return NULL;
-	
-	for( i = 0; node->attr[i].key; i ++ )
-		if( g_strcasecmp( node->attr[i].key, key ) == 0 )
-			break;
-	
-	return node->attr[i].value;
-}
-
-struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children )
-{
-	struct xt_node *node, *c;
-	
-	node = g_new0( struct xt_node, 1 );
-	node->name = g_strdup( name );
-	node->children = children;
-	node->attr = g_new0( struct xt_attr, 1 );
-	
-	if( text )
-	{
-		node->text_len = strlen( text );
-		node->text = g_memdup( text, node->text_len + 1 );
-	}
-	
-	for( c = children; c; c = c->next )
-	{
-		if( c->parent != NULL )
-		{
-			/* ERROR CONDITION: They seem to have a parent already??? */
-		}
-		
-		c->parent = node;
-	}
-	
-	return node;
-}
-
-void xt_add_child( struct xt_node *parent, struct xt_node *child )
-{
-	struct xt_node *node;
-	
-	/* This function can actually be used to add more than one child, so
-	   do handle this properly. */
-	for( node = child; node; node = node->next )
-	{
-		if( node->parent != NULL )
-		{
-			/* ERROR CONDITION: They seem to have a parent already??? */
-		}
-		
-		node->parent = parent;
-	}
-	
-	if( parent->children == NULL )
-	{
-		parent->children = child;
-	}
-	else
-	{
-		for( node = parent->children; node->next; node = node->next );
-		node->next = child;
-	}
-}
-
-void xt_add_attr( struct xt_node *node, const char *key, const char *value )
-{
-	int i;
-	
-	/* Now actually it'd be nice if we can also change existing attributes
-	   (which actually means this function doesn't have the right name).
-	   So let's find out if we have this attribute already... */
-	for( i = 0; node->attr[i].key; i ++ )
-		if( strcmp( node->attr[i].key, key ) == 0 )
-			break;
-	
-	if( node->attr[i].key == NULL )
-	{
-		/* If not, allocate space for a new attribute. */
-		node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
-		node->attr[i].key = g_strdup( key );
-		node->attr[i+1].key = NULL;
-	}
-	else
-	{
-		/* Otherwise, free the old value before setting the new one. */
-		g_free( node->attr[i].value );
-	}
-	
-	node->attr[i].value = g_strdup( value );
-}
-
-int xt_remove_attr( struct xt_node *node, const char *key )
-{
-	int i, last;
-	
-	for( i = 0; node->attr[i].key; i ++ )
-		if( strcmp( node->attr[i].key, key ) == 0 )
-			break;
-	
-	/* If we didn't find the attribute... */
-	if( node->attr[i].key == NULL )
-		return 0;
-	
-	g_free( node->attr[i].key );
-	g_free( node->attr[i].value );
-	
-	/* If it's the last, this is easy: */
-	if( node->attr[i+1].key == NULL )
-	{
-		node->attr[i].key = node->attr[i].value = NULL;
-	}
-	else /* It's also pretty easy, actually. */
-	{
-		/* Find the last item. */
-		for( last = i + 1; node->attr[last+1].key; last ++ );
-		
-		node->attr[i] = node->attr[last];
-		node->attr[last].key = NULL;
-		node->attr[last].value = NULL;
-	}
-	
-	/* Let's not bother with reallocating memory here. It takes time and
-	   most packets don't stay in memory for long anyway. */
-	
-	return 1;
-}
Index: otocols/jabber/xmltree.h
===================================================================
--- protocols/jabber/xmltree.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ 	(revision )
@@ -1,97 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
-*                                                                           *
-*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#ifndef _XMLTREE_H
-#define _XMLTREE_H
-
-typedef enum
-{
-	XT_COMPLETE	= 1,	/* </tag> reached */
-	XT_SEEN		= 2,	/* Handler called (or not defined) */
-} xt_flags;
-
-typedef enum
-{
-	XT_ABORT,		/* Abort, don't handle the rest anymore */
-	XT_HANDLED,		/* Handled this tag properly, go to the next one */
-	XT_NEXT			/* Try if there's another matching handler */
-} xt_status;
-
-struct xt_attr
-{
-	char *key, *value;
-};
-
-struct xt_node
-{
-	struct xt_node *parent;
-	struct xt_node *children;
-	
-	char *name;
-	struct xt_attr *attr;
-	char *text;
-	int text_len;
-	
-	struct xt_node *next;
-	xt_flags flags;
-};
-
-typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data );
-
-struct xt_handler_entry
-{
-	char *name, *parent;
-	xt_handler_func func;
-};
-
-struct xt_parser
-{
-	GMarkupParseContext *parser;
-	struct xt_node *root;
-	struct xt_node *cur;
-	
-	struct xt_handler_entry *handlers;
-	gpointer data;
-	
-	GError *gerr;
-};
-
-struct xt_parser *xt_new( gpointer data );
-void xt_reset( struct xt_parser *xt );
-int xt_feed( struct xt_parser *xt, char *text, int text_len );
-int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth );
-void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth );
-char *xt_to_string( struct xt_node *node );
-void xt_print( struct xt_node *node );
-struct xt_node *xt_dup( struct xt_node *node );
-void xt_free_node( struct xt_node *node );
-void xt_free( struct xt_parser *xt );
-struct xt_node *xt_find_node( struct xt_node *node, const char *name );
-char *xt_find_attr( struct xt_node *node, const char *key );
-
-struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children );
-void xt_add_child( struct xt_node *parent, struct xt_node *child );
-void xt_add_attr( struct xt_node *node, const char *key, const char *value );
-int xt_remove_attr( struct xt_node *node, const char *key );
-
-#endif
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ protocols/msn/ns.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -34,5 +34,5 @@
 static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
 
-static void msn_auth_got_passport_id( struct passport_reply *rep );
+static void msn_auth_got_passport_token( struct msn_auth_data *mad );
 
 gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )
@@ -214,5 +214,5 @@
 		{
 			/* Time for some Passport black magic... */
-			if( !passport_get_id( msn_auth_got_passport_id, ic, ic->acc->user, ic->acc->pass, cmd[4] ) )
+			if( !passport_get_token( msn_auth_got_passport_token, ic, ic->acc->user, ic->acc->pass, cmd[4] ) )
 			{
 				imcb_error( ic, "Error while contacting Passport server" );
@@ -674,21 +674,25 @@
 }
 
-static void msn_auth_got_passport_id( struct passport_reply *rep )
+static void msn_auth_got_passport_token( struct msn_auth_data *mad )
 {
-	struct im_connection *ic = rep->data;
-	struct msn_data *md = ic->proto_data;
-	char *key = rep->result;
-	char buf[1024];
-	
-	if( key == NULL )
-	{
-		imcb_error( ic, "Error during Passport authentication (%s)",
-		               rep->error_string ? rep->error_string : "Unknown error" );
+	struct im_connection *ic = mad->data;
+	struct msn_data *md;
+	
+	/* Dead connection? */
+	if( g_slist_find( msn_connections, ic ) == NULL )
+		return;
+	
+	md = ic->proto_data;
+	if( mad->token )
+	{
+		char buf[1024];
+		
+		g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token );
+		msn_write( ic, buf, strlen( buf ) );
+	}
+	else
+	{
+		imcb_error( ic, "Error during Passport authentication: %s", mad->error );
 		imc_logout( ic, TRUE );
 	}
-	else
-	{
-		g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, key );
-		msn_write( ic, buf, strlen( buf ) );
-	}
 }
Index: protocols/msn/passport.c
===================================================================
--- protocols/msn/passport.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ protocols/msn/passport.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -1,7 +1,6 @@
-/* passport.c
+/** passport.c
  *
- * Functions to login to microsoft passport service for Messenger
- * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>
- * Copyright (C) 2004 Wilmer van der Gaast <wilmer@gaast.net>
+ * Functions to login to Microsoft Passport service for Messenger
+ * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net>
  *
  * This program is free software; you can redistribute it and/or modify             
@@ -24,207 +23,148 @@
 #include "msn.h"
 #include "bitlbee.h"
+#include "url.h"
+#include "misc.h"
+#include "xmltree.h"
 #include <ctype.h>
 #include <errno.h>
 
-#define MSN_BUF_LEN 8192
+static int passport_get_token_real( struct msn_auth_data *mad );
+static void passport_get_token_ready( struct http_request *req );
 
-static char *prd_cached = NULL;
-
-static int passport_get_id_real( gpointer func, gpointer data, char *header );
-static void passport_get_id_ready( struct http_request *req );
-
-static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header );
-static void passport_retrieve_dalogin_ready( struct http_request *req );
-
-static char *passport_create_header( char *cookie, char *email, char *pwd );
-static void destroy_reply( struct passport_reply *rep );
-
-int passport_get_id( gpointer func, gpointer data, char *username, char *password, char *cookie )
+int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie )
 {
-	char *header = passport_create_header( cookie, username, password );
+	struct msn_auth_data *mad = g_new0( struct msn_auth_data, 1 );
+	int i;
 	
-	if( prd_cached == NULL )
-		return passport_retrieve_dalogin( func, data, header );
-	else
-		return passport_get_id_real( func, data, header );
+	mad->username = g_strdup( username );
+	mad->password = g_strdup( password );
+	mad->cookie = g_strdup( cookie );
+	
+	mad->callback = func;
+	mad->data = data;
+	
+	mad->url = g_strdup( SOAP_AUTHENTICATION_URL );
+	mad->ttl = 3; /* Max. # of redirects. */
+	
+	/* HTTP-escape stuff and s/,/&/ */
+	http_decode( mad->cookie );
+	for( i = 0; mad->cookie[i]; i ++ )
+		if( mad->cookie[i] == ',' )
+			mad->cookie[i] = '&';
+	
+	/* Microsoft doesn't allow password longer than 16 chars and silently
+	   fails authentication if you give the "full version" of your passwd. */
+	if( strlen( mad->password ) > MAX_PASSPORT_PWLEN )
+		mad->password[MAX_PASSPORT_PWLEN] = 0;
+	
+	return passport_get_token_real( mad );
 }
 
-static int passport_get_id_real( gpointer func, gpointer data, char *header )
+static int passport_get_token_real( struct msn_auth_data *mad )
 {
-	struct passport_reply *rep;
-	char *server, *dummy, *reqs;
+	char *post_payload, *post_request;
 	struct http_request *req;
+	url_t url;
 	
-	rep = g_new0( struct passport_reply, 1 );
-	rep->data = data;
-	rep->func = func;
-	rep->header = header;
+	url_set( &url, mad->url );
 	
-	server = g_strdup( prd_cached );
-	dummy = strchr( server, '/' );
+	post_payload = g_markup_printf_escaped( SOAP_AUTHENTICATION_PAYLOAD,
+	                                        mad->username,
+	                                        mad->password,
+	                                        mad->cookie );
 	
-	if( dummy == NULL )
-	{
-		destroy_reply( rep );
-		return( 0 );
-	}
+	post_request = g_strdup_printf( SOAP_AUTHENTICATION_REQUEST,
+	                                url.file, url.host,
+	                                (int) strlen( post_payload ),
+	                                post_payload );
+	                                
+	req = http_dorequest( url.host, url.port, 1, post_request,
+	                      passport_get_token_ready, mad );
 	
-	reqs = g_strdup_printf( "GET %s HTTP/1.0\r\n%s\r\n\r\n", dummy, header );
+	g_free( post_request );
+	g_free( post_payload );
 	
-	*dummy = 0;
-	req = http_dorequest( server, 443, 1, reqs, passport_get_id_ready, rep );
-	
-	g_free( server );
-	g_free( reqs );
-	
-	if( req == NULL )
-		destroy_reply( rep );
-	
-	return( req != NULL );
+	return req != NULL;
 }
 
-static void passport_get_id_ready( struct http_request *req )
+static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data );
+static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data );
+
+static const struct xt_handler_entry passport_xt_handlers[] = {
+	{ "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", passport_xt_extract_token },
+	{ "S:Fault",                  "S:Envelope",                 passport_xt_handle_fault  },
+	{ NULL,                       NULL,                         NULL                      }
+};
+
+static void passport_get_token_ready( struct http_request *req )
 {
-	struct passport_reply *rep = req->data;
+	struct msn_auth_data *mad = req->data;
+	struct xt_parser *parser;
 	
-	if( !g_slist_find( msn_connections, rep->data ) )
+	g_free( mad->url );
+	g_free( mad->error );
+	mad->url = mad->error = NULL;
+	
+	if( req->status_code == 200 )
 	{
-		destroy_reply( rep );
-		return;
-	}
-	
-	if( req->finished && req->reply_headers && req->status_code == 200 )
-	{
-		char *dummy;
-		
-		if( ( dummy = strstr( req->reply_headers, "from-PP='" ) ) )
-		{
-			char *responseend;
-			
-			dummy += strlen( "from-PP='" );
-			responseend = strchr( dummy, '\'' );
-			if( responseend )
-				*responseend = 0;
-			
-			rep->result = g_strdup( dummy );
-		}
-		else
-		{
-			rep->error_string = g_strdup( "Could not parse Passport server response" );
-		}
+		parser = xt_new( passport_xt_handlers, mad );
+		xt_feed( parser, req->reply_body, req->body_size );
+		xt_handle( parser, NULL, -1 );
+		xt_free( parser );
 	}
 	else
 	{
-		rep->error_string = g_strdup_printf( "HTTP error: %s",
-		                      req->status_string ? req->status_string : "Unknown error" );
+		mad->error = g_strdup_printf( "HTTP error %d (%s)", req->status_code,
+		                              req->status_string ? req->status_string : "unknown" );
 	}
 	
-	rep->func( rep );
-	destroy_reply( rep );
+	if( mad->error == NULL && mad->token == NULL )
+		mad->error = g_strdup( "Could not parse Passport server response" );
+	
+	if( mad->url && mad->token == NULL )
+	{
+		passport_get_token_real( mad );
+	}
+	else
+	{
+		mad->callback( mad );
+		
+		g_free( mad->url );
+		g_free( mad->username );
+		g_free( mad->password );
+		g_free( mad->cookie );
+		g_free( mad->token );
+		g_free( mad->error );
+		g_free( mad );
+	}
 }
 
-static char *passport_create_header( char *cookie, char *email, char *pwd )
+static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data )
 {
-	char *buffer;
-	char *currenttoken;
-	char *email_enc, *pwd_enc;
+	struct msn_auth_data *mad = data;
+	char *s;
 	
-	currenttoken = strstr( cookie, "lc=" );
-	if( currenttoken == NULL )
-		return NULL;
+	if( ( s = xt_find_attr( node, "Id" ) ) && strcmp( s, "PPToken1" ) == 0 )
+		mad->token = g_memdup( node->text, node->text_len + 1 );
 	
-	email_enc = g_new0( char, strlen( email ) * 3 + 1 );
-	strcpy( email_enc, email );
-	http_encode( email_enc );
-	
-	pwd_enc = g_new0( char, strlen( pwd ) * 3 + 1 );
-	strcpy( pwd_enc, pwd );
-	http_encode( pwd_enc );
-	
-	buffer = g_strdup_printf( "Authorization: Passport1.4 OrgVerb=GET,"
-	                          "OrgURL=http%%3A%%2F%%2Fmessenger%%2Emsn%%2Ecom,"
-	                          "sign-in=%s,pwd=%s,%s", email_enc, pwd_enc,
-	                          currenttoken );
-	
-	g_free( email_enc );
-	g_free( pwd_enc );
-	
-	return buffer;
+	return XT_HANDLED;
 }
 
-static int passport_retrieve_dalogin( gpointer func, gpointer data, char *header )
+static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data )
 {
-	struct passport_reply *rep = g_new0( struct passport_reply, 1 );
-	struct http_request *req;
+	struct msn_auth_data *mad = data;
+	struct xt_node *code = xt_find_node( node->children, "faultcode" );
+	struct xt_node *string = xt_find_node( node->children, "faultstring" );
+	struct xt_node *redirect = xt_find_node( node->children, "psf:redirectUrl" );
 	
-	rep->data = data;
-	rep->func = func;
-	rep->header = header;
+	if( redirect && redirect->text_len && mad->ttl-- > 0 )
+		mad->url = g_memdup( redirect->text, redirect->text_len + 1 );
 	
-	req = http_dorequest_url( "https://nexus.passport.com/rdr/pprdr.asp", passport_retrieve_dalogin_ready, rep );
+	if( code == NULL || code->text_len == 0 )
+		mad->error = g_strdup( "Unknown error" );
+	else
+		mad->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ?
+		                              string->text : "no description available" );
 	
-	if( !req )
-		destroy_reply( rep );
-	
-	return( req != NULL );
+	return XT_HANDLED;
 }
-
-static void passport_retrieve_dalogin_ready( struct http_request *req )
-{
-	struct passport_reply *rep = req->data;
-	char *dalogin;
-	char *urlend;
-	
-	if( !g_slist_find( msn_connections, rep->data ) )
-	{
-		destroy_reply( rep );
-		return;
-	}
-	
-	if( !req->finished || !req->reply_headers || req->status_code != 200 )
-	{
-		rep->error_string = g_strdup_printf( "HTTP error while fetching DALogin: %s",
-		                        req->status_string ? req->status_string : "Unknown error" );
-		goto failure;
-	}
-	
-	dalogin = strstr( req->reply_headers, "DALogin=" );	
-	
-	if( !dalogin )
-	{
-		rep->error_string = g_strdup( "Parse error while fetching DALogin" );
-		goto failure;
-	}
-	
-	dalogin += strlen( "DALogin=" );
-	urlend = strchr( dalogin, ',' );
-	if( urlend )
-		*urlend = 0;
-	
-	/* strip the http(s):// part from the url */
-	urlend = strstr( urlend, "://" );
-	if( urlend )
-		dalogin = urlend + strlen( "://" );
-	
-	if( prd_cached == NULL )
-		prd_cached = g_strdup( dalogin );
-	
-	if( passport_get_id_real( rep->func, rep->data, rep->header ) )
-	{
-		rep->header = NULL;
-		destroy_reply( rep );
-		return;
-	}
-	
-failure:	
-	rep->func( rep );
-	destroy_reply( rep );
-}
-
-static void destroy_reply( struct passport_reply *rep )
-{
-	g_free( rep->result );
-	g_free( rep->header );
-	g_free( rep->error_string );
-	g_free( rep );
-}
Index: protocols/msn/passport.h
===================================================================
--- protocols/msn/passport.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ protocols/msn/passport.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -1,9 +1,6 @@
-#ifndef __PASSPORT_H__
-#define __PASSPORT_H__
 /* passport.h
  *
- * Functions to login to Microsoft Passport Service for Messenger
- * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>,
- *                    Wilmer van der Gaast <wilmer@gaast.net>
+ * Functions to login to Microsoft Passport service for Messenger
+ * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net>
  *
  * This program is free software; you can redistribute it and/or modify             
@@ -18,6 +15,12 @@
  * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA          
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA          
  */
+
+/* Thanks to http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
+   for the specs! */
+
+#ifndef __PASSPORT_H__
+#define __PASSPORT_H__
 
 #include <stdio.h>
@@ -33,14 +36,78 @@
 #include "nogaim.h"
 
-struct passport_reply
+#define MAX_PASSPORT_PWLEN 16
+
+struct msn_auth_data
 {
-	void (*func)( struct passport_reply * );
-	void *data;
-	char *result;
-	char *header;
-	char *error_string;
+	char *url;
+	int ttl;
+	
+	char *username;
+	char *password;
+	char *cookie;
+	
+	/* The end result, the only thing we'll really be interested in
+	   once finished. */
+	char *token;
+	char *error; /* Yeah, or that... */
+	
+	void (*callback)( struct msn_auth_data *mad );
+	gpointer data;
 };
 
-int passport_get_id( gpointer func, gpointer data, char *username, char *password, char *cookie );
+#define SOAP_AUTHENTICATION_URL "https://loginnet.passport.com/RST.srf"
+
+#define SOAP_AUTHENTICATION_REQUEST \
+"POST %s HTTP/1.0\r\n" \
+"Accept: text/*\r\n" \
+"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \
+"Host: %s\r\n" \
+"Content-Length: %d\r\n" \
+"Cache-Control: no-cache\r\n" \
+"\r\n" \
+"%s"
+
+#define SOAP_AUTHENTICATION_PAYLOAD \
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \
+"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \
+  "<Header>" \
+    "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">" \
+      "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \
+      "<ps:BinaryVersion>4</ps:BinaryVersion>" \
+      "<ps:UIVersion>1</ps:UIVersion>" \
+      "<ps:Cookies></ps:Cookies>" \
+      "<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>" \
+    "</ps:AuthInfo>" \
+    "<wsse:Security>" \
+       "<wsse:UsernameToken Id=\"user\">" \
+         "<wsse:Username>%s</wsse:Username>" \
+         "<wsse:Password>%s</wsse:Password>" \
+       "</wsse:UsernameToken>" \
+    "</wsse:Security>" \
+  "</Header>" \
+  "<Body>" \
+    "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">" \
+      "<wst:RequestSecurityToken Id=\"RST0\">" \
+        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+        "<wsp:AppliesTo>" \
+          "<wsa:EndpointReference>" \
+            "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \
+          "</wsa:EndpointReference>" \
+        "</wsp:AppliesTo>" \
+      "</wst:RequestSecurityToken>" \
+      "<wst:RequestSecurityToken Id=\"RST1\">" \
+       "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+        "<wsp:AppliesTo>" \
+          "<wsa:EndpointReference>" \
+            "<wsa:Address>messenger.msn.com</wsa:Address>" \
+          "</wsa:EndpointReference>" \
+        "</wsp:AppliesTo>" \
+        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>" \
+      "</wst:RequestSecurityToken>" \
+    "</ps:RequestMultipleSecurityTokens>" \
+  "</Body>" \
+"</Envelope>"
+
+int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie );
 
 #endif /* __PASSPORT_H__ */
Index: protocols/yahoo/libyahoo2.c
===================================================================
--- protocols/yahoo/libyahoo2.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ protocols/yahoo/libyahoo2.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -737,5 +737,5 @@
 
 	memcpy(data + pos, "YMSG", 4); pos += 4;
-	pos += yahoo_put16(data + pos, 0x0a00);
+	pos += yahoo_put16(data + pos, 0x000c);
 	pos += yahoo_put16(data + pos, 0x0000);
 	pos += yahoo_put16(data + pos, pktlen + extra_pad);
@@ -3351,5 +3351,5 @@
 					break;
 				case 5: 
-					if(cp != "\005")
+					if(strcmp(cp, "5") != 0)
 						yct->location = cp;
 					k = 0;
Index: protocols/yahoo/yahoo.c
===================================================================
--- protocols/yahoo/yahoo.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ protocols/yahoo/yahoo.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -315,5 +315,5 @@
 	struct byahoo_data *yd = (struct byahoo_data *) c->ic->proto_data;
 	
-	yahoo_conference_invite( yd->y2_id, NULL, c->data, c->title, msg );
+	yahoo_conference_invite( yd->y2_id, NULL, c->data, c->title, msg ? msg : "" );
 }
 
Index: query.c
===================================================================
--- query.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ query.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -140,10 +140,16 @@
 	if( ans )
 	{
-		imcb_log( q->ic, "Accepted: %s", q->question );
+		if( q->ic )
+			imcb_log( q->ic, "Accepted: %s", q->question );
+		else
+			irc_usermsg( irc, "Accepted: %s", q->question );
 		q->yes( NULL, q->data );
 	}
 	else
 	{
-		imcb_log( q->ic, "Rejected: %s", q->question );
+		if( q->ic )
+			imcb_log( q->ic, "Rejected: %s", q->question );
+		else
+			irc_usermsg( irc, "Rejected: %s", q->question );
 		q->no( NULL, q->data );
 	}
Index: root_commands.c
===================================================================
--- root_commands.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ root_commands.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -204,4 +204,24 @@
 }
 
+void cmd_account_del_yes( gpointer w, void *data )
+{
+	account_t *a = data;
+	irc_t *irc = a->irc;
+	
+	if( a->ic )
+	{
+		irc_usermsg( irc, "Account is still logged in, can't delete" );
+	}
+	else
+	{
+		account_del( irc, a );
+		irc_usermsg( irc, "Account deleted" );
+	}
+}
+
+void cmd_account_del_no( gpointer w, void *data )
+{
+}
+
 static void cmd_account( irc_t *irc, char **cmd )
 {
@@ -258,6 +278,13 @@
 		else
 		{
-			account_del( irc, a );
-			irc_usermsg( irc, "Account deleted" );
+			char *msg;
+			
+			msg = g_strdup_printf( "If you remove this account (%s(%s)), BitlBee will "
+			                       "also forget all your saved nicknames. If you want "
+			                       "to change your username/password, use the `account "
+			                       "set' command. Are you sure you want to delete this "
+			                       "account?", a->prpl->name, a->user );
+			query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, a );
+			g_free( msg );
 		}
 	}
@@ -769,4 +796,7 @@
 		else
 			irc_usermsg( irc, "%s is empty", set_name );
+
+		if( strchr( set_name, '/' ) )
+			irc_usermsg( irc, "Warning: / found in setting name, you're probably looking for the `account set' command." );
 	}
 	else
Index: set.c
===================================================================
--- set.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ set.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -230,17 +230,2 @@
 	return value;
 }
-
-char *set_eval_charset( set_t *set, char *value )
-{
-	GIConv cd;
-
-	if ( g_strncasecmp( value, "none", 4 ) == 0 )
-		return value;
-
-	cd = g_iconv_open( "UTF-8", value );
-	if( cd == (GIConv) -1 )
-		return NULL;
-
-	g_iconv_close( cd );
-	return value;
-}
Index: set.h
===================================================================
--- set.h	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ set.h	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -97,5 +97,4 @@
 char *set_eval_to_char( set_t *set, char *value );
 char *set_eval_ops( set_t *set, char *value );
-char *set_eval_charset( set_t *set, char *value );
 
 #endif /* __SET_H__ */
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ storage_xml.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -80,5 +80,5 @@
 		char *nick = xml_attr( attr_names, attr_values, "nick" );
 		char *pass = xml_attr( attr_names, attr_values, "password" );
-		md5_byte_t *pass_dec = NULL;
+		int st;
 		
 		if( !nick || !pass )
@@ -87,40 +87,21 @@
 			             "Missing attributes for %s element", element_name );
 		}
-		else if( base64_decode( pass, &pass_dec ) != 21 )
-		{
+		else if( ( st = md5_verify_password( xd->given_pass, pass ) ) == -1 )
+		{
+			xd->pass_st = XML_PASS_WRONG;
 			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
 			             "Error while decoding password attribute" );
 		}
+		else if( st == 0 )
+		{
+			if( xd->pass_st != XML_PASS_CHECK_ONLY )
+				xd->pass_st = XML_PASS_OK;
+		}
 		else
 		{
-			md5_byte_t pass_md5[16];
-			md5_state_t md5_state;
-			int i;
-			
-			md5_init( &md5_state );
-			md5_append( &md5_state, (md5_byte_t*) xd->given_pass, strlen( xd->given_pass ) );
-			md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
-			md5_finish( &md5_state, pass_md5 );
-			
-			for( i = 0; i < 16; i ++ )
-			{
-				if( pass_dec[i] != pass_md5[i] )
-				{
-					xd->pass_st = XML_PASS_WRONG;
-					g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-					             "Password mismatch" );
-					break;
-				}
-			}
-			
-			/* If we reached the end of the loop, it was a match! */
-			if( i == 16 )
-			{
-				if( xd->pass_st != XML_PASS_CHECK_ONLY )
-					xd->pass_st = XML_PASS_OK;
-			}
-		}
-		
-		g_free( pass_dec );
+			xd->pass_st = XML_PASS_WRONG;
+			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+			             "Password mismatch" );
+		}
 	}
 	else if( xd->pass_st < XML_PASS_OK )
@@ -428,5 +409,5 @@
 		int pass_len;
 		
-		pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password );
+		pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password, 12 );
 		pass_b64 = base64_encode( pass_cr, pass_len );
 		g_free( pass_cr );
Index: tests/check_arc.c
===================================================================
--- tests/check_arc.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ tests/check_arc.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -7,5 +7,5 @@
 #include "arc.h"
 
-char *password = "TotT";
+char *password = "ArcVier";
 
 char *clear_tests[] =
@@ -14,4 +14,5 @@
 	"ItllBeBitlBee",
 	"One more boring password",
+	"Hoi hoi",
 	NULL
 };
@@ -28,5 +29,5 @@
 		int len;
 		
-		len = arc_encode( clear_tests[i], 0, &crypted, password );
+		len = arc_encode( clear_tests[i], 0, &crypted, password, 12 );
 		len = arc_decode( crypted, len, &decrypted, password );
 		
@@ -41,25 +42,33 @@
 struct
 {
-	unsigned char crypted[24];
+	unsigned char crypted[30];
 	int len;
 	char *decrypted;
 } decrypt_tests[] = {
+	/* One block with padding. */
 	{
 		{
-			0xc3, 0x0d, 0x43, 0xc3, 0xee, 0x80, 0xe2, 0x8c, 0x0b, 0x29, 0x32, 0x7e,
-			0x38, 0x05, 0x82, 0x10, 0x21, 0x1c, 0x4a, 0x00, 0x2c
-		}, 21, "Debugging sucks"
+			0x3f, 0x79, 0xb0, 0xf5, 0x91, 0x56, 0xd2, 0x1b, 0xd1, 0x4b, 0x67, 0xac,
+			0xb1, 0x31, 0xc9, 0xdb, 0xf9, 0xaa
+		}, 18, "short pass"
 	},
+	
+	/* Two blocks with padding. */
 	{
 		{
-			0xb0, 0x00, 0x57, 0x0d, 0x0d, 0x0d, 0x70, 0xe1, 0xc0, 0x00, 0xa4, 0x25,
-			0x7d, 0xbe, 0x03, 0xcc, 0x24, 0xd1, 0x0c
-		}, 19, "Testing rocks"
+			0xf9, 0xa6, 0xec, 0x5d, 0xc7, 0x06, 0xb8, 0x6b, 0x63, 0x9f, 0x2d, 0xb5,
+			0x7d, 0xaa, 0x32, 0xbb, 0xd8, 0x08, 0xfd, 0x81, 0x2e, 0xca, 0xb4, 0xd7,
+			0x2f, 0x36, 0x9c, 0xac, 0xa0, 0xbc
+		}, 30, "longer password"
 	},
+
+	/* This string is exactly two "blocks" long, to make sure unpadded strings also decrypt
+	   properly. */
 	{
 		{
-			0xb6, 0x92, 0x59, 0xe4, 0xf9, 0xc1, 0x7a, 0xf6, 0xf3, 0x18, 0xea, 0x28,
-			0x73, 0x6d, 0xb3, 0x0a, 0x6f, 0x0a, 0x2b, 0x43, 0x57, 0xe9, 0x3e, 0x63
-		}, 24, "OSCAR is creepy..."
+			0x95, 0x4d, 0xcf, 0x4d, 0x5e, 0x6c, 0xcf, 0xef, 0xb9, 0x80, 0x00, 0xef,
+			0x25, 0xe9, 0x17, 0xf6, 0x29, 0x6a, 0x82, 0x79, 0x1c, 0xca, 0x68, 0xb5,
+			0x4e, 0xd0, 0xc1, 0x41, 0x8e, 0xe6
+		}, 30, "OSCAR is really creepy.."
 	},
 	{ "", 0, NULL }
@@ -80,5 +89,5 @@
 		
 		fail_if( strcmp( decrypt_tests[i].decrypted, decrypted ) != 0,
-		         "%s didn't decrypt properly", clear_tests[i] );
+		         "`%s' didn't decrypt properly", decrypt_tests[i].decrypted );
 		
 		g_free( decrypted );
Index: tests/check_help.c
===================================================================
--- tests/check_help.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ tests/check_help.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -7,9 +7,12 @@
 #include "help.h"
 
-START_TEST(test_help_none)
+START_TEST(test_help_initfree)
 	help_t *h, *r;
 	r = help_init(&h, "/dev/null");
 	fail_if(r == NULL);
 	fail_if(r != h);
+	
+	help_free(&h);
+	fail_if(h != NULL);
 END_TEST
 
@@ -25,5 +28,5 @@
 	TCase *tc_core = tcase_create("Core");
 	suite_add_tcase (s, tc_core);
-	tcase_add_test (tc_core, test_help_none);
+	tcase_add_test (tc_core, test_help_initfree);
 	tcase_add_test (tc_core, test_help_nonexistent);
 	return s;
Index: tests/check_irc.c
===================================================================
--- tests/check_irc.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ tests/check_irc.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -37,6 +37,6 @@
 	irc = irc_new(g_io_channel_unix_get_fd(ch1));
 
-	fail_unless(g_io_channel_write_chars(ch2, "NICK bla\r\n"
-			"USER a a a a\r\n", -1, NULL, NULL) == G_IO_STATUS_NORMAL);
+	fail_unless(g_io_channel_write_chars(ch2, "NICK bla\r\r\n"
+			"USER a a a a\n", -1, NULL, NULL) == G_IO_STATUS_NORMAL);
 	fail_unless(g_io_channel_flush(ch2, NULL) == G_IO_STATUS_NORMAL);
 
Index: tests/check_jabber_sasl.c
===================================================================
--- tests/check_jabber_sasl.c	(revision 0db75ad966458610427dacdd31ecbaddbca18935)
+++ tests/check_jabber_sasl.c	(revision dd345753c1742905c9f81aa71d8b09109fbc5456)
@@ -18,5 +18,5 @@
 struct
 {
-	const char *challenge;
+	char *challenge;
 	char *key;
 	char *value;
