changeset 30708:9a86ddd0914e

propagate from branch 'im.pidgin.pidgin' (head fc8a21d1f30004cf7a468e8ab79c0f922c6c4a3e) to branch 'im.pidgin.cpw.qulogic.msnp16' (head 338fa5f69bb7675fc80f890f27023b462eadb1fa)
author masca@cpw.pidgin.im
date Wed, 10 Mar 2010 17:58:21 +0000
parents 994798816048 (current diff) 47cda4fb5d5d (diff)
children 4d7dfeae29c6
files libpurple/protocols/msn/slp.c libpurple/protocols/msn/slplink.c libpurple/protocols/msnp9/Makefile.am libpurple/protocols/msnp9/Makefile.mingw libpurple/protocols/msnp9/cmdproc.c libpurple/protocols/msnp9/cmdproc.h libpurple/protocols/msnp9/command.c libpurple/protocols/msnp9/command.h libpurple/protocols/msnp9/dialog.c libpurple/protocols/msnp9/dialog.h libpurple/protocols/msnp9/directconn.c libpurple/protocols/msnp9/directconn.h libpurple/protocols/msnp9/error.c libpurple/protocols/msnp9/error.h libpurple/protocols/msnp9/group.c libpurple/protocols/msnp9/group.h libpurple/protocols/msnp9/history.c libpurple/protocols/msnp9/history.h libpurple/protocols/msnp9/httpconn.c libpurple/protocols/msnp9/httpconn.h libpurple/protocols/msnp9/msg.c libpurple/protocols/msnp9/msg.h libpurple/protocols/msnp9/msn-utils.c libpurple/protocols/msnp9/msn-utils.h libpurple/protocols/msnp9/msn.c libpurple/protocols/msnp9/msn.h libpurple/protocols/msnp9/nexus.c libpurple/protocols/msnp9/nexus.h libpurple/protocols/msnp9/notification.c libpurple/protocols/msnp9/notification.h libpurple/protocols/msnp9/object.c libpurple/protocols/msnp9/object.h libpurple/protocols/msnp9/page.c libpurple/protocols/msnp9/page.h libpurple/protocols/msnp9/servconn.c libpurple/protocols/msnp9/servconn.h libpurple/protocols/msnp9/session.c libpurple/protocols/msnp9/session.h libpurple/protocols/msnp9/slp.c libpurple/protocols/msnp9/slp.h libpurple/protocols/msnp9/slpcall.c libpurple/protocols/msnp9/slpcall.h libpurple/protocols/msnp9/slplink.c libpurple/protocols/msnp9/slplink.h libpurple/protocols/msnp9/slpmsg.c libpurple/protocols/msnp9/slpmsg.h libpurple/protocols/msnp9/slpsession.c libpurple/protocols/msnp9/slpsession.h libpurple/protocols/msnp9/state.c libpurple/protocols/msnp9/state.h libpurple/protocols/msnp9/switchboard.c libpurple/protocols/msnp9/switchboard.h libpurple/protocols/msnp9/sync.c libpurple/protocols/msnp9/sync.h libpurple/protocols/msnp9/table.c libpurple/protocols/msnp9/table.h libpurple/protocols/msnp9/transaction.c libpurple/protocols/msnp9/transaction.h libpurple/protocols/msnp9/user.c libpurple/protocols/msnp9/user.h libpurple/protocols/msnp9/userlist.c libpurple/protocols/msnp9/userlist.h
diffstat 175 files changed, 10147 insertions(+), 23948 deletions(-) [+]
line wrap: on
line diff
--- a/.mtn-ignore	Fri Mar 05 04:19:05 2010 +0000
+++ b/.mtn-ignore	Wed Mar 10 17:58:21 2010 +0000
@@ -38,7 +38,10 @@
 pidgin.spec$
 pidgin-.*.tar.gz
 pidgin-.*.tar.bz2
+pidgin-*.*.*-dbgsym$
+pidgin-*.*.*-dbgsym.zip$
 pidgin-*.*.*-win32bin$
+pidgin-*.*.*-win32-bin.zip$
 pidgin/pidgin$
 pidgin/pixmaps/emotes/default/24/theme
 pidgin/pixmaps/emotes/none/theme
@@ -48,6 +51,9 @@
 pidgin/plugins/perl/common/Makefile.old
 pidgin/win32/pidgin_dll_rc.rc$
 pidgin/win32/pidgin_exe_rc.rc$
+pidgin/win32/nsis/gtk-runtime-*.*.*.*.zip
+pidgin/win32/nsis/gtk_runtime_stage$
+pidgin/win32/nsis/pidgin-translations.nsh$
 install-sh
 libpurple/dbus-bindings.c
 libpurple/dbus-signals.c
--- a/COPYRIGHT	Fri Mar 05 04:19:05 2010 +0000
+++ b/COPYRIGHT	Wed Mar 10 17:58:21 2010 +0000
@@ -303,6 +303,7 @@
 Peter McCurdy
 Kurt McKee
 Torrey McMahon
+Greg McNew
 Robert McQueen
 Mihály Mészáros
 Robert Mibus
--- a/ChangeLog	Fri Mar 05 04:19:05 2010 +0000
+++ b/ChangeLog	Wed Mar 10 17:58:21 2010 +0000
@@ -15,8 +15,28 @@
 	* Use GtkStatusIcon for the docklet, providing better integration in
 	  notification area.
 	* Added UI for sending attentions (buzz, nudge) on supporting protocols.
-	* Make the search dialog unobtrusive in the conversation window (by making
-	  it look and behave like the search dialog in Firefox)
+	* Make the search dialog unobtrusive in the conversation window (by
+	  making it look and behave like the search dialog in Firefox)
+	* The Recent Log Activity sort method for the Buddy List now
+	  distinguishes between no activity and a small amount of activity
+	  in the distant past.  (Greg McNew)
+
+	Bonjour:
+	* Added support for IPv6. (Thanks to T_X for testing)
+
+	Gadu-Gadu:
+	* Updated our bundled libgadu to 1.9.0-rc2 (many thanks to Krzysztof
+	  Klinikowski for the work and testing put in here!)
+	* Minimum requirement for external libgadu is now also 1.9.0-rc2.
+
+	ICQ:
+	* X-Status (Custom ICQ status icon) support (Andrew Ivanov, Tomáš Kebert,
+	  Yuriy Yevgrafov, and trac users bob007, salieff, and nops)
+
+	XMPP:
+	* Direct messages to a specific resource only upon receipt of a message
+	  with content (as opposed to a typing notification, etc).  (Thanks to
+	  rjoly for testing)
 
 version 2.6.6 (02/18/2010):
 	libpurple:
--- a/ChangeLog.win32	Fri Mar 05 04:19:05 2010 +0000
+++ b/ChangeLog.win32	Wed Mar 10 17:58:21 2010 +0000
@@ -4,6 +4,10 @@
 	* Win9x no longer supported.
 	* Crash Report files (pidgin.RPT) are now generated in the ~/.purple
 	  directory instead of the installation directory.
+	* NSS SSL Library upgraded to 3.12.5 (thanks to Berke Viktor)
+	* GtkSpell upgraded to 2.0.16, changing the spellchecking backend to
+	  enchant.  This means that myspell, hunspell (OpenOffice) and existing
+	  aspell dictionaries can be used.
 
 version 2.6.6 (02/18/2010):
 	* Installer translations for: Norwegian nynorsk
--- a/Makefile.mingw	Fri Mar 05 04:19:05 2010 +0000
+++ b/Makefile.mingw	Wed Mar 10 17:58:21 2010 +0000
@@ -31,10 +31,7 @@
     exit; \
 }' VERSION)
 
-GTK_INSTALL_VERSION = $(shell \
-  source ../gtk_installer/version.sh; \
-  echo $$gtk_version \
-)
+GTK_INSTALL_VERSION = 2.14.7.0
 
 STRIPPED_RELEASE_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-win32bin
 DEBUG_SYMBOLS_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-dbgsym
@@ -44,19 +41,22 @@
 # should be included in this list so they don't get stripped
 EXTERNAL_DLLS = \
 	comerr32.dll \
+	exchndl.dll \
 	freebl3.dll \
 	gssapi32.dll \
 	k5sprt32.dll \
 	krb5_32.dll \
-	libgtkspell.dll \
+	libenchant.dll \
+	libgtkspell-0.dll \
 	libmeanwhile-1.dll \
+	libnspr4.dll \
+	libplc4.dll \
+	libplds4.dll \
 	libsasl.dll \
 	libxml2.dll \
-	nspr4.dll \
 	nss3.dll \
 	nssckbi.dll \
-	plc4.dll \
-	plds4.dll \
+	nssutil3.dll \
 	saslANONYMOUS.dll \
 	saslCRAMMD5.dll \
 	saslDIGESTMD5.dll \
@@ -67,12 +67,13 @@
 	libsilcclient-1-1-2.dll \
 	smime3.dll \
 	softokn3.dll \
+	sqlite3.dll \
 	ssl3.dll
 
 #build an expression for `find` to use to ignore the above files
 EXTERNAL_DLLS_FIND_EXP = $(patsubst %,-o -name %,$(EXTERNAL_DLLS))
 
-.PHONY: all docs install installer installer_offline installer_zip debug_symbols_zip installers clean uninstall create_release_install_dir generate_translations_installer_include $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT)
+.PHONY: all docs install installer installer_offline installer_zip debug_symbols_zip installers clean uninstall create_release_install_dir generate_installer_includes $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT)
 
 all: $(PIDGIN_CONFIG_H) $(PIDGIN_REVISION_H)
 	$(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE)
@@ -89,26 +90,48 @@
 endif
 	$(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) install
 	$(MAKE) -C share/sounds -f $(MINGW_MAKEFILE) install
+	mkdir -p $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp $(GTKSPELL_TOP)/bin/libgtkspell-0.dll $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp $(ENCHANT_TOP)/bin/libenchant.dll $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp -R $(ENCHANT_TOP)/lib $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp $(WIN32_DEV_TOP)/pidgin-inst-deps-20100223/exchndl.dll $(PIDGIN_INSTALL_DIR)
 
-generate_translations_installer_include: create_release_install_dir 
-	rm -f pidgin/win32/nsis/pidgin-translations.nsh
+pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip:
+	pidgin/win32/nsis/generate_gtk_zip.sh `pwd`
+
+generate_installer_includes: create_release_install_dir pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip debug_symbols_zip
+	rm -f pidgin/win32/nsis/pidgin-translations.nsh pidgin/win32/nsis/pidgin-spellcheck.nsh pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh
 	find $(STRIPPED_RELEASE_DIR)/locale -maxdepth 1 -mindepth 1 \
 	 -exec basename {} ';' \
 	 | sed -e s/^/\!insertmacro\ LANG_SECTION\ \"/ -e s/$$/\"/ \
 	 > pidgin/win32/nsis/pidgin-translations.nsh
+	#Convert the available.lst lines to "!insertmacro SPELLCHECK_SECTION lang lang_name lang_file"
+	sed -e "/^#/d" -e "s/^[^,]\{1,\},[^,]\{1,\},/\"/" \
+	 -e "s/,/\"\ \"/" -e "s/,/\"\ \"/" -e "s/[\ \t]*$$/\"/" \
+	 -e "s/^/\!insertmacro\ SPELLCHECK_SECTION\ /" \
+         pidgin/win32/nsis/available.lst \
+         > pidgin/win32/nsis/pidgin-spellcheck.nsh
+	#Convert the lines to "!insertmacro CHECK_SPELLCHECK_SECTION lang"
+	iconv -f latin1 -t utf-8 pidgin/win32/nsis/pidgin-spellcheck.nsh | \
+	 sed -e "s/SPELLCHECK_SECTION/CHECK_SPELLCHECK_SECTION/" \
+	 -e "s/ \"[^\"]*\"\ \"[^\"]*\"[\t\ ]*$$//" | \
+         iconv -f utf-8 -t latin1 \
+        > pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh
 
 create_release_install_dir: install
 	rm -rf $(STRIPPED_RELEASE_DIR)
-	cp -R $(PIDGIN_INSTALL_DIR) $(STRIPPED_RELEASE_DIR)
+	mkdir $(STRIPPED_RELEASE_DIR)
+	tar -cf - $(PIDGIN_INSTALL_DIR) --exclude=Gtk --exclude=spellcheck/share \
+	 | tar --strip 2 -xC $(STRIPPED_RELEASE_DIR) -f -
 	find $(STRIPPED_RELEASE_DIR) \( -name '*.dll' -o -name '*.exe' \) \
 	 -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) \
 	 -exec $(STRIP) --strip-unneeded {} ';'
 
-installer: create_release_install_dir generate_translations_installer_include
+installer: generate_installer_includes
 	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
 	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe ./
 
-installer_offline: create_release_install_dir generate_translations_installer_include debug_symbols_zip
+installer_offline: generate_installer_includes
 	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DOFFLINE_INSTALLER $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
 	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe ./
 
--- a/configure.ac	Fri Mar 05 04:19:05 2010 +0000
+++ b/configure.ac	Wed Mar 10 17:58:21 2010 +0000
@@ -72,8 +72,8 @@
 ])
 fi
 
-AC_CANONICAL_SYSTEM
-AM_CONFIG_HEADER(config.h)
+AC_CANONICAL_HOST
+AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([1.9 -Wno-portability dist-bzip2])
 
 PURPLE_MAJOR_VERSION=purple_major_version
@@ -152,7 +152,7 @@
 
 if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT$INTLTOOL_MSGFMT = x
 then
-	AC_ERROR([
+	AC_MSG_ERROR([
 
 The msgfmt command is required to build libpurple.  If it is installed
 on your system, ensure that it is in your path.  If it is not, install
@@ -168,9 +168,6 @@
 ])
 fi
 
-dnl we don't use autobreak on cygwin!!
-dnl AC_CYGWIN
-
 dnl Checks for header files.
 AC_HEADER_STDC
 AC_HEADER_SYS_WAIT
@@ -198,11 +195,11 @@
 
 dnl Check for inet_aton
 AC_CHECK_FUNC(inet_aton, , [AC_CHECK_LIB(resolv, inet_aton, ,
-				         [AC_ERROR(inet_aton not found)])])
+				         [AC_MSG_ERROR([inet_aton not found])])])
 AC_CHECK_LIB(resolv, __res_query)
 AC_CHECK_LIB(nsl, gethostent)
 AC_CHECK_FUNC(socket, ,
-	[AC_CHECK_LIB(socket, socket, , [AC_ERROR([socket not found])])])
+	[AC_CHECK_LIB(socket, socket, , [AC_MSG_ERROR([socket not found])])])
 dnl If all goes well, by this point the previous two checks will have
 dnl pulled in -lsocket and -lnsl if we need them.
 AC_CHECK_FUNC(getaddrinfo,
@@ -214,19 +211,18 @@
 AC_CHECK_FUNCS(getifaddrs)
 dnl Check for socklen_t (in Unix98)
 AC_MSG_CHECKING(for socklen_t)
-AC_TRY_COMPILE([
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 	#include <sys/types.h>
 	#include <sys/socket.h>
 	socklen_t x;
-], [],
-[
+]], [[]])], [
 	AC_MSG_RESULT(yes)
 ], [
-	AC_TRY_COMPILE([
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 		#include <sys/types.h>
 		#include <sys/socket.h>
 		int accept(int, struct sockaddr *, size_t *);
-	], [], [
+	]], [[]])], [
 		AC_MSG_RESULT(size_t)
 		AC_DEFINE(socklen_t, size_t, [socklen_t size])
 	], [
@@ -249,7 +245,7 @@
 AC_CHECK_FUNC(dlopen, LIBDL="", [AC_CHECK_LIB(dl, dlopen, LIBDL="-ldl")])
 
 AC_MSG_CHECKING(for fileno())
-AC_TRY_RUN([
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <stdio.h>
 
 int main(int argc, char *argv[])
@@ -260,7 +256,7 @@
 
 	return !(fd > 0);
 }
-], [
+]])], [
 	AC_MSG_RESULT(yes)
 	AC_DEFINE([HAVE_FILENO], [1],
 	          [Define to 1 if your stdio has int fileno(FILE *).])
@@ -273,7 +269,7 @@
 ])
 
 AC_MSG_CHECKING(for the %z format string in strftime())
-AC_TRY_RUN([
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
@@ -297,7 +293,7 @@
 	         (buf[4] >= '0' && buf[4] <= '9')
 	        );
 }
-], [
+]])], [
 	AC_MSG_RESULT(yes)
 	AC_DEFINE([HAVE_STRFTIME_Z_FORMAT], [1],
 		[Define to 1 if you have a strftime() that supports the %z format string.])
@@ -307,8 +303,7 @@
 	# Fallback for Cross Compiling...
 	# This will enable the compatibility code.
 	AC_MSG_RESULT(no)
-]
-)
+])
 
 dnl #######################################################################
 dnl # Check for GLib 2.12 (required)
@@ -458,10 +453,10 @@
 			if test "x$XSS_LIBS" != "x"; then
 				oldCPPFLAGS="$CPPFLAGS"
 				CPPFLAGS="$CPPFLAGS $x_incpath_add"
-				AC_TRY_COMPILE([
+				AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 					#include <X11/Xlib.h>
 					#include <X11/extensions/scrnsaver.h>
-					], [], [], [enable_screensaver=no])
+					]], [[]])], [], [enable_screensaver=no])
 				CPPFLAGS="$oldCPPFLAGS"
 			else
 				enable_screensaver=no
@@ -648,14 +643,14 @@
 			f="$location/ncurses.h"
 			AC_CHECK_HEADER($f,[
 				AC_MSG_CHECKING([if $f supports wide characters])
-				AC_TRY_COMPILE([
+				AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 					#define _XOPEN_SOURCE_EXTENDED
 					#include <$f>
-				], [
+				]], [[
 					#ifndef get_wch
 					# error get_wch not found!
 					#endif
-				], [
+				]])], [
 					dir=$location
 					if test x"$dir" != x"." ; then
 						GNT_CFLAGS="-I$dir/"
@@ -997,10 +992,10 @@
 	CPPFLAGS_save="$CPPFLAGS"
 	CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
 		AC_MSG_CHECKING(for silcmime.h)
-		AC_TRY_COMPILE([
+		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 #include <silcincludes.h>
 #include <silcmime.h>
-		], [], [
+		]], [[]])], [
 		AC_MSG_RESULT(yes)
 		AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h])
 		], [
@@ -1022,7 +1017,7 @@
 	gadu_manual_check="no"
 fi
 if test "x$gadu_manual_check" = "xno"; then
-	PKG_CHECK_MODULES(GADU, libgadu, [
+	PKG_CHECK_MODULES(GADU, [libgadu >= 1.9.0-rc2], [
 		gadu_includes="yes"
 		gadu_libs="yes"
 	], [
@@ -1049,14 +1044,33 @@
 	AC_MSG_CHECKING(for libgadu GPL compatibility)
 	CPPFLAGS_save="$CPPFLAGS"
 	CPPFLAGS="$CPPFLAGS $GADU_CFLAGS"
-	AC_TRY_COMPILE([#include <libgadu.h>], [
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[
 #if defined(__GG_LIBGADU_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_OPENSSL)
 #error "libgadu is not compatible with the GPL when compiled with OpenSSL support."
 #endif
-	], [
-		AC_MSG_RESULT(yes)
-		AC_DEFINE([HAVE_LIBGADU], [1],
-			[Define to 1 if you have libgadu.])
+	]])], [
+		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[
+#if GG_DEFAULT_PROTOCOL_VERSION < 0x2e
+#error "Your libgadu version is too old. libpurple requires 1.9.0-rc2 or higher."
+#endif
+		]])], [
+			AC_MSG_RESULT(yes)
+			AC_DEFINE([HAVE_LIBGADU], [1],
+				[Define to 1 if you have libgadu.])
+		], [
+			AC_MSG_RESULT(no)
+			echo
+			echo
+			echo "Your supplied copy of libgadu is too old."
+			echo "Install version 1.9.0-rc2 or newer."
+			echo "Then rerun this ./configure"
+			echo
+			echo "Falling back to using our own copy of libgadu"
+			echo
+			GADU_LIBS=""
+			GADU_CFLAGS=""
+			gadu_libs=no
+		])
 	], [
 		AC_MSG_RESULT(no)
 		echo
@@ -1077,12 +1091,13 @@
 
 AM_CONDITIONAL(USE_INTERNAL_LIBGADU, test "x$gadu_libs" != "xyes")
 
+if test "x$gadu_libs" = "x"; then
+	gadu_libs=no
+fi
+
 AC_SUBST(GADU_LIBS)
 AC_SUBST(GADU_CFLAGS)
 
-# change the next line to not make MSNP15 the default (s/disable/enable/; s/yes/no/;)
-AC_ARG_ENABLE(msnp15,[AC_HELP_STRING([--disable-msnp15], [Disable the newer MSNP15 protocol])],enable_msnp15=$enableval,enable_msnp15=yes)
-
 AC_ARG_ENABLE(distrib,,,enable_distrib=no)
 AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes")
 DYNAMIC_PRPLS=all
@@ -1100,9 +1115,6 @@
 if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
 	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'`
 fi
-if test "x$enable_msnp15" != "xyes" ; then
-	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/msn/msnp9/'`
-fi
 if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
 	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc/silc10/'`
 fi
@@ -1133,8 +1145,6 @@
 			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.la"
 		elif test "x$i" = "xsilc10"; then
 			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libsilcpurple.la"
-		elif test "x$i" = "xmsnp9"; then
-			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libmsn.la"
 		else
 			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la"
 		fi
@@ -1147,7 +1157,6 @@
 		irc)		static_irc=yes ;;
 		jabber)		static_jabber=yes ;;
 		msn)		static_msn=yes ;;
-		msnp9)		static_msn=yes ;;
 		myspace)	static_myspace=yes ;;
 		mxit)		static_mxit=yes ;;
 		novell)		static_novell=yes ;;
@@ -1193,9 +1202,6 @@
 if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
 	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'`
 fi
-if test "x$enable_msnp15" != "xyes" ; then
-	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/msn/msnp9/'`
-fi
 if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
 	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc/silc10/'`
 fi
@@ -1210,7 +1216,6 @@
 		irc)		dynamic_irc=yes ;;
 		jabber)		dynamic_jabber=yes ;;
 		msn)		dynamic_msn=yes ;;
-		msnp9)		dynamic_msn=yes ;;
 		myspace)	dynamic_myspace=yes ;;
 		mxit)		dynamic_mxit=yes ;;
 		novell)		dynamic_novell=yes ;;
@@ -1281,9 +1286,9 @@
 		orig_CFLAGS="$CFLAGS"
 		CFLAGS="$CFLAGS $newflag"
 		AC_MSG_CHECKING(for $newflag option to gcc)
-		AC_TRY_COMPILE([], [
+		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
 			int main() {return 0;}
-		], [
+		]])], [
 			AC_MSG_RESULT(yes)
 			CFLAGS="$orig_CFLAGS"
 			DEBUG_CFLAGS="$DEBUG_CFLAGS $newflag"
@@ -1295,7 +1300,7 @@
 
 	if test "x$enable_fortify" = "xyes"; then
 		AC_MSG_CHECKING(for FORTIFY_SOURCE support)
-		AC_TRY_COMPILE([#include <features.h>], [
+		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <features.h>]], [[
 			int main() {
 			#if !(__GNUC_PREREQ (4, 1) \
 				|| (defined __GNUC_RH_RELEASE__ && __GNUC_PREREQ (4, 0)) \
@@ -1307,7 +1312,7 @@
 			#endif
 				return 0;
 			}
-		], [
+		]])], [
 			AC_MSG_RESULT(yes)
 			DEBUG_CFLAGS="$DEBUG_CFLAGS -Wp,-D_FORTIFY_SOURCE=2"
 		], [
@@ -1481,12 +1486,15 @@
 	PY_MAJOR=`$pythonpath -c 'import sys ; print sys.version[0:2]'`
 	changequote([, ])dnl
 	if test -f $PY_PREFIX/include/python$PY_VERSION/Python.h -a "$PY_MAJOR" = "2."; then
+		AC_MSG_RESULT()
 		AC_CHECK_LIB(pthread, pthread_create, )
 		AC_CHECK_LIB(util, openpty, )
 		AC_CHECK_LIB(db, dbopen, )
 		PY_LIBS="-L$PY_EXEC_PREFIX/lib/python$PY_VERSION/config -lpython$PY_VERSION"
 		PY_CFLAGS="-I$PY_PREFIX/include/python$PY_VERSION"
 		AC_DEFINE(USE_PYTHON, [1], [Define if python headers are available.])
+		dnl Because the above AC_CHECK_LIB get in the way...
+		AC_MSG_CHECKING(for Python compile flags)
 		AC_MSG_RESULT(ok)
 	else
 		AC_MSG_RESULT([Can't find Python.h])
@@ -2144,6 +2152,7 @@
 		if test -f $dir/tclConfig.sh; then
 			TCLCONFIG=$dir/tclConfig.sh
 			AC_MSG_RESULT([yes ($TCLCONFIG)])
+			break
 		fi
 	done
 	if test "$TCLCONFIG" = "no"; then
@@ -2169,8 +2178,8 @@
 			CPPFLAGS="$CPPFLAGS $TCL_INCLUDE_SPEC -I$TCL_PREFIX/include"
 			oldLIBS=$LIBS
 			LIBS="$LIBS $TCL_LIB_SPEC"
-			AC_TRY_LINK([#include <tcl.h>],
-				[Tcl_Interp *interp=NULL; Tcl_Init(interp)],
+			AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <tcl.h>]],
+				[[Tcl_Interp *interp=NULL; Tcl_Init(interp)]])],
 				[AC_MSG_RESULT([yes]);enable_tcl=yes],
 				[AC_MSG_RESULT([no]);enable_tcl=no])
 			CPPFLAGS="$oldCPPFLAGS"
@@ -2214,6 +2223,7 @@
 		if test -f $dir/tkConfig.sh; then
 			TKCONFIG=$dir/tkConfig.sh
 			AC_MSG_RESULT([yes ($TKCONFIG)])
+			break
 		fi
 	done
 	if test "$TKCONFIG" = "no"; then
@@ -2233,8 +2243,8 @@
 		CPPFLAGS="$CPPFLAGS $TCL_CFLAGS"
 		oldLIBS=$LIBS
 		LIBS="$LIBS $TCL_LIB_SPEC $TK_LIB_SPEC"
-		AC_TRY_LINK([#include <tk.h>],
-				[Tcl_Interp *interp=NULL; Tcl_Init(interp); Tk_Init(interp);],
+		AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <tk.h>]],
+				[[Tcl_Interp *interp=NULL; Tcl_Init(interp); Tk_Init(interp);]])],
 				[AC_MSG_RESULT([yes]);enable_tk=yes],
 				[AC_MSG_RESULT([no]);enable_tk=no])
 		CPPFLAGS="$oldCPPFLAGS"
@@ -2286,7 +2296,7 @@
 			SASL_LIBS=-"lsasl2"
 		], [
 			AM_CONDITIONAL(USE_CYRUS_SASL, false)
-			AC_ERROR(Cyrus SASL library not found)
+			AC_MSG_ERROR([Cyrus SASL library not found])
 		])
 else
 	AM_CONDITIONAL(USE_CYRUS_SASL, false)
@@ -2319,7 +2329,7 @@
 			[KRB4_LIBS="-lkrb4 -ldes425 -lkrb5 -lk5crypto -lcom_err"],
 			[AC_CHECK_LIB(krb, krb_rd_req,
 				[KRB4_LIBS="-lkrb -ldes"],
-				[AC_ERROR(Kerberos 4 libraries not found)],
+				[AC_MSG_ERROR([Kerberos 4 libraries not found])],
 				-ldes)],
 			-ldes425 -lkrb5 -lk5crypto -lcom_err)
 	orig_LIBS="$LIBS"
@@ -2353,7 +2363,7 @@
 	LDFLAGS="$LDFLAGS $ZEPHYR_LDFLAGS"
 	AC_CHECK_LIB(zephyr, ZInitialize,
 		[ZEPHYR_LIBS="-lzephyr"],
-		[AC_ERROR(Zephyr libraries not found)],
+		[AC_MSG_ERROR([Zephyr libraries not found])],
 		-lzephyr)
 	orig_LIBS="$LIBS"
 	LIBS="$orig_LIBS"
@@ -2384,16 +2394,40 @@
 AC_VAR_TIMEZONE_EXTERNALS
 
 AC_CACHE_CHECK(for tm_gmtoff in struct tm, ac_cv_struct_tm_gmtoff,
-        AC_TRY_COMPILE([
+        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
                 #include <time.h>
-        ], [
+        ]], [[
                 struct tm tm;
                 tm.tm_gmtoff = 1;
-        ], ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no))
+        ]])], [ac_cv_struct_tm_gmtoff=yes], [ac_cv_struct_tm_gmtoff=no]))
 if test $ac_cv_struct_tm_gmtoff = yes; then
         AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define if you have a tm_gmtoff member in struct tm])
 fi
 
+AC_CACHE_CHECK([whether va_lists can be copied by value], ac_cv_va_val_copy,[
+	AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdarg.h>
+#include <stdlib.h>
+	void f (int i, ...) {
+	va_list args1, args2;
+	va_start (args1, i);
+	args2 = args1;
+	if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42)
+	  exit (1);
+	va_end (args1); va_end (args2);
+	}
+	int main() {
+	  f (0, 42);
+	  return 0;
+	}]])],
+	[ac_cv_va_val_copy=yes],
+	[ac_cv_va_val_copy=no],
+	[ac_cv_va_val_copy=yes])
+])
+
+if test "x$ac_cv_va_val_copy" = "xno"; then
+	AC_DEFINE(VA_COPY_AS_ARRAY, 1, ['va_lists' cannot be copied as values])
+fi
+
 dnl #######################################################################
 dnl # Check for check
 dnl #######################################################################
@@ -2482,7 +2516,7 @@
 
 AM_CONDITIONAL(PURPLE_AVAILABLE, true)
 
-AC_OUTPUT([Makefile
+AC_CONFIG_FILES([Makefile
 		   Doxyfile
 		   doc/Makefile
 		   doc/pidgin.1
@@ -2528,7 +2562,6 @@
 		   libpurple/protocols/irc/Makefile
 		   libpurple/protocols/jabber/Makefile
 		   libpurple/protocols/msn/Makefile
-		   libpurple/protocols/msnp9/Makefile
 		   libpurple/protocols/myspace/Makefile
 		   libpurple/protocols/mxit/Makefile
 		   libpurple/protocols/novell/Makefile
@@ -2555,6 +2588,7 @@
 		   po/Makefile.in
 		   pidgin.spec
 		  ])
+AC_OUTPUT
 
 echo
 echo $PACKAGE $VERSION
@@ -2583,6 +2617,7 @@
 echo Build with Cyrus SASL support. : $enable_cyrus_sasl
 echo Use kerberos 4 with zephyr.... : $kerberos
 echo Use external libzephyr........ : $zephyr
+echo Use external libgadu.......... : $gadu_libs
 echo Install pixmaps............... : $enable_pixmaps
 echo Install translations.......... : $enable_i18n
 echo Has you....................... : yes
--- a/libpurple/connection.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/connection.h	Wed Mar 10 17:58:21 2010 +0000
@@ -44,8 +44,9 @@
 	PURPLE_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */
 	PURPLE_CONNECTION_NO_URLDESC = 0x0040,  /**< Connection does not support descriptions with links */
 	PURPLE_CONNECTION_NO_IMAGES = 0x0080,  /**< Connection does not support sending of images */
-	PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100 /**< Connection supports sending and receiving custom smileys */
-	
+	PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100, /**< Connection supports sending and receiving custom smileys */
+	PURPLE_CONNECTION_SUPPORT_MOODS = 0x0200, /**< Connection supports setting moods */
+	PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES = 0x0400 /**< Connection supports setting a message on moods */
 } PurpleConnectionFlags;
 
 typedef enum
--- a/libpurple/example/nullclient.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/example/nullclient.c	Wed Mar 10 17:58:21 2010 +0000
@@ -27,7 +27,11 @@
 
 #include <signal.h>
 #include <string.h>
+#ifndef _WIN32
 #include <unistd.h>
+#else
+#include "win32/win32dep.h"
+#endif
 
 #include "defines.h"
 
@@ -80,7 +84,11 @@
 	if (condition & PURPLE_INPUT_WRITE)
 		cond |= PURPLE_GLIB_WRITE_COND;
 
+#if defined _WIN32 && !defined WINPIDGIN_USE_GLIB_IO_CHANNEL
+	channel = wpurple_g_io_channel_win32_new_socket(fd);
+#else
 	channel = g_io_channel_unix_new(fd);
+#endif
 	closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
 					      purple_glib_io_invoke, closure, purple_glib_io_destroy);
 
@@ -253,12 +261,14 @@
 	PurpleSavedStatus *status;
 	char *res;
 
+#ifndef _WIN32
 	/* libpurple's built-in DNS resolution forks processes to perform
 	 * blocking lookups without blocking the main process.  It does not
 	 * handle SIGCHLD itself, so if the UI does not you quickly get an army
 	 * of zombie subprocesses marching around.
 	 */
 	signal(SIGCHLD, SIG_IGN);
+#endif
 
 	init_libpurple();
 
--- a/libpurple/ft.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/ft.c	Wed Mar 10 17:58:21 2010 +0000
@@ -579,7 +579,7 @@
 	type = purple_xfer_get_type(xfer);
 	account = purple_xfer_get_account(xfer);
 
-	purple_debug_misc("xfer", "request accepted for %p\n", xfer); 
+	purple_debug_misc("xfer", "request accepted for %p\n", xfer);
 
 	if (!filename && type == PURPLE_XFER_RECEIVE) {
 		xfer->status = PURPLE_XFER_STATUS_ACCEPTED;
@@ -1085,9 +1085,10 @@
 			return;
 		}
 	} else if (xfer->type == PURPLE_XFER_SEND) {
-		size_t result;
+		size_t result = 0;
 		size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size);
 		PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
+		gboolean read = TRUE;
 
 		/* this is so the prpl can keep the connection open
 		   if it needs to for some odd reason. */
@@ -1099,54 +1100,59 @@
 			return;
 		}
 
-		if (ui_ops && ui_ops->ui_read) {
-			gssize tmp = ui_ops->ui_read(xfer, &buffer, s);
-			if (tmp == 0) {
-				/*
-				 * The UI claimed it was ready, but didn't have any data for
-				 * us...  It will call purple_xfer_ui_ready when ready, which
-				 * sets back up this watcher.
-				 */
-				if (xfer->watcher != 0) {
-					purple_input_remove(xfer->watcher);
-					xfer->watcher = 0;
+		if (priv->buffer) {
+			if (priv->buffer->len < s) {
+				s -= priv->buffer->len;
+				read = TRUE;
+			} else {
+				read = FALSE;
+			}
+		}
+
+		if (read) {
+			if (ui_ops && ui_ops->ui_read) {
+				gssize tmp = ui_ops->ui_read(xfer, &buffer, s);
+				if (tmp == 0) {
+					/*
+					 * The UI claimed it was ready, but didn't have any data for
+					 * us...  It will call purple_xfer_ui_ready when ready, which
+					 * sets back up this watcher.
+					 */
+					if (xfer->watcher != 0) {
+						purple_input_remove(xfer->watcher);
+						xfer->watcher = 0;
+					}
+
+					/* Need to indicate the prpl is still ready... */
+					priv->ready |= PURPLE_XFER_READY_PRPL;
+
+					g_return_if_reached();
+				} else if (tmp < 0) {
+					purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
+					purple_xfer_cancel_local(xfer);
+					return;
 				}
 
-				/* Need to indicate the prpl is still ready... */
-				priv->ready |= PURPLE_XFER_READY_PRPL;
-
-				/*
-				 * if we requested 0 bytes it's only normal that end up here 
-				 * we shouldn't return as we still have something to 
-				 * write in priv->buffer
-				 */
-				if (s != 0)
-					g_return_if_reached();
-			} else if (tmp < 0) {
-				purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
-				purple_xfer_cancel_local(xfer);
-				return;
-			}
-
-			result = tmp;
-		} else {
-			buffer = g_malloc0(s);
-			result = fread(buffer, 1, s, xfer->dest_fp);
-			if (result != s) {
-				purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
-				purple_xfer_cancel_local(xfer);
-				g_free(buffer);
-				return;
+				result = tmp;
+			} else {
+				buffer = g_malloc(s);
+				result = fread(buffer, 1, s, xfer->dest_fp);
+				if (result != s) {
+					purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
+					purple_xfer_cancel_local(xfer);
+					g_free(buffer);
+					return;
+				}
 			}
 		}
-	
+
 		if (priv->buffer) {
 			priv->buffer = g_byte_array_append(priv->buffer, buffer, result);
 			g_free(buffer);
 			buffer = priv->buffer->data;
 			result = priv->buffer->len;
 		}
-	
+
 		r = purple_xfer_write(xfer, buffer, result);
 
 		if (r == -1) {
@@ -1161,12 +1167,12 @@
 			 */
 			purple_xfer_increase_buffer_size(xfer);
 		} else {
-			if (ui_ops && ui_ops->data_not_sent) 
-				ui_ops->data_not_sent(xfer, buffer + r, result -r);
+			if (ui_ops && ui_ops->data_not_sent)
+				ui_ops->data_not_sent(xfer, buffer + r, result - r);
 		}
 
 		if (priv->buffer) {
-			/* 
+			/*
 			 * Remove what we wrote
 			 * If we wrote the whole buffer the byte array will be empty
 			 * Otherwise we'll kee what wasn't sent for next time.
--- a/libpurple/log.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/log.c	Wed Mar 10 17:58:21 2010 +0000
@@ -302,7 +302,7 @@
 			}
 		}
 
-		score = (gint)score_double;
+		score = (gint) ceil(score_double);
 		g_hash_table_replace(logsize_users_decayed, lu, GINT_TO_POINTER(score));
 	}
 	return score;
--- a/libpurple/plugins/perl/common/module.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/plugins/perl/common/module.h	Wed Mar 10 17:58:21 2010 +0000
@@ -9,6 +9,7 @@
 #include <glib.h>
 #ifdef _WIN32
 #undef pipe
+#undef STRINGIFY
 #endif
 #include <EXTERN.h>
 #include <perl.h>
--- a/libpurple/plugins/perl/perl-common.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/plugins/perl/perl-common.c	Wed Mar 10 17:58:21 2010 +0000
@@ -472,74 +472,74 @@
 }
 
 SV *
-purple_perl_sv_from_vargs(const PurpleValue *value, va_list args, void ***copy_arg)
+purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_arg)
 {
 	if (purple_value_is_outgoing(value)) {
 		switch (purple_value_get_type(value)) {
 			case PURPLE_TYPE_SUBTYPE:
-				if ((*copy_arg = va_arg(args, void **)) == NULL)
+				if ((*copy_arg = va_arg(*args, void **)) == NULL)
 					return &PL_sv_undef;
 
 				return purple_perl_sv_from_subtype(value, *(void **)*copy_arg);
 
 			case PURPLE_TYPE_BOOLEAN:
-				if ((*copy_arg = (void *)va_arg(args, gboolean *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, gboolean *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(gboolean *)*copy_arg);
 
 			case PURPLE_TYPE_INT:
-				if ((*copy_arg = (void *)va_arg(args, int *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, int *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(int *)*copy_arg);
 
 			case PURPLE_TYPE_UINT:
-				if ((*copy_arg = (void *)va_arg(args, unsigned int *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, unsigned int *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVuv(*(unsigned int *)*copy_arg);
 
 			case PURPLE_TYPE_LONG:
-				if ((*copy_arg = (void *)va_arg(args, long *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, long *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(long *)*copy_arg);
 
 			case PURPLE_TYPE_ULONG:
-				if ((*copy_arg = (void *)va_arg(args,
+				if ((*copy_arg = (void *)va_arg(*args,
 												unsigned long *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVuv(*(unsigned long *)*copy_arg);
 
 			case PURPLE_TYPE_INT64:
-				if ((*copy_arg = (void *)va_arg(args, gint64 *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, gint64 *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(gint64 *)*copy_arg);
 
 			case PURPLE_TYPE_UINT64:
-				if ((*copy_arg = (void *)va_arg(args, guint64 *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, guint64 *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVuv(*(guint64 *)*copy_arg);
 
 			case PURPLE_TYPE_STRING:
-				if ((*copy_arg = (void *)va_arg(args, char **)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, char **)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVGChar(*(char **)*copy_arg);
 
 			case PURPLE_TYPE_POINTER:
-				if ((*copy_arg = va_arg(args, void **)) == NULL)
+				if ((*copy_arg = va_arg(*args, void **)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv((IV)*(void **)*copy_arg);
 
 			case PURPLE_TYPE_BOXED:
 				/* Uh.. I dunno. Try this? */
-				if ((*copy_arg = va_arg(args, void **)) == NULL)
+				if ((*copy_arg = va_arg(*args, void **)) == NULL)
 					return &PL_sv_undef;
 
 				return sv_2mortal(purple_perl_bless_object(
@@ -553,40 +553,40 @@
 	} else {
 		switch (purple_value_get_type(value)) {
 			case PURPLE_TYPE_SUBTYPE:
-				if ((*copy_arg = va_arg(args, void *)) == NULL)
+				if ((*copy_arg = va_arg(*args, void *)) == NULL)
 					return &PL_sv_undef;
 
 				return purple_perl_sv_from_subtype(value, *copy_arg);
 
 			case PURPLE_TYPE_BOOLEAN:
-				*copy_arg = GINT_TO_POINTER( va_arg(args, gboolean) );
+				*copy_arg = GINT_TO_POINTER( va_arg(*args, gboolean) );
 
 				return newSViv((gboolean)GPOINTER_TO_INT(*copy_arg));
 
 			case PURPLE_TYPE_INT:
-				*copy_arg = GINT_TO_POINTER( va_arg(args, int) );
+				*copy_arg = GINT_TO_POINTER( va_arg(*args, int) );
 
 				return newSViv(GPOINTER_TO_INT(*copy_arg));
 
 			case PURPLE_TYPE_UINT:
-				*copy_arg = GUINT_TO_POINTER(va_arg(args, unsigned int));
+				*copy_arg = GUINT_TO_POINTER(va_arg(*args, unsigned int));
 
 				return newSVuv(GPOINTER_TO_UINT(*copy_arg));
 
 			case PURPLE_TYPE_LONG:
-				*copy_arg = (void *)va_arg(args, long);
+				*copy_arg = (void *)va_arg(*args, long);
 
 				return newSViv((long)*copy_arg);
 
 			case PURPLE_TYPE_ULONG:
-				*copy_arg = (void *)va_arg(args, unsigned long);
+				*copy_arg = (void *)va_arg(*args, unsigned long);
 
 				return newSVuv((unsigned long)*copy_arg);
 
 			case PURPLE_TYPE_INT64:
 #if 0
 				/* XXX This yells and complains. */
-				*copy_arg = va_arg(args, gint64);
+				*copy_arg = va_arg(*args, gint64);
 
 				return newSViv(*copy_arg);
 #endif
@@ -595,27 +595,27 @@
 			case PURPLE_TYPE_UINT64:
 				/* XXX This also yells and complains. */
 #if 0
-				*copy_arg = (void *)va_arg(args, guint64);
+				*copy_arg = (void *)va_arg(*args, guint64);
 
 				return newSVuv(*copy_arg);
 #endif
 				break;
 
 			case PURPLE_TYPE_STRING:
-				if ((*copy_arg = (void *)va_arg(args, char *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, char *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVGChar((char *)*copy_arg);
 
 			case PURPLE_TYPE_POINTER:
-				if ((*copy_arg = (void *)va_arg(args, void *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv((IV)*copy_arg);
 
 			case PURPLE_TYPE_BOXED:
 				/* Uh.. I dunno. Try this? */
-				if ((*copy_arg = (void *)va_arg(args, void *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL)
 					return &PL_sv_undef;
 
 				return sv_2mortal(purple_perl_bless_object(*copy_arg,
--- a/libpurple/plugins/perl/perl-common.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/plugins/perl/perl-common.h	Wed Mar 10 17:58:21 2010 +0000
@@ -3,6 +3,7 @@
 
 #include <glib.h>
 #ifdef _WIN32
+#undef STRINGIFY
 #undef pipe
 #endif
 #include <EXTERN.h>
@@ -65,7 +66,7 @@
 #endif
 
 void *purple_perl_data_from_sv(PurpleValue *value, SV *sv);
-SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list args,
+SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args,
                             void ***copy_arg);
 SV *purple_perl_sv_from_fun(PurplePlugin *plugin, SV *callback);
 #endif /* _PURPLE_PERL_COMMON_H_ */
--- a/libpurple/plugins/perl/perl-handlers.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/plugins/perl/perl-handlers.c	Wed Mar 10 17:58:21 2010 +0000
@@ -298,7 +298,11 @@
 
 	for (i = 0; i < value_count; i++) {
 		sv_args[i] = purple_perl_sv_from_vargs(values[i],
-		                                       args,
+#ifdef VA_COPY_AS_ARRAY
+		                                       (va_list*)args,
+#else
+		                                       (va_list*)&args,
+#endif
 		                                       &copy_args[i]);
 
 		XPUSHs(sv_args[i]);
--- a/libpurple/plugins/ssl/Makefile.mingw	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/plugins/ssl/Makefile.mingw	Wed Mar 10 17:58:21 2010 +0000
@@ -15,14 +15,16 @@
 
 NEEDED_DLLS = \
 			$(NSS_TOP)/lib/freebl3.dll \
+			$(NSS_TOP)/lib/libnspr4.dll \
+			$(NSS_TOP)/lib/libplc4.dll \
+			$(NSS_TOP)/lib/libplds4.dll \
 			$(NSS_TOP)/lib/nss3.dll \
 			$(NSS_TOP)/lib/nssckbi.dll \
-			$(NSS_TOP)/lib/softokn3.dll \
+			$(NSS_TOP)/lib/nssutil3.dll \
 			$(NSS_TOP)/lib/smime3.dll \
-			$(NSS_TOP)/lib/ssl3.dll \
-			$(NSPR_TOP)/lib/nspr4.dll \
-			$(NSPR_TOP)/lib/plc4.dll \
-			$(NSPR_TOP)/lib/plds4.dll
+			$(NSS_TOP)/lib/softokn3.dll \
+                        $(NSS_TOP)/lib/sqlite3.dll \
+			$(NSS_TOP)/lib/ssl3.dll 
 
 ##
 ## INCLUDE PATHS
@@ -34,13 +36,11 @@
 			-I$(PURPLE_TOP) \
 			-I$(PURPLE_TOP)/win32 \
 			-I$(PIDGIN_TREE_TOP) \
-			-I$(NSS_TOP)/include \
-			-I$(NSPR_TOP)/include
+			-I$(NSS_TOP)/include
 
 LIB_PATHS +=		-L$(GTK_TOP)/lib \
 			-L$(PURPLE_TOP) \
-			-L$(NSS_TOP)/lib \
-			-L$(NSPR_TOP)/lib
+			-L$(NSS_TOP)/lib
 
 ##
 ##  SOURCES, OBJECTS
--- a/libpurple/protocols/Makefile.am	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/Makefile.am	Wed Mar 10 17:58:21 2010 +0000
@@ -1,5 +1,5 @@
 EXTRA_DIST = Makefile.mingw
 
-DIST_SUBDIRS = bonjour gg irc jabber msn msnp9 myspace mxit novell null oscar qq sametime silc silc10 simple yahoo zephyr
+DIST_SUBDIRS = bonjour gg irc jabber msn myspace mxit novell null oscar qq sametime silc silc10 simple yahoo zephyr
 
 SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS)
--- a/libpurple/protocols/bonjour/bonjour.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Wed Mar 10 17:58:21 2010 +0000
@@ -101,6 +101,8 @@
 
 	/* Start waiting for jabber connections (iChat style) */
 	bd->jabber_data = g_new0(BonjourJabber, 1);
+	bd->jabber_data->socket = -1;
+	bd->jabber_data->socket6 = -1;
 	bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
 	bd->jabber_data->account = account;
 
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Wed Mar 10 17:58:21 2010 +0000
@@ -747,8 +747,7 @@
 	XepIq *iq;
 	xmlnode *query, *streamhost;
 	gchar *port;
-	const char *next_ip, *local_ip;
-	const char token [] = ";";
+	GSList *local_ips;
 	BonjourData *bd;
 
 	purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
@@ -773,17 +772,16 @@
 
 	xfer->local_port = purple_network_get_port_from_fd(sock);
 
-	local_ip = purple_network_get_my_ip_ext2(sock);
-	/* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */
-	next_ip = strtok((char *)local_ip, token);
+	local_ips = bonjour_jabber_get_local_ips(sock);
 
 	port = g_strdup_printf("%hu", xfer->local_port);
-	while(next_ip != NULL) {
+	while(local_ips) {
 		streamhost = xmlnode_new_child(query, "streamhost");
 		xmlnode_set_attrib(streamhost, "jid", xf->sid);
-		xmlnode_set_attrib(streamhost, "host", next_ip);
+		xmlnode_set_attrib(streamhost, "host", local_ips->data);
 		xmlnode_set_attrib(streamhost, "port", port);
-		next_ip = strtok(NULL, token);
+		g_free(local_ips->data);
+		local_ips = g_slist_delete_link(local_ips, local_ips);
 	}
 	g_free(port);
 
@@ -796,15 +794,17 @@
 	XepXfer *xf;
 	if(xfer == NULL)
 		return;
+
 	purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
 	xf = xfer->data;
+
 	purple_network_listen_map_external(FALSE);
 	xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
 						      bonjour_bytestreams_listen, xfer);
 	purple_network_listen_map_external(TRUE);
-	if (xf->listen_data == NULL) {
+	if (xf->listen_data == NULL)
 		purple_xfer_cancel_local(xfer);
-	}
+
 	return;
 }
 
--- a/libpurple/protocols/bonjour/jabber.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Wed Mar 10 17:58:21 2010 +0000
@@ -44,6 +44,11 @@
 #endif
 #include <fcntl.h>
 
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
+
 #include "network.h"
 #include "eventloop.h"
 #include "connection.h"
@@ -623,15 +628,22 @@
 
 }
 
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
 static void
 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
 {
 	BonjourJabber *jdata = data;
-	struct sockaddr_in their_addr; /* connector's address information */
-	socklen_t sin_size = sizeof(struct sockaddr);
+	struct sockaddr_storage their_addr; /* connector's address information */
+	socklen_t sin_size = sizeof(struct sockaddr_storage);
 	int client_socket;
 	int flags;
-	char *address_text = NULL;
+#ifdef HAVE_INET_NTOP
+	char addrstr[INET6_ADDRSTRLEN];
+#endif
+	const char *address_text;
 	struct _match_buddies_by_address_t *mbba;
 	BonjourJabberConversation *bconv;
 	GSList *buddies;
@@ -640,7 +652,9 @@
 	if (condition != PURPLE_INPUT_READ)
 		return;
 
-	if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1)
+	memset(&their_addr, 0, sin_size);
+
+	if ((client_socket = accept(server_socket, (struct sockaddr*)&their_addr, &sin_size)) == -1)
 		return;
 
 	flags = fcntl(client_socket, F_GETFL);
@@ -650,7 +664,16 @@
 #endif
 
 	/* Look for the buddy that has opened the conversation and fill information */
-	address_text = inet_ntoa(their_addr.sin_addr);
+#ifdef HAVE_INET_NTOP
+	if (their_addr.ss_family == AF_INET6)
+		address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in6 *)&their_addr)->sin6_addr,
+			addrstr, sizeof(addrstr));
+	else
+		address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in *)&their_addr)->sin_addr,
+			addrstr, sizeof(addrstr));
+#else
+	address_text = inet_ntoa(((struct sockaddr_in *)&their_addr)->sin_addr);
+#endif
 	purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
 	mbba = g_new0(struct _match_buddies_by_address_t, 1);
 	mbba->address = address_text;
@@ -680,52 +703,42 @@
 
 }
 
-gint
-bonjour_jabber_start(BonjourJabber *jdata)
+static int
+start_serversocket_listening(int port, int socket, struct sockaddr *addr, size_t addr_size, gboolean ip6, gboolean allow_port_fallback)
 {
-	struct sockaddr_in my_addr;
+	int ret_port = port;
 
-	/* Open a listening socket for incoming conversations */
-	jdata->socket = socket(PF_INET, SOCK_STREAM, 0);
-	if (jdata->socket < 0) {
-		gchar *buf = g_strdup_printf(_("Unable to create socket: %s"),
-				g_strerror(errno));
-		purple_connection_error_reason(jdata->account->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
-		g_free(buf);
-		return -1;
-	}
-
-	memset(&my_addr, 0, sizeof(struct sockaddr_in));
-	my_addr.sin_family = AF_INET;
+	purple_debug_info("bonjour", "Attempting to bind IPv%d socket to port %d.\n", ip6 ? 6 : 4, port);
 
 	/* Try to use the specified port - if it isn't available, use a random port */
-	my_addr.sin_port = htons(jdata->port);
-	if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
-	{
+	if (bind(socket, addr, addr_size) != 0) {
+
 		purple_debug_info("bonjour", "Unable to bind to specified "
-				"port %i: %s\n", jdata->port, g_strerror(errno));
-		my_addr.sin_port = 0;
-		if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
-		{
-			gchar *buf = g_strdup_printf(_("Unable to bind socket "
-					"to port: %s"), g_strerror(errno));
-			purple_connection_error_reason(jdata->account->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
-			g_free(buf);
+				"port %i: %s\n", port, g_strerror(errno));
+
+		if (!allow_port_fallback) {
+			purple_debug_warning("bonjour", "Not attempting random port assignment.\n");
 			return -1;
 		}
-		jdata->port = purple_network_get_port_from_fd(jdata->socket);
+#ifdef PF_INET6
+		if (ip6)
+			((struct sockaddr_in6 *) addr)->sin6_port = 0;
+		else
+#endif
+		((struct sockaddr_in *) addr)->sin_port = 0;
+
+		if (bind(socket, addr, addr_size) != 0) {
+			purple_debug_error("bonjour", "Unable to bind IPv%d socket to port: %s\n", ip6 ? 6 : 4, g_strerror(errno));
+			return -1;
+		}
+		ret_port = purple_network_get_port_from_fd(socket);
 	}
 
+	purple_debug_info("bonjour", "Bound IPv%d socket to port %d.\n", ip6 ? 6 : 4, ret_port);
+
 	/* Attempt to listen on the bound socket */
-	if (listen(jdata->socket, 10) != 0)
-	{
-		gchar *buf = g_strdup_printf(_("Unable to listen on socket: %s"),
-				g_strerror(errno));
-		purple_connection_error_reason(jdata->account->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
-		g_free(buf);
+	if (listen(socket, 10) != 0) {
+		purple_debug_error("bonjour", "Unable to listen on IPv%d socket: %s\n", ip6 ? 6 : 4, g_strerror(errno));
 		return -1;
 	}
 
@@ -739,8 +752,66 @@
 	}
 #endif
 
-	/* Open a watcher in the socket we have just opened */
-	jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+	return ret_port;
+}
+
+gint
+bonjour_jabber_start(BonjourJabber *jdata)
+{
+	int ipv6_port = -1, ipv4_port = -1;
+
+	/* Open a listening socket for incoming conversations */
+#ifdef PF_INET6
+	jdata->socket6 = socket(PF_INET6, SOCK_STREAM, 0);
+#endif
+	jdata->socket = socket(PF_INET, SOCK_STREAM, 0);
+	if (jdata->socket == -1 && jdata->socket6 == -1) {
+		purple_debug_error("bonjour", "Unable to create socket: %s",
+				g_strerror(errno));
+		return -1;
+	}
+
+#ifdef PF_INET6
+	if (jdata->socket6 != -1) {
+		struct sockaddr_in6 addr6;
+	        memset(&addr6, 0, sizeof(addr6));
+		addr6.sin6_family = AF_INET6;
+		addr6.sin6_port = htons(jdata->port);
+      		addr6.sin6_addr = in6addr_any;
+		ipv6_port = start_serversocket_listening(jdata->port, jdata->socket6, (struct sockaddr *) &addr6, sizeof(addr6), TRUE, TRUE);
+		/* Open a watcher in the socket we have just opened */
+		if (ipv6_port > 0) {
+			jdata->watcher_id6 = purple_input_add(jdata->socket6, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+			jdata->port = ipv6_port;
+		} else {
+			purple_debug_error("bonjour", "Failed to start listening on IPv6 socket.\n");
+			close(jdata->socket6);
+			jdata->socket6 = -1;
+		}
+	}
+#endif
+	if (jdata->socket != -1) {
+		struct sockaddr_in addr4;
+		memset(&addr4, 0, sizeof(addr4));
+		addr4.sin_family = AF_INET;
+		addr4.sin_port = htons(jdata->port);
+		ipv4_port = start_serversocket_listening(jdata->port, jdata->socket, (struct sockaddr *) &addr4, sizeof(addr4), FALSE, ipv6_port != -1);
+		/* Open a watcher in the socket we have just opened */
+		if (ipv4_port > 0) {
+			jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+			jdata->port = ipv4_port;
+		} else {
+			purple_debug_error("bonjour", "Failed to start listening on IPv4 socket.\n");
+			close(jdata->socket);
+			jdata->socket = -1;
+		}
+	}
+
+	if (!(ipv6_port > 0 || ipv4_port > 0)) {
+		purple_debug_error("bonjour", "Unable to listen on socket: %s",
+				g_strerror(errno));
+		return -1;
+	}
 
 	return jdata->port;
 }
@@ -1101,6 +1172,10 @@
 		close(jdata->socket);
 	if (jdata->watcher_id > 0)
 		purple_input_remove(jdata->watcher_id);
+	if (jdata->socket6 >= 0)
+		close(jdata->socket6);
+	if (jdata->watcher_id6 > 0)
+		purple_input_remove(jdata->watcher_id6);
 
 	/* Close all the conversation sockets and remove all the watchers after sending end streams */
 	if (jdata->account->gc != NULL) {
@@ -1234,58 +1309,97 @@
 	return (ret >= 0) ? 0 : -1;
 }
 
-/* This returns a ';' delimited string containing all non-localhost IPs */
-const char *
-purple_network_get_my_ip_ext2(int fd)
+/* This returns a list containing all non-localhost IPs */
+GSList *
+bonjour_jabber_get_local_ips(int fd)
 {
-	char buffer[1024];
-	static char ip_ext[17 * 10];
+	GSList *ips = NULL;
+	const char *address_text;
+	int ret;
+
+#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
+	{
+	struct ifaddrs *ifap, *ifa;
+	struct sockaddr *addr;
+	char addrstr[INET6_ADDRSTRLEN];
+
+	ret = getifaddrs(&ifap);
+	if (ret != 0) {
+		const char *error = g_strerror(errno);
+		purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
+		return NULL;
+	}
+
+	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+		if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
+			continue;
+
+		addr = ifa->ifa_addr;
+		address_text = NULL;
+		switch (addr->sa_family) {
+			case AF_INET:
+				address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr,
+					addrstr, sizeof(addrstr));
+				break;
+#ifdef PF_INET6
+			case AF_INET6:
+				address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr,
+					addrstr, sizeof(addrstr));
+				break;
+#endif
+		}
+
+		if (address_text != NULL) {
+			if (addr->sa_family == AF_INET)
+				ips = g_slist_append(ips, g_strdup(address_text));
+			else
+				ips = g_slist_prepend(ips, g_strdup(address_text));
+		}
+	}
+
+	freeifaddrs(ifap);
+
+	}
+#else
+	{
 	char *tmp;
-	char *tip;
 	struct ifconf ifc;
 	struct ifreq *ifr;
+	char buffer[1024];
 	struct sockaddr_in *sinptr;
-	guint32 lhost = htonl(127 * 256 * 256 * 256 + 1);
-	long unsigned int add;
 	int source = fd;
-	int len, count = 0;
 
 	if (fd < 0)
 		source = socket(PF_INET, SOCK_STREAM, 0);
 
 	ifc.ifc_len = sizeof(buffer);
 	ifc.ifc_req = (struct ifreq *)buffer;
-	ioctl(source, SIOCGIFCONF, &ifc);
+	ret = ioctl(source, SIOCGIFCONF, &ifc);
 
 	if (fd < 0)
 		close(source);
 
-	memset(ip_ext, 0, sizeof(ip_ext));
-	memcpy(ip_ext, "0.0.0.0", 7);
+	if (ret < 0) {
+		const char *error = g_strerror(errno);
+		purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
+		return NULL;
+	}
+
 	tmp = buffer;
-	tip = ip_ext;
-	while (tmp < buffer + ifc.ifc_len && count < 10)
-	{
+	while (tmp < buffer + ifc.ifc_len) {
 		ifr = (struct ifreq *)tmp;
 		tmp += HX_SIZE_OF_IFREQ(*ifr);
 
-		if (ifr->ifr_addr.sa_family == AF_INET)
-		{
+		if (ifr->ifr_addr.sa_family == AF_INET) {
 			sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
-			if (sinptr->sin_addr.s_addr != lhost)
-			{
-				add = ntohl(sinptr->sin_addr.s_addr);
-				len = g_snprintf(tip, 17, "%lu.%lu.%lu.%lu;",
-					((add >> 24) & 255),
-					((add >> 16) & 255),
-					((add >> 8) & 255),
-					add & 255);
-				tip = &tip[len];
-				count++;
-				continue;
+			if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) {
+				address_text = inet_ntoa(sinptr->sin_addr);
+				ips = g_slist_prepend(ips, g_strdup(address_text));
 			}
-		}
+ 		}
 	}
+	}
+#endif
 
-	return ip_ext;
+	return ips;
 }
--- a/libpurple/protocols/bonjour/jabber.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Wed Mar 10 17:58:21 2010 +0000
@@ -37,7 +37,9 @@
 {
 	gint port;
 	gint socket;
+	gint socket6;
 	gint watcher_id;
+	gint watcher_id6;
 	PurpleAccount *account;
 	GSList *pending_conversations;
 } BonjourJabber;
@@ -105,6 +107,6 @@
 
 XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id);
 int xep_iq_send_and_free(XepIq *iq);
-const char *purple_network_get_my_ip_ext2(int fd);
+GSList * bonjour_jabber_get_local_ips(int fd);
 
 #endif /* _BONJOUR_JABBER_H_ */
--- a/libpurple/protocols/bonjour/mdns_avahi.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/bonjour/mdns_avahi.c	Wed Mar 10 17:58:21 2010 +0000
@@ -189,8 +189,12 @@
 					bb->ips = g_slist_remove(bb->ips, rd->ip);
 					g_free((gchar *) rd->ip);
 				}
-				bb->ips = g_slist_prepend(bb->ips, g_strdup(ip));
-				rd->ip = bb->ips->data;
+				rd->ip = g_strdup(ip);
+				/* IPv6 goes at the front of the list and IPv4 at the end so that we "prefer" IPv6, if present */
+				if (protocol == AVAHI_PROTO_INET6)
+					bb->ips = g_slist_prepend(bb->ips, (gchar *) rd->ip);
+				else
+					bb->ips = g_slist_append(bb->ips, (gchar *) rd->ip);
 			}
 
 			bb->port_p2pj = port;
@@ -249,7 +253,7 @@
 			/* Make sure it isn't us */
 			if (purple_utf8_strcasecmp(name, account->username) != 0) {
 				if (!avahi_service_resolver_new(avahi_service_browser_get_client(b),
-						interface, protocol, name, type, domain, AVAHI_PROTO_INET,
+						interface, protocol, name, type, domain, protocol,
 						0, _resolver_callback, account)) {
 					purple_debug_warning("bonjour", "_browser_callback -- Error initiating resolver: %s\n",
 						avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
@@ -448,14 +452,14 @@
 		case PUBLISH_START:
 			publish_result = avahi_entry_group_add_service_strlst(
 				idata->group, AVAHI_IF_UNSPEC,
-				AVAHI_PROTO_INET, 0,
+				AVAHI_PROTO_UNSPEC, 0,
 				purple_account_get_username(data->account),
 				LINK_LOCAL_RECORD_NAME, NULL, NULL, data->port_p2pj, lst);
 			break;
 		case PUBLISH_UPDATE:
 			publish_result = avahi_entry_group_update_service_txt_strlst(
 				idata->group, AVAHI_IF_UNSPEC,
-				AVAHI_PROTO_INET, 0,
+				AVAHI_PROTO_UNSPEC, 0,
 				purple_account_get_username(data->account),
 				LINK_LOCAL_RECORD_NAME, NULL, lst);
 			break;
@@ -487,7 +491,7 @@
 
 	g_return_val_if_fail(idata != NULL, FALSE);
 
-	idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account);
+	idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account);
 	if (!idata->sb) {
 
 		purple_debug_error("bonjour",
@@ -533,7 +537,7 @@
 				purple_account_get_username(data->account));
 
 		ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC,
-			AVAHI_PROTO_INET, flags, svc_name,
+			AVAHI_PROTO_UNSPEC, flags, svc_name,
 			AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len);
 
 		g_free(svc_name);
@@ -622,7 +626,7 @@
 
 	name = g_strdup_printf("%s." LINK_LOCAL_RECORD_NAME "local", buddy->name);
 	idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC,
-		AVAHI_PROTO_INET, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
+		AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
 		_buddy_icon_record_cb, buddy);
 	g_free(name);
 
--- a/libpurple/protocols/bonjour/mdns_win32.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Wed Mar 10 17:58:21 2010 +0000
@@ -251,7 +251,7 @@
 
 static void DNSSD_API
 _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
-    const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
+    const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context)
 {
 	ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
--- a/libpurple/protocols/gg/Makefile.am	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/Makefile.am	Wed Mar 10 17:58:21 2010 +0000
@@ -4,14 +4,19 @@
 	lib/compat.h \
 	lib/COPYING \
 	lib/dcc.c \
+	lib/dcc7.c \
 	lib/events.c \
 	lib/http.c \
 	lib/libgadu.c \
 	lib/libgadu-config.h \
 	lib/libgadu.h \
 	lib/obsolete.c \
+	lib/protocol.h \
+	lib/pubdir.c \
 	lib/pubdir50.c \
-	lib/pubdir.c
+	lib/resolver.c \
+	lib/resolver.h \
+	lib/sha1.c
 
 pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
 
@@ -20,16 +25,21 @@
 	lib/common.c \
 	lib/compat.h \
 	lib/dcc.c \
+	lib/dcc7.c \
 	lib/events.c \
 	lib/http.c \
 	lib/libgadu.c \
 	lib/libgadu-config.h \
 	lib/libgadu.h \
 	lib/obsolete.c \
+	lib/protocol.h \
+	lib/pubdir.c \
 	lib/pubdir50.c \
-	lib/pubdir.c
+	lib/resolver.c \
+	lib/resolver.h \
+	lib/sha1.c
 
-INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib
+INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib -DGG_IGNORE_DEPRECATED
 endif
 
 GGSOURCES = \
--- a/libpurple/protocols/gg/Makefile.mingw	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/Makefile.mingw	Wed Mar 10 17:58:21 2010 +0000
@@ -8,7 +8,7 @@
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
 TARGET = libgg
-CFLAGS += -include win32dep.h
+CFLAGS += -include win32dep.h -DGG_IGNORE_DEPRECATED
 TYPE = PLUGIN
 
 # Static or Plugin...
@@ -41,12 +41,16 @@
 ##
 C_SRC =	\
 	lib/common.c \
+	lib/dcc.c \
+	lib/dcc7.c \
 	lib/events.c \
 	lib/http.c \
 	lib/libgadu.c \
 	lib/obsolete.c \
 	lib/pubdir.c \
 	lib/pubdir50.c \
+	lib/resolver.c \
+	lib/sha1.c \
 	buddylist.c \
 	confer.c \
 	gg.c \
--- a/libpurple/protocols/gg/buddylist.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/buddylist.c	Wed Mar 10 17:58:21 2010 +0000
@@ -82,7 +82,7 @@
 	gchar **users_tbl;
 	int i;
 	char *utf8buddylist = charset_convert(buddylist, "CP1250", "UTF-8");
-
+	
 	/* Don't limit the number of records in a buddylist. */
 	users_tbl = g_strsplit(utf8buddylist, "\r\n", -1);
 
--- a/libpurple/protocols/gg/gg.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/gg.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1000,8 +1000,8 @@
 				       int status, const char *descr)
 {
 	gchar *from;
+	gchar *msg;
 	const char *st;
-	gchar *msg;
 	gchar *avatarurl;
 	PurpleUtilFetchUrlData *url_data;
 
@@ -1018,29 +1018,37 @@
 	switch (status) {
 		case GG_STATUS_NOT_AVAIL:
 		case GG_STATUS_NOT_AVAIL_DESCR:
-			st = "offline";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE);
+			break;
+		case GG_STATUS_FFC:
+		case GG_STATUS_FFC_DESCR:
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
 			break;
 		case GG_STATUS_AVAIL:
 		case GG_STATUS_AVAIL_DESCR:
-			st = "available";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
 			break;
 		case GG_STATUS_BUSY:
 		case GG_STATUS_BUSY_DESCR:
-			st = "away";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY);
 			break;
+		case GG_STATUS_DND:
+		case GG_STATUS_DND_DESCR:
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_UNAVAILABLE);
 		case GG_STATUS_BLOCKED:
 			/* user is blocking us.... */
 			st = "blocked";
 			break;
 		default:
-			st = "available";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
 			purple_debug_info("gg",
 				"GG_EVENT_NOTIFY: Unknown status: %d\n", status);
 			break;
 	}
 
 	purple_debug_info("gg", "st = %s\n", st);
-	msg = charset_convert(descr, "CP1250", "UTF-8");
+	//msg = charset_convert(descr, "CP1250", "UTF-8");
+	msg = g_strdup_printf("%s", descr);
 	purple_prpl_got_user_status(purple_connection_get_account(gc),
 				  from, st, "message", msg, NULL);
 	g_free(from);
@@ -1078,6 +1086,12 @@
 		case GG_STATUS_AVAIL_DESCR:
 			st = _("Available");
 			break;
+		case GG_STATUS_FFC:
+		case GG_STATUS_FFC_DESCR:
+			return _("Chatty");
+		case GG_STATUS_DND:
+		case GG_STATUS_DND_DESCR:
+			return _("Do Not Disturb");
 		case GG_STATUS_BUSY:
 		case GG_STATUS_BUSY_DESCR:
 			st = _("Away");
@@ -1347,8 +1361,11 @@
 
 	from = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender);
 
+	/*
 	tmp = charset_convert((const char *)ev->event.msg.message,
 			      "CP1250", "UTF-8");
+	*/
+	tmp = g_strdup_printf("%s", ev->event.msg.message);
 	purple_str_strip_char(tmp, '\r');
 	msg = g_markup_escape_text(tmp, -1);
 	g_free(tmp);
@@ -1562,7 +1579,7 @@
 
 				purple_debug_info("gg", "notify_pre: (%d) status: %d\n",
 						ev->event.notify->uin,
-						ev->event.notify->status);
+						GG_S(ev->event.notify->status));
 
 				n = (ev->type == GG_EVENT_NOTIFY) ? ev->event.notify
 								  : ev->event.notify_descr.notify;
@@ -1573,17 +1590,17 @@
 
 					purple_debug_info("gg",
 						"notify: (%d) status: %d; descr: %s\n",
-						n->uin, n->status, descr ? descr : "(null)");
+						n->uin, GG_S(n->status), descr ? descr : "(null)");
 
 					ggp_generic_status_handler(gc,
-						n->uin, n->status, descr);
+						n->uin, GG_S(n->status), descr);
 				}
 			}
 			break;
 		case GG_EVENT_NOTIFY60:
 			purple_debug_info("gg",
 				"notify60_pre: (%d) status=%d; version=%d; descr=%s\n",
-				ev->event.notify60->uin, ev->event.notify60->status,
+				ev->event.notify60->uin, GG_S(ev->event.notify60->status),
 				ev->event.notify60->version,
 				ev->event.notify60->descr ? ev->event.notify60->descr : "(null)");
 
@@ -1602,7 +1619,7 @@
 			break;
 		case GG_EVENT_STATUS:
 			purple_debug_info("gg", "status: (%d) status=%d; descr=%s\n",
-					ev->event.status.uin, ev->event.status.status,
+					ev->event.status.uin, GG_S(ev->event.status.status),
 					ev->event.status.descr ? ev->event.status.descr : "(null)");
 
 			ggp_generic_status_handler(gc, ev->event.status.uin,
@@ -1611,12 +1628,12 @@
 		case GG_EVENT_STATUS60:
 			purple_debug_info("gg",
 				"status60: (%d) status=%d; version=%d; descr=%s\n",
-				ev->event.status60.uin, ev->event.status60.status,
+				ev->event.status60.uin, GG_S(ev->event.status60.status),
 				ev->event.status60.version,
 				ev->event.status60.descr ? ev->event.status60.descr : "(null)");
 
 			ggp_generic_status_handler(gc, ev->event.status60.uin,
-				ev->event.status60.status, ev->event.status60.descr);
+				GG_S(ev->event.status60.status), ev->event.status60.descr);
 			break;
 		case GG_EVENT_USERLIST:
 	    		if (ev->event.userlist.type == GG_USERLIST_GET_REPLY) {
@@ -1829,6 +1846,16 @@
 			NULL);
 	types = g_list_append(types, type);
 
+ 	/*
+	 * New statuses for GG 8.0 like PoGGadaj ze mna (not yet because 
+	 * libpurple can't support Chatty status) and Nie przeszkadzac
+	 */
+	type = purple_status_type_new_with_attrs(
+			PURPLE_STATUS_UNAVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
+			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	types = g_list_append(types, type);
+	
 	/*
 	 * This status is necessary to display guys who are blocking *us*.
 	 */
@@ -1930,6 +1957,9 @@
 	presence = purple_account_get_presence(account);
 	status = purple_presence_get_active_status(presence);
 
+	glp->encoding = GG_ENCODING_UTF8;
+	glp->protocol_features = (GG_FEATURE_STATUS80|GG_FEATURE_DND_FFC);
+	
 	glp->async = 1;
 	glp->status = ggp_to_gg_status(status, &glp->status_descr);
 	glp->tls = 0;
@@ -2100,8 +2130,11 @@
 		plain = purple_unescape_html(msg);
 	}
 
+	/*
 	tmp = charset_convert(plain, "UTF-8", "CP1250");
-
+	*/
+	tmp = g_strdup_printf("%s", plain);
+	
 	if (tmp && (format_length - sizeof(struct gg_msg_richtext))) {
 		if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) {
 			ret = -1;
@@ -2160,6 +2193,9 @@
 	} else if (strcmp(status_id, "away") == 0) {
 		new_status = GG_STATUS_BUSY;
 		new_status_descr = GG_STATUS_BUSY_DESCR;
+	} else if (strcmp(status_id, "unavailable") == 0) {
+		new_status = GG_STATUS_DND;
+		new_status_descr = GG_STATUS_DND_DESCR;
 	} else if (strcmp(status_id, "invisible") == 0) {
 		new_status = GG_STATUS_INVISIBLE;
 		new_status_descr = GG_STATUS_INVISIBLE_DESCR;
@@ -2177,9 +2213,12 @@
 	new_msg = purple_status_get_attr_string(status, "message");
 
 	if(new_msg) {
+		/*
 		char *tmp = purple_markup_strip_html(new_msg);
 		*msg = charset_convert(tmp, "UTF-8", "CP1250");
 		g_free(tmp);
+		*/
+		*msg = purple_markup_strip_html(new_msg);
 
 		return new_status_descr;
 	} else {
@@ -2279,7 +2318,8 @@
 	GGPInfo *info = gc->proto_data;
 	GGPChat *chat = NULL;
 	GList *l;
-	char *msg, *plain;
+	/* char *msg, *plain; */
+	gchar *msg;
 	uin_t *uins;
 	int count = 0;
 
@@ -2310,9 +2350,12 @@
 		uins[count++] = uin;
 	}
 
+	/*
 	plain = purple_unescape_html(message);
 	msg = charset_convert(plain, "UTF-8", "CP1250");
 	g_free(plain);
+	*/
+	msg = purple_unescape_html(message);
 	gg_send_message_confer(info->session, GG_CLASS_CHAT, count, uins,
 				(unsigned char *)msg);
 	g_free(msg);
--- a/libpurple/protocols/gg/lib/common.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/common.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1,8 +1,8 @@
-/* $Id: common.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: common.c 878 2009-11-16 23:48:19Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
+ *                          Robert J. Woźny <speedy@ziew.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,109 +15,180 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/*
+ * Funkcje konwersji między UTF-8 i CP1250 są oparte o kod biblioteki iconv.
+ * Informacje o prawach autorskich oryginalnego kodu zamieszczono poniżej:
+ *
+ * Copyright (C) 1999-2001, 2004 Free Software Foundation, Inc.
+ * This file is part of the GNU LIBICONV Library.
+ *
+ * The GNU LIBICONV Library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * The GNU LIBICONV 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the GNU LIBICONV Library; see the file COPYING.LIB.
+ * If not, write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * \file common.c
+ *
+ * \brief Funkcje wykorzystywane przez różne moduły biblioteki
+ */
+
 #include "libgadu.h"
+#include "libgadu-internal.h"
 
 #ifndef _WIN32
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#ifdef sun
-#  include <sys/filio.h>
-#endif
+#  include <sys/types.h>
+#  include <sys/ioctl.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  ifdef sun
+#    include <sys/filio.h>
+#  endif
 #endif
 
 #include <errno.h>
 #include <fcntl.h>
+
 #ifndef _WIN32
-#include <netdb.h>
+# include <netdb.h>
 #endif
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
+/**
+ * Plik, do którego będą przekazywane informacje odpluskwiania.
+ *
+ * Funkcja \c gg_debug() i pochodne mogą być przechwytywane przez aplikację
+ * korzystającą z biblioteki, by wyświetlić je na żądanie użytkownika lub
+ * zapisać do późniejszej analizy. Jeśli nie określono pliku, wybrane
+ * informacje będą wysyłane do standardowego wyjścia błędu (\c stderr).
+ *
+ * \ingroup debug
+ */
 FILE *gg_debug_file = NULL;
 
 #ifndef GG_DEBUG_DISABLE
 
-/*
- * gg_debug() // funkcja wewntrzna
+/**
+ * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji.
+ *
+ * Jeśli aplikacja ustawiła odpowiednią funkcję obsługi w
+ * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoływana.
+ * W przeciwnym wypadku wynik jest wysyłany do standardowego wyjścia błędu.
  *
- * wywietla komunikat o danym poziomie, o ile uytkownik sobie tego yczy.
+ * \param sess Struktura sesji (może być \c NULL)
+ * \param level Poziom informacji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
+ */
+void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
+{
+	if (gg_debug_handler_session)
+		(*gg_debug_handler_session)(sess, level, format, ap);
+	else if (gg_debug_handler)
+		(*gg_debug_handler)(level, format, ap);
+	else if (gg_debug_level & level)
+		vfprintf(gg_debug_file ? gg_debug_file : stderr, format, ap);
+}
+
+
+/**
+ * \internal Przekazuje informację odpluskawiania.
  *
- *  - level - poziom wiadomoci
- *  - format... - tre wiadomoci (kompatybilna z printf())
+ * \param level Poziom wiadomości
+ * \param format Format wiadomości (zgodny z \c printf)
+ *
+ * \ingroup debug
  */
 void gg_debug(int level, const char *format, ...)
 {
 	va_list ap;
 	int old_errno = errno;
-	
-	if (gg_debug_handler) {
-		va_start(ap, format);
-		(*gg_debug_handler)(level, format, ap);
-		va_end(ap);
+	va_start(ap, format);
+	gg_debug_common(NULL, level, format, ap);
+	va_end(ap);
+	errno = old_errno;
+}
 
-		goto cleanup;
-	}
-	
-	if ((gg_debug_level & level)) {
-		va_start(ap, format);
-		vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap);
-		va_end(ap);
-	}
-
-cleanup:
+/**
+ * \internal Przekazuje informację odpluskwiania związaną z sesją.
+ *
+ * \param sess Struktura sesji
+ * \param level Poziom wiadomości
+ * \param format Format wiadomości (zgodny z \c printf)
+ *
+ * \ingroup debug
+ */
+void gg_debug_session(struct gg_session *sess, int level, const char *format, ...)
+{
+	va_list ap;
+	int old_errno = errno;
+	va_start(ap, format);
+	gg_debug_common(sess, level, format, ap);
+	va_end(ap);
 	errno = old_errno;
 }
 
 #endif
 
-/*
- * gg_vsaprintf() // funkcja pomocnicza
+/**
+ * \internal Odpowiednik funkcji \c vsprintf alokujący miejsce na wynik.
+ *
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
  *
- * robi dokadnie to samo, co vsprintf(), tyle e alokuje sobie wczeniej
- * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj
- * vsnprintf() zgodn z C99, jak i na wczeniejszych.
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
  *
- *  - format - opis wywietlanego tekstu jak dla printf()
- *  - ap - lista argumentw dla printf()
+ * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
  *
- * zaalokowany bufor, ktry naley pniej zwolni, lub NULL
- * jeli nie udao si wykona zadania.
+ * \ingroup helper
  */
 char *gg_vsaprintf(const char *format, va_list ap)
 {
 	int size = 0;
 	const char *start;
 	char *buf = NULL;
-	
-#ifdef __GG_LIBGADU_HAVE_VA_COPY
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
 	va_list aq;
 
 	va_copy(aq, ap);
 #else
-#  ifdef __GG_LIBGADU_HAVE___VA_COPY
+#  ifdef GG_CONFIG_HAVE___VA_COPY
 	va_list aq;
 
 	__va_copy(aq, ap);
 #  endif
 #endif
 
-	start = format; 
+	start = format;
 
-#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF
+#ifndef GG_CONFIG_HAVE_C99_VSNPRINTF
 	{
 		int res;
 		char *tmp;
-		
+
 		size = 128;
 		do {
 			size *= 2;
@@ -132,9 +203,9 @@
 #else
 	{
 		char tmp[2];
-		
-		/* libce Solarisa przy buforze NULL zawsze zwracaj -1, wic
-		 * musimy poda co istniejcego jako cel printf()owania. */
+
+		/* libce Solarisa przy buforze NULL zawsze zwracają -1, więc
+		 * musimy podać coś istniejącego jako cel printf()owania. */
 		size = vsnprintf(tmp, sizeof(tmp), format, ap);
 		if (!(buf = malloc(size + 1)))
 			return NULL;
@@ -142,33 +213,33 @@
 #endif
 
 	format = start;
-	
-#ifdef __GG_LIBGADU_HAVE_VA_COPY
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
 	vsnprintf(buf, size + 1, format, aq);
 	va_end(aq);
 #else
-#  ifdef __GG_LIBGADU_HAVE___VA_COPY
+#  ifdef GG_CONFIG_HAVE___VA_COPY
 	vsnprintf(buf, size + 1, format, aq);
 	va_end(aq);
 #  else
 	vsnprintf(buf, size + 1, format, ap);
 #  endif
 #endif
-	
+
 	return buf;
 }
 
-/*
- * gg_saprintf() // funkcja pomocnicza
+/**
+ * \internal Odpowiednik funkcji \c sprintf alokujący miejsce na wynik.
+ *
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
  *
- * robi dokadnie to samo, co sprintf(), tyle e alokuje sobie wczeniej
- * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj
- * vsnprintf() zgodn z C99, jak i na wczeniejszych.
+ * \param format Format wiadomości (zgodny z \c printf)
  *
- *  - format... - tre taka sama jak w funkcji printf()
+ * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
  *
- * zaalokowany bufor, ktry naley pniej zwolni, lub NULL
- * jeli nie udao si wykona zadania.
+ * \ingroup helper
  */
 char *gg_saprintf(const char *format, ...)
 {
@@ -182,18 +253,17 @@
 	return res;
 }
 
-/*
- * gg_get_line() // funkcja pomocnicza
- * 
- * podaje kolejn lini z bufora tekstowego. niszczy go bezpowrotnie, dzielc
- * na kolejne stringi. zdarza si, nie ma potrzeby pisania funkcji dublujcej
- * bufor eby tylko mie nieruszone dane wejciowe, skoro i tak nie bd nam
- * poniej potrzebne. obcina `\r\n'.
- * 
- *  - ptr - wskanik do zmiennej, ktra przechowuje aktualn pozycj
- *    w przemiatanym buforze
- * 
- * wskanik do kolejnej linii tekstu lub NULL, jeli to ju koniec bufora.
+/**
+ * \internal Pobiera linię tekstu z bufora.
+ *
+ * Funkcja niszczy bufor źródłowy bezpowrotnie, dzieląc go na kolejne ciągi
+ * znaków i obcina znaki końca linii.
+ *
+ * \param ptr Wskaźnik do zmiennej, która przechowuje aktualne położenie
+ *            w analizowanym buforze
+ *
+ * \return Wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec
+ *         bufora.
  */
 char *gg_get_line(char **ptr)
 {
@@ -207,99 +277,30 @@
 	if (!(foo = strchr(*ptr, '\n')))
 		*ptr += strlen(*ptr);
 	else {
+		size_t len;
 		*ptr = foo + 1;
 		*foo = 0;
-		if (strlen(res) > 1 && res[strlen(res) - 1] == '\r')
-			res[strlen(res) - 1] = 0;
+
+		len = strlen(res);
+
+		if (len > 1 && res[len - 1] == '\r')
+			res[len - 1] = 0;
 	}
 
 	return res;
 }
 
-/*
- * gg_connect() // funkcja pomocnicza
+/**
+ * \internal Czyta linię tekstu z gniazda.
  *
- * czy si z serwerem. pierwszy argument jest typu (void *), eby nie
- * musie niczego inkludowa w libgadu.h i nie psu jaki gupich zalenoci
- * na dziwnych systemach.
- *
- *  - addr - adres serwera (struct in_addr *)
- *  - port - port serwera
- *  - async - asynchroniczne poczenie
+ * Funkcja czyta tekst znak po znaku, więc nie jest efektywna, ale dzięki
+ * brakowi buforowania, nie koliduje z innymi funkcjami odczytu.
  *
- * deskryptor gniazda lub -1 w przypadku bdu (kod bdu w zmiennej errno).
- */
-int gg_connect(void *addr, int port, int async)
-{
-	int sock, one = 1, errno2;
-	struct sockaddr_in sin;
-	struct in_addr *a = addr;
-	struct sockaddr_in myaddr;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
-	
-	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno));
-		return -1;
-	}
-
-	memset(&myaddr, 0, sizeof(myaddr));
-	myaddr.sin_family = AF_INET;
-
-	myaddr.sin_addr.s_addr = gg_local_ip;
-
-	if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno));
-		return -1;
-	}
-
-#ifdef ASSIGN_SOCKETS_TO_THREADS
-	gg_win32_thread_socket(0, sock);
-#endif
-
-	if (async) {
-#ifdef FIONBIO
-		if (ioctl(sock, FIONBIO, &one) == -1) {
-#else
-		if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
-#endif
-			gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno));
-			errno2 = errno;
-			close(sock);
-			errno = errno2;
-			return -1;
-		}
-	}
-
-	sin.sin_port = htons(port);
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = a->s_addr;
-	
-	if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
-		if (errno && (!async || errno != EINPROGRESS)) {
-			gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno));
-			errno2 = errno;
-			close(sock);
-			errno = errno2;
-			return -1;
-		}
-		gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
-	}
-	
-	return sock;
-}
-
-/*
- * gg_read_line() // funkcja pomocnicza
+ * \param sock Deskryptor gniazda
+ * \param buf Wskaźnik do bufora
+ * \param length Długość bufora
  *
- * czyta jedn lini tekstu z gniazda.
- *
- *  - sock - deskryptor gniazda
- *  - buf - wskanik do bufora
- *  - length - dugo bufora
- *
- * jeli trafi na bd odczytu lub podano nieprawidowe parametry, zwraca NULL.
- * inaczej zwraca buf.
+ * \return Zwraca \c buf jeśli się powiodło, lub \c NULL w przypadku błędu.
  */
 char *gg_read_line(int sock, char *buf, int length)
 {
@@ -331,38 +332,116 @@
 	return buf;
 }
 
-/*
- * gg_chomp() // funkcja pomocnicza
+/**
+ * \internal Nawiązuje połączenie TCP.
+ *
+ * \param addr Wskaźnik na strukturę \c in_addr z adresem serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego połączenia
+ *
+ * \return Deskryptor gniazda lub -1 w przypadku błędu
  *
- * ucina "\r\n" lub "\n" z koca linii.
+ * \ingroup helper
+ */
+int gg_connect(void *addr, int port, int async)
+{
+	int sock, one = 1, errno2;
+	struct sockaddr_in sin;
+	struct in_addr *a = addr;
+	struct sockaddr_in myaddr;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
+
+	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno));
+		return -1;
+	}
+
+	memset(&myaddr, 0, sizeof(myaddr));
+	myaddr.sin_family = AF_INET;
+
+	myaddr.sin_addr.s_addr = gg_local_ip;
+
+	if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno));
+		errno2 = errno;
+		close(sock);
+		errno = errno2;
+		return -1;
+	}
+
+#ifdef ASSIGN_SOCKETS_TO_THREADS
+	gg_win32_thread_socket(0, sock);
+#endif
+
+	if (async) {
+#ifdef FIONBIO
+		if (ioctl(sock, FIONBIO, &one) == -1) {
+#else
+		if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+			gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno));
+			errno2 = errno;
+			close(sock);
+			errno = errno2;
+			return -1;
+		}
+	}
+
+	sin.sin_port = htons(port);
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = a->s_addr;
+
+	if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
+		if (errno && (!async || errno != EINPROGRESS)) {
+			gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno));
+			errno2 = errno;
+			close(sock);
+			errno = errno2;
+			return -1;
+		}
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
+	}
+
+	return sock;
+}
+
+/**
+ * \internal Usuwa znaki końca linii.
  *
- *  - line - linia do przycicia
+ * Funkcja działa bezpośrednio na buforze.
+ *
+ * \param line Bufor z tekstem
+ *
+ * \ingroup helper
  */
 void gg_chomp(char *line)
 {
 	int len;
-	
+
 	if (!line)
 		return;
 
 	len = strlen(line);
-	
+
 	if (len > 0 && line[len - 1] == '\n')
 		line[--len] = 0;
 	if (len > 0 && line[len - 1] == '\r')
 		line[--len] = 0;
 }
 
-/*
- * gg_urlencode() // funkcja wewntrzna
+/**
+ * \internal Koduje ciąg znaków do postacji adresu HTTP.
  *
- * zamienia podany tekst na cig znakw do formularza http. przydaje si
- * przy rnych usugach katalogu publicznego.
+ * Zamienia znaki niedrukowalne, spoza ASCII i mające specjalne znaczenie
+ * dla protokołu HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkową
+ * wartością znaku.
+ * 
+ * \param str Ciąg znaków do zakodowania
  *
- *  - str - cig znakw do zakodowania
+ * \return Zaalokowany bufor lub \c NULL w przypadku błędu.
  *
- * zaalokowany bufor, ktry naley pniej zwolni albo NULL
- * w przypadku bdu.
+ * \ingroup helper
  */
 char *gg_urlencode(const char *str)
 {
@@ -400,16 +479,19 @@
 	return buf;
 }
 
-/*
- * gg_http_hash() // funkcja wewntrzna
+/**
+ * \internal Wyznacza skrót dla usług HTTP.
  *
- * funkcja liczca hash dla adresu e-mail, hasa i paru innych.
+ * Funkcja jest wykorzystywana do wyznaczania skrótu adresu e-mail, hasła
+ * i innych wartości przekazywanych jako parametry usług HTTP.
  *
- *  - format... - format kolejnych parametrw ('s' jeli dany parametr jest
- *                cigiem znakw lub 'u' jeli numerem GG)
+ * W parametrze \c format należy umieścić znaki określające postać kolejnych
+ * parametrów: \c 's' jeśli parametr jest ciągiem znaków, \c 'u' jeśli jest
+ * liczbą.
  *
- * hash wykorzystywany przy rejestracji i wszelkich manipulacjach wasnego
- * wpisu w katalogu publicznym.
+ * \param format Format kolejnych parametrów (niezgodny z \c printf)
+ *
+ * \return Wartość skrótu
  */
 int gg_http_hash(const char *format, ...)
 {
@@ -428,7 +510,7 @@
 		} else {
 			if (!(arg = va_arg(ap, char*)))
 				arg = "";
-		}	
+		}
 
 		i = 0;
 		while ((c = (unsigned char) arg[i++]) != 0) {
@@ -442,95 +524,6 @@
 	return (b < 0 ? -b : b);
 }
 
-/*
- * gg_gethostbyname() // funkcja pomocnicza
- *
- * odpowiednik gethostbyname() troszczcy si o wspbieno, gdy mamy do
- * dyspozycji funkcj gethostbyname_r().
- *
- *  - hostname - nazwa serwera
- *
- * zwraca wskanik na struktur in_addr, ktr naley zwolni.
- */
-struct in_addr *gg_gethostbyname(const char *hostname)
-{
-	struct in_addr *addr = NULL;
-
-#ifdef HAVE_GETHOSTBYNAME_R
-	char *tmpbuf = NULL, *buf = NULL;
-	struct hostent *hp = NULL, *hp2 = NULL;
-	int h_errnop, ret;
-	size_t buflen = 1024;
-	int new_errno;
-	
-	new_errno = ENOMEM;
-	
-	if (!(addr = malloc(sizeof(struct in_addr))))
-		goto cleanup;
-	
-	if (!(hp = calloc(1, sizeof(*hp))))
-		goto cleanup;
-
-	if (!(buf = malloc(buflen)))
-		goto cleanup;
-
-	tmpbuf = buf;
-	
-	while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) {
-		buflen *= 2;
-		
-		if (!(tmpbuf = realloc(buf, buflen)))
-			break;
-		
-		buf = tmpbuf;
-	}
-	
-	if (ret)
-		new_errno = h_errnop;
-
-	if (ret || !hp2 || !tmpbuf)
-		goto cleanup;
-	
-	memcpy(addr, hp->h_addr, sizeof(struct in_addr));
-	
-	free(buf);
-	free(hp);
-	
-	return addr;
-	
-cleanup:
-	errno = new_errno;
-	
-	if (addr)
-		free(addr);
-	if (hp)
-		free(hp);
-	if (buf)
-		free(buf);
-	
-	return NULL;
-#else
-	struct hostent *hp;
-
-	if (!(addr = malloc(sizeof(struct in_addr)))) {
-		goto cleanup;
-	}
-
-	if (!(hp = gethostbyname(hostname)))
-		goto cleanup;
-
-	memcpy(addr, hp->h_addr, sizeof(struct in_addr));
-
-	return addr;
-	
-cleanup:
-	if (addr)
-		free(addr);
-
-	return NULL;
-#endif
-}
-
 #ifdef ASSIGN_SOCKETS_TO_THREADS
 
 typedef struct gg_win32_thread {
@@ -541,23 +534,22 @@
 
 struct gg_win32_thread *gg_win32_threads = 0;
 
-/*
- * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32
+/**
+ * \internal Zwraca deskryptor gniazda, które było ostatnio tworzone dla wątku.
  *
- * zwraca deskryptor gniazda, ktre byo ostatnio tworzone dla wtku
- * o podanym identyfikatorze.
+ * Jeśli na win32 przy połączeniach synchronicznych zapamiętamy w jakim
+ * wątku uruchomiliśmy funkcję, która się z czymkolwiek łączy, to z osobnego
+ * wątku możemy anulować połączenie poprzez \c gg_win32_thread_socket(watek,-1)
  *
- * jeli na win32 przy poczeniach synchronicznych zapamitamy w jakim
- * wtku uruchomilimy funkcj, ktra si z czymkolwiek czy, to z osobnego
- * wtku moemy anulowa poczenie poprzez gg_win32_thread_socket(watek, -1);
- * 
- * - thread_id - id wtku. jeli jest rwne 0, brany jest aktualny wtek,
- *               jeli rwne -1, usuwa wpis o podanym sockecie.
- * - socket - deskryptor gniazda. jeli rwne 0, zwraca deskryptor gniazda
- *            dla podanego wtku, jeli rwne -1, usuwa wpis, jeli co
- *            innego, ustawia dla podanego wtku dany numer deskryptora.
+ * \param thread_id Identyfikator wątku (jeśli jest równe 0, brany jest
+ *                  aktualny wątek, jeśli równe -1, usuwa wpis dotyczący
+ *                  danego gniazda sockecie)
+ * \param socket Deskryptor gniazda (jeśli równe 0, zwraca deskryptor gniazda
+ *               dla podanego wątku, jeśli równe -1, usuwa wpis, jeśli coś
+ *               innego, ustawia dla podanego wątku dany numer deskryptora)
  *
- * jeli socket jest rwne 0, zwraca deskryptor gniazda dla podanego wtku.
+ * \return Jeśli socket jest równe 0, zwraca deskryptor gniazda dla podanego
+ *         wątku.
  */
 int gg_win32_thread_socket(int thread_id, int socket)
 {
@@ -567,7 +559,7 @@
 
 	if (!thread_id)
 		thread_id = GetCurrentThreadId();
-	
+
 	while (wsk) {
 		if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) {
 			if (close) {
@@ -593,7 +585,7 @@
 		closesocket(socket);
 	if (close || !socket)
 		return 0;
-	
+
 	/* Dodaje nowy element */
 	wsk = malloc(sizeof(gg_win32_thread));
 	wsk->id = thread_id;
@@ -606,28 +598,33 @@
 
 #endif /* ASSIGN_SOCKETS_TO_THREADS */
 
+/**
+ * \internal Zestaw znaków kodowania base64.
+ */
 static char gg_base64_charset[] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-/*
- * gg_base64_encode()
+/**
+ * \internal Koduje ciąg znaków do base64.
+ *
+ * Wynik funkcji należy zwolnić za pomocą \c free.
  *
- * zapisuje cig znakw w base64.
+ * \param buf Bufor z danami do zakodowania
  *
- *  - buf - cig znakw.
+ * \return Zaalokowany bufor z zakodowanymi danymi
  *
- * zaalokowany bufor.
+ * \ingroup helper
  */
 char *gg_base64_encode(const char *buf)
 {
 	char *out, *res;
 	unsigned int i = 0, j = 0, k = 0, len = strlen(buf);
-	
+
 	res = out = malloc((len / 3 + 1) * 4 + 2);
 
 	if (!res)
 		return NULL;
-	
+
 	while (j <= len) {
 		switch (i % 4) {
 			case 0:
@@ -660,20 +657,22 @@
 	if (i % 4)
 		for (j = 0; j < 4 - (i % 4); j++, out++)
 			*out = '=';
-	
+
 	*out = 0;
-	
+
 	return res;
 }
 
-/*
- * gg_base64_decode()
+/**
+ * \internal Dekoduje ciąg znaków zapisany w base64.
+ *
+ * Wynik funkcji należy zwolnić za pomocą \c free.
  *
- * dekoduje cig znakw z base64.
+ * \param buf Bufor źródłowy z danymi do zdekodowania
  *
- *  - buf - cig znakw.
+ * \return Zaalokowany bufor ze zdekodowanymi danymi
  *
- * zaalokowany bufor.
+ * \ingroup helper
  */
 char *gg_base64_decode(const char *buf)
 {
@@ -683,7 +682,7 @@
 
 	if (!buf)
 		return NULL;
-	
+
 	save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2);
 
 	if (!save)
@@ -720,23 +719,24 @@
 		index %= 4;
 	}
 	*res = 0;
-	
+
 	return save;
 }
 
-/*
- * gg_proxy_auth() // funkcja wewntrzna
+/**
+ * \internal Tworzy nagłówek autoryzacji serwera pośredniczącego.
  *
- * tworzy nagwek autoryzacji dla proxy.
- * 
- * zaalokowany tekst lub NULL, jeli proxy nie jest wczone lub nie wymaga
- * autoryzacji.
+ * Dane pobiera ze zmiennych globalnych \c gg_proxy_username i
+ * \c gg_proxy_password.
+ *
+ * \return Zaalokowany bufor z tekstem lub NULL, jeśli serwer pośredniczący
+ *         nie jest używany lub nie wymaga autoryzacji.
  */
 char *gg_proxy_auth()
 {
 	char *tmp, *enc, *out;
 	unsigned int tmp_size;
-	
+
 	if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password)
 		return NULL;
 
@@ -749,14 +749,14 @@
 		free(tmp);
 		return NULL;
 	}
-	
+
 	free(tmp);
 
 	if (!(out = malloc(strlen(enc) + 40))) {
 		free(enc);
 		return NULL;
 	}
-	
+
 	snprintf(out, strlen(enc) + 40,  "Proxy-Authorization: Basic %s\r\n", enc);
 
 	free(enc);
@@ -764,13 +764,21 @@
 	return out;
 }
 
+/**
+ * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej.
+ */
 static uint32_t gg_crc32_table[256];
+
+/**
+ * \internal Flaga wypełnienia tablicy pomocniczej do wyznaczania sumy
+ * kontrolnej.
+ */
 static int gg_crc32_initialized = 0;
 
-/*
- * gg_crc32_make_table()  // funkcja wewntrzna
+/**
+ * \internal Tworzy tablicę pomocniczą do wyznaczania sumy kontrolnej.
  */
-static void gg_crc32_make_table()
+static void gg_crc32_make_table(void)
 {
 	uint32_t h = 1;
 	unsigned int i, j;
@@ -787,16 +795,15 @@
 	gg_crc32_initialized = 1;
 }
 
-/*
- * gg_crc32()
- *
- * wyznacza sum kontroln CRC32 danego bloku danych.
+/**
+ * Wyznacza sumę kontrolną CRC32.
  *
- *  - crc - suma kontrola poprzedniego bloku danych lub 0 jeli pierwszy
- *  - buf - bufor danych
- *  - size - ilo danych
+ * \param crc Suma kontrola poprzedniego bloku danych lub 0 jeśli liczona
+ *            jest suma kontrolna pierwszego bloku
+ * \param buf Bufor danych
+ * \param len Długość bufora danych
  *
- * suma kontrolna CRC32.
+ * \return Suma kontrolna.
  */
 uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
 {
@@ -814,6 +821,186 @@
 	return crc ^ 0xffffffffL;
 }
 
+/**
+ * \internal Tablica konwersji między CP1250 a UTF-8.
+ */
+static const uint16_t table_cp1250[] = {
+	0x20ac, '?',    0x201a,    '?', 0x201e, 0x2026, 0x2020, 0x2021, 
+	   '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, 
+	   '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 
+	   '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, 
+	0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, 
+	0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, 
+	0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 
+	0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, 
+	0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, 
+	0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, 
+	0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, 
+	0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, 
+	0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, 
+	0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, 
+	0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, 
+	0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, 
+};
+
+/**
+ * \internal Zamienia tekst kodowany CP1250 na UTF-8.
+ *
+ * \param b Tekst źródłowy w CP1250.
+ *
+ * \return Zaalokowany bufor z tekstem w UTF-8.
+ */
+char *gg_cp_to_utf8(const char *b)
+{
+	unsigned char *buf = (unsigned char *) b;
+	char *newbuf;
+	int newlen = 0;
+	int i, j;
+
+	for (i = 0; buf[i]; i++) {
+		uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
+
+		if (znak < 0x80)	newlen += 1;
+		else if (znak < 0x800)	newlen += 2;
+		else			newlen += 3;
+	}
+
+	if (!(newbuf = malloc(newlen+1))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_cp_to_utf8() not enough memory\n");
+		return NULL;
+	}
+
+	for (i = 0, j = 0; buf[i]; i++) {
+		uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
+		int count;
+
+		if (znak < 0x80)	count = 1;
+		else if (znak < 0x800)	count = 2;
+		else			count = 3;
+
+		switch (count) {
+			case 3: newbuf[j+2] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0x800;
+			case 2: newbuf[j+1] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0xc0;
+			case 1: newbuf[j] = znak;
+		}
+		j += count;
+	}
+	newbuf[j] = '\0';
+
+	return newbuf;
+}
+
+/**
+ * \internal Dekoduje jeden znak UTF-8.
+ *
+ * \note Funkcja nie jest kompletną implementacją UTF-8, a wersją uproszczoną
+ * do potrzeb kodowania CP1250.
+ *
+ * \param s Tekst źródłowy.
+ * \param n Długość tekstu źródłowego.
+ * \param ch Wskaźnik na wynik dekodowania.
+ *
+ * \return Długość zdekodowanej sekwencji w bajtach lub wartość mniejsza
+ * od zera w przypadku błędu.
+ */
+static int gg_utf8_helper(unsigned char *s, int n, uint16_t *ch)
+{
+	unsigned char c = s[0];
+
+	if (c < 0x80) {
+		*ch = c;
+		return 1;
+	}
+
+	if (c < 0xc2) 
+		return -1;
+
+	if (c < 0xe0) {
+		if (n < 2)
+			return -2;
+		if (!((s[1] ^ 0x80) < 0x40))
+			return -1;
+		*ch = ((uint16_t) (c & 0x1f) << 6) | (uint16_t) (s[1] ^ 0x80);
+		return 2;
+	} 
+	
+	if (c < 0xf0) {
+		if (n < 3)
+			return -2;
+		if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (c >= 0xe1 || s[1] >= 0xa0)))
+			return -1;
+		*ch = ((uint16_t) (c & 0x0f) << 12) | ((uint16_t) (s[1] ^ 0x80) << 6) | (uint16_t) (s[2] ^ 0x80);
+		return 3;
+	}
+
+	return -1;
+}
+
+/**
+ * \internal Zamienia tekst kodowany UTF-8 na CP1250.
+ *
+ * \param b Tekst źródłowy w UTF-8.
+ *
+ * \return Zaalokowany bufor z tekstem w CP1250.
+ */
+char *gg_utf8_to_cp(const char *b)
+{
+	unsigned char *buf = (unsigned char *) b;
+	char *newbuf;
+	int newlen = 0;
+	int len;
+	int i, j;
+
+	len = strlen(b);
+
+	for (i = 0; i < len; newlen++) {
+		uint16_t discard;
+		int ret;
+		
+		ret = gg_utf8_helper(&buf[i], len - i, &discard);
+
+		if (ret > 0)
+			i += ret;
+		else
+			i++;
+	}
+
+	if (!(newbuf = malloc(newlen+1))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_utf8_to_cp() not enough memory\n");
+		return NULL;
+	}
+
+	for (i = 0, j = 0; buf[i]; j++) {
+		uint16_t znak;
+		int ret, k;
+
+		ret = gg_utf8_helper(&buf[i], len - i, &znak);
+
+		if (ret > 0) {
+			i += ret;
+		} else {
+			znak = '?';
+			i++;
+		}
+
+		if (znak < 0x80) {
+			newbuf[j] = znak;
+			continue;
+		}
+
+		newbuf[j] = '?';
+
+		for (k = 0; k < (sizeof(table_cp1250)/sizeof(table_cp1250[0])); k++) {
+			if (table_cp1250[k] == znak) {
+				newbuf[j] = (0x80 | k);
+				break;
+			}
+		}
+	}
+	newbuf[j] = '\0';
+
+	return newbuf;
+}
 
 /*
  * Local variables:
--- a/libpurple/protocols/gg/lib/compat.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/compat.h	Wed Mar 10 17:58:21 2010 +0000
@@ -1,8 +1,8 @@
-/* $Id: compat.h 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: compat.h 506 2008-01-14 22:15:05Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
+ *                          Robert J. Woźny <speedy@ziew.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,10 +15,16 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file compat.h
+ *
+ * \brief Makra zapewniające kompatybilność API na różnych systemach
+ */
+
 #ifndef __COMPAT_H
 #define __COMPAT_H
 
--- a/libpurple/protocols/gg/lib/dcc.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/dcc.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1,8 +1,9 @@
-/* $Id: dcc.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: dcc.c 711 2009-04-16 00:52:47Z darkjames $ */
 
 /*
- *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Tomasz Chiliski <chilek@chilan.com>
+ *  (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,22 +16,28 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file dcc.c
+ *
+ * \brief Obsługa połączeń bezpośrednich do wersji Gadu-Gadu 6.x
+ */
 #include "libgadu.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
+
 #ifndef _WIN32
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#ifdef sun
-#  include <sys/filio.h>
-#endif
+#  include <sys/ioctl.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  ifdef sun
+#    include <sys/filio.h>
+#  endif
 #endif
 
 #include <ctype.h>
@@ -43,67 +50,71 @@
 #include <unistd.h>
 
 #include "compat.h"
+
 #ifndef GG_DEBUG_DISABLE
-/*
- * gg_dcc_debug_data() // funkcja wewntrzna
+
+/**
+ * \internal Przekazuje zawartość pakietu do odpluskwiania.
  *
- * wywietla zrzut pakietu w hexie.
- * 
- *  - prefix - prefiks zrzutu pakietu
- *  - fd - deskryptor gniazda
- *  - buf - bufor z danymi
- *  - size - rozmiar danych
+ * \param prefix Prefiks informacji
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danumi
+ * \param size Rozmiar bufora z danymi
  */
 static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size)
 {
 	unsigned int i;
-	
+
 	gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size);
-	
+
 	for (i = 0; i < size; i++)
 		gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]);
-	
+
 	gg_debug(GG_DEBUG_MISC, "\n");
 }
 #else
 #define gg_dcc_debug_data(a,b,c,d) do { } while (0)
 #endif
 
-/*
+/**
+ * Wysyła żądanie zwrotnego połączenia bezpośredniego.
+ *
+ * Funkcję wykorzystuje się, jeśli nie ma możliwości połączenia się z odbiorcą
+ * pliku lub rozmowy głosowej. Po otrzymaniu żądania druga strona spróbuje
+ * nawiązać zwrotne połączenie bezpośrednie z nadawcą.
  * gg_dcc_request()
  *
- * wysya informacj o tym, e dany klient powinien si z nami poczy.
- * wykorzystywane, kiedy druga strona, ktrej chcemy co wysa jest za
- * maskarad.
+ * \param sess Struktura sesji
+ * \param uin Numer odbiorcy
  *
- *  - sess - struktura opisujca sesj GG
- *  - uin - numerek odbiorcy
+ * \return Patrz \c gg_send_message_ctcp()
  *
- * patrz gg_send_message_ctcp().
+ * \ingroup dcc6
  */
 int gg_dcc_request(struct gg_session *sess, uin_t uin)
 {
-	return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (const unsigned char *)"\002", 1);
+	return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (unsigned char*) "\002", 1);
 }
 
-/* 
- * gg_dcc_fill_filetime()  // funkcja wewntrzna
+/**
+ * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32.
  *
- * zamienia czas w postaci unixowej na windowsowy.
+ * \note Funkcja działa jedynie gdy kompilator obsługuje typ danych
+ * \c long \c long.
  *
- *  - unix - czas w postaci unixowej
- *  - filetime - czas w postaci windowsowej
+ * \param ut Czas w postaci uniksowej
+ * \param ft Czas w postaci API WIN32
  */
 static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft)
 {
-#ifdef __GG_LIBGADU_HAVE_LONG_LONG
+#ifdef GG_CONFIG_HAVE_LONG_LONG
 	unsigned long long tmp;
 
 	tmp = ut;
 	tmp += 11644473600LL;
 	tmp *= 10000000LL;
 
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
 	ft[0] = (uint32_t) tmp;
 	ft[1] = (uint32_t) (tmp >> 32);
 #else
@@ -114,31 +125,33 @@
 #endif
 }
 
-/*
- * gg_dcc_fill_file_info()
+/**
+ * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
  *
- * wypenia pola struct gg_dcc niezbdne do wysania pliku.
+ * \note Większą funkcjonalność zapewnia funkcja \c gg_dcc_fill_file_info2().
  *
- *  - d - struktura opisujca poczenie DCC
- *  - filename - nazwa pliku
+ * \param d Struktura połączenia
+ * \param filename Nazwa pliku
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename)
 {
 	return gg_dcc_fill_file_info2(d, filename, filename);
 }
 
-/*
- * gg_dcc_fill_file_info2()
- *
- * wypenia pola struct gg_dcc niezbdne do wysania pliku.
+/**
+ * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
  *
- *  - d - struktura opisujca poczenie DCC
- *  - filename - nazwa pliku
- *  - local_filename - nazwa na lokalnym systemie plikw
+ * \param d Struktura połączenia
+ * \param filename Nazwa pliku zapisywana w strukturze
+ * \param local_filename Nazwa pliku w lokalnym systemie plików
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename)
 {
@@ -225,25 +238,23 @@
 			*q = 175;
 		}
 	}
-	
+
 	gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename);
-	strncpy((char *)d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
+	strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
 
 	return 0;
 }
 
-/*
- * gg_dcc_transfer() // funkcja wewntrzna
- * 
- * inicjuje proces wymiany pliku z danym klientem.
+/**
+ * \internal Rozpoczyna połączenie bezpośrednie z danym klientem.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wasny numer
- *  - peer_uin - numer obiorcy
- *  - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET)
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer odbiorcy
+ * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET)
  *
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
  */
 static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type)
 {
@@ -251,9 +262,9 @@
 	struct in_addr addr;
 
 	addr.s_addr = ip;
-	
+
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET");
-	
+
 	if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) {
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n");
 		errno = EINVAL;
@@ -284,18 +295,17 @@
 	return d;
 }
 
-/*
- * gg_dcc_get_file()
- * 
- * inicjuje proces odbierania pliku od danego klienta, gdy ten wysa do
- * nas danie poczenia.
+/**
+ * Rozpoczyna odbieranie pliku przez zwrotne połączenie bezpośrednie.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wasny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP nadawcy
+ * \param port Port nadawcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer nadawcy
  *
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
 {
@@ -304,17 +314,17 @@
 	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET);
 }
 
-/*
- * gg_dcc_send_file()
- * 
- * inicjuje proces wysyania pliku do danego klienta.
+/**
+ * Rozpoczyna wysyłanie pliku.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wasny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer odbiorcy
  *
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
 {
@@ -323,17 +333,17 @@
 	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND);
 }
 
-/*
- * gg_dcc_voice_chat()
- * 
- * prbuje nawiza poczenie gosowe.
+/**
+ * Rozpoczyna połączenie głosowe.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wasny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer odbiorcy
  *
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
 {
@@ -342,30 +352,34 @@
 	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE);
 }
 
-/*
- * gg_dcc_set_type()
+/**
+ * Ustawia typ przychodzącego połączenia bezpośredniego.
+ *
+ * Funkcję należy wywołać po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK.
  *
- * po zdarzeniu GG_EVENT_DCC_CALLBACK naley ustawi typ poczenia za
- * pomoc tej funkcji.
+ * \param d Struktura połączenia
+ * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub
+ *             \c GG_SESSION_DCC_VOICE)
  *
- *  - d - struktura opisujca poczenie
- *  - type - typ poczenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE)
+ * \ingroup dcc6
  */
 void gg_dcc_set_type(struct gg_dcc *d, int type)
 {
 	d->type = type;
 	d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST;
 }
-	
-/*
- * gg_dcc_callback() // funkcja wewntrzna
+
+/**
+ * \internal Funkcja zwrotna połączenia bezpośredniego.
  *
- * wywoywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza
- * rezultat w struct gg_dcc->event.
+ * Pole \c callback struktury \c gg_dcc zawiera wskaźnik do tej funkcji.
+ * Wywołuje ona \c gg_watch_fd() i zachowuje wynik w polu \c event.
  *
- *  - d - structura opisujca poczenie
+ * \note Funkcjonalność funkcjo zwrotnej nie jest już wspierana.
  *
- * 0, -1.
+ * \param d Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
 static int gg_dcc_callback(struct gg_dcc *d)
 {
@@ -376,25 +390,26 @@
 	return (e != NULL) ? 0 : -1;
 }
 
-/*
- * gg_dcc_socket_create()
+/**
+ * Tworzy gniazdo nasłuchujące dla połączeń bezpośrednich.
  *
- * tworzy gniazdo dla bezporedniej komunikacji midzy klientami.
+ * Funkcja przywiązuje gniazdo do pierwszego wolnego portu TCP.
  *
- *  - uin - wasny numer
- *  - port - preferowany port, jeli rwny 0 lub -1, prbuje domylnego
+ * \param uin Własny numer
+ * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
  *
- * zaalokowana struct gg_dcc, ktr poniej naley zwolni funkcj
- * gg_dcc_free(), albo NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
 {
 	struct gg_dcc *c;
 	struct sockaddr_in sin;
 	int sock, bound = 0, errno2;
-	
+
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port);
-	
+
 	if (!uin) {
 		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n");
 		errno = EINVAL;
@@ -408,12 +423,12 @@
 
 	if (!port)
 		port = GG_DEFAULT_DCC_PORT;
-	
+
 	while (!bound) {
 		sin.sin_family = AF_INET;
 		sin.sin_addr.s_addr = INADDR_ANY;
 		sin.sin_port = htons(port);
-	
+
 		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port);
 		if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin)))
 			bound = 1;
@@ -433,7 +448,7 @@
 		errno = errno2;
 		return NULL;
 	}
-	
+
 	gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port);
 
 	if (!(c = malloc(sizeof(*c)))) {
@@ -452,20 +467,20 @@
 	c->check = GG_CHECK_READ;
 	c->callback = gg_dcc_callback;
 	c->destroy = gg_dcc_free;
-	
+
 	return c;
 }
 
-/*
- * gg_dcc_voice_send()
- *
- * wysya ramk danych dla rozmowy gosowej.
+/**
+ * Wysyła ramkę danych połączenia głosowego.
  *
- *  - d - struktura opisujca poczenie dcc
- *  - buf - bufor z danymi
- *  - length - rozmiar ramki
+ * \param d Struktura połączenia
+ * \param buf Bufor z danymi
+ * \param length Długość bufora z danymi
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
 {
@@ -500,7 +515,14 @@
 	return 0;
 }
 
-#define gg_read(fd, buf, size) \
+/**
+ * \internal Odbiera dane z połączenia bezpośredniego z obsługą błędów.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor na dane
+ * \param size Rozmiar bufora na dane
+ */
+#define gg_dcc_read(fd, buf, size) \
 { \
 	int tmp = read(fd, buf, size); \
 	\
@@ -517,9 +539,16 @@
 		return e; \
 	} \
 	gg_dcc_debug_data("read", fd, buf, size); \
-} 
+}
 
-#define gg_write(fd, buf, size) \
+/**
+ * \internal Wysyła dane do połączenia bezpośredniego z obsługą błędów.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danymi
+ * \param size Rozmiar bufora z danymi
+ */
+#define gg_dcc_write(fd, buf, size) \
 { \
 	int tmp; \
 	gg_dcc_debug_data("write", fd, buf, size); \
@@ -536,14 +565,18 @@
 	} \
 }
 
-/*
- * gg_dcc_watch_fd()
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
  *
- * funkcja, ktr naley wywoa, gdy co si zmieni na gg_dcc->fd.
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free.
  *
- *  - h - struktura zwrcona przez gg_create_dcc_socket()
+ * \param h Struktura połączenia
  *
- * zaalokowana struct gg_event lub NULL, jeli zabrako pamici na ni.
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup dcc6
  */
 struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
 {
@@ -551,7 +584,7 @@
 	int foo;
 
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h);
-	
+
 	if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) {
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n");
 		errno = EINVAL;
@@ -568,10 +601,9 @@
 	if (h->type == GG_SESSION_DCC_SOCKET) {
 		struct sockaddr_in sin;
 		struct gg_dcc *c;
-		int fd;
-		socklen_t sin_len = sizeof(sin);
-		int one = 1;
-		
+		int fd, one = 1;
+		unsigned int sin_len = sizeof(sin);
+
 		if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno));
 			return e;
@@ -607,7 +639,7 @@
 		c->file_fd = -1;
 		c->remote_addr = sin.sin_addr.s_addr;
 		c->remote_port = ntohs(sin.sin_port);
-		
+
 		e->type = GG_EVENT_DCC_NEW;
 		e->event.dcc_new = c;
 
@@ -617,8 +649,7 @@
 		struct gg_dcc_small_packet small;
 		struct gg_dcc_big_packet big;
 		int size, tmp, res;
-		socklen_t res_size = sizeof(res);
-		unsigned int utmp;
+		unsigned int utmp, res_size = sizeof(res);
 		char buf[1024], ack[] = "UDAG";
 
 		struct gg_dcc_file_info_packet {
@@ -634,8 +665,8 @@
 				uin_t uin;
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2);
-				
-				gg_read(h->fd, &uin, sizeof(uin));
+
+				gg_dcc_read(h->fd, &uin, sizeof(uin));
 
 				if (h->state == GG_STATE_READING_UIN_1) {
 					h->state = GG_STATE_READING_UIN_2;
@@ -656,7 +687,7 @@
 			case GG_STATE_SENDING_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n");
 
-				gg_write(h->fd, ack, 4);
+				gg_dcc_write(h->fd, ack, 4);
 
 				h->state = GG_STATE_READING_TYPE;
 				h->check = GG_CHECK_READ;
@@ -666,8 +697,8 @@
 
 			case GG_STATE_READING_TYPE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n");
-				
-				gg_read(h->fd, &small, sizeof(small));
+
+				gg_dcc_read(h->fd, &small, sizeof(small));
 
 				small.type = gg_fix32(small.type);
 
@@ -680,7 +711,7 @@
 						h->timeout = GG_DEFAULT_TIMEOUT;
 
 						e->type = GG_EVENT_DCC_CALLBACK;
-			
+
 						break;
 
 					case 0x0002:	/* XXX */
@@ -703,8 +734,8 @@
 
 			case GG_STATE_READING_REQUEST:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n");
-				
-				gg_read(h->fd, &small, sizeof(small));
+
+				gg_dcc_read(h->fd, &small, sizeof(small));
 
 				small.type = gg_fix32(small.type);
 
@@ -715,7 +746,7 @@
 						h->check = GG_CHECK_READ;
 						h->timeout = GG_DEFAULT_TIMEOUT;
 						break;
-						
+
 					case 0x0003:	/* XXX */
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n");
 						h->state = GG_STATE_SENDING_VOICE_ACK;
@@ -725,22 +756,22 @@
 						e->type = GG_EVENT_DCC_NEED_VOICE_ACK;
 
 						break;
-						
+
 					default:
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin);
 						e->type = GG_EVENT_DCC_ERROR;
 						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 				}
-		 	
+
 				return e;
 
 			case GG_STATE_READING_FILE_INFO:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n");
-				
-				gg_read(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+				gg_dcc_read(h->fd, &file_info_packet, sizeof(file_info_packet));
 
 				memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info));
-		
+
 				h->file_info.mode = gg_fix32(h->file_info.mode);
 				h->file_info.size = gg_fix32(h->file_info.size);
 
@@ -749,17 +780,17 @@
 				h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
 
 				e->type = GG_EVENT_DCC_NEED_FILE_ACK;
-				
+
 				return e;
 
 			case GG_STATE_SENDING_FILE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n");
-				
+
 				big.type = gg_fix32(0x0006);	/* XXX */
 				big.dunno1 = gg_fix32(h->offset);
 				big.dunno2 = 0;
 
-				gg_write(h->fd, &big, sizeof(big));
+				gg_dcc_write(h->fd, &big, sizeof(big));
 
 				h->state = GG_STATE_READING_FILE_HEADER;
 				h->chunk_size = sizeof(big);
@@ -773,25 +804,25 @@
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				return e;
-				
+
 			case GG_STATE_SENDING_VOICE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n");
-				
+
 				tiny.type = 0x01;	/* XXX */
 
-				gg_write(h->fd, &tiny, sizeof(tiny));
+				gg_dcc_write(h->fd, &tiny, sizeof(tiny));
 
 				h->state = GG_STATE_READING_VOICE_HEADER;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				h->offset = 0;
-				
+
 				return e;
-				
+
 			case GG_STATE_READING_FILE_HEADER:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n");
-				
+
 				tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
 
 				if (tmp == -1) {
@@ -802,7 +833,7 @@
 				}
 
 				gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
-				
+
 				h->chunk_offset += tmp;
 
 				if (h->chunk_offset < h->chunk_size)
@@ -823,7 +854,7 @@
 					return e;
 				}
 
-				if (h->chunk_size == 0) { 
+				if (h->chunk_size == 0) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n");
 					e->type = GG_EVENT_DCC_DONE;
 					return e;
@@ -833,13 +864,13 @@
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 				h->established = 1;
-			 	
+
 				return e;
 
 			case GG_STATE_READING_VOICE_HEADER:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n");
-				
-				gg_read(h->fd, &tiny, sizeof(tiny));
+
+				gg_dcc_read(h->fd, &tiny, sizeof(tiny));
 
 				switch (tiny.type) {
 					case 0x03:	/* XXX */
@@ -850,19 +881,19 @@
 						break;
 					case 0x04:	/* XXX */
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n");
-						/* XXX zwraca odpowiedni event */
+						/* XXX zwracać odpowiedni event */
 					default:
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type);
 						e->type = GG_EVENT_DCC_ERROR;
 						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 				}
-			 	
+
 				return e;
 
 			case GG_STATE_READING_VOICE_SIZE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n");
-				
-				gg_read(h->fd, &small, sizeof(small));
+
+				gg_dcc_read(h->fd, &small, sizeof(small));
 
 				small.type = gg_fix32(small.type);
 
@@ -870,7 +901,7 @@
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type);
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_NET;
-					
+
 					return e;
 				}
 
@@ -879,18 +910,19 @@
 
 				if (!(h->voice_buf = malloc(h->chunk_size))) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n");
+					free(e);
 					return NULL;
 				}
 
 				h->state = GG_STATE_READING_VOICE_DATA;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
-			 	
+
 				return e;
 
 			case GG_STATE_READING_VOICE_DATA:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n");
-				
+
 				tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
 				if (tmp < 1) {
 					if (tmp == -1) {
@@ -909,7 +941,7 @@
 
 				if (h->chunk_offset >= h->chunk_size) {
 					e->type = GG_EVENT_DCC_VOICE_DATA;
-					e->event.dcc_voice_data.data = h->voice_buf;
+					e->event.dcc_voice_data.data = (unsigned char*) h->voice_buf;
 					e->event.dcc_voice_data.length = h->chunk_size;
 					h->state = GG_STATE_READING_VOICE_HEADER;
 					h->voice_buf = NULL;
@@ -917,7 +949,7 @@
 
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
-				
+
 				return e;
 
 			case GG_STATE_CONNECTING:
@@ -925,7 +957,7 @@
 				uin_t uins[2];
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n");
-				
+
 				res = 0;
 				if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res));
@@ -935,23 +967,23 @@
 				}
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n");
-				
+
 				uins[0] = gg_fix32(h->uin);
 				uins[1] = gg_fix32(h->peer_uin);
 
-				gg_write(h->fd, uins, sizeof(uins));
-				
+				gg_dcc_write(h->fd, uins, sizeof(uins));
+
 				h->state = GG_STATE_READING_ACK;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
-				
+
 				return e;
 			}
 
 			case GG_STATE_READING_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n");
-				
-				gg_read(h->fd, buf, 4);
+
+				gg_dcc_read(h->fd, buf, 4);
 
 				if (strncmp(buf, ack, 4)) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n");
@@ -964,29 +996,29 @@
 				h->check = GG_CHECK_WRITE;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 				h->state = GG_STATE_SENDING_REQUEST;
-				
+
 				return e;
 
 			case GG_STATE_SENDING_VOICE_REQUEST:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n");
 
 				small.type = gg_fix32(0x0003);
-				
-				gg_write(h->fd, &small, sizeof(small));
+
+				gg_dcc_write(h->fd, &small, sizeof(small));
 
 				h->state = GG_STATE_READING_VOICE_ACK;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
-				
+
 				return e;
-			
+
 			case GG_STATE_SENDING_REQUEST:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n");
 
 				small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002);	/* XXX */
-				
-				gg_write(h->fd, &small, sizeof(small));
-				
+
+				gg_dcc_write(h->fd, &small, sizeof(small));
+
 				switch (h->type) {
 					case GG_SESSION_DCC_GET:
 						h->state = GG_STATE_READING_REQUEST;
@@ -1002,7 +1034,7 @@
 						if (h->file_fd == -1)
 							e->type = GG_EVENT_DCC_NEED_FILE_INFO;
 						break;
-						
+
 					case GG_SESSION_DCC_VOICE:
 						h->state = GG_STATE_SENDING_VOICE_REQUEST;
 						h->check = GG_CHECK_WRITE;
@@ -1011,7 +1043,7 @@
 				}
 
 				return e;
-			
+
 			case GG_STATE_SENDING_FILE_INFO:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n");
 
@@ -1021,8 +1053,8 @@
 				}
 
 				small.type = gg_fix32(0x0001);	/* XXX */
-				
-				gg_write(h->fd, &small, sizeof(small));
+
+				gg_dcc_write(h->fd, &small, sizeof(small));
 
 				file_info_packet.big.type = gg_fix32(0x0003);	/* XXX */
 				file_info_packet.big.dunno1 = 0;
@@ -1030,26 +1062,26 @@
 
 				memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info));
 
-				/* zostaj teraz u nas, wic odwracamy z powrotem */
+				/* zostają teraz u nas, więc odwracamy z powrotem */
 				h->file_info.size = gg_fix32(h->file_info.size);
 				h->file_info.mode = gg_fix32(h->file_info.mode);
-				
-				gg_write(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+				gg_dcc_write(h->fd, &file_info_packet, sizeof(file_info_packet));
 
 				h->state = GG_STATE_READING_FILE_ACK;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
 
 				return e;
-				
+
 			case GG_STATE_READING_FILE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n");
-				
-				gg_read(h->fd, &big, sizeof(big));
+
+				gg_dcc_read(h->fd, &big, sizeof(big));
 
-				/* XXX sprawdza wynik */
+				/* XXX sprawdzać wynik */
 				h->offset = gg_fix32(big.dunno1);
-				
+
 				h->state = GG_STATE_SENDING_FILE_HEADER;
 				h->check = GG_CHECK_WRITE;
 				h->timeout = GG_DEFAULT_TIMEOUT;
@@ -1060,8 +1092,8 @@
 
 			case GG_STATE_READING_VOICE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n");
-				
-				gg_read(h->fd, &tiny, sizeof(tiny));
+
+				gg_dcc_read(h->fd, &tiny, sizeof(tiny));
 
 				if (tiny.type != 0x01) {
 					gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type);
@@ -1075,14 +1107,14 @@
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				e->type = GG_EVENT_DCC_ACK;
-				
+
 				return e;
 
 			case GG_STATE_SENDING_FILE_HEADER:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n");
-				
+
 				h->chunk_offset = 0;
-				
+
 				if ((h->chunk_size = h->file_info.size - h->offset) > 4096) {
 					h->chunk_size = 4096;
 					big.type = gg_fix32(0x0003);  /* XXX */
@@ -1091,8 +1123,8 @@
 
 				big.dunno1 = gg_fix32(h->chunk_size);
 				big.dunno2 = 0;
-				
-				gg_write(h->fd, &big, sizeof(big));
+
+				gg_dcc_write(h->fd, &big, sizeof(big));
 
 				h->state = GG_STATE_SENDING_FILE;
 				h->check = GG_CHECK_WRITE;
@@ -1100,13 +1132,13 @@
 				h->established = 1;
 
 				return e;
-				
+
 			case GG_STATE_SENDING_FILE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n");
-				
+
 				if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
 					utmp = sizeof(buf);
-				
+
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size);
 
 				/* koniec pliku? */
@@ -1117,11 +1149,17 @@
 					return e;
 				}
 
+				if (h->offset >= h->file_info.size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
+					e->type = GG_EVENT_DCC_DONE;
+					return e;
+				}
+
 				lseek(h->file_fd, h->offset, SEEK_SET);
 
 				size = read(h->file_fd, buf, utmp);
 
-				/* bd */
+				/* błąd */
 				if (size == -1) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
 
@@ -1139,8 +1177,8 @@
 
 					return e;
 				}
-				
-				/* jeli wczytalimy wicej, utnijmy. */
+
+				/* jeśli wczytaliśmy więcej, utnijmy. */
 				if (h->offset + size > h->file_info.size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size);
 					size = h->file_info.size - h->offset;
@@ -1161,15 +1199,22 @@
 					return e;
 				}
 
-				h->offset += size;
-				
+				if (tmp == 0) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (connection reset)\n");
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_NET;
+					return e;
+				}
+
+				h->offset += tmp;
+
 				if (h->offset >= h->file_info.size) {
 					e->type = GG_EVENT_DCC_DONE;
 					return e;
 				}
-				
-				h->chunk_offset += size;
-				
+
+				h->chunk_offset += tmp;
+
 				if (h->chunk_offset >= h->chunk_size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
 					h->state = GG_STATE_SENDING_FILE_HEADER;
@@ -1178,22 +1223,28 @@
 					h->state = GG_STATE_SENDING_FILE;
 					h->timeout = GG_DCC_TIMEOUT_SEND;
 				}
-				
+
 				h->check = GG_CHECK_WRITE;
 
 				return e;
 
 			case GG_STATE_GETTING_FILE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n");
-				
+
 				if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
 					utmp = sizeof(buf);
-				
+
+				if (h->offset >= h->file_info.size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
+					e->type = GG_EVENT_DCC_DONE;
+					return e;
+				}
+
 				size = read(h->fd, buf, utmp);
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size);
-				
-				/* bd */
+
+				/* błąd */
 				if (size == -1) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
 
@@ -1211,9 +1262,9 @@
 
 					return e;
 				}
-				
+
 				tmp = write(h->file_fd, buf, size);
-				
+
 				if (tmp == -1 || tmp < size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno));
 					e->type = GG_EVENT_DCC_ERROR;
@@ -1222,14 +1273,14 @@
 				}
 
 				h->offset += size;
-				
+
 				if (h->offset >= h->file_info.size) {
 					e->type = GG_EVENT_DCC_DONE;
 					return e;
 				}
 
 				h->chunk_offset += size;
-				
+
 				if (h->chunk_offset >= h->chunk_size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
 					h->state = GG_STATE_READING_FILE_HEADER;
@@ -1245,11 +1296,11 @@
 					h->state = GG_STATE_GETTING_FILE;
 					h->timeout = GG_DCC_TIMEOUT_GET;
 				}
-				
+
 				h->check = GG_CHECK_READ;
 
 				return e;
-				
+
 			default:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n");
 				e->type = GG_EVENT_DCC_ERROR;
@@ -1258,35 +1309,28 @@
 				return e;
 		}
 	}
-	
+
 	return e;
 }
 
-#undef gg_read
-#undef gg_write
-
-/*
- * gg_dcc_free()
+/**
+ * Zwalnia zasoby używane przez połączenie bezpośrednie.
  *
- * zwalnia pami po strukturze poczenia dcc.
+ * \param d Struktura połączenia
  *
- *  - d - zwalniana struktura
+ * \ingroup dcc6
  */
 void gg_dcc_free(struct gg_dcc *d)
 {
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d);
-	
+
 	if (!d)
 		return;
 
 	if (d->fd != -1)
 		close(d->fd);
 
-	if (d->chunk_buf) {
-		free(d->chunk_buf);
-		d->chunk_buf = NULL;
-	}
-
+	free(d->chunk_buf);
 	free(d);
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/dcc7.c	Wed Mar 10 17:58:21 2010 +0000
@@ -0,0 +1,1217 @@
+/* $Id: dcc7.c 711 2009-04-16 00:52:47Z darkjames $ */
+
+/*
+ *  (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
+ *  
+ *  Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110,
+ *  USA.
+ */
+
+/**
+ * \file dcc7.c
+ *
+ * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x
+ */
+
+#include "libgadu.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef _WIN32
+#  include <sys/ioctl.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  ifdef sun
+#    include <sys/filio.h>
+#  endif
+#endif
+
+#include <time.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "compat.h"
+
+#define gg_debug_dcc(dcc, fmt...) \
+	gg_debug_session((dcc) ? (dcc)->sess : NULL, fmt)
+
+/**
+ * \internal Dodaje połączenie bezpośrednie do sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc);
+
+	if (!sess || !dcc || dcc->next) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	dcc->next = sess->dcc7_list;
+	sess->dcc7_list = dcc;
+
+	return 0;
+}
+
+/**
+ * \internal Usuwa połączenie bezpośrednie z sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	struct gg_dcc7 *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc);
+
+	if (!sess || !dcc) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (sess->dcc7_list == dcc) {
+		sess->dcc7_list = dcc->next;
+		dcc->next = NULL;
+		return 0;
+	}
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		if (tmp->next == dcc) {
+			tmp = dcc->next;
+			dcc->next = NULL;
+			return 0;
+		}
+	}
+
+	errno = ENOENT;
+	return -1;
+}
+
+/**
+ * \internal Zwraca strukturę połączenia o danym identyfikatorze.
+ *
+ * \param sess Struktura sesji
+ * \param id Identyfikator połączenia
+ * \param uin Numer nadawcy lub odbiorcy
+ *
+ * \return Struktura połączenia lub \c NULL jeśli nie znaleziono
+ */
+static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin)
+{
+	struct gg_dcc7 *tmp;
+	int empty;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin);
+
+	empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8);
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		if (empty) {
+			if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT)
+				return tmp;
+		} else {
+			if (!memcmp(&tmp->cid, &id, sizeof(id)))
+				return tmp;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * \internal Nawiązuje połączenie bezpośrednie
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_connect(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p, %p)\n", sess, dcc);
+
+	if (!sess || !dcc) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
+		return -1;
+	}
+
+	dcc->state = GG_STATE_CONNECTING;
+	dcc->check = GG_CHECK_WRITE;
+	dcc->timeout = GG_DCC7_TIMEOUT_CONNECT;
+	dcc->soft_timeout = 1;
+
+	return 0;
+}
+
+/**
+ * \internal Tworzy gniazdo nasłuchujące dla połączenia bezpośredniego
+ *
+ * \param dcc Struktura połączenia
+ * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port)
+{
+	struct sockaddr_in sin;
+	int fd;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
+
+	if (!dcc) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));
+		return -1;
+	}
+
+	// XXX losować porty?
+	
+	if (!port)
+		port = GG_DEFAULT_DCC_PORT;
+
+	while (1) {
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = INADDR_ANY;
+		sin.sin_port = htons(port);
+
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() trying port %d\n", port);
+
+		if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin)))
+			break;
+
+		if (port++ == 65535) {
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n");
+			close(fd);
+			errno = ENOENT;
+			return -1;
+		}
+	}
+
+	if (listen(fd, 1)) {
+		int errsv = errno;
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
+		close(fd);
+		errno = errsv;
+		return -1;
+	}
+
+	dcc->fd = fd;
+	dcc->local_port = port;
+	
+	dcc->state = GG_STATE_LISTENING;
+	dcc->check = GG_CHECK_READ;
+	dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
+
+	return 0;
+}
+
+/**
+ * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
+{
+	struct gg_dcc7_info pkt;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
+
+	// XXX dać możliwość konfiguracji?
+	
+	dcc->local_addr = dcc->sess->client_addr;
+
+	if (gg_dcc7_listen(dcc, 0) == -1)
+		return -1;
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.uin = gg_fix32(dcc->peer_uin);
+	pkt.type = GG_DCC7_TYPE_P2P;
+	pkt.id = dcc->cid;
+	snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), dcc->local_port);
+
+	return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Odwraca połączenie po nieudanym connect()
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)
+{
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
+
+	if (dcc->reverse) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
+		return -1;
+	}
+
+	gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
+	close(dcc->fd);
+	dcc->fd = -1;
+	dcc->reverse = 1;
+
+	return gg_dcc7_listen_and_send_info(dcc);
+}
+
+/**
+ * \internal Wysyła do serwera żądanie nadania identyfikatora sesji
+ *
+ * \param sess Struktura sesji
+ * \param type Rodzaj połączenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type)
+{
+	struct gg_dcc7_id_request pkt;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type);
+
+	if (!sess) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n");
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type);
+		errno = EINVAL;
+		return -1;
+	}
+	
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.type = gg_fix32(type);
+
+	return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Rozpoczyna wysyłanie pliku.
+ *
+ * Funkcja jest wykorzystywana przez \c gg_dcc7_send_file() oraz
+ * \c gg_dcc_send_file_fd().
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param fd Deskryptor pliku
+ * \param size Rozmiar pliku
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skrót SHA-1 pliku
+ * \param seek Flaga mówiąca, czy można używać lseek()
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash, int seek)
+{
+	struct gg_dcc7 *dcc = NULL;
+
+	if (!sess || !rcpt || !filename1250 || !hash || fd == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() invalid parameters\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() not enough memory\n");
+		goto fail;
+	}
+
+	if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1)
+		goto fail;
+
+	memset(dcc, 0, sizeof(struct gg_dcc7));
+	dcc->type = GG_SESSION_DCC7_SEND;
+	dcc->dcc_type = GG_DCC7_TYPE_FILE;
+	dcc->state = GG_STATE_REQUESTING_ID;
+	dcc->timeout = GG_DEFAULT_TIMEOUT;
+	dcc->sess = sess;
+	dcc->fd = -1;
+	dcc->uin = sess->uin;
+	dcc->peer_uin = rcpt;
+	dcc->file_fd = fd;
+	dcc->size = size;
+	dcc->seek = seek;
+
+	strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1);
+	dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
+
+	memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN);
+
+	if (gg_dcc7_session_add(sess, dcc) == -1)
+		goto fail;
+
+	return dcc;
+
+fail:
+	free(dcc);
+	return NULL;
+}
+
+/**
+ * Rozpoczyna wysyłanie pliku o danej nazwie.
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param filename Nazwa pliku w lokalnym systemie plików
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skrót SHA-1 pliku (lub \c NULL jeśli ma być wyznaczony)
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash)
+{
+	struct gg_dcc7 *dcc = NULL;
+	const char *tmp;
+	char hash_buf[GG_DCC7_HASH_LEN];
+	struct stat st;
+	int fd = -1;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d, \"%s\", %p)\n", sess, rcpt, filename, hash);
+
+	if (!sess || !rcpt || !filename) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() invalid parameters\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if (!filename1250)
+		filename1250 = filename;
+
+	if (stat(filename, &st) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() failed (%s)\n", strerror(errno));
+		goto fail;
+	}
+
+	if ((st.st_mode & S_IFDIR)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if ((fd = open(filename, O_RDONLY)) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
+		goto fail;
+	}
+
+	if (!hash) {
+		if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1)
+			goto fail;
+
+		hash = hash_buf;
+	}
+
+	if ((tmp = strrchr(filename1250, '/')))
+		filename1250 = tmp + 1;
+
+	if (!(dcc = gg_dcc7_send_file_common(sess, rcpt, fd, st.st_size, filename1250, hash, 1)))
+		goto fail;
+
+	return dcc;
+
+fail:
+	if (fd != -1) {
+		int errsv = errno;
+		close(fd);
+		errno = errsv;
+	}
+
+	free(dcc);
+	return NULL;
+}
+
+/**
+ * \internal Rozpoczyna wysyłanie pliku o danym deskryptorze.
+ *
+ * \note Wysyłanie pliku nie będzie działać poprawnie, jeśli deskryptor
+ * źródłowy jest w trybie nieblokującym i w pewnym momencie zabraknie danych.
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param fd Deskryptor pliku
+ * \param size Rozmiar pliku
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skrót SHA-1 pliku
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, %d, %d, %u, \"%s\", %p)\n", sess, rcpt, fd, size, filename1250, hash);
+
+	return gg_dcc7_send_file_common(sess, rcpt, fd, size, filename1250, hash, 0);
+}
+
+
+/**
+ * Potwierdza chęć odebrania pliku.
+ *
+ * \param dcc Struktura połączenia
+ * \param offset Początkowy offset przy wznawianiu przesyłania pliku
+ *
+ * \note Biblioteka nie zmienia położenia w odbieranych plikach. Jeśli offset
+ * początkowy jest różny od zera, należy ustawić go funkcją \c lseek() lub
+ * podobną.
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
+{
+	struct gg_dcc7_accept pkt;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
+
+	if (!dcc || !dcc->sess) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.uin = gg_fix32(dcc->peer_uin);
+	pkt.id = dcc->cid;
+	pkt.offset = gg_fix32(offset);
+
+	if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1)
+		return -1;
+
+	dcc->offset = offset;
+
+	return gg_dcc7_listen_and_send_info(dcc);
+}
+
+/**
+ * Odrzuca próbę przesłania pliku.
+ *
+ * \param dcc Struktura połączenia
+ * \param reason Powód odrzucenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
+{
+	struct gg_dcc7_reject pkt;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
+
+	if (!dcc || !dcc->sess) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.uin = gg_fix32(dcc->peer_uin);
+	pkt.id = dcc->cid;
+	pkt.reason = gg_fix32(reason);
+
+	return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Obsługuje pakiet identyfikatora połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_id_reply *p = payload;
+	struct gg_dcc7 *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, state %d, type %d\n", tmp, tmp->state, tmp->dcc_type);
+
+		if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != gg_fix32(p->type))
+			continue;
+		
+		tmp->cid = p->id;
+
+		switch (tmp->dcc_type) {
+			case GG_DCC7_TYPE_FILE:
+			{
+				struct gg_dcc7_new s;
+
+				memset(&s, 0, sizeof(s));
+				s.id = tmp->cid;
+				s.type = gg_fix32(GG_DCC7_TYPE_FILE);
+				s.uin_from = gg_fix32(tmp->uin);
+				s.uin_to = gg_fix32(tmp->peer_uin);
+				s.size = gg_fix32(tmp->size);
+
+				strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN);
+
+				tmp->state = GG_STATE_WAITING_FOR_ACCEPT;
+				tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
+
+				return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet akceptacji połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_accept *p = payload;
+	struct gg_dcc7 *dcc;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n");
+		// XXX wysłać reject?
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+	
+	// XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT?
+	
+	dcc->offset = gg_fix32(p->offset);
+	dcc->state = GG_STATE_WAITING_FOR_INFO;
+
+	return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_info *p = payload;
+	struct gg_dcc7 *dcc;
+	char *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n");
+		return 0;
+	}
+	
+	if (p->type != GG_DCC7_TYPE_P2P) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type);
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	// jeśli nadal czekamy na połączenie przychodzące, a druga strona nie
+	// daje rady i oferuje namiary na siebie, bierzemy co dają.
+
+	if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if (dcc->state == GG_STATE_LISTENING) {
+		close(dcc->fd);
+		dcc->fd = -1;
+		dcc->reverse = 1;
+	}
+	
+	if (dcc->type == GG_SESSION_DCC7_SEND) {
+		e->type = GG_EVENT_DCC7_ACCEPT;
+		e->event.dcc7_accept.dcc7 = dcc;
+		e->event.dcc7_accept.type = gg_fix32(p->type);
+		e->event.dcc7_accept.remote_ip = dcc->remote_addr;
+		e->event.dcc7_accept.remote_port = dcc->remote_port;
+	} else {
+		e->type = GG_EVENT_DCC7_PENDING;
+	}
+
+	if (gg_dcc7_connect(sess, dcc) == -1) {
+		if (gg_dcc7_reverse_connect(dcc) == -1) {
+			e->type = GG_EVENT_DCC7_ERROR;
+			e->event.dcc7_error = GG_ERROR_DCC7_NET;
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet odrzucenia połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_reject *p = payload;
+	struct gg_dcc7 *dcc;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n");
+		return 0;
+	}
+	
+	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	e->type = GG_EVENT_DCC7_REJECT;
+	e->event.dcc7_reject.dcc7 = dcc;
+	e->event.dcc7_reject.reason = gg_fix32(p->reason);
+
+	// XXX ustawić state na rejected?
+
+	return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet nowego połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_new *p = payload;
+	struct gg_dcc7 *dcc;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	switch (gg_fix32(p->type)) {
+		case GG_DCC7_TYPE_FILE:
+			if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n");
+				return -1;
+			}
+			
+			memset(dcc, 0, sizeof(struct gg_dcc7));
+			dcc->type = GG_SESSION_DCC7_GET;
+			dcc->dcc_type = GG_DCC7_TYPE_FILE;
+			dcc->fd = -1;
+			dcc->file_fd = -1;
+			dcc->uin = sess->uin;
+			dcc->peer_uin = gg_fix32(p->uin_from);
+			dcc->cid = p->id;
+			dcc->sess = sess;
+
+			if (gg_dcc7_session_add(sess, dcc) == -1) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+				gg_dcc7_free(dcc);
+				return -1;
+			}
+
+			dcc->size = gg_fix32(p->size);
+			strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1);
+			dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
+			memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN);
+
+			e->type = GG_EVENT_DCC7_NEW;
+			e->event.dcc7_new = dcc;
+
+			break;
+
+		case GG_DCC7_TYPE_VOICE:
+			if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n");
+				return -1;
+			}
+			
+			memset(dcc, 0, sizeof(struct gg_dcc7));
+
+			dcc->type = GG_SESSION_DCC7_VOICE;
+			dcc->dcc_type = GG_DCC7_TYPE_VOICE;
+			dcc->fd = -1;
+			dcc->file_fd = -1;
+			dcc->uin = sess->uin;
+			dcc->peer_uin = gg_fix32(p->uin_from);
+			dcc->cid = p->id;
+			dcc->sess = sess;
+
+			if (gg_dcc7_session_add(sess, dcc) == -1) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+				gg_dcc7_free(dcc);
+				return -1;
+			}
+
+			e->type = GG_EVENT_DCC7_NEW;
+			e->event.dcc7_new = dcc;
+
+			break;
+
+		default:
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from));
+
+			break;
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Ustawia odpowiednie stany wewnętrzne w zależności od rodzaju
+ * połączenia.
+ * 
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu.
+ */
+static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)
+{
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
+
+	if (!dcc) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	switch (dcc->type) {
+		case GG_SESSION_DCC7_GET:
+			dcc->state = GG_STATE_GETTING_FILE;
+			dcc->check = GG_CHECK_READ;
+			return 0;
+
+		case GG_SESSION_DCC7_SEND:
+			dcc->state = GG_STATE_SENDING_FILE;
+			dcc->check = GG_CHECK_WRITE;
+			return 0;
+
+		case GG_SESSION_DCC7_VOICE:
+			dcc->state = GG_STATE_READING_VOICE_DATA;
+			dcc->check = GG_CHECK_READ;
+			return 0;
+	}
+
+	errno = EINVAL;
+
+	return -1;
+}
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup dcc7
+ */
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
+{
+	struct gg_event *e;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);
+
+	if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!(e = malloc(sizeof(struct gg_event)))) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
+		return NULL;
+	}
+
+	memset(e, 0, sizeof(struct gg_event));
+	e->type = GG_EVENT_NONE;
+
+	switch (dcc->state) {
+		case GG_STATE_LISTENING:
+		{
+			struct sockaddr_in sin;
+			int fd, one = 1;
+			socklen_t sin_len = sizeof(sin);
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
+
+			if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
+				return e;
+			}
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+
+#ifdef FIONBIO
+			if (ioctl(fd, FIONBIO, &one) == -1) {
+#else
+			if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
+				close(fd);
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			close(dcc->fd);
+			dcc->fd = fd;
+
+			dcc->state = GG_STATE_READING_ID;
+			dcc->check = GG_CHECK_READ;
+			dcc->timeout = GG_DEFAULT_TIMEOUT;
+			dcc->incoming = 1;
+
+			dcc->remote_port = ntohs(sin.sin_port);
+			dcc->remote_addr = sin.sin_addr.s_addr;
+
+			e->type = GG_EVENT_DCC7_CONNECTED;
+			e->event.dcc7_connected.dcc7 = dcc;
+
+			return e;
+		}
+
+		case GG_STATE_CONNECTING:
+		{
+			int res = 0, error = 0;
+			unsigned int error_size = sizeof(error);
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
+
+			dcc->soft_timeout = 0;
+
+			if (dcc->timeout == 0)
+				error = ETIMEDOUT;
+
+			if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
+
+				if (gg_dcc7_reverse_connect(dcc) != -1) {
+					e->type = GG_EVENT_DCC7_PENDING;
+				} else {
+					e->type = GG_EVENT_DCC7_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC7_NET;
+				}
+
+				return e;
+			}
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
+
+			dcc->state = GG_STATE_SENDING_ID;
+			dcc->check = GG_CHECK_WRITE;
+			dcc->timeout = GG_DEFAULT_TIMEOUT;
+			dcc->incoming = 0;
+
+			return e;
+		}
+
+		case GG_STATE_READING_ID:
+		{
+			gg_dcc7_id_t id;
+			int res;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
+
+			if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (memcmp(&id, &dcc->cid, sizeof(id))) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (dcc->incoming) {
+				dcc->state = GG_STATE_SENDING_ID;
+				dcc->check = GG_CHECK_WRITE;
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			} else {
+				gg_dcc7_postauth_fixup(dcc);
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			}
+
+			return e;
+		}
+
+		case GG_STATE_SENDING_ID:
+		{
+			int res;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
+
+			if ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (dcc->incoming) {
+				gg_dcc7_postauth_fixup(dcc);
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			} else {
+				dcc->state = GG_STATE_READING_ID;
+				dcc->check = GG_CHECK_READ;
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			}
+
+			return e;
+		}
+
+		case GG_STATE_SENDING_FILE:
+		{
+			char buf[1024];
+			int chunk, res;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_FILE;
+				return e;
+			}
+
+			if ((chunk = dcc->size - dcc->offset) > sizeof(buf))
+				chunk = sizeof(buf);
+
+			if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
+				return e;
+			}
+
+			if ((res = write(dcc->fd, buf, res)) == -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_NET;
+				return e;
+			}
+
+			dcc->offset += res;
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			dcc->state = GG_STATE_SENDING_FILE;
+			dcc->check = GG_CHECK_WRITE;
+			dcc->timeout = GG_DCC7_TIMEOUT_SEND;
+
+			return e;
+		}
+
+		case GG_STATE_GETTING_FILE:
+		{
+			char buf[1024];
+			int res, wres;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
+				return e;
+			}
+
+			// XXX zapisywać do skutku?
+
+			if ((wres = write(dcc->file_fd, buf, res)) < res) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_FILE;
+				return e;
+			}
+
+			dcc->offset += res;
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			dcc->state = GG_STATE_GETTING_FILE;
+			dcc->check = GG_CHECK_READ;
+			dcc->timeout = GG_DCC7_TIMEOUT_GET;
+
+			return e;
+		}
+
+		default:
+		{
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
+			e->type = GG_EVENT_DCC7_ERROR;
+			e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+
+			return e;
+		}
+	}
+
+	return e;
+}
+
+/**
+ * Zwalnia zasoby używane przez połączenie bezpośrednie.
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \ingroup dcc7
+ */
+void gg_dcc7_free(struct gg_dcc7 *dcc)
+{
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
+
+	if (!dcc)
+		return;
+
+	if (dcc->fd != -1)
+		close(dcc->fd);
+
+	if (dcc->file_fd != -1)
+		close(dcc->file_fd);
+
+	if (dcc->sess)
+		gg_dcc7_session_remove(dcc->sess, dcc);
+
+	free(dcc);
+}
+
--- a/libpurple/protocols/gg/lib/events.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/events.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1,9 +1,10 @@
-/* $Id: events.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: events.c 855 2009-10-12 21:42:51Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
- *                          Arkadiusz Mikiewicz <arekm@pld-linux.org>
+ *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -16,71 +17,78 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file events.c
+ *
+ * \brief Obsługa zdarzeń
+ */
+
 #include "libgadu.h"
+#include "libgadu-internal.h"
 
 #include <sys/types.h>
+
 #ifndef _WIN32
-#include <sys/wait.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#  include <sys/ioctl.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
 #endif
 
-#include "libgadu-config.h"
+#include "compat.h"
+#include "protocol.h"
 
 #include <errno.h>
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-#  include <pthread.h>
-#endif
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <time.h>
 #include <unistd.h>
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#include <ctype.h>
+#ifdef GG_CONFIG_HAVE_OPENSSL
 #  include <openssl/err.h>
 #  include <openssl/x509.h>
 #endif
 
-#include "compat.h"
-
-/*
- * gg_event_free()
+/**
+ * Zwalnia pamięć zajmowaną przez informację o zdarzeniu.
  *
- * zwalnia pami zajmowan przez informacj o zdarzeniu.
+ * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci
+ * strukturę \c gg_event.
  *
- *  - e - wskanik do informacji o zdarzeniu
+ * \param e Struktura zdarzenia
+ *
+ * \ingroup events
  */
 void gg_event_free(struct gg_event *e)
 {
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
-			
+
 	if (!e)
 		return;
-	
+
 	switch (e->type) {
 		case GG_EVENT_MSG:
 			free(e->event.msg.message);
 			free(e->event.msg.formats);
 			free(e->event.msg.recipients);
 			break;
-	
+
 		case GG_EVENT_NOTIFY:
 			free(e->event.notify);
 			break;
-	
+
 		case GG_EVENT_NOTIFY60:
 		{
 			int i;
 
 			for (i = 0; e->event.notify60[i].uin; i++)
 				free(e->event.notify60[i].descr);
-		
+
 			free(e->event.notify60);
 
 			break;
@@ -89,7 +97,7 @@
 		case GG_EVENT_STATUS60:
 			free(e->event.status60.descr);
 			break;
-	
+
 		case GG_EVENT_STATUS:
 			free(e->event.status.descr);
 			break;
@@ -112,26 +120,30 @@
 		case GG_EVENT_USERLIST:
 			free(e->event.userlist.reply);
 			break;
-	
+
 		case GG_EVENT_IMAGE_REPLY:
 			free(e->event.image_reply.filename);
 			free(e->event.image_reply.image);
 			break;
+
+		case GG_EVENT_XML_EVENT:
+			free(e->event.xml_event.data);
+			break;
 	}
 
 	free(e);
 }
 
-/*
- * gg_image_queue_remove()
- *
- * usuwa z kolejki dany wpis.
+/** \cond internal */
+
+/**
+ * \internal Usuwa obrazek z kolejki do wysłania.
  *
- *  - s - sesja
- *  - q - kolejka
- *  - freeq - czy zwolni kolejk
+ * \param s Struktura sesji
+ * \param q Struktura obrazka
+ * \param freeq Flaga zwolnienia elementu kolejki
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
  */
 int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
 {
@@ -162,13 +174,14 @@
 	return 0;
 }
 
-/*
- * gg_image_queue_parse() // funkcja wewntrzna
+/**
+ * \internal Analizuje przychodzący pakiet z obrazkiem.
  *
- * parsuje przychodzcy pakiet z obrazkiem.
- *
- *  - e - opis zdarzenia
- *  - 
+ * \param e Struktura zdarzenia
+ * \param p Bufor z danymi
+ * \param len Długość bufora
+ * \param sess Struktura sesji
+ * \param sender Numer nadawcy
  */
 static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender)
 {
@@ -180,8 +193,8 @@
 		return;
 	}
 
-	/* znajd dany obrazek w kolejce danej sesji */
-	
+	/* znajdź dany obrazek w kolejce danej sesji */
+
 	for (qq = sess->images, q = NULL; qq; qq = qq->next) {
 		if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) {
 			q = qq;
@@ -190,34 +203,23 @@
 	}
 
 	if (!q) {
-		gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
 		return;
 	}
 
 	if (p[0] == 0x05) {
-		unsigned int i, ok = 0;
-		
 		q->done = 0;
 
 		len -= sizeof(struct gg_msg_image_reply);
 		p += sizeof(struct gg_msg_image_reply);
 
-		/* sprawd, czy mamy tekst zakoczony \0 */
-
-		for (i = 0; i < len; i++) {
-			if (!p[i]) {
-				ok = 1;
-				break;
-			}
-		}
-
-		if (!ok) {
-			gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
+		if (memchr(p, 0, len) == NULL) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
 			return;
 		}
 
 		if (!(q->filename = strdup(p))) {
-			gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
 			return;
 		}
 
@@ -230,11 +232,11 @@
 
 	if (q->done + len > q->size)
 		len = q->size - q->done;
-		
+
 	memcpy(q->image + q->done, p, len);
 	q->done += len;
 
-	/* jeli skoczono odbiera obrazek, wygeneruj zdarzenie */
+	/* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
 
 	if (q->done >= q->size) {
 		e->type = GG_EVENT_IMAGE_REPLY;
@@ -250,78 +252,55 @@
 	}
 }
 
-/*
- * gg_handle_recv_msg() // funkcja wewntrzna
- *
- * obsuguje pakiet z przychodzc wiadomoci, rozbijajc go na dodatkowe
- * struktury (konferencje, kolorki) w razie potrzeby.
+/**
+ * \internal Analizuje informacje rozszerzone wiadomości.
+ * 
+ * \param sess Struktura sesji.
+ * \param e Struktura zdarzenia.
+ * \param sender Numer nadawcy.
+ * \param p Wskaźnik na dane rozszerzone.
+ * \param packet_end Wskaźnik na koniec pakietu.
  *
- *  - h - nagwek pakietu
- *  - e - opis zdarzenia
- *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 jeśli wiadomość obsłużono i wynik ma
+ * zostać przekazany aplikacji, -2 jeśli wystąpił błąd ogólny, -3 jeśli
+ * wiadomość jest niepoprawna.
  */
-static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end)
 {
-	struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header));
-	char *p, *packet_end = (char*) r + h->length;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
-
-	if (!r->seq && !r->msgclass) {
-		gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
-		e->type = GG_EVENT_NONE;
-		return 0;
-	}
-	
-	for (p = (char*) r + sizeof(*r); *p; p++) {
-		if (*p == 0x02 && p == packet_end - 1) {
-			gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
-			break;
-		}
-		if (p >= packet_end) {
-			gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
-			goto malformed;
-		}
-	}
-	
-	p++;
-
-	/* przeanalizuj dodatkowe opcje */
 	while (p < packet_end) {
 		switch (*p) {
 			case 0x01:		/* konferencja */
 			{
 				struct gg_msg_recipients *m = (void*) p;
 				uint32_t i, count;
-			
+
 				p += sizeof(*m);
-			
+
 				if (p > packet_end) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n");
 					goto malformed;
 				}
 
 				count = gg_fix32(m->count);
 
 				if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n");
 					goto malformed;
 				}
-			
+
 				if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n");
 					goto fail;
 				}
-			
+
 				for (i = 0; i < count; i++, p += sizeof(uint32_t)) {
 					uint32_t u;
 					memcpy(&u, p, sizeof(uint32_t));
 					e->event.msg.recipients[i] = gg_fix32(u);
 				}
-				
+
 				e->event.msg.recipients_count = count;
-				
+
 				break;
 			}
 
@@ -329,9 +308,9 @@
 			{
 				uint16_t len;
 				char *buf;
-			
+
 				if (p + 3 > packet_end) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
 					goto malformed;
 				}
 
@@ -339,18 +318,18 @@
 				len = gg_fix16(len);
 
 				if (!(buf = malloc(len))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
 					goto fail;
 				}
 
 				p += 3;
 
 				if (p + len > packet_end) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
 					free(buf);
 					goto malformed;
 				}
-				
+
 				memcpy(buf, p, len);
 
 				e->event.msg.formats = buf;
@@ -366,17 +345,17 @@
 				struct gg_msg_image_request *i = (void*) p;
 
 				if (p + sizeof(*i) > packet_end) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
 					goto malformed;
 				}
 
-				e->event.image_request.sender = gg_fix32(r->sender);
+				e->event.image_request.sender = sender;
 				e->event.image_request.size = gg_fix32(i->size);
 				e->event.image_request.crc32 = gg_fix32(i->crc32);
 
 				e->type = GG_EVENT_IMAGE_REQUEST;
 
-				return 0;
+				goto handled;
 			}
 
 			case 0x05:		/* image_reply */
@@ -386,75 +365,347 @@
 
 				if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
 
-					/* pusta odpowied - klient po drugiej stronie nie ma danego obrazka */
+					/* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */
 
 					e->type = GG_EVENT_IMAGE_REPLY;
-					e->event.image_reply.sender = gg_fix32(r->sender);
+					e->event.image_reply.sender = sender;
 					e->event.image_reply.size = 0;
 					e->event.image_reply.crc32 = gg_fix32(rep->crc32);
 					e->event.image_reply.filename = NULL;
 					e->event.image_reply.image = NULL;
-					return 0;
+					goto handled;
 
 				} else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
 
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n");
 					goto malformed;
 				}
 
 				rep->size = gg_fix32(rep->size);
 				rep->crc32 = gg_fix32(rep->crc32);
-				gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender));
-
-				return 0;
+				gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender);
+
+				goto handled;
 			}
 
 			default:
 			{
-				gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
 				p = packet_end;
 			}
 		}
 	}
 
+	return 0;
+
+handled:
+	return -1;
+
+fail:
+	return -2;
+
+malformed:
+	return -3;
+}
+
+/**
+ * \internal Analizuje przychodzący pakiet z wiadomością.
+ *
+ * Rozbija pakiet na poszczególne składniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wskaźnik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+{
+	struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header));
+	char *p, *packet_end = (char*) r + h->length;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
+
+	if (!r->seq && !r->msgclass) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
+		e->type = GG_EVENT_NONE;
+		return 0;
+	}
+
+	/* znajdź \0 */
+	for (p = (char*) r + sizeof(*r); ; p++) {
+		if (p >= packet_end) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
+			goto malformed;
+		}
+
+		if (*p == 0x02 && p == packet_end - 1) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
+			break;
+		}
+
+		if (!*p)
+			break;
+	}
+
+	p++;
+
+	switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), p, packet_end)) {
+		case -1:	// handled
+			return 0;
+
+		case -2:	// failed
+			goto fail;
+
+		case -3:	// malformed
+			goto malformed;
+	}
+
 	e->type = GG_EVENT_MSG;
 	e->event.msg.msgclass = gg_fix32(r->msgclass);
 	e->event.msg.sender = gg_fix32(r->sender);
 	e->event.msg.time = gg_fix32(r->time);
-	e->event.msg.message = (unsigned char *)strdup((char*) r + sizeof(*r));
+	e->event.msg.seq = gg_fix32(r->seq);
+	e->event.msg.message = (unsigned char*) strdup((char*) r + sizeof(*r));
 
 	return 0;
 
 malformed:
 	e->type = GG_EVENT_NONE;
-
+	free(e->event.msg.message);
 	free(e->event.msg.recipients);
 	free(e->event.msg.formats);
 
 	return 0;
 
 fail:
+	free(e->event.msg.message);
 	free(e->event.msg.recipients);
 	free(e->event.msg.formats);
 	return -1;
 }
 
-/*
- * gg_watch_fd_connected() // funkcja wewntrzna
+/**
+ * \internal Zamienia tekst w formacie HTML na czysty tekst.
+ *
+ * \param dst Bufor wynikowy (może być \c NULL)
+ * \param html Tekst źródłowy
+ *
+ * \note Dokleja \c \\0 na końcu bufora wynikowego.
  *
- * patrzy na gniazdo, odbiera pakiet i wypenia struktur zdarzenia.
+ * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
+ */
+static int gg_convert_from_html(char *dst, const char *html)
+{
+	const char *src, *entity, *tag;
+	int len, in_tag, in_entity;
+
+	len = 0;
+	in_tag = 0;
+	tag = NULL;
+	in_entity = 0;
+	entity = NULL;
+
+	for (src = html; *src != 0; src++) {
+		if (*src == '<') {
+			tag = src;
+			in_tag = 1;
+			continue;
+		}
+
+		if (in_tag && (*src == '>')) {
+			if (strncmp(tag, "<br", 3) == 0) {
+				if (dst != NULL)
+					dst[len] = '\n';
+				len++;
+			}
+			in_tag = 0;
+			continue;
+		}
+
+		if (in_tag)
+			continue;
+
+		if (*src == '&') {
+			in_entity = 1;
+			entity = src;
+			continue;
+		}
+
+		if (in_entity && *src == ';') {
+			in_entity = 0;
+			if (dst != NULL) {
+				if (strncmp(entity, "&lt;", 4) == 0)
+					dst[len] = '<';
+				else if (strncmp(entity, "&gt;", 4) == 0)
+					dst[len] = '>';
+				else if (strncmp(entity, "&quot;", 6) == 0)
+					dst[len] = '"';
+				else if (strncmp(entity, "&apos;", 6) == 0)
+					dst[len] = '\'';
+				else if (strncmp(entity, "&amp;", 5) == 0)
+					dst[len] = '&';
+				else
+					dst[len] = '?';
+			}
+			len++;
+			continue;
+		}
+
+		if (in_entity && !(isalnum(*src) || *src == '#'))
+			in_entity = 0;
+
+		if (in_entity)
+			continue;
+
+		if (dst != NULL)
+			dst[len] = *src;
+
+		len++;
+	}
+
+	if (dst != NULL)
+		dst[len] = 0;
+	
+	return len;
+}
+
+/**
+ * \internal Analizuje przychodzący pakiet z wiadomością protokołu Gadu-Gadu 8.0.
+ *
+ * Rozbija pakiet na poszczególne składniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wskaźnik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
  *
- *  - sess - struktura opisujca sesj
- *  - e - opis zdarzenia
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+{
+	char *packet = (char*) h + sizeof(struct gg_header);
+	struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet;
+	uint32_t offset_plain;
+	uint32_t offset_attr;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %p);\n", h, e);
+
+	if (!r->seq && !r->msgclass) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n");
+		goto malformed;
+	}
+
+	offset_plain = gg_fix32(r->offset_plain);
+	offset_attr  = gg_fix32(r->offset_attr);
+
+	if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= h->length) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n");
+		goto malformed;
+	}
+
+	if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > h->length) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n");
+		offset_attr = 0;	/* nie parsuj attr. */
+		/* goto ignore; */
+	}
+
+	/* Normalna sytuacja, więc nie podpada pod powyższy warunek. */
+	if (offset_attr == h->length)
+		offset_attr = 0;
+
+	if (memchr(packet + offset_plain, 0, h->length - offset_plain) == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n");
+		goto malformed;
+	}
+
+	if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n");
+		goto malformed;
+	}
+
+	e->type = GG_EVENT_MSG;
+	e->event.msg.msgclass = gg_fix32(r->msgclass);
+	e->event.msg.sender = gg_fix32(r->sender);
+	e->event.msg.time = gg_fix32(r->time);
+	e->event.msg.seq = gg_fix32(r->seq);
+
+	if (sess->encoding == GG_ENCODING_CP1250) {
+		e->event.msg.message = (unsigned char*) strdup(packet + offset_plain);
+	} else {
+		if (offset_plain > sizeof(struct gg_recv_msg80)) {
+			int len;
+
+			len = gg_convert_from_html(NULL, packet + sizeof(struct gg_recv_msg80));
+
+			e->event.msg.message = malloc(len + 1);
+
+			if (e->event.msg.message == NULL)
+				goto fail;
+
+			gg_convert_from_html((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80));
+		} else {
+			e->event.msg.message = (unsigned char*) gg_cp_to_utf8(packet + offset_plain);
+		}
+	}
+
+	if (offset_plain > sizeof(struct gg_recv_msg80)) {
+		if (sess->encoding == GG_ENCODING_UTF8)
+			e->event.msg.xhtml_message = strdup(packet + sizeof(struct gg_recv_msg80));
+		else
+			e->event.msg.xhtml_message = gg_utf8_to_cp(packet + sizeof(struct gg_recv_msg80));
+	} else {
+		e->event.msg.xhtml_message = NULL;
+	}
+
+	if (offset_attr != 0) {
+		switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) {
+			case -1:	// handled
+				return 0;
+
+			case -2:	// failed
+				goto fail;
+
+			case -3:	// malformed
+				goto malformed;
+		}
+	}
+
+	return 0;
+
+fail:
+	free(e->event.msg.message);
+	free(e->event.msg.xhtml_message);
+	free(e->event.msg.recipients);
+	free(e->event.msg.formats);
+	return -1;
+
+malformed:
+	e->type = GG_EVENT_NONE;
+	free(e->event.msg.message);
+	free(e->event.msg.xhtml_message);
+	free(e->event.msg.recipients);
+	free(e->event.msg.formats);
+	return 0;
+}
+
+/**
+ * \internal Odbiera pakiet od serwera.
  *
- * 0, -1.
+ * Analizuje pakiet i wypełnia strukturę zdarzenia.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ *
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
  */
 static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
 {
 	struct gg_header *h = NULL;
 	char *p;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
 
 	if (!sess) {
 		errno = EFAULT;
@@ -462,76 +713,86 @@
 	}
 
 	if (!(h = gg_recv_packet(sess))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
 		goto fail;
 	}
 
 	p = (char*) h + sizeof(struct gg_header);
-	
+
 	switch (h->type) {
 		case GG_RECV_MSG:
 		{
 			if (h->length >= sizeof(struct gg_recv_msg))
 				if (gg_handle_recv_msg(h, e, sess))
 					goto fail;
-			
+
 			break;
 		}
 
+		case GG_RECV_MSG80:
+		{
+			if (h->length >= sizeof(struct gg_recv_msg80))
+				if (gg_handle_recv_msg80(h, e, sess))
+					goto fail;
+
+			break;
+		}
+
+
 		case GG_NOTIFY_REPLY:
 		{
 			struct gg_notify_reply *n = (void*) p;
 			unsigned int count, i;
 			char *tmp;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
 
 			if (h->length < sizeof(*n)) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n");
 				errno = EINVAL;
 				goto fail;
 			}
 
 			if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) {
 				e->type = GG_EVENT_NOTIFY_DESCR;
-				
+
 				if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 					goto fail;
 				}
 				e->event.notify_descr.notify[1].uin = 0;
 				memcpy(e->event.notify_descr.notify, p, sizeof(*n));
 				e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin);
 				e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status);
-				e->event.notify_descr.notify[0].remote_ip = e->event.notify_descr.notify[0].remote_ip;
 				e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port);
+				e->event.notify_descr.notify[0].version = gg_fix32(e->event.notify_descr.notify[0].version);
 
 				count = h->length - sizeof(*n);
 				if (!(tmp = malloc(count + 1))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 					goto fail;
 				}
 				memcpy(tmp, p + sizeof(*n), count);
 				tmp[count] = 0;
 				e->event.notify_descr.descr = tmp;
-				
+
 			} else {
 				e->type = GG_EVENT_NOTIFY;
-				
+
 				if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 					goto fail;
 				}
-				
+
 				memcpy(e->event.notify, p, h->length);
 				count = h->length / sizeof(*n);
 				e->event.notify[count].uin = 0;
-				
+
 				for (i = 0; i < count; i++) {
 					e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin);
 					e->event.notify[i].status = gg_fix32(e->event.notify[i].status);
-					e->event.notify[i].remote_ip = e->event.notify[i].remote_ip;
 					e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port);
+					e->event.notify[i].version = gg_fix32(e->event.notify[i].version);
 				}
 			}
 
@@ -542,7 +803,7 @@
 		{
 			struct gg_status *s = (void*) p;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
 
 			if (h->length >= sizeof(*s)) {
 				e->type = GG_EVENT_STATUS;
@@ -564,23 +825,176 @@
 			break;
 		}
 
-		case GG_NOTIFY_REPLY60:
+		case GG_NOTIFY_REPLY77:
+		case GG_NOTIFY_REPLY80BETA:
 		{
-			struct gg_notify_reply60 *n = (void*) p;
+			struct gg_notify_reply77 *n = (void*) p;
 			unsigned int length = h->length, i = 0;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
 
 			e->type = GG_EVENT_NOTIFY60;
 			e->event.notify60 = malloc(sizeof(*e->event.notify60));
 
 			if (!e->event.notify60) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 				goto fail;
 			}
 
 			e->event.notify60[0].uin = 0;
-			
+
+			while (length >= sizeof(struct gg_notify_reply77)) {
+				uin_t uin = gg_fix32(n->uin);
+				char *tmp;
+
+				e->event.notify60[i].uin = uin & 0x00ffffff;
+				e->event.notify60[i].status = n->status;
+				e->event.notify60[i].remote_ip = n->remote_ip;
+				e->event.notify60[i].remote_port = gg_fix16(n->remote_port);
+				e->event.notify60[i].version = n->version;
+				e->event.notify60[i].image_size = n->image_size;
+				e->event.notify60[i].descr = NULL;
+				e->event.notify60[i].time = 0;
+
+				if (uin & 0x40000000)
+					e->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
+				if (uin & 0x20000000)
+					e->event.notify60[i].version |= GG_HAS_AUDIO7_MASK;
+				if (uin & 0x08000000)
+					e->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
+
+				if (GG_S_D(n->status)) {
+					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply77));
+
+					if (sizeof(struct gg_notify_reply77) + descr_len <= length) {
+						char *descr;
+
+						if (!(descr = malloc(descr_len + 1))) {
+							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+							goto fail;
+						}
+
+						memcpy(descr, (char*) n + sizeof(struct gg_notify_reply77) + 1, descr_len);
+						descr[descr_len] = 0;
+
+						if (h->type == GG_NOTIFY_REPLY80BETA && sess->encoding != GG_ENCODING_UTF8) {
+							char *cp_descr = gg_utf8_to_cp(descr);
+
+							if (!cp_descr) {
+								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+								free(descr);
+								goto fail;
+							}
+
+							free(descr);
+							descr = cp_descr;
+						}
+
+						e->event.notify60[i].descr = descr;
+
+						/* XXX czas */
+							
+						length -= sizeof(struct gg_notify_reply77) + descr_len + 1;
+						n = (void*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1);
+					} else {
+						length = 0;
+					}
+
+				} else {
+					length -= sizeof(struct gg_notify_reply77);
+					n = (void*) ((char*) n + sizeof(struct gg_notify_reply77));
+				}
+
+				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					free(e->event.notify60);
+					goto fail;
+				}
+
+				e->event.notify60 = (void*) tmp;
+				e->event.notify60[++i].uin = 0;
+			}
+
+			break;
+		}
+
+		case GG_STATUS77:
+		case GG_STATUS80BETA:
+		{
+			struct gg_status77 *s = (void*) p;
+			uint32_t uin;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+			if (h->length < sizeof(*s))
+				break;
+
+			uin = gg_fix32(s->uin);
+
+			e->type = GG_EVENT_STATUS60;
+			e->event.status60.uin = uin & 0x00ffffff;
+			e->event.status60.status = s->status;
+			e->event.status60.remote_ip = s->remote_ip;
+			e->event.status60.remote_port = gg_fix16(s->remote_port);
+			e->event.status60.version = s->version;
+			e->event.status60.image_size = s->image_size;
+			e->event.status60.descr = NULL;
+			e->event.status60.time = 0;
+
+			if (uin & 0x40000000)
+				e->event.status60.version |= GG_HAS_AUDIO_MASK;
+			if (uin & 0x20000000)
+				e->event.status60.version |= GG_HAS_AUDIO7_MASK;
+			if (uin & 0x08000000)
+				e->event.status60.version |= GG_ERA_OMNIX_MASK;
+
+			if (h->length > sizeof(*s)) {
+				int len = h->length - sizeof(*s);
+				char *buf = malloc(len + 1);
+
+				/* XXX, jesli malloc() sie nie uda to robic tak samo jak przy GG_NOTIFY_REPLY* ?
+				 * 	- goto fail; (?)
+				 */
+				if (buf) {
+					memcpy(buf, (char*) p + sizeof(*s), len);
+					buf[len] = 0;
+
+					if (h->type == GG_STATUS80BETA && sess->encoding != GG_ENCODING_UTF8) {
+						char *cp_buf = gg_utf8_to_cp(buf);
+						free(buf);
+						buf = cp_buf;
+					}
+				}
+
+				e->event.status60.descr = buf;
+
+				if (len > 4 && p[h->length - 5] == 0) {
+					uint32_t t;
+					memcpy(&t, p + h->length - 4, sizeof(uint32_t));
+					e->event.status60.time = gg_fix32(t);
+				}
+			}
+
+			break;
+		}
+
+		case GG_NOTIFY_REPLY60:
+		{
+			struct gg_notify_reply60 *n = (void*) p;
+			unsigned int length = h->length, i = 0;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+			e->type = GG_EVENT_NOTIFY60;
+			e->event.notify60 = malloc(sizeof(*e->event.notify60));
+
+			if (!e->event.notify60) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+				goto fail;
+			}
+
+			e->event.notify60[0].uin = 0;
+
 			while (length >= sizeof(struct gg_notify_reply60)) {
 				uin_t uin = gg_fix32(n->uin);
 				char *tmp;
@@ -602,9 +1016,9 @@
 				if (GG_S_D(n->status)) {
 					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60));
 
-					if (descr_len < length) {
+					if (sizeof(struct gg_notify_reply60) + descr_len <= length) {
 						if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) {
-							gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 							goto fail;
 						}
 
@@ -612,17 +1026,20 @@
 						e->event.notify60[i].descr[descr_len] = 0;
 
 						/* XXX czas */
+							
+						length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
+						n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
+					} else {
+						length = 0;
 					}
-					
-					length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
-					n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
+
 				} else {
 					length -= sizeof(struct gg_notify_reply60);
 					n = (void*) ((char*) n + sizeof(struct gg_notify_reply60));
 				}
 
 				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 					free(e->event.notify60);
 					goto fail;
 				}
@@ -633,13 +1050,13 @@
 
 			break;
 		}
-		
+
 		case GG_STATUS60:
 		{
 			struct gg_status60 *s = (void*) p;
 			uint32_t uin;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
 
 			if (h->length < sizeof(*s))
 				break;
@@ -682,11 +1099,132 @@
 			break;
 		}
 
+		case GG_STATUS80:
+		{
+			struct gg_notify_reply80 *s = (void*) p;
+			uint32_t descr_len;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+			if (h->length < sizeof(*s))
+				break;
+
+			e->type = GG_EVENT_STATUS60;
+			e->event.status60.uin		= gg_fix32(s->uin);
+			e->event.status60.status	= gg_fix32(s->status);
+			e->event.status60.remote_ip	= s->remote_ip;
+			e->event.status60.remote_port	= gg_fix16(s->remote_port);
+			e->event.status60.image_size	= s->image_size;
+			e->event.status60.descr		= NULL;
+			e->event.status60.version	= 0x00;	/* not-supported */
+			e->event.status60.time		= 0;	/* not-supported */
+
+			descr_len = gg_fix32(s->descr_len);
+
+			if (descr_len > 0 && h->length-sizeof(*s) >= descr_len) {
+				char *buf = malloc(descr_len + 1);
+
+				if (buf) {
+					memcpy(buf, (char*) p + sizeof(*s), descr_len);
+					buf[descr_len] = 0;
+
+					if (sess->encoding != GG_ENCODING_UTF8) {
+						char *cp_buf = gg_utf8_to_cp(buf);
+						free(buf);
+						buf = cp_buf;
+					}
+				}
+
+				e->event.status60.descr = buf;
+			}
+			break;
+		}
+
+		case GG_NOTIFY_REPLY80:
+		{
+			struct gg_notify_reply80 *n = (void*) p;
+			int length = h->length, i = 0;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+			e->type = GG_EVENT_NOTIFY60;
+			e->event.notify60 = malloc(sizeof(*e->event.notify60));
+			if (!e->event.notify60) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+				goto fail;
+			}
+
+			e->event.notify60[0].uin = 0;
+
+			while (length >= sizeof(struct gg_notify_reply80)) {
+				uint32_t descr_len;
+				char *tmp;
+				
+				e->event.notify60[i].uin	= gg_fix32(n->uin);
+				e->event.notify60[i].status	= gg_fix32(n->status);
+				e->event.notify60[i].remote_ip	= n->remote_ip;
+				e->event.notify60[i].remote_port= gg_fix16(n->remote_port);
+				e->event.notify60[i].image_size	= n->image_size;
+				e->event.notify60[i].descr	= NULL;
+				e->event.notify60[i].version	= 0x00;	/* not-supported */
+				e->event.notify60[i].time	= 0;	/* not-supported */
+
+				
+				descr_len = gg_fix32(n->descr_len);
+
+				length -= sizeof(struct gg_notify_reply80);
+				n = (void*) ((char*) n + sizeof(struct gg_notify_reply80));
+
+				if (descr_len) {
+					if (length >= descr_len) {
+						/* XXX, GG_S_D(n->status) */
+						char *descr;
+
+						if (!(descr = malloc(descr_len + 1))) {
+							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+							goto fail;
+						}
+
+						memcpy(descr, n, descr_len);
+						descr[descr_len] = 0;
+
+						if (sess->encoding != GG_ENCODING_UTF8) {
+							char *cp_descr = gg_utf8_to_cp(descr);
+
+							if (!cp_descr) {
+								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+								free(descr);
+								goto fail;
+							}
+
+							free(descr);
+							descr = cp_descr;
+						}
+						e->event.notify60[i].descr = descr;
+
+						length -= descr_len;
+						n = (void*) ((char*) n + descr_len);
+					} else
+						length = 0;
+				}
+
+				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					free(e->event.notify60);
+					goto fail;
+				}
+
+				e->event.notify60 = (void*) tmp;
+				e->event.notify60[++i].uin = 0;
+			}
+			break;
+		}
+
 		case GG_SEND_MSG_ACK:
 		{
 			struct gg_send_msg_ack *s = (void*) p;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
 
 			if (h->length < sizeof(*s))
 				break;
@@ -699,9 +1237,9 @@
 			break;
 		}
 
-		case GG_PONG: 
+		case GG_PONG:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
 
 			e->type = GG_EVENT_PONG;
 			sess->last_pong = time(NULL);
@@ -711,27 +1249,47 @@
 
 		case GG_DISCONNECTING:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
 			e->type = GG_EVENT_DISCONNECT;
 			break;
 		}
 
+		case GG_DISCONNECT_ACK:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection acknowledge\n");
+			e->type = GG_EVENT_DISCONNECT_ACK;
+			break;
+		}
+
+		case GG_XML_EVENT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n");
+			e->type = GG_EVENT_XML_EVENT;
+			if (!(e->event.xml_event.data = (char *) malloc(h->length + 1))) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML event data\n");
+				goto fail;
+			}
+			memcpy(e->event.xml_event.data, p, h->length);
+			e->event.xml_event.data[h->length] = 0;
+			break;
+		}
+
 		case GG_PUBDIR50_REPLY:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
-			if (gg_pubdir50_handle_reply(e, p, h->length) == -1)
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
+			if (gg_pubdir50_handle_reply_sess(sess, e, p, h->length) == -1)
 				goto fail;
 			break;
 		}
 
 		case GG_USERLIST_REPLY:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
 
 			if (h->length < 1)
 				break;
 
-			/* jeli odpowied na eksport, wywoaj zdarzenie tylko
+			/* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
 			 * gdy otrzymano wszystkie odpowiedzi */
 			if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) {
 				if (--sess->userlist_blocks)
@@ -743,11 +1301,11 @@
 			if (h->length > 1) {
 				char *tmp;
 				unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0;
-				
-				gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
-				
+
+				gg_debug_session(sess, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
+
 				if (!(tmp = realloc(sess->userlist_reply, len + h->length))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
 					free(sess->userlist_reply);
 					sess->userlist_reply = NULL;
 					goto fail;
@@ -769,10 +1327,75 @@
 			break;
 		}
 
+		case GG_DCC7_ID_REPLY:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n");
+
+			if (h->length < sizeof(struct gg_dcc7_id_reply))
+				break;
+
+			if (gg_dcc7_handle_id(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_ACCEPT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n");
+
+			if (h->length < sizeof(struct gg_dcc7_accept))
+				break;
+
+			if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_NEW:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n");
+
+			if (h->length < sizeof(struct gg_dcc7_new))
+				break;
+
+			if (gg_dcc7_handle_new(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_REJECT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n");
+
+			if (h->length < sizeof(struct gg_dcc7_reject))
+				break;
+
+			if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_INFO:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n");
+
+			if (h->length < sizeof(struct gg_dcc7_info))
+				break;
+
+			if (gg_dcc7_handle_info(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
 		default:
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
 	}
-	
+
 	free(h);
 	return 0;
 
@@ -781,19 +1404,20 @@
 	return -1;
 }
 
-/*
- * gg_watch_fd()
- *
- * funkcja, ktr naley wywoa, gdy co si stanie z obserwowanym
- * deskryptorem. zwraca klientowi informacj o tym, co si dzieje.
+/** \endcond */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
  *
- *  - sess - opis sesji
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
  *
- * wskanik do struktury gg_event, ktr trzeba zwolni pniej
- * za pomoc gg_event_free(). jesli rodzaj zdarzenia jest rwny
- * GG_EVENT_NONE, naley je zignorowa. jeli zwrcio NULL,
- * stao si co niedobrego -- albo zabrako pamici albo zerwao
- * poczenie.
+ * \param sess Struktura sesji
+ *
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup events
  */
 struct gg_event *gg_watch_fd(struct gg_session *sess)
 {
@@ -802,68 +1426,79 @@
 	int port = 0;
 	int errno2 = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
-	
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
+
 	if (!sess) {
 		errno = EFAULT;
 		return NULL;
 	}
 
 	if (!(e = (void*) calloc(1, sizeof(*e)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
 		return NULL;
 	}
 
 	e->type = GG_EVENT_NONE;
 
+	if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);
+
+		res = write(sess->fd, sess->send_buf, sess->send_left);
+
+		if (res == -1 && errno != EAGAIN) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno));
+
+			if (sess->state == GG_STATE_READING_REPLY)
+				goto fail_connecting;
+			else
+				goto done;
+		}
+
+		if (res == sess->send_left) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
+			free(sess->send_buf);
+			sess->send_buf = NULL;
+			sess->send_left = 0;
+		} else if (res > 0) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res);
+
+			memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
+			sess->send_left -= res;
+		}
+	}
+
 	switch (sess->state) {
 		case GG_STATE_RESOLVING:
 		{
 			struct in_addr addr;
 			int failed = 0;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
 
 			if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
 				failed = 1;
 				errno2 = errno;
 			}
-			
+
 			close(sess->fd);
 			sess->fd = -1;
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-			if (sess->resolver) {
-				pthread_cancel(*((pthread_t*) sess->resolver));
-				free(sess->resolver);
-				sess->resolver = NULL;
-			}
-#elif defined _WIN32
-			if (sess->resolver) {
-				HANDLE h = sess->resolver;
-				TerminateThread(h, 0);
-				CloseHandle(h);
-				sess->resolver = NULL;
-			}
-#else
-			waitpid(sess->pid, NULL, 0);
-			sess->pid = -1;
-#endif
+			sess->resolver_cleanup(&sess->resolver, 0);
 
 			if (failed) {
 				errno = errno2;
 				goto fail_resolving;
 			}
 
-			/* jeli jestemy w resolverze i mamy ustawiony port
-			 * proxy, znaczy, e resolvowalimy proxy. zatem
+			/* jeśli jesteśmy w resolverze i mamy ustawiony port
+			 * proxy, znaczy, że resolvowaliśmy proxy. zatem
 			 * wpiszmy jego adres. */
 			if (sess->proxy_port)
 				sess->proxy_addr = addr.s_addr;
 
 			/* zapiszmy sobie adres huba i adres serwera (do
-			 * bezporedniego poczenia, jeli hub ley)
+			 * bezpośredniego połączenia, jeśli hub leży)
 			 * z resolvera. */
 			if (sess->proxy_addr && sess->proxy_port)
 				port = sess->proxy_port;
@@ -872,21 +1507,27 @@
 				port = GG_APPMSG_PORT;
 			}
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
-			
-			/* czymy si albo z hubem, albo z proxy, zalenie
-			 * od tego, co resolvowalimy. */
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
+
+			/* łączymy się albo z hubem, albo z proxy, zależnie
+			 * od tego, co resolvowaliśmy. */
 			if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) {
-				/* jeli w trybie asynchronicznym gg_connect()
-				 * zwrci bd, nie ma sensu prbowa dalej. */
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+				/* jeśli w trybie asynchronicznym gg_connect()
+				 * zwróci błąd, nie ma sensu próbować dalej. */
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
 				goto fail_connecting;
 			}
 
-			/* jeli podano serwer i czmy si przez proxy,
-			 * jest to bezporednie poczenie, inaczej jest
+			/* jeśli podano serwer i łączmy się przez proxy,
+			 * jest to bezpośrednie połączenie, inaczej jest
 			 * do huba. */
-			sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB;
+
+			if (sess->proxy_addr && sess->proxy_port && sess->server_addr) {
+				sess->state = GG_STATE_CONNECTING_GG;
+				sess->soft_timeout = 1;
+			} else
+				sess->state = GG_STATE_CONNECTING_HUB;
+
 			sess->check = GG_CHECK_WRITE;
 			sess->timeout = GG_DEFAULT_TIMEOUT;
 
@@ -898,42 +1539,25 @@
 			char buf[1024], *client, *auth;
 			int res = 0;
 			socklen_t res_size = sizeof(res);
-			const char *host, *appmsg;
-
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
-
-			/* jeli asynchroniczne, sprawdzamy, czy nie wystpi
-			 * przypadkiem jaki bd. */
+			const char *host;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
+
+			/* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił
+			 * przypadkiem jakiś błąd. */
 			if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-				/* no tak, nie udao si poczy z proxy. nawet
-				 * nie prbujemy dalej. */
-				if (sess->proxy_addr && sess->proxy_port) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
-					goto fail_connecting;
-				}
-					
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res));
-				close(sess->fd);
-
-				if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) {
-					/* przy asynchronicznych, gg_connect()
-					 * zwraca -1 przy bdach socket(),
-					 * ioctl(), braku routingu itd. dlatego
-					 * nawet nie prbujemy dalej. */
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
-					goto fail_connecting;
-				}
-
-				sess->state = GG_STATE_CONNECTING_GG;
-				sess->check = GG_CHECK_WRITE;
-				sess->timeout = GG_DEFAULT_TIMEOUT;
-				break;
+				if (sess->proxy_addr && sess->proxy_port)
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+				else
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s)\n", res, strerror(res));
+
+				goto fail_connecting;
 			}
-			
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
 
 			if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
 				goto fail_connecting;
 			}
 
@@ -942,41 +1566,43 @@
 			else
 				host = "";
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-			if (sess->ssl)
-				appmsg = "appmsg3.asp";
-			else
+			auth = gg_proxy_auth();
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+			if (sess->ssl) {
+				snprintf(buf, sizeof(buf) - 1,
+					"GET %s/appsvc/appmsg3.asp?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n"
+					"Host: " GG_APPMSG_HOST "\r\n"
+					"User-Agent: " GG_HTTP_USERAGENT "\r\n"
+					"Pragma: no-cache\r\n"
+					"%s"
+					"\r\n", host, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
+			} else
 #endif
-				appmsg = "appmsg2.asp";
-
-			auth = gg_proxy_auth();
-
-			snprintf(buf, sizeof(buf) - 1,
-				"GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n"
-				"Host: " GG_APPMSG_HOST "\r\n"
-				"User-Agent: " GG_HTTP_USERAGENT "\r\n"
-				"Pragma: no-cache\r\n"
-				"%s" 
-				"\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
-
-			if (auth)
-				free(auth);
-			
+			{
+				snprintf(buf, sizeof(buf) - 1,
+					"GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
+					"Host: " GG_APPMSG_HOST "\r\n"
+					"%s"
+					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
+			}
+
+			free(auth);
 			free(client);
 
-			/* zwolnij pami po wersji klienta. */
+			/* zwolnij pamięć po wersji klienta. */
 			if (sess->client_version) {
 				free(sess->client_version);
 				sess->client_version = NULL;
 			}
 
-			gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
-	 
-			/* zapytanie jest krtkie, wic zawsze zmieci si
-			 * do bufora gniazda. jeli write() zwrci mniej,
-			 * stao si co zego. */
+			gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
+
+			/* zapytanie jest krótkie, więc zawsze zmieści się
+			 * do bufora gniazda. jeśli write() zwróci mniej,
+			 * stało się coś złego. */
 			if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
 
 				e->type = GG_EVENT_CONN_FAILED;
 				e->event.failure = GG_FAILURE_WRITING;
@@ -999,72 +1625,36 @@
 			int port = GG_DEFAULT_PORT;
 			struct in_addr addr;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
-
-			/* czytamy lini z gniazda i obcinamy \r\n. */
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
+
+			/* czytamy linię z gniazda i obcinamy \r\n. */
 			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 			gg_chomp(buf);
-			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
-	
-			/* sprawdzamy, czy wszystko w porzdku. */
+			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
+
+			/* sprawdzamy, czy wszystko w porządku. */
 			if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n");
-
-				close(sess->fd);
-
-				/* jeli otrzymalimy jakie dziwne informacje,
-				 * prbujemy si czy z pominiciem huba. */
-				if (sess->proxy_addr && sess->proxy_port) {
-					if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
-						/* trudno. nie wyszo. */
-						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
-						goto fail_connecting;
-					}
-
-					sess->state = GG_STATE_CONNECTING_GG;
-					sess->check = GG_CHECK_WRITE;
-					sess->timeout = GG_DEFAULT_TIMEOUT;
-					break;
-				}
-				
-				sess->port = GG_DEFAULT_PORT;
-
-				/* czymy si na port 8074 huba. */
-				if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
-
-					sess->port = GG_HTTPS_PORT;
-					
-					/* czymy si na port 443. */
-					if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
-						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
-						goto fail_connecting;
-					}
-				}
-				
-				sess->state = GG_STATE_CONNECTING_GG;
-				sess->check = GG_CHECK_WRITE;
-				sess->timeout = GG_DEFAULT_TIMEOUT;
-				break;
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
+				goto fail_connecting;
 			}
-	
-			/* ignorujemy reszt nagwka. */
+
+			/* ignorujemy resztę nagłówka. */
 			while (strcmp(buf, "\r\n") && strcmp(buf, ""))
 				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 
-			/* czytamy pierwsz lini danych. */
+			/* czytamy pierwszą linię danych. */
 			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 			gg_chomp(buf);
-			
-			/* jeli pierwsza liczba w linii nie jest rwna zeru,
-			 * oznacza to, e mamy wiadomo systemow. */
+
+			/* jeśli pierwsza liczba w linii nie jest równa zeru,
+			 * oznacza to, że mamy wiadomość systemową. */
 			if (atoi(buf)) {
 				char tmp[1024], *foo, *sysmsg_buf = NULL;
 				int len = 0;
-				
+
 				while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) {
 					if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) {
-						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
+						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
 						break;
 					}
 
@@ -1074,23 +1664,32 @@
 						strcpy(sysmsg_buf, tmp);
 					else
 						strcat(sysmsg_buf, tmp);
-					
+
 					len += strlen(tmp);
 				}
-				
+
 				e->type = GG_EVENT_MSG;
 				e->event.msg.msgclass = atoi(buf);
 				e->event.msg.sender = 0;
-				e->event.msg.message = (unsigned char *)sysmsg_buf;
+				e->event.msg.message = (unsigned char*) sysmsg_buf;
 			}
-	
+
 			close(sess->fd);
-	
-			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
+
+			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
 
 			/* analizujemy otrzymane dane. */
 			tmp = buf;
-			
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+			if (!sess->ssl)
+#endif
+			{
+				while (*tmp && *tmp != ' ')
+					tmp++;
+				while (*tmp && *tmp == ' ')
+					tmp++;
+			}
 			while (*tmp && *tmp != ' ')
 				tmp++;
 			while (*tmp && *tmp == ' ')
@@ -1105,36 +1704,43 @@
 				port = atoi(tmp + 1);
 			}
 
+			if (!strcmp(host, "notoperating")) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno));
+				sess->fd = -1;
+				goto fail_unavailable;
+			}
+
 			addr.s_addr = inet_addr(host);
 			sess->server_addr = addr.s_addr;
 
 			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) {
-				/* jeli mamy proxy, czymy si z nim. */
+				/* jeśli mamy proxy, łączymy się z nim. */
 				if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
-					/* nie wyszo? trudno. */
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
+					/* nie wyszło? trudno. */
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
 					goto fail_connecting;
 				}
-				
+
 				sess->state = GG_STATE_CONNECTING_GG;
 				sess->check = GG_CHECK_WRITE;
 				sess->timeout = GG_DEFAULT_TIMEOUT;
+				sess->soft_timeout = 1;
 				break;
 			}
 
 			sess->port = port;
 
-			/* czymy si z waciwym serwerem. */
+			/* łączymy się z właściwym serwerem. */
 			if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
 
 				sess->port = GG_HTTPS_PORT;
 
-				/* nie wyszo? prbujemy portu 443. */
+				/* nie wyszło? próbujemy portu 443. */
 				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
-					/* ostatnia deska ratunku zawioda?
+					/* ostatnia deska ratunku zawiodła?
 					 * w takim razie zwijamy manatki. */
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 					goto fail_connecting;
 				}
 			}
@@ -1142,7 +1748,8 @@
 			sess->state = GG_STATE_CONNECTING_GG;
 			sess->check = GG_CHECK_WRITE;
 			sess->timeout = GG_DEFAULT_TIMEOUT;
-		
+			sess->soft_timeout = 1;
+
 			break;
 		}
 
@@ -1151,14 +1758,16 @@
 			int res = 0;
 			socklen_t res_size = sizeof(res);
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
-
-			/* jeli wystpi bd podczas czenia si... */
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
+
+			sess->soft_timeout = 0;
+
+			/* jeśli wystąpił błąd podczas łączenia się... */
 			if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-				/* jeli nie udao si poczenie z proxy,
-				 * nie mamy czego prbowa wicej. */
+				/* jeśli nie udało się połączenie z proxy,
+				 * nie mamy czego próbować więcej. */
 				if (sess->proxy_addr && sess->proxy_port) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
 					goto fail_connecting;
 				}
 
@@ -1170,36 +1779,46 @@
 					errno = ETIMEDOUT;
 #endif
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-				/* jeli logujemy si po TLS, nie prbujemy
-				 * si czy ju z niczym innym w przypadku
-				 * bdu. nie do, e nie ma sensu, to i
-				 * trzeba by si bawi w tworzenie na nowo
+#ifdef GG_CONFIG_HAVE_OPENSSL
+				/* jeśli logujemy się po TLS, nie próbujemy
+				 * się łączyć już z niczym innym w przypadku
+				 * błędu. nie dość, że nie ma sensu, to i
+				 * trzeba by się bawić w tworzenie na nowo
 				 * SSL i SSL_CTX. */
 
 				if (sess->ssl) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
 					goto fail_connecting;
 				}
 #endif
 
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
+
+				if (sess->port == GG_HTTPS_PORT)
+					goto fail_connecting;
 
 				sess->port = GG_HTTPS_PORT;
 
-				/* prbujemy na port 443. */
+				/* próbujemy na port 443. */
 				if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 					goto fail_connecting;
 				}
+
+				sess->state = GG_STATE_CONNECTING_GG;
+				sess->check = GG_CHECK_WRITE;
+				sess->timeout = GG_DEFAULT_TIMEOUT;
+				sess->soft_timeout = 1;
+
+				break;
 			}
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
-			
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
+
 			if (gg_proxy_http_only)
 				sess->proxy_port = 0;
 
-			/* jeli mamy proxy, wylijmy zapytanie. */
+			/* jeśli mamy proxy, wyślijmy zapytanie. */
 			if (sess->proxy_addr && sess->proxy_port) {
 				char buf[100], *auth = gg_proxy_auth();
 				struct in_addr addr;
@@ -1211,20 +1830,22 @@
 
 				snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
 
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n//   %s", buf);
-				
-				/* wysyamy zapytanie. jest ono na tyle krtkie,
-				 * e musi si zmieci w buforze gniazda. jeli
-				 * write() zawiedzie, stao si co zego. */
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n//   %s", buf);
+
+				/* wysyłamy zapytanie. jest ono na tyle krótkie,
+				 * że musi się zmieścić w buforze gniazda. jeśli
+				 * write() zawiedzie, stało się coś złego. */
 				if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					free(auth);
 					goto fail_connecting;
 				}
 
 				if (auth) {
-					gg_debug(GG_DEBUG_MISC, "//   %s", auth);
+					gg_debug_session(sess, GG_DEBUG_MISC, "//   %s", auth);
 					if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) {
-						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+						free(auth);
 						goto fail_connecting;
 					}
 
@@ -1232,12 +1853,12 @@
 				}
 
 				if (write(sess->fd, "\r\n", 2) < 2) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
 					goto fail_connecting;
 				}
 			}
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 			if (sess->ssl) {
 				SSL_set_fd(sess->ssl, sess->fd);
 
@@ -1256,19 +1877,19 @@
 			break;
 		}
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 		case GG_STATE_TLS_NEGOTIATION:
 		{
 			int res;
 			X509 *peer;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
 
 			if ((res = SSL_connect(sess->ssl)) <= 0) {
 				int err = SSL_get_error(sess->ssl, res);
 
 				if (res == 0) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
 
 					e->type = GG_EVENT_CONN_FAILED;
 					e->event.failure = GG_FAILURE_TLS;
@@ -1277,9 +1898,9 @@
 					sess->fd = -1;
 					break;
 				}
-				
+
 				if (err == SSL_ERROR_WANT_READ) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
 
 					sess->state = GG_STATE_TLS_NEGOTIATION;
 					sess->check = GG_CHECK_READ;
@@ -1287,7 +1908,7 @@
 
 					break;
 				} else if (err == SSL_ERROR_WANT_WRITE) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
 
 					sess->state = GG_STATE_TLS_NEGOTIATION;
 					sess->check = GG_CHECK_WRITE;
@@ -1299,8 +1920,8 @@
 
 					ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
 
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
- 
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
+
 					e->type = GG_EVENT_CONN_FAILED;
 					e->event.failure = GG_FAILURE_TLS;
 					sess->state = GG_STATE_IDLE;
@@ -1310,20 +1931,20 @@
 				}
 			}
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n//   cipher: %s\n", SSL_get_cipher_name(sess->ssl));
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n//   cipher: %s\n", SSL_get_cipher_name(sess->ssl));
 
 			peer = SSL_get_peer_certificate(sess->ssl);
 
 			if (!peer)
-				gg_debug(GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n");
 			else {
 				char buf[1024];
 
 				X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
-				gg_debug(GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
+				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
 
 				X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
-				gg_debug(GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
+				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
 			}
 
 			sess->state = GG_STATE_READING_KEY;
@@ -1336,45 +1957,48 @@
 
 		case GG_STATE_READING_KEY:
 		{
-			struct gg_header *h;			
+			struct gg_header *h;
 			struct gg_welcome *w;
-			struct gg_login60 l;
-			unsigned int hash;
-			char *password = sess->password;
+			unsigned char *password = (unsigned char*) sess->password;
 			int ret;
-			
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
-
-			memset(&l, 0, sizeof(l));
-			l.dunno2 = 0xbe;
-
-			/* XXX bardzo, bardzo, bardzo gupi pomys na pozbycie
-			 * si tekstu wrzucanego przez proxy. */
+			uint8_t login_hash[64];
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
+
+			memset(login_hash, 0, sizeof(login_hash));
+
+			/* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie
+			 * się tekstu wrzucanego przez proxy. */
 			if (sess->proxy_addr && sess->proxy_port) {
 				char buf[100];
 
 				strcpy(buf, "");
 				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 				gg_chomp(buf);
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n//   %s\n", buf);
-				
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n//   %s\n", buf);
+
 				while (strcmp(buf, "")) {
 					gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 					gg_chomp(buf);
 					if (strcmp(buf, ""))
-						gg_debug(GG_DEBUG_MISC, "//   %s\n", buf);
+						gg_debug_session(sess, GG_DEBUG_MISC, "//   %s\n", buf);
 				}
 
 				/* XXX niech czeka jeszcze raz w tej samej
-				 * fazie. gupio, ale dziaa. */
+				 * fazie. głupio, ale działa. */
 				sess->proxy_port = 0;
-				
+
 				break;
 			}
 
 			/* czytaj pierwszy pakiet. */
 			if (!(h = gg_recv_packet(sess))) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+				if (errno == EAGAIN) {
+					sess->check = GG_CHECK_READ;
+					break;
+				}
+
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
 
 				e->type = GG_EVENT_CONN_FAILED;
 				e->event.failure = GG_FAILURE_READING;
@@ -1387,7 +2011,7 @@
 			}
 
 			if (h->type != GG_WELCOME) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
 				free(h);
 				close(sess->fd);
 				sess->fd = -1;
@@ -1397,61 +2021,127 @@
 				sess->state = GG_STATE_IDLE;
 				break;
 			}
-	
+
 			w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header));
 			w->key = gg_fix32(w->key);
 
-			hash = gg_login_hash((unsigned char *)password, w->key);
-	
-			gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash);
-	
+			switch (sess->hash_type) {
+				case GG_LOGIN_HASH_GG32:
+				{
+					unsigned int hash;
+
+					hash = gg_fix32(gg_login_hash(password, w->key));
+					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash);
+					memcpy(login_hash, &hash, sizeof(hash));
+
+					break;
+				}
+
+				case GG_LOGIN_HASH_SHA1:
+				{
+					char tmp[41];
+					int i;
+
+					gg_login_hash_sha1((char*) password, w->key, login_hash);
+					for (i = 0; i < 40; i += 2)
+						snprintf(tmp + i, sizeof(tmp) - i, "%02x", login_hash[i / 2]);
+
+					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp);
+
+					break;
+				}
+			}
+
 			free(h);
-
 			free(sess->password);
 			sess->password = NULL;
 
-			{
-				struct in_addr dcc_ip;
-				dcc_ip.s_addr = gg_dcc_ip;
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip));
-			}
-			
 			if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
 				struct sockaddr_in sin;
 				socklen_t sin_len = sizeof(sin);
 
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
 
 				if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
-					l.local_ip = sin.sin_addr.s_addr;
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
+					sess->client_addr = sin.sin_addr.s_addr;
 				} else {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
-					l.local_ip = 0;
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
+					sess->client_addr = 0;
 				}
-			} else 
-				l.local_ip = gg_dcc_ip;
-		
-			l.uin = gg_fix32(sess->uin);
-			l.hash = gg_fix32(hash);
-			l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
-			l.version = gg_fix32(sess->protocol_version);
-			l.local_port = gg_fix16(gg_dcc_port);
-			l.image_size = sess->image_size;
-			
-			if (sess->external_addr && sess->external_port > 1023) {
-				l.external_ip = sess->external_addr;
-				l.external_port = gg_fix16(sess->external_port);
+			} else
+				sess->client_addr = gg_dcc_ip;
+
+			if (sess->protocol_version >= 0x2e) {
+				struct gg_login80 l;
+
+				uint32_t tmp_version_len	= gg_fix32(strlen(GG8_VERSION));
+				uint32_t tmp_descr_len		= gg_fix32((sess->initial_descr) ? strlen(sess->initial_descr) : 0);
+				
+				memset(&l, 0, sizeof(l));
+				l.uin           = gg_fix32(sess->uin);
+				memcpy(l.language, GG8_LANG, sizeof(l.language));
+				l.hash_type     = sess->hash_type;
+				memcpy(l.hash, login_hash, sizeof(login_hash));
+				l.status        = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+				l.flags		= gg_fix32(0x00800001);
+				l.features	= gg_fix32(sess->protocol_features);
+				l.image_size    = sess->image_size;
+				l.dunno2        = 0x64;
+
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80 packet\n");
+				ret = gg_send_packet(sess, GG_LOGIN80, 
+						&l, sizeof(l), 
+						&tmp_version_len, sizeof(uint32_t), GG8_VERSION, strlen(GG8_VERSION),
+						&tmp_descr_len, sizeof(uint32_t), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+						NULL);
+
+			} else if (sess->protocol_version == 0x2d) {
+				struct gg_login70 l;
+
+				memset(&l, 0, sizeof(l));
+				l.uin		= gg_fix32(sess->uin);
+				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr;
+				l.local_port	= gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
+				l.hash_type	= sess->hash_type;
+				memcpy(l.hash, login_hash, sizeof(login_hash));
+				l.image_size	= sess->image_size;
+				l.dunno2 	= 0x64;
+				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags);
+
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80BETA packet\n");
+				ret = gg_send_packet(sess, GG_LOGIN80BETA,
+						&l, sizeof(l),
+						sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+						(sess->initial_descr) ? "\0" : NULL, (sess->initial_descr) ? 1 : 0,
+						NULL);
+			} else {
+				struct gg_login70 l;
+
+				memset(&l, 0, sizeof(l));
+				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr;
+				l.uin		= gg_fix32(sess->uin);
+				l.local_port	= gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
+				l.hash_type	= sess->hash_type;
+				memcpy(l.hash, login_hash, sizeof(login_hash));
+				l.image_size	= sess->image_size;
+				l.dunno2	= 0xbe;
+				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags);
+
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN70 packet\n");
+				ret = gg_send_packet(sess, GG_LOGIN70,
+						&l, sizeof(l),
+						sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+						NULL);
 			}
 
-			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n");
-			ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL);
-
 			free(sess->initial_descr);
 			sess->initial_descr = NULL;
 
 			if (ret == -1) {
-				gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
 				errno2 = errno;
 				close(sess->fd);
 				errno = errno2;
@@ -1461,8 +2151,9 @@
 				sess->state = GG_STATE_IDLE;
 				break;
 			}
-	
+
 			sess->state = GG_STATE_READING_REPLY;
+			sess->check = GG_CHECK_READ;
 
 			break;
 		}
@@ -1471,10 +2162,15 @@
 		{
 			struct gg_header *h;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
 
 			if (!(h = gg_recv_packet(sess))) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+				if (errno == EAGAIN) {
+					sess->check = GG_CHECK_READ;
+					break;
+				}
+
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
 				e->type = GG_EVENT_CONN_FAILED;
 				e->event.failure = GG_FAILURE_READING;
 				sess->state = GG_STATE_IDLE;
@@ -1484,11 +2180,12 @@
 				sess->fd = -1;
 				break;
 			}
-	
-			if (h->type == GG_LOGIN_OK) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
+
+			if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL || h->type == GG_LOGIN80_OK) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
 				e->type = GG_EVENT_CONN_SUCCESS;
 				sess->state = GG_STATE_CONNECTED;
+				sess->check = GG_CHECK_READ;
 				sess->timeout = -1;
 				sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL;
 				free(h);
@@ -1496,15 +2193,15 @@
 			}
 
 			if (h->type == GG_LOGIN_FAILED) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
 				e->event.failure = GG_FAILURE_PASSWORD;
 				errno = EACCES;
-			} else if (h->type == GG_NEED_EMAIL) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() email change needed\n");
-				e->event.failure = GG_FAILURE_NEED_EMAIL;
+			} else if (h->type == GG_DISCONNECTING) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n");
+				e->event.failure = GG_FAILURE_INTRUDER;
 				errno = EACCES;
 			} else {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
 				e->event.failure = GG_FAILURE_INVALID;
 				errno = EINVAL;
 			}
@@ -1522,13 +2219,13 @@
 
 		case GG_STATE_CONNECTED:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
 
 			sess->last_event = time(NULL);
-			
+
 			if ((res = gg_watch_fd_connected(sess, e)) == -1) {
 
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno));
 
  				if (errno == EAGAIN) {
 					e->type = GG_EVENT_NONE;
@@ -1536,6 +2233,9 @@
 				} else
 					res = -1;
 			}
+
+			sess->check = GG_CHECK_READ;
+
 			break;
 		}
 	}
@@ -1544,10 +2244,13 @@
 	if (res == -1) {
 		free(e);
 		e = NULL;
+	} else {
+		if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED))
+			sess->check |= GG_CHECK_WRITE;
 	}
 
 	return e;
-	
+
 fail_connecting:
 	if (sess->fd != -1) {
 		errno2 = errno;
@@ -1565,6 +2268,12 @@
 	e->event.failure = GG_FAILURE_RESOLVING;
 	sess->state = GG_STATE_IDLE;
 	goto done;
+
+fail_unavailable:
+	e->type = GG_EVENT_CONN_FAILED;
+	e->event.failure = GG_FAILURE_UNAVAILABLE;
+	sess->state = GG_STATE_IDLE;
+	goto done;
 }
 
 /*
--- a/libpurple/protocols/gg/lib/http.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/http.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1,4 +1,4 @@
-/* $Id: http.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: http.c 833 2009-10-01 20:48:01Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -14,52 +14,68 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file http.c
+ *
+ * \brief Obsługa połączeń HTTP
+ */
+
 #include "libgadu.h"
 
 #include <sys/types.h>
+
 #ifndef _WIN32
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
 #endif
 
-#include "libgadu-config.h"
+#include "compat.h"
+#include "resolver.h"
 
 #include <ctype.h>
 #include <errno.h>
+
 #ifndef _WIN32
-#include <netdb.h>
+#  include <netdb.h>
 #endif
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-#  include <pthread.h>
-#endif
+
+#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include "compat.h"
-
-/*
- * gg_http_connect() // funkcja pomocnicza
+/**
+ * Rozpoczyna połączenie HTTP.
  *
- * rozpoczyna poczenie po http.
+ * Funkcja przeprowadza połączenie HTTP przy połączeniu synchronicznym,
+ * zwracając wynik w polach struktury \c gg_http, lub błąd, gdy sesja się
+ * nie powiedzie.
+ *
+ * Przy połączeniu asynchronicznym, funkcja rozpoczyna połączenie, a dalsze
+ * etapy będą przeprowadzane po wykryciu zmian (\c watch) na obserwowanym
+ * deskryptorze (\c fd) i wywołaniu funkcji \c gg_http_watch_fd().
  *
- *  - hostname - adres serwera
- *  - port - port serwera
- *  - async - asynchroniczne poczenie
- *  - method - metoda http (GET, POST, cokolwiek)
- *  - path - cieka do zasobu (musi by poprzedzona ,,/'')
- *  - header - nagwek zapytania plus ewentualne dane dla POST
+ * Po zakończeniu, należy zwolnić strukturę za pomocą funkcji
+ * \c gg_http_free(). Połączenie asynchroniczne można zatrzymać w każdej
+ * chwili za pomocą \c gg_http_stop().
  *
- * zaalokowana struct gg_http, ktr poniej naley
- * zwolni funkcj gg_http_free(), albo NULL jeli wystpi bd.
+ * \param hostname Adres serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego połączenia
+ * \param method Metoda HTTP
+ * \param path Ścieżka do zasobu (musi być poprzedzona znakiem '/')
+ * \param header Nagłówek zapytania plus ewentualne dane dla POST
+ *
+ * \return Zaalokowana struktura \c gg_http lub NULL, jeśli wystąpił błąd.
+ *
+ * \ingroup http
  */
 struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header)
 {
@@ -70,7 +86,7 @@
 		errno = EFAULT;
 		return NULL;
 	}
-	
+
 	if (!(h = malloc(sizeof(*h))))
 		return NULL;
 	memset(h, 0, sizeof(*h));
@@ -80,6 +96,8 @@
 	h->fd = -1;
 	h->type = GG_SESSION_HTTP;
 
+	gg_http_set_resolver(h, GG_RESOLVER_DEFAULT);
+
 	if (gg_proxy_enabled) {
 		char *auth = gg_proxy_auth();
 
@@ -88,9 +106,8 @@
 				"", header);
 		hostname = gg_proxy_host;
 		h->port = port = gg_proxy_port;
+		free(auth);
 
-		if (auth)
-			free(auth);
 	} else {
 		h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
 				method, path, header);
@@ -102,17 +119,11 @@
 		errno = ENOMEM;
 		return NULL;
 	}
-	
+
 	gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
 
 	if (async) {
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-		if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) {
-#elif defined _WIN32
-		if (gg_resolve_win32thread(&h->fd, &h->resolver, hostname)) {
-#else
-		if (gg_resolve(&h->fd, &h->pid, hostname)) {
-#endif
+		if (h->resolver_start(&h->fd, &h->resolver, hostname) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
 			gg_http_free(h);
 			errno = ENOENT;
@@ -125,19 +136,16 @@
 		h->check = GG_CHECK_READ;
 		h->timeout = GG_DEFAULT_TIMEOUT;
 	} else {
-		struct in_addr *hn, a;
+		struct in_addr addr;
 
-		if (!(hn = gg_gethostbyname(hostname))) {
+		if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
 			gg_http_free(h);
 			errno = ENOENT;
 			return NULL;
-		} else {
-			a.s_addr = hn->s_addr;
-			free(hn);
 		}
 
-		if (!(h->fd = gg_connect(&a, port, 0)) == -1) {
+		if ((h->fd = gg_connect(&addr, port, 0)) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 			gg_http_free(h);
 			return NULL;
@@ -159,10 +167,12 @@
 
 	h->callback = gg_http_watch_fd;
 	h->destroy = gg_http_free;
-	
+
 	return h;
 }
 
+#ifndef DOXYGEN
+
 #define gg_http_error(x) \
 	close(h->fd); \
 	h->fd = -1; \
@@ -170,17 +180,22 @@
 	h->error = x; \
 	return 0;
 
-/*
- * gg_http_watch_fd()
- *
- * przy asynchronicznej obsudze HTTP funkcj t naley wywoa, jeli
- * zmienio si co na obserwowanym deskryptorze.
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
  *
- *  - h - struktura opisujca poczenie
+ * Operacja będzie zakończona, gdy pole \c state będzie równe
+ * \c GG_STATE_PARSING. W tym miejscu działanie przejmuje zwykle funkcja
+ * korzystająca z \c gg_http_watch_fd(). W przypadku błędu połączenia,
+ * pole \c state będzie równe \c GG_STATE_ERROR, a kod błędu znajdzie się
+ * w polu \c error.
  *
- * jeli wszystko poszo dobrze to 0, inaczej -1. poczenie bdzie
- * zakoczone, jeli h->state == GG_STATE_PARSING. jeli wystpi jaki
- * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error.
+ * \param h Struktura połączenia
+ *
+ * \return \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup http
  */
 int gg_http_watch_fd(struct gg_http *h)
 {
@@ -205,22 +220,7 @@
 		close(h->fd);
 		h->fd = -1;
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-		if (h->resolver) {
-			pthread_cancel(*((pthread_t *) h->resolver));
-			free(h->resolver);
-			h->resolver = NULL;
-		}
-#elif defined _WIN32
-		if (h->resolver) {
-			HANDLE hnd = h->resolver;
-			TerminateThread(hnd, 0);
-			CloseHandle(hnd);
-			h->resolver = NULL;
-		}
-#else
-		waitpid(h->pid, NULL, 0);
-#endif
+		h->resolver_cleanup(&h->resolver, 0);
 
 		gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port);
 
@@ -264,7 +264,7 @@
 			gg_http_error(GG_ERROR_WRITING);
 		}
 
-		if (res < 0 || (size_t)res < strlen(h->query)) {
+		if (res < strlen(h->query)) {
 			gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res);
 
 			memmove(h->query, h->query + res, strlen(h->query) - res + 1);
@@ -346,7 +346,7 @@
 			h->body_size = 0;
 			line = h->header;
 			*tmp = 0;
-                        
+
 			gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
 
 			while (line) {
@@ -449,7 +449,7 @@
 
 		return 0;
 	}
-	
+
 	if (h->fd != -1)
 		close(h->fd);
 
@@ -460,14 +460,14 @@
 	return -1;
 }
 
-#undef gg_http_error
-
-/*
- * gg_http_stop()
+/**
+ * Kończy asynchroniczne połączenie HTTP.
  *
- * jeli poczenie jest w trakcie, przerywa je. nie zwalnia h->data.
- * 
- *  - h - struktura opisujca poczenie
+ * Po zatrzymaniu należy zwolnić zasoby funkcją \c gg_http_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup http
  */
 void gg_http_stop(struct gg_http *h)
 {
@@ -477,15 +477,20 @@
 	if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
 		return;
 
-	if (h->fd != -1)
+	if (h->fd != -1) {
 		close(h->fd);
-	h->fd = -1;
+		h->fd = -1;
+	}
+
+	h->resolver_cleanup(&h->resolver, 1);
 }
 
-/*
- * gg_http_free_fields() // funkcja wewntrzna
+/**
+ * \internal Zwalnia pola struktury \c gg_http.
  *
- * zwalnia pola struct gg_http, ale nie zwalnia samej struktury.
+ * Funkcja zwalnia same pola, nie zwalnia struktury.
+ *
+ * \param h Struktura połączenia
  */
 void gg_http_free_fields(struct gg_http *h)
 {
@@ -501,19 +506,21 @@
 		free(h->query);
 		h->query = NULL;
 	}
-	
+
 	if (h->header) {
 		free(h->header);
 		h->header = NULL;
 	}
 }
 
-/*
- * gg_http_free()
+/**
+ * Zwalnia zasoby po połączeniu HTTP.
  *
- * prbuje zamkn poczenie i zwalnia pami po nim.
+ * Jeśli połączenie nie zostało jeszcze zakończone, jest przerywane.
  *
- *  - h - struktura, ktr naley zlikwidowa
+ * \param h Struktura połączenia
+ *
+ * \ingroup http
  */
 void gg_http_free(struct gg_http *h)
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/libgadu-internal.h	Wed Mar 10 17:58:21 2010 +0000
@@ -0,0 +1,30 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser 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.
+ */
+
+#ifndef LIBGADU_INTERNAL_H
+#define LIBGADU_INTERNAL_H
+
+#include "libgadu.h"
+
+char *gg_cp_to_utf8(const char *b);
+char *gg_utf8_to_cp(const char *b);
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length);
+
+#endif /* LIBGADU_INTERNAL_H */
--- a/libpurple/protocols/gg/lib/libgadu.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/libgadu.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1,10 +1,11 @@
-/* $Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
- *                          Arkadiusz Mikiewicz <arekm@pld-linux.org>
- *                          Tomasz Chiliski <chilek@chilan.com>
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -17,227 +18,200 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file libgadu.c
+ *
+ * \brief Główny moduł biblioteki
+ */
+
 #include "libgadu.h"
+#include "libgadu-config.h"
+#include "libgadu-internal.h"
 
 #include <sys/types.h>
-#ifndef _WIN32
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#ifdef sun
-#  include <sys/filio.h>
-#endif
+
+#ifdef _WIN32
+#  include <io.h>
+#  include <fcntl.h>
+#  include <errno.h>
+#  define SHUT_RDWR SD_BOTH
 #else
-#include <io.h>
-#include <fcntl.h>
-#include <errno.h>
-#define SHUT_RDWR SD_BOTH
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  ifdef sun
+#    include <sys/filio.h>
+#  endif
 #endif
 
-#include "libgadu-config.h"
+#include "compat.h"
+#include "protocol.h"
+#include "resolver.h"
 
-#include <errno.h>
 #ifndef _WIN32
-#include <netdb.h>
+#  include <errno.h> /* on Win32 this is included above */
+#  include <netdb.h>
 #endif
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-#  include <pthread.h>
-#endif
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <signal.h>
+#include <time.h>
 #include <unistd.h>
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 #  include <openssl/err.h>
 #  include <openssl/rand.h>
 #endif
 
-#include "compat.h"
+#define GG_LIBGADU_VERSION "1.9.0-rc2"
+
+/**
+ * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową
+ * składającą się ze stałych \c GG_DEBUG_...
+ *
+ * \ingroup debug
+ */
+int gg_debug_level = 0;
 
-int gg_debug_level = 0;
+/**
+ * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe
+ * \c NULL, informacje są wysyłane do standardowego wyjścia błędu (\c stderr).
+ *
+ * \param level Poziom rejestracji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
+ *
+ * \note Funkcja jest przesłaniana przez \c gg_debug_handler_session.
+ *
+ * \ingroup debug
+ */
 void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL;
 
+/**
+ * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe
+ * \c NULL, informacje są wysyłane do standardowego wyjścia błędu.
+ *
+ * \param sess Sesja której dotyczy informacja lub \c NULL
+ * \param level Poziom rejestracji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
+ *
+ * \note Funkcja przesłania przez \c gg_debug_handler_session.
+ *
+ * \ingroup debug
+ */
+void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL;
+
+/**
+ * Port gniazda nasłuchującego dla połączeń bezpośrednich.
+ * 
+ * \ingroup ip
+ */
 int gg_dcc_port = 0;
+
+/**
+ * Adres IP gniazda nasłuchującego dla połączeń bezpośrednich.
+ *
+ * \ingroup ip
+ */
 unsigned long gg_dcc_ip = 0;
 
+/**
+ * Adres lokalnego interfejsu IP, z którego wywoływane są wszystkie połączenia.
+ *
+ * \ingroup ip
+ */
 unsigned long gg_local_ip = 0;
-/*
- * zmienne opisujce parametry proxy http.
+
+/**
+ * Flaga włączenia połączeń przez serwer pośredniczący.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_enabled = 0;
+
+/**
+ * Adres serwera pośredniczącego.
+ *
+ * \ingroup proxy
  */
 char *gg_proxy_host = NULL;
+
+/**
+ * Port serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
 int gg_proxy_port = 0;
-int gg_proxy_enabled = 0;
+
+/**
+ * Flaga używania serwera pośredniczącego jedynie dla usług HTTP.
+ *
+ * \ingroup proxy
+ */
 int gg_proxy_http_only = 0;
+
+/**
+ * Nazwa użytkownika do autoryzacji serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
 char *gg_proxy_username = NULL;
+
+/**
+ * Hasło użytkownika do autoryzacji serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
 char *gg_proxy_password = NULL;
 
-#ifndef lint 
+#ifndef DOXYGEN
+
+#ifndef lint
 static char rcsid[]
 #ifdef __GNUC__
 __attribute__ ((unused))
 #endif
-= "$Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $";
-#endif 
-
-#ifdef _WIN32
-/**
- *  Deal with the fact that you can't select() on a win32 file fd.
- *  This makes it practically impossible to tie into purple's event loop.
- *
- *  -This is thanks to Tor Lillqvist.
- *  XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu.
- */
-static int
-socket_pipe (int *fds)
-{
-	SOCKET temp, socket1 = -1, socket2 = -1;
-	struct sockaddr_in saddr;
-	int len;
-	u_long arg;
-	fd_set read_set, write_set;
-	struct timeval tv;
-
-	temp = socket(AF_INET, SOCK_STREAM, 0);
-
-	if (temp == INVALID_SOCKET) {
-		goto out0;
-	}
-
-	arg = 1;
-	if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out0;
-	}
-
-	memset(&saddr, 0, sizeof(saddr));
-	saddr.sin_family = AF_INET;
-	saddr.sin_port = 0;
-	saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
-	if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) {
-		goto out0;
-	}
-
-	if (listen(temp, 1) == SOCKET_ERROR) {
-		goto out0;
-	}
-
-	len = sizeof(saddr);
-	if (getsockname(temp, (struct sockaddr *)&saddr, &len)) {
-		goto out0;
-	}
-
-	socket1 = socket(AF_INET, SOCK_STREAM, 0);
-
-	if (socket1 == INVALID_SOCKET) {
-		goto out0;
-	}
-
-	arg = 1;
-	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out1;
-	}
-
-	if (connect(socket1, (struct sockaddr  *)&saddr, len) != SOCKET_ERROR ||
-			WSAGetLastError() != WSAEWOULDBLOCK) {
-		goto out1;
-	}
-
-	FD_ZERO(&read_set);
-	FD_SET(temp, &read_set);
-
-	tv.tv_sec = 0;
-	tv.tv_usec = 0;
-
-	if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) {
-		goto out1;
-	}
-
-	if (!FD_ISSET(temp, &read_set)) {
-		goto out1;
-	}
-
-	socket2 = accept(temp, (struct sockaddr *) &saddr, &len);
-	if (socket2 == INVALID_SOCKET) {
-		goto out1;
-	}
-
-	FD_ZERO(&write_set);
-	FD_SET(socket1, &write_set);
-
-	tv.tv_sec = 0;
-	tv.tv_usec = 0;
-
-	if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) {
-		goto out2;
-	}
-
-	if (!FD_ISSET(socket1, &write_set)) {
-		goto out2;
-	}
-
-	arg = 0;
-	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out2;
-	}
-
-	arg = 0;
-	if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out2;
-	}
-
-	fds[0] = socket1;
-	fds[1] = socket2;
-
-	closesocket (temp);
-
-	return 0;
-
-out2:
-	closesocket (socket2);
-out1:
-	closesocket (socket1);
-out0:
-	closesocket (temp);
-	errno = EIO;            /* XXX */
-
-	return -1;
-}
+= "$Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $";
 #endif
 
-/*
- * gg_libgadu_version()
+#endif /* DOXYGEN */
+
+/**
+ * Zwraca wersję biblioteki.
  *
- * zwraca wersj libgadu.
+ * \return Wskaźnik na statyczny bufor z wersją biblioteki.
  *
- *  - brak
- *
- * wersja libgadu.
+ * \ingroup version
  */
 const char *gg_libgadu_version()
 {
 	return GG_LIBGADU_VERSION;
 }
 
-/*
- * gg_fix32()
+/**
+ * \internal Zamienia kolejność bajtów w 32-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych odwraca kolejność bajtów w słowie.
  *
- * zamienia kolejno bajtw w liczbie 32-bitowej tak, by odpowiadaa
- * kolejnoci bajtw w protokole GG. ze wzgldu na LE-owo serwera,
- * zamienia tylko na maszynach BE-wych.
+ * \param x Liczba do zamiany
  *
- *  - x - liczba do zamiany
+ * \return Liczba z odpowiednią kolejnością bajtów
  *
- * liczba z odpowiedni kolejnoci bajtw.
+ * \ingroup helper
  */
 uint32_t gg_fix32(uint32_t x)
 {
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
 	return x;
 #else
 	return (uint32_t)
@@ -248,20 +222,21 @@
 #endif
 }
 
-/*
- * gg_fix16()
+/**
+ * \internal Zamienia kolejność bajtów w 16-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych zamienia kolejność bajtów w słowie.
  *
- * zamienia kolejno bajtw w liczbie 16-bitowej tak, by odpowiadaa
- * kolejnoci bajtw w protokole GG. ze wzgldu na LE-owo serwera,
- * zamienia tylko na maszynach BE-wych.
+ * \param x Liczba do zamiany
  *
- *  - x - liczba do zamiany
+ * \return Liczba z odpowiednią kolejnością bajtów
  *
- * liczba z odpowiedni kolejnoci bajtw.
+ * \ingroup helper
  */
 uint16_t gg_fix16(uint16_t x)
 {
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
 	return x;
 #else
 	return (uint16_t)
@@ -270,15 +245,13 @@
 #endif
 }
 
-/* 
- * gg_login_hash() // funkcja wewntrzna
- * 
- * liczy hash z hasa i danego seeda.
- * 
- *  - password - haso do hashowania
- *  - seed - warto podana przez serwer
+/**
+ * \internal Liczy skrót z hasła i ziarna.
  *
- * hash.
+ * \param password Hasło
+ * \param seed Ziarno podane przez serwer
+ *
+ * \return Wartość skrótu
  */
 unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
 {
@@ -304,313 +277,22 @@
 	return y;
 }
 
-#ifndef _WIN32
-
-/*
- * gg_resolve() // funkcja wewntrzna
- *
- * tworzy potok, forkuje si i w drugim procesie zaczyna resolvowa 
- * podanego hosta. zapisuje w sesji deskryptor potoku. jeli co tam
- * bdzie gotowego, znaczy, e mona wczyta struct in_addr. jeli
- * nie znajdzie, zwraca INADDR_NONE.
- *
- *  - fd - wskanik gdzie wrzuci deskryptor
- *  - pid - gdzie wrzuci pid procesu potomnego
- *  - hostname - nazwa hosta do zresolvowania
+/**
+ * \internal Odbiera od serwera dane binarne.
  *
- * 0, -1.
- */
-int gg_resolve(int *fd, int *pid, const char *hostname)
-{
-	int pipes[2], res;
-	struct in_addr a;
-	int errno2;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname);
-	
-	if (!fd || !pid) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (pipe(pipes) == -1)
-		return -1;
-
-	if ((res = fork()) == -1) {
-		errno2 = errno;
-		close(pipes[0]);
-		close(pipes[1]);
-		errno = errno2;
-		return -1;
-	}
-
-	if (!res) {
-		if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
-			struct in_addr *hn;
-		
-			if (!(hn = gg_gethostbyname(hostname)))
-				a.s_addr = INADDR_NONE;
-			else {
-				a.s_addr = hn->s_addr;
-				free(hn);
-			}
-		}
-
-		write(pipes[1], &a, sizeof(a));
-
-		_exit(0);
-	}
-
-	close(pipes[1]);
-
-	*fd = pipes[0];
-	*pid = res;
-
-	return 0;
-}
-#endif
-
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-
-struct gg_resolve_pthread_data {
-	char *hostname;
-	int fd;
-};
-
-static void *gg_resolve_pthread_thread(void *arg)
-{
-	struct gg_resolve_pthread_data *d = arg;
-	struct in_addr a;
-
-	pthread_detach(pthread_self());
-
-	if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
-		struct in_addr *hn;
-		
-		if (!(hn = gg_gethostbyname(d->hostname)))
-			a.s_addr = INADDR_NONE;
-		else {
-			a.s_addr = hn->s_addr;
-			free(hn);
-		}
-	}
-
-	write(d->fd, &a, sizeof(a));
-	close(d->fd);
-
-	free(d->hostname);
-	d->hostname = NULL;
-
-	free(d);
-
-	pthread_exit(NULL);
-
-	return NULL;	/* eby kompilator nie marudzi */
-}
-
-/*
- * gg_resolve_pthread() // funkcja wewntrzna
- *
- * tworzy potok, nowy wtek i w nim zaczyna resolvowa podanego hosta.
- * zapisuje w sesji deskryptor potoku. jeli co tam bdzie gotowego,
- * znaczy, e mona wczyta struct in_addr. jeli nie znajdzie, zwraca
- * INADDR_NONE.
- *
- *  - fd - wskanik do zmiennej przechowujcej desktyptor resolvera
- *  - resolver - wskanik do wskanika resolvera
- *  - hostname - nazwa hosta do zresolvowania
+ * Funkcja odbiera dane od serwera zajmując się TLS w razie konieczności.
  *
- * 0, -1.
- */
-int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
-{
-	struct gg_resolve_pthread_data *d = NULL;
-	pthread_t *tmp;
-	int pipes[2], new_errno;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname);
-	
-	if (!resolver || !fd || !hostname) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n");
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (!(tmp = malloc(sizeof(pthread_t)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n");
-		return -1;
-	}
-	
-	if (pipe(pipes) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
-		free(tmp);
-		return -1;
-	}
-
-	if (!(d = malloc(sizeof(*d)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n");
-		new_errno = errno;
-		goto cleanup;
-	}
-	
-	d->hostname = NULL;
-
-	if (!(d->hostname = strdup(hostname))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n");
-		new_errno = errno;
-		goto cleanup;
-	}
-
-	d->fd = pipes[1];
-
-	if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n");
-		new_errno = errno;
-		goto cleanup;
-	}
-
-	gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp);
-
-	*resolver = tmp;
-
-	*fd = pipes[0];
-
-	return 0;
-
-cleanup:
-	if (d) {
-		free(d->hostname);
-		free(d);
-	}
-
-	close(pipes[0]);
-	close(pipes[1]);
-
-	free(tmp);
-
-	errno = new_errno;
-
-	return -1;
-}
-
-#elif defined _WIN32
-
-struct gg_resolve_win32thread_data {
-	char *hostname;
-	int fd;
-};
-
-static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg)
-{
-	struct gg_resolve_win32thread_data *d = arg;
-	struct in_addr a;
-
-	if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
-		struct in_addr *hn;
-		
-		if (!(hn = gg_gethostbyname(d->hostname)))
-			a.s_addr = INADDR_NONE;
-		else {
-			a.s_addr = hn->s_addr;
-			free(hn);
-		}
-	}
-
-	write(d->fd, &a, sizeof(a));
-	close(d->fd);
-
-	free(d->hostname);
-	d->hostname = NULL;
-
-	free(d);
-
-	return 0;
-}
-
-
-int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname)
-{
-	struct gg_resolve_win32thread_data *d = NULL;
-	HANDLE h;
-	DWORD dwTId;
-	int pipes[2], new_errno;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname);
-	
-	if (!resolver || !fd || !hostname) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n");
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (socket_pipe(pipes) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
-		return -1;
-	}
-
-	if (!(d = malloc(sizeof(*d)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
-		new_errno = GetLastError();
-		goto cleanup;
-	}
-
-	d->hostname = NULL;
-
-	if (!(d->hostname = strdup(hostname))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
-		new_errno = GetLastError();
-		goto cleanup;
-	}
-
-	d->fd = pipes[1];
-
-	h = CreateThread(NULL, 0, gg_resolve_win32thread_thread,
-		d, 0, &dwTId);
-
-	if (h == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n");
-		new_errno = GetLastError();
-		goto cleanup;
-	}
-
-	*resolver = h;
-	*fd = pipes[0];
-
-	return 0;
-
-cleanup:
-	if (d) {
-		free(d->hostname);
-		free(d);
-	}
-
-	close(pipes[0]);
-	close(pipes[1]);
-
-	errno = new_errno;
-
-	return -1;
-
-}
-#endif
-
-/*
- * gg_read() // funkcja pomocnicza
+ * \param sess Struktura sesji
+ * \param buf Bufor na danymi
+ * \param length Długość bufora
  *
- * czyta z gniazda okrelon ilo bajtw. bierze pod uwag, czy mamy
- * poczenie zwyke czy TLS.
- *
- *  - sess - sesja,
- *  - buf - bufor,
- *  - length - ilo bajtw,
- *
- * takie same wartoci jak read().
+ * \return To samo co funkcja systemowa \c read
  */
 int gg_read(struct gg_session *sess, char *buf, int length)
 {
 	int res;
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 	if (sess->ssl) {
 		int err;
 
@@ -631,23 +313,22 @@
 	return res;
 }
 
-/*
- * gg_write() // funkcja pomocnicza
+/**
+ * \internal Wysyła do serwera dane binarne.
  *
- * zapisuje do gniazda okrelon ilo bajtw. bierze pod uwag, czy mamy
- * poczenie zwyke czy TLS.
+ * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności.
  *
- *  - sess - sesja,
- *  - buf - bufor,
- *  - length - ilo bajtw,
+ * \param sess Struktura sesji
+ * \param buf Bufor z danymi
+ * \param length Długość bufora
  *
- * takie same wartoci jak write().
+ * \return To samo co funkcja systemowa \c write
  */
 int gg_write(struct gg_session *sess, const char *buf, int length)
 {
 	int res = 0;
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 	if (sess->ssl) {
 		int err;
 
@@ -664,19 +345,48 @@
 	} else
 #endif
 	{
-		int written = 0;
-		
-		while (written < length) {
-			res = write(sess->fd, buf + written, length - written);
+		if (!sess->async) {
+			int written = 0;
+
+			while (written < length) {
+				res = write(sess->fd, buf + written, length - written);
+
+				if (res == -1) {
+					if (errno != EINTR)
+						break;
+
+					continue;
+				}
+
+				written += res;
+				res = written;
+			}
+		} else {
+			if (!sess->send_buf)
+				res = write(sess->fd, buf, length);
+			else
+				res = 0;
 
 			if (res == -1) {
-				if (errno == EAGAIN)
-					continue;
-				else
-					break;
-			} else {
-				written += res;
-				res = written;
+				if (errno != EAGAIN)
+					return res;
+
+				res = 0;
+			}
+
+			if (res < length) {
+				char *tmp;
+
+				if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) {
+					errno = ENOMEM;
+					return -1;
+				}
+
+				sess->send_buf = tmp;
+
+				memcpy(sess->send_buf + sess->send_left, buf + res, length - res);
+
+				sess->send_left += length - res;
 			}
 		}
 	}
@@ -684,17 +394,19 @@
 	return res;
 }
 
-/*
- * gg_recv_packet() // funkcja wewntrzna
+/**
+ * \internal Odbiera pakiet od serwera.
  *
- * odbiera jeden pakiet i zwraca wskanik do niego. pami po nim
- * naley zwolni za pomoc free().
+ * Funkcja odczytuje nagłówek pakietu, a następnie jego zawartość i zwraca
+ * w zaalokowanym buforze.
  *
- *  - sess - opis sesji
+ * Przy połączeniach asynchronicznych, funkcja może nie być w stanie
+ * skompletować całego pakietu -- w takim przypadku zwróci -1, a kodem błędu
+ * będzie \c EAGAIN.
  *
- * w przypadku bdu NULL, kod bdu w errno. naley zwrci uwag, e gdy
- * poczenie jest nieblokujce, a kod bdu wynosi EAGAIN, nie udao si
- * odczyta caego pakietu i nie naley tego traktowa jako bd.
+ * \param sess Struktura sesji
+ *
+ * \return Wskaźnik do zaalokowanego bufora
  */
 void *gg_recv_packet(struct gg_session *sess)
 {
@@ -702,8 +414,8 @@
 	char *buf = NULL;
 	int ret = 0, offset, size = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
-	
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
+
 	if (!sess) {
 		errno = EFAULT;
 		return NULL;
@@ -712,7 +424,7 @@
 	if (sess->recv_left < 1) {
 		if (sess->header_buf) {
 			memcpy(&h, sess->header_buf, sess->header_done);
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done);
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done);
 			free(sess->header_buf);
 			sess->header_buf = NULL;
 		} else
@@ -721,34 +433,36 @@
 		while (sess->header_done < sizeof(h)) {
 			ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done);
 
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret);
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret);
 
 			if (!ret) {
 				errno = ECONNRESET;
-				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n");
 				return NULL;
 			}
 
 			if (ret == -1) {
 				if (errno == EINTR) {
-					gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n");
 					continue;
 				}
 
 				if (errno == EAGAIN) {
-					gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n");
 
 					if (!(sess->header_buf = malloc(sess->header_done))) {
-						gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n");
+						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n");
 						return NULL;
 					}
 
 					memcpy(sess->header_buf, &h, sess->header_done);
 
+					errno = EAGAIN;
+
 					return NULL;
 				}
 
-				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
 
 				return NULL;
 			}
@@ -761,22 +475,22 @@
 		h.length = gg_fix32(h.length);
 	} else
 		memcpy(&h, sess->recv_buf, sizeof(h));
-	
-	/* jakie sensowne limity na rozmiar pakietu */
+
+	/* jakieś sensowne limity na rozmiar pakietu */
 	if (h.length > 65535) {
-		gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length);
 		errno = ERANGE;
 		return NULL;
 	}
 
 	if (sess->recv_left > 0) {
-		gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n");
 		size = sess->recv_left;
 		offset = sess->recv_done;
 		buf = sess->recv_buf;
 	} else {
 		if (!(buf = malloc(sizeof(h) + h.length + 1))) {
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
 			return NULL;
 		}
 
@@ -788,24 +502,23 @@
 
 	while (size > 0) {
 		ret = gg_read(sess, buf + sizeof(h) + offset, size);
-		gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret);
 		if (!ret) {
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n");
-			free(buf);
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n");
 			errno = ECONNRESET;
 			return NULL;
 		}
 		if (ret > -1 && ret <= size) {
 			offset += ret;
 			size -= ret;
-		} else if (ret == -1) {	
+		} else if (ret == -1) {
 			int errno2 = errno;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno));
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno));
 			errno = errno2;
 
 			if (errno == EAGAIN) {
-				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size);
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size);
 				sess->recv_buf = buf;
 				sess->recv_left = size;
 				sess->recv_done = offset;
@@ -823,49 +536,45 @@
 	if ((gg_debug_level & GG_DEBUG_DUMP)) {
 		unsigned int i;
 
-		gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type);
-		for (i = 0; i < sizeof(h) + h.length; i++) 
-			gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]);
-		gg_debug(GG_DEBUG_DUMP, "\n");
+		gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type);
+		for (i = 0; i < sizeof(h) + h.length; i++)
+			gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]);
+		gg_debug_session(sess, GG_DEBUG_DUMP, "\n");
 	}
 
 	return buf;
 }
 
-/*
- * gg_send_packet() // funkcja wewntrzna
+/**
+ * \internal Wysyła pakiet do serwera.
  *
- * konstruuje pakiet i wysya go do serwera.
+ * Funkcja konstruuje pakiet do wysłania z dowolnej liczby fragmentów. Jeśli
+ * rozmiar pakietu jest za duży, by móc go wysłać za jednym razem, pozostała
+ * część zostanie zakolejkowana i wysłana, gdy będzie to możliwe.
  *
- *  - sock - deskryptor gniazda
- *  - type - typ pakietu
- *  - payload_1 - pierwsza cz pakietu
- *  - payload_length_1 - dugo pierwszej czci
- *  - payload_2 - druga cz pakietu
- *  - payload_length_2 - dugo drugiej czci
- *  - ... - kolejne czci pakietu i ich dugoci
- *  - NULL - kocowym parametr (konieczny!)
+ * \param sess Struktura sesji
+ * \param type Rodzaj pakietu
+ * \param ... Lista kolejnych części pakietu (wskaźnik na bufor i długość
+ *            typu \c int) zakończona \c NULL
  *
- * jeli si powiodo, zwraca 0, w przypadku bdu -1. jeli errno == ENOMEM,
- * zabrako pamici. inaczej by bd przy wysyaniu pakietu. dla errno == 0
- * nie wysano caego pakietu.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
 int gg_send_packet(struct gg_session *sess, int type, ...)
 {
 	struct gg_header *h;
 	char *tmp;
-	int tmp_length;
+	unsigned int tmp_length;
 	void *payload;
 	unsigned int payload_length;
 	va_list ap;
 	int res;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type);
 
 	tmp_length = sizeof(struct gg_header);
 
 	if (!(tmp = malloc(tmp_length))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n");
 		return -1;
 	}
 
@@ -879,14 +588,14 @@
 		payload_length = va_arg(ap, unsigned int);
 
 		if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
-			gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n");
 			free(tmp);
 			va_end(ap);
 			return -1;
 		}
 
 		tmp = tmp2;
-		
+
 		memcpy(tmp + tmp_length, payload, payload_length);
 		tmp_length += payload_length;
 
@@ -902,52 +611,81 @@
 	if ((gg_debug_level & GG_DEBUG_DUMP)) {
 		int i;
 
-		gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
+		gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
 		for (i = 0; i < tmp_length; ++i)
-			gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]);
-		gg_debug(GG_DEBUG_DUMP, "\n");
+			gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]);
+		gg_debug_session(sess, GG_DEBUG_DUMP, "\n");
 	}
-	
-	if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) {
-		gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
-		free(tmp);
+
+	res = gg_write(sess, tmp, tmp_length);
+
+	free(tmp);
+
+	if (res == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
 		return -1;
 	}
-	
-	free(tmp);	
+
+	if (sess->async)
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() partial write(), %d sent, %d left, %d total left\n", res, tmp_length - res, sess->send_left);
+
+	if (sess->send_buf)
+		sess->check |= GG_CHECK_WRITE;
+
 	return 0;
 }
 
-/*
- * gg_session_callback() // funkcja wewntrzna
+/**
+ * \internal Funkcja zwrotna sesji.
+ *
+ * Pole \c callback struktury \c gg_session zawiera wskaźnik do tej funkcji.
+ * Wywołuje ona \c gg_watch_fd i zachowuje wynik w polu \c event.
+ *
+ * \note Korzystanie z tej funkcjonalności nie jest już zalecane.
  *
- * wywoywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje
- * do gg_session->event jego wynik.
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
-static int gg_session_callback(struct gg_session *s)
+static int gg_session_callback(struct gg_session *sess)
 {
-	if (!s) {
+	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
 
-	return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1;
+	return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1;
 }
 
-/*
- * gg_login()
+/**
+ * Łączy się z serwerem Gadu-Gadu.
  *
- * rozpoczyna procedur czenia si z serwerem. reszt obsuguje si przez
- * gg_watch_fd().
+ * Przy połączeniu synchronicznym funkcja zakończy działanie po nawiązaniu
+ * połączenia lub gdy wystąpi błąd. Po udanym połączeniu należy wywoływać
+ * funkcję \c gg_watch_fd(), która odbiera informacje od serwera i zwraca
+ * informacje o zdarzeniach.
  *
- * UWAGA! program musi obsuy SIGCHLD, jeli czy si asynchronicznie,
- * eby poprawnie zamkn proces resolvera.
+ * Przy połączeniu asynchronicznym funkcja rozpocznie procedurę połączenia
+ * i zwróci zaalokowaną strukturę. Pole \c fd struktury \c gg_session zawiera
+ * deskryptor, który należy obserwować funkcją \c select, \c poll lub za
+ * pomocą mechanizmów użytej pętli zdarzeń (Glib, Qt itp.). Pole \c check
+ * jest maską bitową mówiącą, czy biblioteka chce być informowana o możliwości
+ * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE).
+ * Po zaobserwowaniu zmian na deskryptorze należy wywołać funkcję
+ * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie
+ * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę
+ * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD.
  *
- *  - p - struktura opisujca pocztkowy stan. wymagane pola: uin, 
- *    password
+ * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów
+ * za pomocą funkcji \c gg_notify() lub \c gg_notify_ex().
  *
- * w przypadku bdu NULL, jeli idzie dobrze (async) albo poszo
- * dobrze (sync), zwrci wskanik do zaalokowanej struct gg_session.
+ * \param p Struktura opisująca parametry połączenia. Wymagane pola: uin,
+ *          password, async.
+ *
+ * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL
+ *         w przypadku błędu.
+ *
+ * \ingroup login
  */
 struct gg_session *gg_login(const struct gg_login_params *p)
 {
@@ -981,8 +719,9 @@
 		goto fail;
 	}
 
-	if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
+	if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type);
+		errno = EFAULT;
 		goto fail;
 	}
 
@@ -999,18 +738,59 @@
 	sess->server_addr = p->server_addr;
 	sess->external_port = p->external_port;
 	sess->external_addr = p->external_addr;
+
+	sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77));
+
+	if (!(p->protocol_features & GG_FEATURE_STATUS77))
+		sess->protocol_features |= GG_FEATURE_STATUS80;
+
+	if (!(p->protocol_features & GG_FEATURE_MSG77))
+		sess->protocol_features |= GG_FEATURE_MSG80;
+
 	sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION;
+
 	if (p->era_omnix)
-		sess->protocol_version |= GG_ERA_OMNIX_MASK;
+		sess->protocol_flags |= GG_ERA_OMNIX_MASK;
 	if (p->has_audio)
-		sess->protocol_version |= GG_HAS_AUDIO_MASK;
+		sess->protocol_flags |= GG_HAS_AUDIO_MASK;
 	sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL;
 	sess->last_sysmsg = p->last_sysmsg;
 	sess->image_size = p->image_size;
 	sess->pid = -1;
+	sess->encoding = p->encoding;
+
+	if (gg_session_set_resolver(sess, p->resolver) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unsupported resolver type (%d)\n", p->resolver);
+		errno = EFAULT;
+		goto fail;
+	}
+
+	if (p->status_descr) {
+		int max_length;
+
+		if (sess->protocol_version >= 0x2d)
+			max_length = GG_STATUS_DESCR_MAXSIZE;
+		else
+			max_length = GG_STATUS_DESCR_MAXSIZE_PRE_8_0;
+
+		if (sess->protocol_version >= 0x2d && p->encoding != GG_ENCODING_UTF8)
+			sess->initial_descr = gg_cp_to_utf8(p->status_descr);
+		else
+			sess->initial_descr = strdup(p->status_descr);
+
+		if (!sess->initial_descr) {
+			gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
+			goto fail;
+		}
+		
+		// XXX pamiętać, żeby nie ciąć w środku znaku utf-8
+		
+		if (strlen(sess->initial_descr) > max_length)
+			sess->initial_descr[max_length] = 0;
+	}
 
 	if (p->tls == 1) {
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 		char buf[1024];
 
 		OpenSSL_add_ssl_algorithms();
@@ -1023,7 +803,7 @@
 			} rstruct;
 
 			time(&rstruct.time);
-			rstruct.ptr = (void *) &rstruct;			
+			rstruct.ptr = (void *) &rstruct;
 
 			RAND_seed((void *) rdata, sizeof(rdata));
 			RAND_seed((void *) &rstruct, sizeof(rstruct));
@@ -1050,7 +830,7 @@
 		gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");
 #endif
 	}
-	
+
 	if (gg_proxy_enabled) {
 		hostname = gg_proxy_host;
 		sess->proxy_port = port = gg_proxy_port;
@@ -1059,37 +839,50 @@
 		port = GG_APPMSG_PORT;
 	}
 
-	if (!p->async) {
-		struct in_addr a;
+	if (p->hash_type)
+		sess->hash_type = p->hash_type;
+	else
+		sess->hash_type = GG_LOGIN_HASH_SHA1;
 
-		if (!p->server_addr || !p->server_port) {
-			if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
-				struct in_addr *hn;
-	
-				if (!(hn = gg_gethostbyname(hostname))) {
+	if (!p->async) {
+		struct in_addr addr;
+
+		if (!sess->server_addr) {
+			if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+				if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
 					gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname);
 					goto fail;
-				} else {
-					a.s_addr = hn->s_addr;
-					free(hn);
 				}
 			}
 		} else {
-			a.s_addr = p->server_addr;
-			port = p->server_port;
+			addr.s_addr = sess->server_addr;
+			port = sess->port;
 		}
 
-		sess->hub_addr = a.s_addr;
+		sess->hub_addr = addr.s_addr;
 
 		if (gg_proxy_enabled)
-			sess->proxy_addr = a.s_addr;
+			sess->proxy_addr = addr.s_addr;
+
+		if ((sess->fd = gg_connect(&addr, port, 0)) == -1) {
+			gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+
+			/* nie wyszło? próbujemy portu 443. */
+			if (sess->server_addr) {
+				sess->port = GG_HTTPS_PORT;
 
-		if ((sess->fd = gg_connect(&a, port, 0)) == -1) {
-			gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
-			goto fail;
+				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, 0)) == -1) {
+					/* ostatnia deska ratunku zawiodła?
+					 * w takim razie zwijamy manatki. */
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					goto fail;
+				}
+			} else {
+				goto fail;
+			}
 		}
 
-		if (p->server_addr && p->server_port)
+		if (sess->server_addr)
 			sess->state = GG_STATE_CONNECTING_GG;
 		else
 			sess->state = GG_STATE_CONNECTING_HUB;
@@ -1114,15 +907,9 @@
 
 		return sess;
 	}
-	
+
 	if (!sess->server_addr || gg_proxy_enabled) {
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-		if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) {
-#elif defined _WIN32
-		if (gg_resolve_win32thread(&sess->fd, &sess->resolver, hostname)) {
-#else
-		if (gg_resolve(&sess->fd, &sess->pid, hostname)) {
-#endif
+		if (sess->resolver_start(&sess->fd, &sess->resolver, hostname) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
 			goto fail;
 		}
@@ -1133,49 +920,121 @@
 		}
 		sess->state = GG_STATE_CONNECTING_GG;
 		sess->check = GG_CHECK_WRITE;
+		sess->soft_timeout = 1;
 	}
 
 	return sess;
 
 fail:
 	if (sess) {
-		if (sess->password)
-			free(sess->password);
-		if (sess->initial_descr)
-			free(sess->initial_descr);
+		free(sess->password);
+		free(sess->initial_descr);
 		free(sess);
 	}
-	
+
 	return NULL;
 }
 
-/* 
- * gg_free_session()
+/**
+ * Wysyła do serwera pakiet utrzymania połączenia.
+ *
+ * Klient powinien regularnie co minutę wysyłać pakiet utrzymania połączenia,
+ * inaczej serwer uzna, że klient stracił łączność z siecią i zerwie
+ * połączenie.
+ *
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  *
- * prbuje zamkn poczenia i zwalnia pami zajmowan przez sesj.
+ * \ingroup login
+ */
+int gg_ping(struct gg_session *sess)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	return gg_send_packet(sess, GG_PING, NULL);
+}
+
+/**
+ * Kończy połączenie z serwerem.
  *
- *  - sess - opis sesji
+ * Funkcja nie zwalnia zasobów, więc po jej wywołaniu należy użyć
+ * \c gg_free_session(). Jeśli chce się ustawić opis niedostępności, należy
+ * wcześniej wywołać funkcję \c gg_change_status_descr() lub
+ * \c gg_change_status_descr_time().
+ *
+ * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze
+ * dane (np. z powodu strat pakietów na łączu), prawdopodobnie zostaną one
+ * utracone przy zrywaniu połączenia.
+ *
+ * \param sess Struktura sesji
+ *
+ * \ingroup login
  */
-void gg_free_session(struct gg_session *sess)
+void gg_logoff(struct gg_session *sess)
 {
 	if (!sess)
 		return;
 
-	/* XXX dopisa zwalnianie i zamykanie wszystkiego, co mogo zosta */
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
+
+	if (GG_S_NA(sess->status))
+		gg_change_status(sess, GG_STATUS_NOT_AVAIL);
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+	if (sess->ssl)
+		SSL_shutdown(sess->ssl);
+#endif
+
+	sess->resolver_cleanup(&sess->resolver, 1);
 
-	if (sess->password)
-		free(sess->password);
-	
-	if (sess->initial_descr)
-		free(sess->initial_descr);
+	if (sess->fd != -1) {
+		shutdown(sess->fd, SHUT_RDWR);
+		close(sess->fd);
+		sess->fd = -1;
+	}
+
+	if (sess->send_buf) {
+		free(sess->send_buf);
+		sess->send_buf = NULL;
+		sess->send_left = 0;
+	}
+}
 
-	if (sess->client_version)
-		free(sess->client_version);
+/**
+ * Zwalnia zasoby używane przez połączenie z serwerem. Funkcję należy wywołać
+ * po zamknięciu połączenia z serwerem, by nie doprowadzić do wycieku zasobów
+ * systemowych.
+ *
+ * \param sess Struktura sesji
+ *
+ * \ingroup login
+ */
+void gg_free_session(struct gg_session *sess)
+{
+	struct gg_dcc7 *dcc;
 
-	if (sess->header_buf)
-		free(sess->header_buf);
+	if (!sess)
+		return;
+
+	/* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+	free(sess->password);
+	free(sess->initial_descr);
+	free(sess->client_version);
+	free(sess->header_buf);
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
 	if (sess->ssl)
 		SSL_free(sess->ssl);
 
@@ -1183,23 +1042,7 @@
 		SSL_CTX_free(sess->ssl_ctx);
 #endif
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-	if (sess->resolver) {
-		pthread_cancel(*((pthread_t*) sess->resolver));
-		free(sess->resolver);
-		sess->resolver = NULL;
-	}
-#elif defined _WIN32
-	if (sess->resolver) {
-		HANDLE h = sess->resolver;
-		TerminateThread(h, 0);
-		CloseHandle(h);
-		sess->resolver = NULL;
-	}
-#else
-	if (sess->pid != -1)
-		waitpid(sess->pid, NULL, WNOHANG);
-#endif
+	sess->resolver_cleanup(&sess->resolver, 1);
 
 	if (sess->fd != -1)
 		close(sess->fd);
@@ -1207,24 +1050,37 @@
 	while (sess->images)
 		gg_image_queue_remove(sess, sess->images, 1);
 
+	free(sess->send_buf);
+
+	for (dcc = sess->dcc7_list; dcc; dcc = dcc->next)
+		dcc->sess = NULL;
+
 	free(sess);
 }
 
-/*
- * gg_change_status()
+#ifndef DOXYGEN
+
+/**
+ * \internal Funkcja wysyłająca pakiet zmiany statusu użytkownika.
  *
- * zmienia status uytkownika. przydatne do /away i /busy oraz /quit.
+ * \param sess Struktura sesji
+ * \param status Nowy status użytkownika
+ * \param descr Opis statusu użytkownika (lub \c NULL)
+ * \param time Czas powrotu w postaci uniksowego znacznika czasu (lub 0)
  *
- *  - sess - opis sesji
- *  - status - nowy status uytkownika
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  *
- * 0, -1.
+ * \ingroup status
  */
-int gg_change_status(struct gg_session *sess, int status)
+static int gg_change_status_common(struct gg_session *sess, int status, const char *descr, int time)
 {
-	struct gg_new_status p;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
+	char *new_descr = NULL;
+	uint32_t new_time;
+	int descr_len = 0;
+	int descr_len_max;
+	int packet_type;
+	int append_null = 0;
+	int res;
 
 	if (!sess) {
 		errno = EFAULT;
@@ -1236,67 +1092,422 @@
 		return -1;
 	}
 
-	p.status = gg_fix32(status);
+	/* XXX, obcinać stany których stary protokół niezna (czyt. dnd->aw; ffc->av) */
+
+	/* dodaj flagę obsługi połączeń głosowych zgodną z GG 7.x */
+	if ((sess->protocol_version >= 0x2a) && (sess->protocol_version < 0x2d /* ? */ ) && (sess->protocol_flags & GG_HAS_AUDIO_MASK) && !GG_S_I(status))
+		status |= GG_STATUS_VOICE_MASK;
 
 	sess->status = status;
 
-	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL);
+	if (sess->protocol_version >= 0x2d) {
+		if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
+			new_descr = gg_cp_to_utf8(descr);
+
+			if (!new_descr)
+				return -1;
+		}
+
+		if (sess->protocol_version >= 0x2e)
+			packet_type = GG_NEW_STATUS80;
+		else /* sess->protocol_version == 0x2d */
+			packet_type = GG_NEW_STATUS80BETA;
+		descr_len_max = GG_STATUS_DESCR_MAXSIZE;
+		append_null = 1;
+
+	} else {
+		packet_type = GG_NEW_STATUS;
+		descr_len_max = GG_STATUS_DESCR_MAXSIZE_PRE_8_0;
+
+		if (time != 0)
+			append_null = 1;
+	}
+
+	if (descr) {
+		descr_len = strlen((new_descr) ? new_descr : descr);
+
+		if (descr_len > descr_len_max)
+			descr_len = descr_len_max;
+
+		// XXX pamiętać o tym, żeby nie ucinać w środku znaku utf-8
+	}
+
+	if (time)
+		new_time = gg_fix32(time);
+
+	if (packet_type == GG_NEW_STATUS80) {
+		struct gg_new_status80 p;
+
+		p.status		= gg_fix32(status);
+		p.flags			= gg_fix32(0x00800001);
+		p.description_size	= gg_fix32(descr_len);
+		res = gg_send_packet(sess,
+				packet_type,
+				&p,
+				sizeof(p),
+				(new_descr) ? new_descr : descr,
+				descr_len,
+				NULL);
+
+	} else {
+		struct gg_new_status p;
+
+		p.status = gg_fix32(status);
+		res = gg_send_packet(sess,
+				packet_type,
+				&p,
+				sizeof(p),
+				(new_descr) ? new_descr : descr,
+				descr_len,
+				(append_null) ? "\0" : NULL,
+				(append_null) ? 1 : 0,
+				(time) ? &new_time : NULL,
+				(time) ? sizeof(new_time) : 0,
+				NULL);
+	}
+
+	free(new_descr);
+	return res;
 }
 
-/*
- * gg_change_status_descr()
+#endif /* DOXYGEN */
+
+/**
+ * Zmienia status użytkownika.
  *
- * zmienia status uytkownika na opisowy.
+ * \param sess Struktura sesji
+ * \param status Nowy status użytkownika
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  *
- *  - sess - opis sesji
- *  - status - nowy status uytkownika
- *  - descr - opis statusu
+ * \ingroup status
+ */
+int gg_change_status(struct gg_session *sess, int status)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
+
+	return gg_change_status_common(sess, status, NULL, 0);
+}
+
+/**
+ * Zmienia status użytkownika na status opisowy.
  *
- * 0, -1.
+ * \param sess Struktura sesji
+ * \param status Nowy status użytkownika
+ * \param descr Opis statusu użytkownika
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup status
  */
 int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
 {
-	struct gg_new_status p;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
-
-	if (!sess || !descr) {
-		errno = EFAULT;
-		return -1;
-	}
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
 
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	p.status = gg_fix32(status);
-
-	sess->status = status;
-
-	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL);
+	return gg_change_status_common(sess, status, descr, 0);
 }
 
-/*
- * gg_change_status_descr_time()
- *
- * zmienia status uytkownika na opisowy z godzin powrotu.
+/**
+ * Zmienia status użytkownika na status opisowy z podanym czasem powrotu.
  *
- *  - sess - opis sesji
- *  - status - nowy status uytkownika
- *  - descr - opis statusu
- *  - time - czas w formacie uniksowym
+ * \param sess Struktura sesji
+ * \param status Nowy status użytkownika
+ * \param descr Opis statusu użytkownika
+ * \param time Czas powrotu w postaci uniksowego znacznika czasu
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup status
  */
 int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time)
 {
-	struct gg_new_status p;
-	uint32_t newtime;
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
+
+	return gg_change_status_common(sess, status, descr, time);
+}
+
+/**
+ * Wysyła wiadomość do użytkownika.
+ *
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipient Numer adresata
+ * \param message Treść wiadomości
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
+ */
+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message);
+
+	return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, NULL, 0);
+}
+
+/**
+ * Wysyła wiadomość formatowaną.
+ *
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipient Numer adresata
+ * \param message Treść wiadomości
+ * \param format Informacje o formatowaniu
+ * \param formatlen Długość informacji o formatowaniu
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
+ */
+int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen);
+
+	return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, format, formatlen);
+}
+
+/**
+ * Wysyła wiadomość w ramach konferencji.
+ *
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipients_count Liczba adresatów
+ * \param recipients Wskaźnik do tablicy z numerami adresatów
+ * \param message Treść wiadomości
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
+ */
+int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message);
+
+	return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0);
+}
+
+/**
+ * \internal Dodaje tekst na koniec bufora.
+ * 
+ * \param dst Wskaźnik na bufor roboczy
+ * \param pos Wskaźnik na aktualne położenie w buforze roboczym
+ * \param src Dodawany tekst
+ * \param len Długość dodawanego tekstu
+ */
+static void gg_append(char *dst, int *pos, const void *src, int len)
+{
+	if (dst != NULL)
+		memcpy(&dst[*pos], src, len);
+
+	*pos += len;
+}
+
+/**
+ * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML.
+ *
+ * \param dst Bufor wynikowy (może być \c NULL)
+ * \param utf_msg Tekst źródłowy
+ * \param format Atrybuty tekstu źródłowego
+ * \param format_len Długość bloku atrybutów tekstu źródłowego
+ *
+ * \note Dokleja \c \\0 na końcu bufora wynikowego.
+ *
+ * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
+ */
+static int gg_convert_to_html(char *dst, const char *utf_msg, const unsigned char *format, int format_len)
+{
+	const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">";
+	const int span_len = 75;
+	const char img_fmt[] = "<img src=\"%02x%02x%02x%02x%02x%02x%02x%02x\">";
+	const int img_len = 28;
+	int char_pos = 0;
+	int format_idx = 3;
+	unsigned char old_attr = 0;
+	const unsigned char *color = (const unsigned char*) "\x00\x00\x00";
+	int len, i;
+
+	len = 0;
+
+	for (i = 0; utf_msg[i] != 0; i++) {
+		unsigned char attr;
+		int attr_pos;
+
+		if (format_idx + 3 <= format_len) {
+			attr_pos = format[format_idx] | (format[format_idx + 1] << 8);
+			attr = format[format_idx + 2];
+		} else {
+			attr_pos = -1;
+			attr = 0;
+		}
+
+		if (attr_pos == char_pos) {
+			format_idx += 3;
+
+			if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0) {
+				if (char_pos != 0) {
+					if ((old_attr & GG_FONT_UNDERLINE) != 0)
+						gg_append(dst, &len, "</u>", 4);
+
+					if ((old_attr & GG_FONT_ITALIC) != 0)
+						gg_append(dst, &len, "</i>", 4);
+
+					if ((old_attr & GG_FONT_BOLD) != 0)
+						gg_append(dst, &len, "</b>", 4);
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
+					gg_append(dst, &len, "</span>", 7);
+				}
+
+				if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) {
+					color = &format[format_idx];
+					format_idx += 3;
+				} else {
+					color = (const unsigned char*) "\x00\x00\x00";
+				}
+
+				if (dst != NULL)
+					sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
+				len += span_len;
+			} else if (char_pos == 0) {
+				if (dst != NULL)
+					sprintf(&dst[len], span_fmt, 0, 0, 0);
+				len += span_len;
+			}
+
+			if ((attr & GG_FONT_BOLD) != 0)
+				gg_append(dst, &len, "<b>", 3);
+
+			if ((attr & GG_FONT_ITALIC) != 0)
+				gg_append(dst, &len, "<i>", 3);
+
+			if ((attr & GG_FONT_UNDERLINE) != 0)
+				gg_append(dst, &len, "<u>", 3);
+
+			if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) {
+				if (dst != NULL) {
+					sprintf(&dst[len], img_fmt,
+						format[format_idx + 9],
+						format[format_idx + 8], 
+						format[format_idx + 7],
+						format[format_idx + 6], 
+						format[format_idx + 5],
+						format[format_idx + 4],
+						format[format_idx + 3],
+						format[format_idx + 2]);
+				}
+
+				len += img_len;
+				format_idx += 10;
+			}
+
+			old_attr = attr;
+		} else if (i == 0) {
+			if (dst != NULL)
+				sprintf(&dst[len], span_fmt, 0, 0, 0);
+
+			len += span_len;
+		}
 
-	if (!sess || !descr || !time) {
+		switch (utf_msg[i]) {
+			case '&':
+				gg_append(dst, &len, "&amp;", 5);
+				break;
+			case '<':
+				gg_append(dst, &len, "&lt;", 4);
+				break;
+			case '>':
+				gg_append(dst, &len, "&gt;", 4);
+				break;
+			case '\'':
+				gg_append(dst, &len, "&apos;", 6);
+				break;
+			case '\"':
+				gg_append(dst, &len, "&quot;", 6);
+				break;
+			case '\n':
+				gg_append(dst, &len, "<br>", 4);
+				break;
+			case '\r':
+				break;
+			default:
+				if (dst != NULL)
+					dst[len] = utf_msg[i];
+				len++;
+		}
+
+		/* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */
+
+		if ((utf_msg[i] & 0xc0) != 0xc0)
+			char_pos++;
+	}
+
+	if ((old_attr & GG_FONT_UNDERLINE) != 0)
+		gg_append(dst, &len, "</u>", 4);
+
+	if ((old_attr & GG_FONT_ITALIC) != 0)
+		gg_append(dst, &len, "</i>", 4);
+
+	if ((old_attr & GG_FONT_BOLD) != 0)
+		gg_append(dst, &len, "</b>", 4);
+
+	/* Dla pustych tekstów dodaj pusty <span>. */
+
+	if (i == 0) {
+		if (dst != NULL)
+			sprintf(&dst[len], span_fmt, 0, 0, 0);
+
+		len += span_len;
+	}
+
+	gg_append(dst, &len, "</span>", 7);
+
+	if (dst != NULL)
+		dst[len] = 0;
+
+	return len;
+}
+
+/**
+ * Wysyła wiadomość formatowaną w ramach konferencji.
+ *
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipients_count Liczba adresatów
+ * \param recipients Wskaźnik do tablicy z numerami adresatów
+ * \param message Treść wiadomości
+ * \param format Informacje o formatowaniu
+ * \param formatlen Długość informacji o formatowaniu
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ * 
+ * \ingroup messages
+ */
+int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+	struct gg_send_msg s;
+	struct gg_send_msg80 s80;
+	struct gg_msg_recipients r;
+	char *cp_msg = NULL;
+	char *utf_msg = NULL;
+	char *html_msg = NULL;
+	int seq_no;
+	int i, j, k;
+	uin_t *recps;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen);
+
+	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
@@ -1306,75 +1517,184 @@
 		return -1;
 	}
 
-	p.status = gg_fix32(status);
+	if (message == NULL || recipients_count <= 0 || recipients_count > 0xffff || (recipients_count != 1 && recipients == NULL)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (sess->encoding == GG_ENCODING_UTF8) {
+		if (!(cp_msg = gg_utf8_to_cp((const char *) message)))
+			return -1;
+
+		utf_msg = (char*) message;
+	} else {
+		if (sess->protocol_version >= 0x2d) {
+			if (!(utf_msg = gg_cp_to_utf8((const char *) message)))
+				return -1;
+		}
+
+		cp_msg = (char*) message;
+	}
+
+	if (sess->protocol_version < 0x2d) {
+		if (!sess->seq)
+			sess->seq = 0x01740000 | (rand() & 0xffff);
+		seq_no = sess->seq;
+		sess->seq += (rand() % 0x300) + 0x300;
 
-	sess->status = status;
+		s.msgclass = gg_fix32(msgclass);
+		s.seq = gg_fix32(seq_no);
+	} else {
+		int len;
+		
+		// Drobne odchylenie od protokołu. Jeśli wysyłamy kilka
+		// wiadomości w ciągu jednej sekundy, zwiększamy poprzednią
+		// wartość, żeby każda wiadomość miała unikalny numer.
+
+		seq_no = time(NULL);
+
+		if (seq_no <= sess->seq)
+			seq_no = sess->seq + 1;
+
+		sess->seq = seq_no;
+
+		if (format == NULL || formatlen < 3) {
+			format = (unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00";
+			formatlen = 9;
+		}
+
+		len = gg_convert_to_html(NULL, utf_msg, format, formatlen);
+
+		html_msg = malloc(len + 1);
+
+		if (html_msg == NULL) {
+			seq_no = -1;
+			goto cleanup;
+		}
+
+		gg_convert_to_html(html_msg, utf_msg, format, formatlen);
 
-	newtime = gg_fix32(time);
+		s80.seq = gg_fix32(seq_no);
+		s80.msgclass = gg_fix32(msgclass);
+		s80.offset_plain = gg_fix32(sizeof(s80) + strlen(html_msg) + 1);
+		s80.offset_attr = gg_fix32(sizeof(s80) + strlen(html_msg) + 1 + strlen(cp_msg) + 1);
+	}
+
+	if (recipients_count > 1) {
+		r.flag = 0x01;
+		r.count = gg_fix32(recipients_count - 1);
+
+		recps = malloc(sizeof(uin_t) * recipients_count);
+
+		if (!recps) {
+			seq_no = -1;
+			goto cleanup;
+		}
+
+		for (i = 0; i < recipients_count; i++) {
+			for (j = 0, k = 0; j < recipients_count; j++) {
+				if (recipients[j] != recipients[i]) {
+					recps[k] = gg_fix32(recipients[j]);
+					k++;
+				}
+			}
+
+			if (sess->protocol_version < 0x2d) {
+				s.recipient = gg_fix32(recipients[i]);
 
-	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL);
+				if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1)
+					seq_no = -1;
+			} else {
+				s80.recipient = gg_fix32(recipients[i]);
+
+				if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1)
+					seq_no = -1;
+			}
+		}
+
+		free(recps);
+	} else {
+		if (sess->protocol_version < 0x2d) {
+			s.recipient = gg_fix32(recipients[0]);
+
+			if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
+				seq_no = -1;
+		} else {
+			s80.recipient = gg_fix32(recipients[0]);
+
+			if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
+				seq_no = -1;
+		}
+	}
+
+cleanup:
+	if (cp_msg != (char*) message)
+		free(cp_msg);
+
+	if (utf_msg != (char*) message)
+		free(utf_msg);
+
+	free(html_msg);
+
+	return seq_no;
 }
 
-/*
- * gg_logoff()
+/**
+ * Wysyła wiadomość binarną przeznaczoną dla klienta.
+ *
+ * Wiadomości między klientami przesyła się np. w celu wywołania zwrotnego
+ * połączenia bezpośredniego. Funkcja zwraca losowy numer sekwencyjny,
+ * który można zignorować albo wykorzystać do potwierdzenia.
  *
- * wylogowuje uytkownika i zamyka poczenie, ale nie zwalnia pamici.
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipient Numer adresata
+ * \param message Treść wiadomości
+ * \param message_len Długość wiadomości
  *
- *  - sess - opis sesji
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
  */
-void gg_logoff(struct gg_session *sess)
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
 {
-	if (!sess)
-		return;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
-
-	if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK))
-		gg_change_status(sess, GG_STATUS_NOT_AVAIL);
-
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-	if (sess->ssl)
-		SSL_shutdown(sess->ssl);
-#endif
+	struct gg_send_msg s;
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-	if (sess->resolver) {
-		pthread_cancel(*((pthread_t*) sess->resolver));
-		free(sess->resolver);
-		sess->resolver = NULL;
-	}
-#elif defined _WIN32
-	if (sess->resolver) {
-		HANDLE h = sess->resolver;
-		TerminateThread(h, 0);
-		CloseHandle(h);
-		sess->resolver = NULL;
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
 	}
-#else
-	if (sess->pid != -1) {
-		waitpid(sess->pid, NULL, WNOHANG);
-		sess->pid = -1;
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
 	}
-#endif
-	
-	if (sess->fd != -1) {
-		shutdown(sess->fd, SHUT_RDWR);
-		close(sess->fd);
-		sess->fd = -1;
-	}
+
+	s.recipient = gg_fix32(recipient);
+	s.seq = gg_fix32(0);
+	s.msgclass = gg_fix32(msgclass);
+
+	return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
 }
 
-/*
- * gg_image_request()
+/**
+ * Wysyła żądanie obrazka o podanych parametrach.
  *
- * wysya danie wysania obrazka o podanych parametrach.
+ * Wiadomości obrazkowe nie zawierają samych obrazków, a tylko ich rozmiary
+ * i sumy kontrolne. Odbiorca najpierw szuka obrazków w swojej pamięci
+ * podręcznej i dopiero gdy ich nie znajdzie, wysyła żądanie do nadawcy.
+ * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY.
  *
- *  - sess - opis sesji
- *  - recipient - numer adresata
- *  - size - rozmiar obrazka
- *  - crc32 - suma kontrolna obrazka
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param size Rozmiar obrazka w bajtach
+ * \param crc32 Suma kontrola obrazka
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup messages
  */
 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
 {
@@ -1383,13 +1703,13 @@
 	char dummy = 0;
 	int res;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32);
 
 	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
-	
+
 	if (sess->state != GG_STATE_CONNECTED) {
 		errno = ENOTCONN;
 		return -1;
@@ -1407,7 +1727,7 @@
 	r.flag = 0x04;
 	r.size = gg_fix32(size);
 	r.crc32 = gg_fix32(crc32);
-	
+
 	res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL);
 
 	if (!res) {
@@ -1415,14 +1735,14 @@
 		char *buf;
 
 		if (!q) {
-			gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n");
 			return -1;
 		}
 
 		buf = malloc(size);
 		if (size && !buf)
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
 			free(q);
 			return -1;
 		}
@@ -1449,20 +1769,20 @@
 	return res;
 }
 
-/*
- * gg_image_reply()
- *
- * wysya dany obrazek.
+/**
+ * Wysyła żądany obrazek.
  *
- *  - sess - opis sesji
- *  - recipient - numer adresata
- *  - filename - nazwa pliku
- *  - image - bufor z obrazkiem
- *  - size - rozmiar obrazka
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param filename Nazwa pliku
+ * \param image Bufor z obrazkiem
+ * \param size Rozmiar obrazka
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup messages
  */
-int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size)
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
 {
 	struct gg_msg_image_reply *r;
 	struct gg_send_msg s;
@@ -1470,7 +1790,7 @@
 	char buf[1910];
 	int res = -1;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size);
 
 	if (!sess || !filename || !image) {
 		errno = EFAULT;
@@ -1487,7 +1807,7 @@
 		return -1;
 	}
 
-	/* wytnij cieki, zostaw tylko nazw pliku */
+	/* wytnij ścieżki, zostaw tylko nazwę pliku */
 	while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
 		filename = tmp + 1;
 
@@ -1495,7 +1815,7 @@
 		errno = EINVAL;
 		return -1;
 	}
-	
+
 	s.recipient = gg_fix32(recipient);
 	s.seq = gg_fix32(0);
 	s.msgclass = gg_fix32(GG_CLASS_MSG);
@@ -1505,26 +1825,26 @@
 
 	r->flag = 0x05;
 	r->size = gg_fix32(size);
-	r->crc32 = gg_fix32(gg_crc32(0, image, size));
+	r->crc32 = gg_fix32(gg_crc32(0, (unsigned char*) image, size));
 
 	while (size > 0) {
-		size_t buflen, chunklen;
-		
+		int buflen, chunklen;
+
 		/* \0 + struct gg_msg_image_reply */
 		buflen = sizeof(struct gg_msg_image_reply) + 1;
 
-		/* w pierwszym kawaku jest nazwa pliku */
+		/* w pierwszym kawałku jest nazwa pliku */
 		if (r->flag == 0x05) {
 			strcpy(buf + buflen, filename);
 			buflen += strlen(filename) + 1;
 		}
 
-		chunklen = ((size_t)size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t)size;
+		chunklen = (size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : size;
 
 		memcpy(buf + buflen, image, chunklen);
 		size -= chunklen;
 		image += chunklen;
-		
+
 		res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL);
 
 		if (res == -1)
@@ -1536,83 +1856,34 @@
 	return res;
 }
 
-/*
- * gg_send_message_ctcp()
+/**
+ * Wysyła do serwera listę kontaktów.
  *
- * wysya wiadomo do innego uytkownika. zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
+ * Funkcja informuje serwer o liście kontaktów, których statusy będą
+ * obserwowane lub kontaktów, które bedą blokowane. Dla każdego z \c count
+ * kontaktów tablica \c userlist zawiera numer, a tablica \c types rodzaj
+ * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED).
+ *
+ * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli
+ * jest pusta.
  *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipient - numer adresata
- *  - message - tre wiadomoci
- *  - message_len - dugo
+ * \param sess Struktura sesji
+ * \param userlist Wskaźnik do tablicy numerów kontaktów
+ * \param types Wskaźnik do tablicy rodzajów kontaktów
+ * \param count Liczba kontaktów
  *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
  */
-int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
+int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
 {
-	struct gg_send_msg s;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-	
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	s.recipient = gg_fix32(recipient);
-	s.seq = gg_fix32(0);
-	s.msgclass = gg_fix32(msgclass);
-	
-	return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
-}
+	struct gg_notify *n;
+	uin_t *u;
+	char *t;
+	int i, res = 0;
 
-/*
- * gg_send_message()
- *
- * wysya wiadomo do innego uytkownika. zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipient - numer adresata
- *  - message - tre wiadomoci
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
-{
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message);
-
-	return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0);
-}
-
-/*
- * gg_send_message_richtext()
- *
- * wysya kolorow wiadomo do innego uytkownika. zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipient - numer adresata
- *  - message - tre wiadomoci
- *  - format - informacje o formatowaniu
- *  - formatlen - dugo informacji o formatowaniu
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen)
-{
-	struct gg_send_msg s;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
 
 	if (!sess) {
 		errno = EFAULT;
@@ -1624,186 +1895,12 @@
 		return -1;
 	}
 
-	if (!message) {
-		errno = EFAULT;
-		return -1;
-	}
-	
-	s.recipient = gg_fix32(recipient);
-	if (!sess->seq)
-		sess->seq = 0x01740000 | (rand() & 0xffff);
-	s.seq = gg_fix32(sess->seq);
-	s.msgclass = gg_fix32(msgclass);
-	sess->seq += (rand() % 0x300) + 0x300;
-	
-	if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, format, formatlen, NULL) == -1)
-		return -1;
-
-	return gg_fix32(s.seq);
-}
-
-/*
- * gg_send_message_confer()
- *
- * wysya wiadomo do kilku uytkownikow (konferencja). zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipients_count - ilo adresatw
- *  - recipients - numerki adresatw
- *  - message - tre wiadomoci
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message)
-{
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message);
-
-	return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0);
-}
-
-/*
- * gg_send_message_confer_richtext()
- *
- * wysya kolorow wiadomo do kilku uytkownikow (konferencja). zwraca
- * losowy numer sekwencyjny, ktry mona zignorowa albo wykorzysta do
- * potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipients_count - ilo adresatw
- *  - recipients - numerki adresatw
- *  - message - tre wiadomoci
- *  - format - informacje o formatowaniu
- *  - formatlen - dugo informacji o formatowaniu
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen)
-{
-	struct gg_send_msg s;
-	struct gg_msg_recipients r;
-	int i, j, k;
-	uin_t *recps;
-		
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) {
-		errno = EINVAL;
-		return -1;
-	}
-	
-	r.flag = 0x01;
-	r.count = gg_fix32(recipients_count - 1);
-	
-	if (!sess->seq)
-		sess->seq = 0x01740000 | (rand() & 0xffff);
-	s.seq = gg_fix32(sess->seq);
-	s.msgclass = gg_fix32(msgclass);
-
-	recps = malloc(sizeof(uin_t) * recipients_count);
-	if (!recps)
-		return -1;
-
-	for (i = 0; i < recipients_count; i++) {
-	 
-		s.recipient = gg_fix32(recipients[i]);
-		
-		for (j = 0, k = 0; j < recipients_count; j++)
-			if (recipients[j] != recipients[i]) {
-				recps[k] = gg_fix32(recipients[j]);
-				k++;
-			}
-				
-		if (!i)
-			sess->seq += (rand() % 0x300) + 0x300;
-		
-		if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) {
-			free(recps);
-			return -1;
-		}
-	}
-
-	free(recps);
-	
-	return gg_fix32(s.seq);
-}
-
-/*
- * gg_ping()
- *
- * wysya do serwera pakiet ping.
- *
- *  - sess - opis sesji
- *
- * 0, -1.
- */
-int gg_ping(struct gg_session *sess)
-{
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	return gg_send_packet(sess, GG_PING, NULL);
-}
-
-/*
- * gg_notify_ex()
- *
- * wysya serwerowi list kontaktw (wraz z odpowiadajcymi im typami userw),
- * dziki czemu wie, czyj stan nas interesuje.
- *
- *  - sess - opis sesji
- *  - userlist - wskanik do tablicy numerw
- *  - types - wskanik do tablicy typw uytkownikw
- *  - count - ilo numerkw
- *
- * 0, -1.
- */
-int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
-{
-	struct gg_notify *n;
-	uin_t *u;
-	char *t;
-	int i, res = 0;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
-	
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-	
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
 	if (!userlist || !count)
 		return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
-	
+
 	while (count > 0) {
 		int part_count, packet_type;
-		
+
 		if (count > 400) {
 			part_count = 400;
 			packet_type = GG_NOTIFY_FIRST;
@@ -1814,12 +1911,12 @@
 
 		if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
 			return -1;
-	
-		for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { 
+
+		for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) {
 			n[i].uin = gg_fix32(*u);
 			n[i].dunno1 = *t;
 		}
-	
+
 		if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
 			free(n);
 			res = -1;
@@ -1836,17 +1933,19 @@
 	return res;
 }
 
-/*
- * gg_notify()
+/**
+ * Wysyła do serwera listę kontaktów.
  *
- * wysya serwerowi list kontaktw, dziki czemu wie, czyj stan nas
- * interesuje.
+ * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty
+ * są rodzaju \c GG_USER_NORMAL.
  *
- *  - sess - opis sesji
- *  - userlist - wskanik do tablicy numerw
- *  - count - ilo numerkw
+ * \param sess Struktura sesji
+ * \param userlist Wskaźnik do tablicy numerów kontaktów
+ * \param count Liczba kontaktów
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
  */
 int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
 {
@@ -1854,13 +1953,13 @@
 	uin_t *u;
 	int i, res = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count);
-	
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count);
+
 	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
-	
+
 	if (sess->state != GG_STATE_CONNECTED) {
 		errno = ENOTCONN;
 		return -1;
@@ -1868,10 +1967,10 @@
 
 	if (!userlist || !count)
 		return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
-	
+
 	while (count > 0) {
 		int part_count, packet_type;
-		
+
 		if (count > 400) {
 			part_count = 400;
 			packet_type = GG_NOTIFY_FIRST;
@@ -1879,15 +1978,15 @@
 			part_count = count;
 			packet_type = GG_NOTIFY_LAST;
 		}
-			
+
 		if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
 			return -1;
-	
-		for (u = userlist, i = 0; i < part_count; u++, i++) { 
+
+		for (u = userlist, i = 0; i < part_count; u++, i++) {
 			n[i].uin = gg_fix32(*u);
 			n[i].dunno1 = GG_USER_NORMAL;
 		}
-	
+
 		if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
 			res = -1;
 			free(n);
@@ -1903,73 +2002,27 @@
 	return res;
 }
 
-/*
- * gg_add_notify_ex()
+/**
+ * Dodaje kontakt.
  *
- * dodaje do listy kontaktw dany numer w trakcie poczenia.
- * dodawanemu uytkownikowi okrelamy jego typ (patrz protocol.html)
+ * Dodaje do listy kontaktów dany numer w trakcie połączenia. Aby zmienić
+ * rodzaj kontaktu (np. z normalnego na zablokowany), należy najpierw usunąć
+ * poprzedni rodzaj, ponieważ serwer operuje na maskach bitowych.
  *
- *  - sess - opis sesji
- *  - uin - numer
- *  - type - typ
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ * \param type Rodzaj kontaktu
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
  */
 int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
 {
 	struct gg_add_remove a;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
-	
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-	
-	a.uin = gg_fix32(uin);
-	a.dunno1 = type;
-	
-	return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
-}
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
 
-/*
- * gg_add_notify()
- *
- * dodaje do listy kontaktw dany numer w trakcie poczenia.
- *
- *  - sess - opis sesji
- *  - uin - numer
- *
- * 0, -1.
- */
-int gg_add_notify(struct gg_session *sess, uin_t uin)
-{
-	return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
-}
-
-/*
- * gg_remove_notify_ex()
- *
- * usuwa z listy kontaktw w trakcie poczenia.
- * usuwanemu uytkownikowi okrelamy jego typ (patrz protocol.html)
- *
- *  - sess - opis sesji
- *  - uin - numer
- *  - type - typ
- *
- * 0, -1.
- */
-int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
-{
-	struct gg_add_remove a;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
-	
 	if (!sess) {
 		errno = EFAULT;
 		return -1;
@@ -1982,35 +2035,101 @@
 
 	a.uin = gg_fix32(uin);
 	a.dunno1 = type;
-	
+
+	return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
+}
+
+/**
+ * Dodaje kontakt.
+ *
+ * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
+ * kontaktów to \c GG_USER_NORMAL.
+ *
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
+ */
+int gg_add_notify(struct gg_session *sess, uin_t uin)
+{
+	return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
+}
+
+/**
+ * Usuwa kontakt.
+ *
+ * Usuwa z listy kontaktów dany numer w trakcie połączenia.
+ *
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ * \param type Rodzaj kontaktu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
+ */
+int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
+{
+	struct gg_add_remove a;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	a.uin = gg_fix32(uin);
+	a.dunno1 = type;
+
 	return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
 }
 
-/*
- * gg_remove_notify()
+/**
+ * Usuwa kontakt.
  *
- * usuwa z listy kontaktw w trakcie poczenia.
+ * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
+ * kontaktów to \c GG_USER_NORMAL.
  *
- *  - sess - opis sesji
- *  - uin - numer
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
  */
 int gg_remove_notify(struct gg_session *sess, uin_t uin)
 {
 	return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL);
 }
 
-/*
- * gg_userlist_request()
+/**
+ * Wysyła do serwera zapytanie dotyczące listy kontaktów.
  *
- * wysya danie/zapytanie listy kontaktw na serwerze.
+ * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
+ * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
+ * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
+ * listy kontaktów jest ignorowany przez serwer, ale ze względu na
+ * kompatybilność z innymi klientami, należy przechowywać dane w tym samym
+ * formacie co oryginalny klient Gadu-Gadu.
  *
- *  - sess - opis sesji
- *  - type - rodzaj zapytania/dania
- *  - request - tre zapytania/dania (moe by NULL)
+ * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą
+ * z protokołu -- wysyła i odbiera kompletną listę.
  *
- * 0, -1
+ * \param sess Struktura sesji
+ * \param type Rodzaj zapytania
+ * \param request Treść zapytania (może być równe NULL)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup importexport
  */
 int gg_userlist_request(struct gg_session *sess, char type, const char *request)
 {
@@ -2020,7 +2139,7 @@
 		errno = EFAULT;
 		return -1;
 	}
-	
+
 	if (sess->state != GG_STATE_CONNECTED) {
 		errno = ENOTCONN;
 		return -1;
@@ -2030,7 +2149,7 @@
 		sess->userlist_blocks = 1;
 		return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
 	}
-	
+
 	len = strlen(request);
 
 	sess->userlist_blocks = 0;
@@ -2053,6 +2172,8 @@
 	return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
 }
 
+/* @} */
+
 /*
  * Local variables:
  * c-indentation-style: k&r
--- a/libpurple/protocols/gg/lib/libgadu.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/libgadu.h	Wed Mar 10 17:58:21 2010 +0000
@@ -1,12 +1,13 @@
-/* $Id: libgadu.h 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: libgadu.h.in 878 2009-11-16 23:48:19Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
- *                          Arkadiusz Mikiewicz <arekm@pld-linux.org>
- *                          Tomasz Chiliski <chilek@chilan.com>
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliński <chilek@chilan.com>
  *                          Piotr Wysocki <wysek@linux.bydg.org>
  *                          Dawid Jarosz <dawjar@poczta.onet.pl>
+ *                          Jakub Zawadzki <darkjames@darkjames.ath.cx>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -19,338 +20,568 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file libgadu.h
+ *
+ * \brief Główny plik nagłówkowy biblioteki
+ */
+
 #ifndef __GG_LIBGADU_H
 #define __GG_LIBGADU_H
 
 #ifdef __cplusplus
-#ifdef _MSC_VER
+#ifdef _WIN32
 #pragma pack(push, 1)
 #endif
 extern "C" {
 #endif
 
-#include <libgadu-config.h>
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdarg.h>
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+/** \cond ignore */
+
+/* Defined if libgadu was compiled for bigendian machine. */
+#undef GG_CONFIG_BIGENDIAN
+
+/* Defined if this machine has gethostbyname_r(). */
+#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+
+/* Defined if libgadu was compiled and linked with pthread support. */
+#undef GG_CONFIG_HAVE_PTHREAD
+
+/* Defined if pthread resolver is the default one. */
+#undef GG_CONFIG_PTHREAD_DEFAULT 
+
+/* Defined if this machine has C99-compiliant vsnprintf(). */
+#undef GG_CONFIG_HAVE_C99_VSNPRINTF
+
+/* Defined if this machine has va_copy(). */
+#undef GG_CONFIG_HAVE_VA_COPY
+
+/* Defined if this machine has __va_copy(). */
+#undef GG_CONFIG_HAVE___VA_COPY
+
+/* Defined if this machine supports long long. */
+#undef GG_CONFIG_HAVE_LONG_LONG
+
+/* Defined if libgadu was compiled and linked with TLS support. */
+#undef GG_CONFIG_HAVE_OPENSSL
+
+/* Defined if uintX_t types are defined in <stdint.h>. */
+#undef GG_CONFIG_HAVE_STDINT_H
+
+/* Defined if uintX_t types are defined in <inttypes.h>. */
+#undef GG_CONFIG_HAVE_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/inttypes.h>. */
+#undef GG_CONFIG_HAVE_SYS_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/int_types.h>. */
+#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+
+/* Defined if uintX_t types are defined in <sys/types.h>. */
+#undef GG_CONFIG_HAVE_SYS_TYPES_H
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
 #include <openssl/ssl.h>
 #endif
 
-/*
- * typedef uin_t
- *
- * typ reprezentujcy numer osoby.
+#ifdef GG_CONFIG_HAVE_STDINT_H
+#include <stdint.h>
+#else
+#  ifdef GG_CONFIG_HAVE_INTTYPES_H
+#  include <inttypes.h>
+#  else
+#    ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H
+#    include <sys/inttypes.h>
+#    else
+#      ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+#      include <sys/int_types.h>
+#      else
+#        ifdef GG_CONFIG_HAVE_SYS_TYPES_H
+#        include <sys/types.h>
+#        else
+
+#ifndef __AC_STDINT_H
+#define __AC_STDINT_H
+
+/* ISO C 9X: 7.18 Integer types <stdint.h> */
+
+typedef unsigned char   uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int   uint32_t;
+
+#ifndef __CYGWIN__
+#define __int8_t_defined
+typedef   signed char    int8_t;
+typedef   signed short  int16_t;
+typedef   signed int    int32_t;
+#endif
+
+#endif /* __AC_STDINT_H */
+
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/** \endcond */
+
+/**
+ * Numer Gadu-Gadu.
  */
 typedef uint32_t uin_t;
 
-/*
- * oglna struktura opisujca rne sesje. przydatna w klientach.
+/**
+ * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x.
+ */
+typedef struct {
+	uint8_t id[8];
+} gg_dcc7_id_t;
+
+/**
+ * Makro deklarujące pola wspólne dla struktur sesji.
  */
 #define gg_common_head(x) \
-	int fd;			/* podgldany deskryptor */ \
-	int check;		/* sprawdzamy zapis czy odczyt */ \
-	int state;		/* aktualny stan maszynki */ \
-	int error;		/* kod bdu dla GG_STATE_ERROR */ \
-	int type;		/* rodzaj sesji */ \
-	int id;			/* identyfikator */ \
-	int timeout;		/* sugerowany timeout w sekundach */ \
-	int (*callback)(x*); 	/* callback przy zmianach */ \
-	void (*destroy)(x*); 	/* funkcja niszczenia */
+	int fd;			/**< Obserwowany deskryptor */ \
+	int check;		/**< Informacja o żądaniu odczytu/zapisu (patrz \ref gg_check_t) */ \
+	int state;		/**< Aktualny stan połączenia (patrz \ref gg_state_t) */ \
+	int error;		/**< Kod błędu dla \c GG_STATE_ERROR (patrz \ref gg_error_t) */ \
+	int type;		/**< Rodzaj sesji (patrz \ref gg_session_t) */ \
+	int id;			/**< Identyfikator sesji */ \
+	int timeout;		/**< Czas pozostały do zakończenia stanu */ \
+	int (*callback)(x*); 	/**< Funkcja zwrotna */ \
+	void (*destroy)(x*); 	/**< Funkcja zwalniania zasobów */
 
+/**
+ * Struktura wspólna dla wszystkich sesji i połączeń. Pozwala na proste
+ * rzutowanie niezależne od rodzaju połączenia.
+ */
 struct gg_common {
 	gg_common_head(struct gg_common)
 };
 
 struct gg_image_queue;
 
-/*
- * struct gg_session
+struct gg_dcc7;
+
+/**
+ * Sposób rozwiązywania nazw serwerów.
+ */
+typedef enum {
+	GG_RESOLVER_DEFAULT = 0,	/**< Domyślny sposób rozwiązywania nazw (jeden z poniższych) */
+	GG_RESOLVER_FORK,		/**< Rozwiązywanie nazw bazujące na procesach */
+	GG_RESOLVER_PTHREAD,		/**< Rozwiązywanie nazw bazujące na wątkach */
+	GG_RESOLVER_WIN32,
+	GG_RESOLVER_CUSTOM,		/**< Funkcje rozwiązywania nazw dostarczone przed aplikację */
+	GG_RESOLVER_INVALID = -1	/**< Nieprawidłowy sposób rozwiązywania nazw (wynik \c gg_session_get_resolver) */
+} gg_resolver_t;
+
+/**
+ * Rodzaj kodowania znaków.
+ */
+typedef enum {
+	GG_ENCODING_CP1250 = 0,		/**< Kodowanie CP1250 */
+	GG_ENCODING_UTF8,		/**< Kodowanie UTF-8 */
+	GG_ENCODING_INVALID = -1	/**< Nieprawidłowe kodowanie */
+} gg_encoding_t;
+
+/**
+ * Sesja Gadu-Gadu.
  *
- * struktura opisujca dan sesj. tworzona przez gg_login(), zwalniana
- * przez gg_free_session().
+ * Tworzona przez funkcję \c gg_login(), zwalniana przez \c gg_free_session().
+ *
+ * \ingroup login
  */
 struct gg_session {
 	gg_common_head(struct gg_session)
 
-	int async;      	/* czy poczenie jest asynchroniczne */
-	int pid;        	/* pid procesu resolvera */
-	int port;       	/* port, z ktrym si czymy */
-	int seq;        	/* numer sekwencyjny ostatniej wiadomoci */
-	int last_pong;  	/* czas otrzymania ostatniego ping/pong */
-	int last_event;		/* czas otrzymania ostatniego pakietu */
+	int async;      	/**< Flaga połączenia asynchronicznego */
+	int pid;        	/**< Numer procesu rozwiązującego nazwę serwera */
+	int port;       	/**< Port serwera */
+	int seq;        	/**< Numer sekwencyjny ostatniej wiadomości */
+	int last_pong;  	/**< Czas otrzymania ostatniej ramki utrzymaniowej */
+	int last_event;		/**< Czas otrzymania ostatniego pakietu */
 
-	struct gg_event *event;	/* zdarzenie po ->callback() */
+	struct gg_event *event;	/**< Zdarzenie po wywołaniu \c callback */
 
-	uint32_t proxy_addr;	/* adres proxy, keszowany */
-	uint16_t proxy_port;	/* port proxy */
+	uint32_t proxy_addr;	/**< Adres serwera pośredniczącego */
+	uint16_t proxy_port;	/**< Port serwera pośredniczącego */
 
-	uint32_t hub_addr;	/* adres huba po resolvniciu */
-	uint32_t server_addr;	/* adres serwera, od huba */
+	uint32_t hub_addr;	/**< Adres huba po rozwiązaniu nazwy */
+	uint32_t server_addr;	/**< Adres serwera otrzymany od huba */
 
-	uint32_t client_addr;	/* adres klienta */
-	uint16_t client_port;	/* port, na ktrym klient sucha */
+	uint32_t client_addr;	/**< Adres gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
+	uint16_t client_port;	/**< Port gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
 
-	uint32_t external_addr;	/* adres zewnetrzny klienta */
-	uint16_t external_port;	/* port zewnetrzny klienta */
+	uint32_t external_addr;	/**< Publiczny adres dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
+	uint16_t external_port;	/**< Publiczny port dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
 
-	uin_t uin;		/* numerek klienta */
-	char *password;		/* i jego haso. zwalniane automagicznie */
+	uin_t uin;		/**< Własny numer Gadu-Gadu */
+	char *password;		/**< Hasło (zwalniane po użyciu) */
 
-	int initial_status;	/* pocztkowy stan klienta */
-	int status;		/* aktualny stan klienta */
+	int initial_status;	/**< Początkowy status */
+	int status;		/**< Aktualny status */
 
-	char *recv_buf;		/* bufor na otrzymywane pakiety */
-	int recv_done;		/* ile ju wczytano do bufora */
-	int recv_left;		/* i ile jeszcze trzeba wczyta */
+	char *recv_buf;		/**< Bufor na odbierany pakiety */
+	int recv_done;		/**< Liczba wczytanych bajtów pakietu */
+	int recv_left;		/**< Liczba pozostałych do wczytania bajtów pakietu */
 
-	int protocol_version;	/* wersja uywanego protokou */
-	char *client_version;	/* wersja uywanego klienta */
-	int last_sysmsg;	/* ostatnia wiadomo systemowa */
+	int protocol_version;	/**< Wersja protokołu (bez flag) */
+	char *client_version;	/**< Wersja klienta */
+	int last_sysmsg;	/**< Numer ostatniej wiadomości systemowej */
 
-	char *initial_descr;	/* pocztkowy opis stanu klienta */
+	char *initial_descr;	/**< Początkowy opis statusu */
 
-	void *resolver;		/* wskanik na informacje resolvera */
+	void *resolver;		/**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
 
-	char *header_buf;	/* bufor na pocztek nagwka */
-	unsigned int header_done;/* ile ju mamy */
+	char *header_buf;	/**< Bufor na początek nagłówka pakietu */
+	unsigned int header_done;	/**< Liczba wczytanych bajtów nagłówka pakietu */
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-	SSL *ssl;		/* sesja TLS */
-	SSL_CTX *ssl_ctx;	/* kontekst sesji? */
+#ifdef GG_CONFIG_HAVE_OPENSSL
+	SSL *ssl;		/**< Struktura TLS */
+	SSL_CTX *ssl_ctx;	/**< Kontekst sesji TLS */
 #else
-	void *ssl;		/* zachowujemy ABI */
-	void *ssl_ctx;
+	void *ssl;		/**< Struktura TLS */
+	void *ssl_ctx;		/**< Kontekst sesji TLS */
 #endif
 
-	int image_size;		/* maksymalny rozmiar obrazkw w KiB */
+	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+
+	char *userlist_reply;	/**< Bufor z odbieraną listą kontaktów */
+
+	int userlist_blocks;	/**< Liczba części listy kontaktów */
 
-	char *userlist_reply;	/* fragment odpowiedzi listy kontaktw */
+	struct gg_image_queue *images;	/**< Lista wczytywanych obrazków */
+
+	int hash_type;		/**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
+
+	char *send_buf;		/**< Bufor z danymi do wysłania */
+	int send_left;		/**< Liczba bajtów do wysłania */
 
-	int userlist_blocks;	/* na ile kawakw podzielono list kontaktw */
+	struct gg_dcc7 *dcc7_list;	/**< Lista połączeń bezpośrednich skojarzonych z sesją */
+	
+	int soft_timeout;	/**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_watch_fd() */
+
+	int protocol_flags;	/**< Flagi protokołu */
 
-	struct gg_image_queue *images;	/* aktualnie wczytywane obrazki */
+	gg_encoding_t encoding;		/**< Rodzaj kodowania znaków */
+
+	gg_resolver_t resolver_type;	/**< Sposób rozwiązywania nazw serwerów */
+	int (*resolver_start)(int *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynająca rozwiązywanie nazwy */
+	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
+
+	int protocol_features;	/**< Opcje protokołu */
 };
 
-/*
- * struct gg_http
+/**
+ * Połączenie HTTP.
  *
- * oglna struktura opisujca stan wszystkich operacji HTTP. tworzona
- * przez gg_http_connect(), zwalniana przez gg_http_free().
+ * Tworzone przez \c gg_http_connect(), zwalniane przez \c gg_http_free().
+ *
+ * \ingroup http
  */
 struct gg_http {
 	gg_common_head(struct gg_http)
 
-	int async;              /* czy poczenie asynchroniczne */
-	int pid;                /* pid procesu resolvera */
-	int port;               /* port, z ktrym si czymy */
+	int async;              /**< Flaga połączenia asynchronicznego */
+	int pid;                /**< Identyfikator procesu rozwiązującego nazwę serwera */
+	int port;               /**< Port */
+
+	char *query;            /**< Zapytanie HTTP */
+	char *header;           /**< Odebrany nagłówek */
+	int header_size;        /**< Rozmiar wczytanego nagłówka */
+	char *body;             /**< Odebrana strona */
+	unsigned int body_size; /**< Rozmiar strony */
+
+	void *data;             /**< Dane prywatne usługi HTTP */
 
-	char *query;            /* bufor zapytania http */
-	char *header;           /* bufor nagwka */
-	int header_size;        /* rozmiar wczytanego nagwka */
-	char *body;             /* bufor otrzymanych informacji */
-	unsigned int body_size; /* oczekiwana ilo informacji */
+	char *user_data;	/**< Dane prywatne użytkownika (nie są zwalniane) */
+
+	void *resolver;		/**< Dane prywatne procesu lub wątku rozwiązującego nazwę */
+
+	unsigned int body_done;	/**< Liczba odebranych bajtów strony */
 
-	void *data;             /* dane danej operacji http */
-
-	char *user_data;	/* dane uytkownika, nie s zwalniane przez gg_http_free() */
+	gg_resolver_t resolver_type;	/**< Sposób rozwiązywania nazw serwerów */
+	int (*resolver_start)(int *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynająca rozwiązywanie nazwy */
+	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
+};
 
-	void *resolver;		/* wskanik na informacje resolvera */
-
-	unsigned int body_done;	/* ile ju treci odebrano? */
-};
+/** \cond ignore */
 
 #ifdef __GNUC__
 #define GG_PACKED __attribute__ ((packed))
+#ifndef GG_IGNORE_DEPRECATED
+#define GG_DEPRECATED __attribute__ ((deprecated))
+#else
+#define GG_DEPRECATED
+#endif
 #else
 #define GG_PACKED
+#define GG_DEPRECATED
 #endif
 
-#define GG_MAX_PATH 276
+/** \endcond */
+
+#define GG_MAX_PATH 276		/**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */
 
-/*
- * struct gg_file_info
+/**
+ * Odpowiednik struktury WIN32_FIND_DATA z API WIN32.
  *
- * odpowiednik windowsowej struktury WIN32_FIND_DATA niezbdnej przy
- * wysyaniu plikw.
+ * Wykorzystywana przy połączeniach bezpośrednich do wersji Gadu-Gadu 6.x.
  */
 struct gg_file_info {
-	uint32_t mode;			/* dwFileAttributes */
-	uint32_t ctime[2];		/* ftCreationTime */
-	uint32_t atime[2];		/* ftLastAccessTime */
-	uint32_t mtime[2];		/* ftLastWriteTime */
-	uint32_t size_hi;		/* nFileSizeHigh */
-	uint32_t size;			/* nFileSizeLow */
-	uint32_t reserved0;		/* dwReserved0 */
-	uint32_t reserved1;		/* dwReserved1 */
-	unsigned char filename[GG_MAX_PATH - 14];	/* cFileName */
-	unsigned char short_filename[14];		/* cAlternateFileName */
-} GG_PACKED;
+	uint32_t mode;			/**< dwFileAttributes */
+	uint32_t ctime[2];		/**< ftCreationTime */
+	uint32_t atime[2];		/**< ftLastAccessTime */
+	uint32_t mtime[2];		/**< ftLastWriteTime */
+	uint32_t size_hi;		/**< nFileSizeHigh */
+	uint32_t size;			/**< nFileSizeLow */
+	uint32_t reserved0;		/**< dwReserved0 */
+	uint32_t reserved1;		/**< dwReserved1 */
+	unsigned char filename[GG_MAX_PATH - 14];	/**< cFileName */
+	unsigned char short_filename[14];		/**< cAlternateFileName */
+} /** \cond ignore */ GG_PACKED /** \endcond */;
 
-/*
- * struct gg_dcc
+/**
+ * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x.
  *
- * struktura opisujca nasuchujce gniazdo pocze midzy klientami.
- * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free().
+ * Tworzone przez \c gg_dcc_socket_create(), \c gg_dcc_get_file(),
+ * \c gg_dcc_send_file() lub \c gg_dcc_voice_chat(), zwalniane przez
+ * \c gg_dcc_free().
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc {
 	gg_common_head(struct gg_dcc)
 
-	struct gg_event *event;	/* opis zdarzenia */
+	struct gg_event *event;	/**< Zdarzenie po wywołaniu \c callback */
 
-	int active;		/* czy to my si czymy? */
-	int port;		/* port, na ktrym siedzi */
-	uin_t uin;		/* uin klienta */
-	uin_t peer_uin;		/* uin drugiej strony */
-	int file_fd;		/* deskryptor pliku */
-	unsigned int offset;	/* offset w pliku */
-	unsigned int chunk_size;/* rozmiar kawaka */
-	unsigned int chunk_offset;/* offset w aktualnym kawaku */
+	int active;		/**< Flaga połączenia aktywnego (nieużywana) */
+	int port;		/**< Port gniazda nasłuchującego */
+	uin_t uin;		/**< Własny numer Gadu-Gadu */
+	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony połączenia */
+	int file_fd;		/**< deskryptor pliku */
+	unsigned int offset;	/**< Położenie w pliku */
+	unsigned int chunk_size;
+				/**< Rozmiar kawałka pliku */
+	unsigned int chunk_offset;
+				/**< Położenie w aktualnym kawałku pliku */
 	struct gg_file_info file_info;
-				/* informacje o pliku */
-	int established;	/* poczenie ustanowione */
-	uint8_t *voice_buf;	/* bufor na pakiet poczenia gosowego */
-	int incoming;		/* poczenie przychodzce */
-	char *chunk_buf;	/* bufor na kawaek danych */
-	uint32_t remote_addr;	/* adres drugiej strony */
-	uint16_t remote_port;	/* port drugiej strony */
+				/**< Informacje o pliku */
+	int established;	/**< Flaga ustanowienia połączenia */
+	char *voice_buf;	/**< Bufor na pakiet połączenia głosowego */
+	int incoming;		/**< Flaga połączenia przychodzącego */
+	char *chunk_buf;	/**< Bufor na fragment danych */
+	uint32_t remote_addr;	/**< Adres drugiej strony */
+	uint16_t remote_port;	/**< Port drugiej strony */
 };
 
-/*
- * enum gg_session_t
+#define GG_DCC7_HASH_LEN	20	/**< Maksymalny rozmiar skrótu pliku w połączeniach bezpośrenich */
+#define GG_DCC7_FILENAME_LEN	255	/**< Maksymalny rozmiar nazwy pliku w połączeniach bezpośrednich */
+#define GG_DCC7_INFO_LEN	64	/**< Maksymalny rozmiar informacji o połączeniach bezpośrednich */
+
+/**
+ * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x.
  *
- * rodzaje sesji.
+ * \ingroup dcc7
+ */
+struct gg_dcc7 {
+	gg_common_head(struct gg_dcc7)
+
+	gg_dcc7_id_t cid;	/**< Identyfikator połączenia */
+
+	struct gg_event *event;	/**< Struktura zdarzenia */
+
+	uin_t uin;		/**< Własny numer Gadu-Gadu */
+	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony połączenia */
+
+	int file_fd;		/**< Deskryptor przesyłanego pliku */
+	unsigned int offset;	/**< Aktualne położenie w przesyłanym pliku */
+	unsigned int size;	/**< Rozmiar przesyłanego pliku */
+	unsigned char filename[GG_DCC7_FILENAME_LEN + 1];
+				/**< Nazwa przesyłanego pliku */
+	unsigned char hash[GG_DCC7_HASH_LEN];
+				/**< Skrót SHA1 przesyłanego pliku */
+
+	int dcc_type;		/**< Rodzaj połączenia bezpośredniego */
+	int established;	/**< Flaga ustanowienia połączenia */
+	int incoming;		/**< Flaga połączenia przychodzącego */
+	int reverse;		/**< Flaga połączenia zwrotnego */
+
+	uint32_t local_addr;	/**< Adres lokalny */
+	uint16_t local_port;	/**< Port lokalny */
+
+	uint32_t remote_addr;	/**< Adres drugiej strony */
+	uint16_t remote_port;	/**< Port drugiej strony */
+
+	struct gg_session *sess;
+				/**< Sesja do której przypisano połączenie */
+	struct gg_dcc7 *next;	/**< Następne połączenie w liście */
+
+	int soft_timeout;	/**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_dcc7_watch_fd() */
+	int seek;		/**< Flaga mówiąca, że można zmieniać położenie w wysyłanym pliku */
+};
+
+/**
+ * Rodzaj sesji.
  */
 enum gg_session_t {
-	GG_SESSION_GG = 1,	/* poczenie z serwerem gg */
-	GG_SESSION_HTTP,	/* oglna sesja http */
-	GG_SESSION_SEARCH,	/* szukanie */
-	GG_SESSION_REGISTER,	/* rejestrowanie */
-	GG_SESSION_REMIND,	/* przypominanie hasa */
-	GG_SESSION_PASSWD,	/* zmiana hasa */
-	GG_SESSION_CHANGE,	/* zmiana informacji o sobie */
-	GG_SESSION_DCC,		/* oglne poczenie DCC */
-	GG_SESSION_DCC_SOCKET,	/* nasuchujcy socket */
-	GG_SESSION_DCC_SEND,	/* wysyanie pliku */
-	GG_SESSION_DCC_GET,	/* odbieranie pliku */
-	GG_SESSION_DCC_VOICE,	/* rozmowa gosowa */
-	GG_SESSION_USERLIST_GET,	/* pobieranie userlisty */
-	GG_SESSION_USERLIST_PUT,	/* wysyanie userlisty */
-	GG_SESSION_UNREGISTER,	/* usuwanie konta */
-	GG_SESSION_USERLIST_REMOVE,	/* usuwanie userlisty */
-	GG_SESSION_TOKEN,	/* pobieranie tokenu */
+	GG_SESSION_GG = 1,	/**< Połączenie z serwerem Gadu-Gadu */
+	GG_SESSION_HTTP,	/**< Połączenie HTTP */
+	GG_SESSION_SEARCH,	/**< Wyszukiwanie w katalogu publicznym (nieaktualne) */
+	GG_SESSION_REGISTER,	/**< Rejestracja nowego konta */
+	GG_SESSION_REMIND,	/**< Przypominanie hasła */
+	GG_SESSION_PASSWD,	/**< Zmiana hasła */
+	GG_SESSION_CHANGE,	/**< Zmiana informacji w katalogu publicznym (nieaktualne) */
+	GG_SESSION_DCC,		/**< Połączenie bezpośrednie (do wersji 6.x) */
+	GG_SESSION_DCC_SOCKET,	/**< Gniazdo nasłuchujące (do wersji 6.x) */
+	GG_SESSION_DCC_SEND,	/**< Wysyłanie pliku (do wersji 6.x) */
+	GG_SESSION_DCC_GET,	/**< Odbieranie pliku (do wersji 6.x) */
+	GG_SESSION_DCC_VOICE,	/**< Rozmowa głosowa (do wersji 6.x) */
+	GG_SESSION_USERLIST_GET,	/**< Import listy kontaktów z serwera (nieaktualne) */
+	GG_SESSION_USERLIST_PUT,	/**< Eksport listy kontaktów do serwera (nieaktualne) */
+	GG_SESSION_UNREGISTER,	/**< Usuwanie konta */
+	GG_SESSION_USERLIST_REMOVE,	/**< Usuwanie listy kontaktów z serwera (nieaktualne) */
+	GG_SESSION_TOKEN,	/**< Pobieranie tokenu */
+	GG_SESSION_DCC7_SOCKET,	/**< Gniazdo nasłuchujące (od wersji 7.x) */
+	GG_SESSION_DCC7_SEND,	/**< Wysyłanie pliku (od wersji 7.x) */
+	GG_SESSION_DCC7_GET,	/**< Odbieranie pliku (od wersji 7.x) */
+	GG_SESSION_DCC7_VOICE,	/**< Rozmowa głosowa (od wersji 7.x) */
 
-	GG_SESSION_USER0 = 256,	/* zdefiniowana dla uytkownika */
-	GG_SESSION_USER1,	/* j.w. */
-	GG_SESSION_USER2,	/* j.w. */
-	GG_SESSION_USER3,	/* j.w. */
-	GG_SESSION_USER4,	/* j.w. */
-	GG_SESSION_USER5,	/* j.w. */
-	GG_SESSION_USER6,	/* j.w. */
-	GG_SESSION_USER7	/* j.w. */
+	GG_SESSION_USER0 = 256,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER1,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER2,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER3,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER4,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER5,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER6,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER7	/**< Rodzaj zadeklarowany dla użytkownika */
 };
 
-/*
- * enum gg_state_t
- *
- * opisuje stan asynchronicznej maszyny.
+/**
+ * Aktualny stan sesji.
  */
 enum gg_state_t {
-		/* wsplne */
-	GG_STATE_IDLE = 0,		/* nie powinno wystpi. */
-	GG_STATE_RESOLVING,             /* wywoa gethostbyname() */
-	GG_STATE_CONNECTING,            /* wywoa connect() */
-	GG_STATE_READING_DATA,		/* czeka na dane http */
-	GG_STATE_ERROR,			/* wystpi bd. kod w x->error */
+	/* wspólne */
+	GG_STATE_IDLE = 0,		/**< Nie dzieje się nic */
+	GG_STATE_RESOLVING,             /**< Oczekiwanie na rozwiązanie nazwy serwera */
+	GG_STATE_CONNECTING,            /**< Oczekiwanie na połączenie */
+	GG_STATE_READING_DATA,		/**< Oczekiwanie na dane */
+	GG_STATE_ERROR,			/**< Kod błędu w polu \c error */
 
-		/* gg_session */
-	GG_STATE_CONNECTING_HUB,	/* wywoa connect() na huba */
-	GG_STATE_CONNECTING_GG,         /* wywoa connect() na serwer */
-	GG_STATE_READING_KEY,           /* czeka na klucz */
-	GG_STATE_READING_REPLY,         /* czeka na odpowied */
-	GG_STATE_CONNECTED,             /* poczy si */
+	/* gg_session */
+	GG_STATE_CONNECTING_HUB,	/**< Oczekiwanie na połączenie z hubem */
+	GG_STATE_CONNECTING_GG,         /**< Oczekiwanie na połączenie z serwerem */
+	GG_STATE_READING_KEY,           /**< Oczekiwanie na klucz */
+	GG_STATE_READING_REPLY,         /**< Oczekiwanie na odpowiedź serwera */
+	GG_STATE_CONNECTED,             /**< Połączono z serwerem */
 
-		/* gg_http */
-	GG_STATE_SENDING_QUERY,		/* wysya zapytanie http */
-	GG_STATE_READING_HEADER,	/* czeka na nagwek http */
-	GG_STATE_PARSING,               /* przetwarza dane */
-	GG_STATE_DONE,                  /* skoczy */
+	/* gg_http */
+	GG_STATE_SENDING_QUERY,		/**< Wysłano zapytanie HTTP */
+	GG_STATE_READING_HEADER,	/**< Oczekiwanie na nagłówek HTTP */
+	GG_STATE_PARSING,               /**< Przetwarzanie danych */
+	GG_STATE_DONE,                  /**< Połączenie zakończone */
 
-		/* gg_dcc */
-	GG_STATE_LISTENING,		/* czeka na poczenia */
+	/* gg_dcc */
+	GG_STATE_LISTENING,		/* czeka na połączenia */
 	GG_STATE_READING_UIN_1,		/* czeka na uin peera */
-	GG_STATE_READING_UIN_2,		/* czeka na swj uin */
-	GG_STATE_SENDING_ACK,		/* wysya potwierdzenie dcc */
+	GG_STATE_READING_UIN_2,		/* czeka na swój uin */
+	GG_STATE_SENDING_ACK,		/* wysyła potwierdzenie dcc */
 	GG_STATE_READING_ACK,		/* czeka na potwierdzenie dcc */
-	GG_STATE_READING_REQUEST,	/* czeka na komend */
-	GG_STATE_SENDING_REQUEST,	/* wysya komend */
-	GG_STATE_SENDING_FILE_INFO,	/* wysya informacje o pliku */
+	GG_STATE_READING_REQUEST,	/* czeka na komendę */
+	GG_STATE_SENDING_REQUEST,	/* wysyła komendę */
+	GG_STATE_SENDING_FILE_INFO,	/* wysyła informacje o pliku */
 	GG_STATE_READING_PRE_FILE_INFO,	/* czeka na pakiet przed file_info */
 	GG_STATE_READING_FILE_INFO,	/* czeka na informacje o pliku */
-	GG_STATE_SENDING_FILE_ACK,	/* wysya potwierdzenie pliku */
+	GG_STATE_SENDING_FILE_ACK,	/* wysyła potwierdzenie pliku */
 	GG_STATE_READING_FILE_ACK,	/* czeka na potwierdzenie pliku */
-	GG_STATE_SENDING_FILE_HEADER,	/* wysya nagwek pliku */
-	GG_STATE_READING_FILE_HEADER,	/* czeka na nagwek */
+	GG_STATE_SENDING_FILE_HEADER,	/* wysyła nagłówek pliku */
+	GG_STATE_READING_FILE_HEADER,	/* czeka na nagłówek */
 	GG_STATE_GETTING_FILE,		/* odbiera plik */
-	GG_STATE_SENDING_FILE,		/* wysya plik */
+	GG_STATE_SENDING_FILE,		/* wysyła plik */
 	GG_STATE_READING_VOICE_ACK,	/* czeka na potwierdzenie voip */
 	GG_STATE_READING_VOICE_HEADER,	/* czeka na rodzaj bloku voip */
 	GG_STATE_READING_VOICE_SIZE,	/* czeka na rozmiar bloku voip */
 	GG_STATE_READING_VOICE_DATA,	/* czeka na dane voip */
-	GG_STATE_SENDING_VOICE_ACK,	/* wysya potwierdzenie voip */
-	GG_STATE_SENDING_VOICE_REQUEST,	/* wysya danie voip */
-	GG_STATE_READING_TYPE,		/* czeka na typ poczenia */
+	GG_STATE_SENDING_VOICE_ACK,	/* wysyła potwierdzenie voip */
+	GG_STATE_SENDING_VOICE_REQUEST,	/* wysyła żądanie voip */
+	GG_STATE_READING_TYPE,		/* czeka na typ połączenia */
 
 	/* nowe. bez sensu jest to API. */
-	GG_STATE_TLS_NEGOTIATION	/* negocjuje poczenie TLS */
+	GG_STATE_TLS_NEGOTIATION,	/**< Negocjacja połączenia szyfrowanego */
+
+	GG_STATE_REQUESTING_ID,		/**< Oczekiwanie na nadanie identyfikatora połączenia bezpośredniego */
+	GG_STATE_WAITING_FOR_ACCEPT,	/**< Oczekiwanie na potwierdzenie lub odrzucenie połączenia bezpośredniego */
+	GG_STATE_WAITING_FOR_INFO,	/**< Oczekiwanie na informacje o połączeniu bezpośrednim */
+
+	GG_STATE_READING_ID,		/**< Odebranie identyfikatora połączenia bezpośredniego */
+	GG_STATE_SENDING_ID		/**< Wysłano identyfikatora połączenia bezpośredniego */
 };
 
-/*
- * enum gg_check_t
+/**
+ * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać
+ * z deskryptora. Maska bitowa.
  *
- * informuje, co proces klienta powinien sprawdzi na deskryptorze danego
- * poczenia.
+ * \ingroup events
  */
 enum gg_check_t {
-	GG_CHECK_NONE = 0,		/* nic. nie powinno wystpi */
-	GG_CHECK_WRITE = 1,		/* sprawdzamy moliwo zapisu */
-	GG_CHECK_READ = 2		/* sprawdzamy moliwo odczytu */
+	GG_CHECK_NONE = 0,		/**< Nie sprawdzaj niczego */
+	GG_CHECK_WRITE = 1,		/**< Sprawdź możliwość zapisu */
+	GG_CHECK_READ = 2		/**< Sprawdź możliwość odczytu */
 };
 
-/*
- * struct gg_login_params
+/**
+ * Parametry połączenia z serwerem Gadu-Gadu. Parametry zostały przeniesione
+ * do struktury, by uniknąć zmian API po rozszerzeniu protokołu i dodaniu
+ * kolejnych opcji połączenia. Część parametrów, które nie są już aktualne
+ * lub nie mają znaczenia, została usunięta z dokumentacji.
  *
- * parametry gg_login(). przeniesiono do struktury, eby unikn problemw
- * z cigymi zmianami API, gdy dodano co nowego do protokou.
+ * \ingroup login
  */
 struct gg_login_params {
-	uin_t uin;			/* numerek */
-	char *password;			/* haso */
-	int async;			/* asynchroniczne sockety? */
-	int status;			/* pocztkowy status klienta */
-	char *status_descr;		/* opis statusu */
-	uint32_t server_addr;		/* adres serwera gg */
-	uint16_t server_port;		/* port serwera gg */
-	uint32_t client_addr;		/* adres dcc klienta */
-	uint16_t client_port;		/* port dcc klienta */
-	int protocol_version;		/* wersja protokou */
-	char *client_version;		/* wersja klienta */
-	int has_audio;			/* czy ma dwik? */
-	int last_sysmsg;		/* ostatnia wiadomo systemowa */
-	uint32_t external_addr;		/* adres widziany na zewnatrz */
-	uint16_t external_port;		/* port widziany na zewnatrz */
-	int tls;			/* czy czymy po TLS? */
-	int image_size;			/* maksymalny rozmiar obrazka w KiB */
-	int era_omnix;			/* czy udawa klienta era omnix? */
+	uin_t uin;			/**< Numer Gadu-Gadu */
+	char *password;			/**< Hasło */
+	int async;			/**< Flaga asynchronicznego połączenia (domyślnie nie) */
+	int status;			/**< Początkowy status użytkownika (domyślnie \c GG_STATUS_AVAIL) */
+	char *status_descr;		/**< Początkowy opis użytkownika (domyślnie brak) */
+	uint32_t server_addr;		/**< Adres serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
+	uint16_t server_port;		/**< Port serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
+#ifndef DOXYGEN
+	uint32_t client_addr;		/**< Adres połączeń bezpośrednich (nieaktualne) */
+	uint16_t client_port;		/**< Port połączeń bezpośrednich (nieaktualne) */
+#endif
+	int protocol_version;		/**< Wersja protokołu wysyłana do serwera (domyślnie najnowsza obsługiwana) */
+	char *client_version;		/**< Wersja klienta wysyłana do serwera (domyślnie najnowsza znana) */
+	int has_audio;			/**< Flaga obsługi połączeń głosowych */
+	int last_sysmsg;		/**< Numer ostatnio odebranej wiadomości systemowej */
+	uint32_t external_addr;		/**< Adres publiczny dla połączeń bezpośrednich (6.x) */
+	uint16_t external_port;		/**< Port publiczny dla połączeń bezpośrednich (6.x) */
+#ifndef DOXYGEN
+	int tls;			/**< Flaga połączenia szyfrowanego (nieaktualna) */
+#endif
+	int image_size;			/**< Maksymalny rozmiar obsługiwanych obrazków w kilobajtach */
+#ifndef DOXYGEN
+	int era_omnix;			/**< Flaga udawania klienta Era Omnix (nieaktualna) */
+#endif
+	int hash_type;			/**< Rodzaj skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domyślnie SHA1) */
+	gg_encoding_t encoding;		/**< Rodzaj kodowania używanego w sesji (domyślnie CP1250) */
+	gg_resolver_t resolver;		/**< Sposób rozwiązywania nazw (patrz \ref build-resolver) */
+	int protocol_features;		/**< Opcje protokołu (flagi GG_FEATURE_*). */
 
-	char dummy[6 * sizeof(int)];	/* miejsce na kolejnych 6 zmiennych,
-					 * eby z dodaniem parametru nie
-					 * zmienia si rozmiar struktury */
+#ifndef DOXYGEN
+	char dummy[2 * sizeof(int)];	/**< \internal Miejsce na kilka kolejnych
+					  parametrów, żeby wraz z dodawaniem kolejnych
+					  parametrów nie zmieniał się rozmiar struktury */
+#endif
+
 };
 
 struct gg_session *gg_login(const struct gg_login_params *p);
@@ -367,232 +598,337 @@
 int gg_ping(struct gg_session *sess);
 int gg_userlist_request(struct gg_session *sess, char type, const char *request);
 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32);
-int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size);
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size);
 
 uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
 
-struct gg_image_queue {
-	uin_t sender;			/* nadawca obrazka */
-	uint32_t size;			/* rozmiar */
-	uint32_t crc32;			/* suma kontrolna */
-	char *filename;			/* nazwa pliku */
-	char *image;			/* bufor z obrazem */
-	uint32_t done;			/* ile ju wczytano */
+int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type);
+gg_resolver_t gg_session_get_resolver(struct gg_session *gs);
+int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
+
+int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type);
+gg_resolver_t gg_http_get_resolver(struct gg_http *gh);
+int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
 
-	struct gg_image_queue *next;	/* nastpny na licie */
-};
+int gg_global_set_resolver(gg_resolver_t type);
+gg_resolver_t gg_global_get_resolver(void);
+int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
 
-/*
- * enum gg_event_t
+/**
+ * Rodzaj zdarzenia.
  *
- * rodzaje zdarze.
+ * \ingroup events
  */
 enum gg_event_t {
-	GG_EVENT_NONE = 0,		/* nic si nie wydarzyo */
-	GG_EVENT_MSG,			/* otrzymano wiadomo */
-	GG_EVENT_NOTIFY,		/* kto si pojawi */
-	GG_EVENT_NOTIFY_DESCR,		/* kto si pojawi z opisem */
-	GG_EVENT_STATUS,		/* kto zmieni stan */
-	GG_EVENT_ACK,			/* potwierdzenie wysania wiadomoci */
-	GG_EVENT_PONG,			/* pakiet pong */
-	GG_EVENT_CONN_FAILED,		/* poczenie si nie udao */
-	GG_EVENT_CONN_SUCCESS,		/* poczenie si powiodo */
-	GG_EVENT_DISCONNECT,		/* serwer zrywa poczenie */
+	GG_EVENT_NONE = 0,		/**< Nie wydarzyło się nic wartego uwagi */
+	GG_EVENT_MSG,			/**< \brief Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. */
+	GG_EVENT_NOTIFY,		/**< \brief Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+	GG_EVENT_NOTIFY_DESCR,		/**< \brief Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+	GG_EVENT_STATUS,		/**< \brief Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+	GG_EVENT_ACK,			/**< Potwierdzenie doręczenia wiadomości */
+	GG_EVENT_PONG,			/**< \brief Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. */
+	GG_EVENT_CONN_FAILED,		/**< \brief Nie udało się połączyć */
+	GG_EVENT_CONN_SUCCESS,		/**< \brief Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. */
+	GG_EVENT_DISCONNECT,		/**< \brief Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. */
+
+	GG_EVENT_DCC_NEW,		/**< Nowe połączenie bezpośrednie (6.x) */
+	GG_EVENT_DCC_ERROR,		/**< Błąd połączenia bezpośredniego (6.x) */
+	GG_EVENT_DCC_DONE,		/**< Zakończono połączenie bezpośrednie (6.x) */
+	GG_EVENT_DCC_CLIENT_ACCEPT,	/**< Moment akceptacji klienta w połączeniu bezpośrednim (6.x) */
+	GG_EVENT_DCC_CALLBACK,		/**< Zwrotne połączenie bezpośrednie (6.x) */
+	GG_EVENT_DCC_NEED_FILE_INFO,	/**< Należy wypełnić \c file_info dla połączenia bezpośredniego (6.x) */
+	GG_EVENT_DCC_NEED_FILE_ACK,	/**< Czeka na potwierdzenie pliku w połączeniu bezpośrednim (6.x) */
+	GG_EVENT_DCC_NEED_VOICE_ACK,	/**< Czeka na potwierdzenie rozmowy w połączeniu bezpośrednim (6.x) */
+	GG_EVENT_DCC_VOICE_DATA, 	/**< Dane bezpośredniego połączenia głosowego (6.x) */
 
-	GG_EVENT_DCC_NEW,		/* nowe poczenie midzy klientami */
-	GG_EVENT_DCC_ERROR,		/* bd poczenia midzy klientami */
-	GG_EVENT_DCC_DONE,		/* zakoczono poczenie */
-	GG_EVENT_DCC_CLIENT_ACCEPT,	/* moment akceptacji klienta */
-	GG_EVENT_DCC_CALLBACK,		/* klient si poczy na danie */
-	GG_EVENT_DCC_NEED_FILE_INFO,	/* naley wypeni file_info */
-	GG_EVENT_DCC_NEED_FILE_ACK,	/* czeka na potwierdzenie pliku */
-	GG_EVENT_DCC_NEED_VOICE_ACK,	/* czeka na potwierdzenie rozmowy */
-	GG_EVENT_DCC_VOICE_DATA, 	/* ramka danych rozmowy gosowej */
+	GG_EVENT_PUBDIR50_SEARCH_REPLY,	/**< Odpowiedź katalogu publicznego */
+	GG_EVENT_PUBDIR50_READ,		/**< Odczytano własne dane z katalogu publicznego */
+	GG_EVENT_PUBDIR50_WRITE,	/**< Zmieniono własne dane w katalogu publicznym */
+
+	GG_EVENT_STATUS60,		/**< Zmiana statusu osoby z listy kontaktów */
+	GG_EVENT_NOTIFY60,		/**< Informacja o statusach osób z listy kontaktów */
+	GG_EVENT_USERLIST,		/**< Wynik importu lub eksportu listy kontaktów */
+	GG_EVENT_IMAGE_REQUEST,		/**< Żądanie przesłania obrazka z wiadommości */
+	GG_EVENT_IMAGE_REPLY,		/**< Przysłano obrazek z wiadomości */
+	GG_EVENT_DCC_ACK,		/**< Potwierdzenie transmisji w połączeniu bezpośrednim (6.x) */
 
-	GG_EVENT_PUBDIR50_SEARCH_REPLY,	/* odpowiedz wyszukiwania */
-	GG_EVENT_PUBDIR50_READ,		/* odczytano wasne dane z katalogu */
-	GG_EVENT_PUBDIR50_WRITE,	/* wpisano wasne dane do katalogu */
+	GG_EVENT_DCC7_NEW,		/**< Nowe połączenie bezpośrednie (7.x) */
+	GG_EVENT_DCC7_ACCEPT,		/**< Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor */
+	GG_EVENT_DCC7_REJECT,		/**< Odrzucono połączenie bezpośrednie (7.x) */
+	GG_EVENT_DCC7_CONNECTED,	/**< Zestawiono połączenie bezpośrednie (7.x), nowy deskryptor */
+	GG_EVENT_DCC7_ERROR,		/**< Błąd połączenia bezpośredniego (7.x) */
+	GG_EVENT_DCC7_DONE,		/**< Zakończono połączenie bezpośrednie (7.x) */
+	GG_EVENT_DCC7_PENDING,		/**< Trwa próba połączenia bezpośredniego (7.x), nowy deskryptor */
 
-	GG_EVENT_STATUS60,		/* kto zmieni stan w GG 6.0 */
-	GG_EVENT_NOTIFY60,		/* kto si pojawi w GG 6.0 */
-	GG_EVENT_USERLIST,		/* odpowied listy kontaktw w GG 6.0 */
-	GG_EVENT_IMAGE_REQUEST,		/* proba o wysanie obrazka GG 6.0 */
-	GG_EVENT_IMAGE_REPLY,		/* podesany obrazek GG 6.0 */
-	GG_EVENT_DCC_ACK		/* potwierdzenie transmisji */
+	GG_EVENT_XML_EVENT,		/**< Otrzymano komunikat systemowy (7.7) */
+	GG_EVENT_DISCONNECT_ACK,	/**< \brief Potwierdzenie zakończenia sesji. Informuje o tym, że zmiana stanu na niedostępny z opisem dotarła do serwera i można zakończyć połączenie TCP. */
 };
 
 #define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
 
-/*
- * enum gg_failure_t
- *
- * okrela powd nieudanego poczenia.
+/**
+ * Powód nieudanego połączenia.
  */
 enum gg_failure_t {
-	GG_FAILURE_RESOLVING = 1,	/* nie znaleziono serwera */
-	GG_FAILURE_CONNECTING,		/* nie mona si poczy */
-	GG_FAILURE_INVALID,		/* serwer zwrci nieprawidowe dane */
-	GG_FAILURE_READING,		/* zerwano poczenie podczas odczytu */
-	GG_FAILURE_WRITING,		/* zerwano poczenie podczas zapisu */
-	GG_FAILURE_PASSWORD,		/* nieprawidowe haso */
-	GG_FAILURE_404, 		/* XXX nieuywane */
-	GG_FAILURE_TLS,			/* bd negocjacji TLS */
-	GG_FAILURE_NEED_EMAIL 		/* serwer rozczy nas z prob o zmian emaila */
+	GG_FAILURE_RESOLVING = 1,	/**< Nie znaleziono serwera */
+	GG_FAILURE_CONNECTING,		/**< Błąd połączenia */
+	GG_FAILURE_INVALID,		/**< Serwer zwrócił nieprawidłowe dane */
+	GG_FAILURE_READING,		/**< Zerwano połączenie podczas odczytu */
+	GG_FAILURE_WRITING,		/**< Zerwano połączenie podczas zapisu */
+	GG_FAILURE_PASSWORD,		/**< Nieprawidłowe hasło */
+	GG_FAILURE_404, 		/**< Nieużywane */
+	GG_FAILURE_TLS,			/**< Błąd negocjacji szyfrowanego połączenia */
+	GG_FAILURE_NEED_EMAIL, 		/**< Serwer rozłączył nas z prośbą o zmianę adresu e-mail */
+	GG_FAILURE_INTRUDER,		/**< Zbyt wiele prób połączenia z nieprawidłowym hasłem */
+	GG_FAILURE_UNAVAILABLE		/**< Serwery są wyłączone */
 };
 
-/*
- * enum gg_error_t
+/**
+ * Kod błędu danej operacji.
  *
- * okrela rodzaj bdu wywoanego przez dan operacj. nie zawiera
- * przesadnie szczegowych informacji o powodzie bdu, by nie komplikowa
- * obsugi bdw. jeli wymagana jest wiksza dokadno, naley sprawdzi
- * zawarto zmiennej errno.
+ * Nie zawiera przesadnie szczegółowych informacji o powodach błędów, by nie
+ * komplikować ich obsługi. Jeśli wymagana jest większa dokładność, należy
+ * sprawdzić zawartość zmiennej systemowej \c errno.
  */
 enum gg_error_t {
-	GG_ERROR_RESOLVING = 1,		/* bd znajdowania hosta */
-	GG_ERROR_CONNECTING,		/* bd aczenia si */
-	GG_ERROR_READING,		/* bd odczytu */
-	GG_ERROR_WRITING,		/* bd wysyania */
+	GG_ERROR_RESOLVING = 1,		/**< Nie znaleziono hosta */
+	GG_ERROR_CONNECTING,		/**< Błąd połączenia */
+	GG_ERROR_READING,		/**< Błąd odczytu/odbierania */
+	GG_ERROR_WRITING,		/**< Błąd zapisu/wysyłania */
 
-	GG_ERROR_DCC_HANDSHAKE,		/* bd negocjacji */
-	GG_ERROR_DCC_FILE,		/* bd odczytu/zapisu pliku */
-	GG_ERROR_DCC_EOF,		/* plik si skoczy? */
-	GG_ERROR_DCC_NET,		/* bd wysyania/odbierania */
-	GG_ERROR_DCC_REFUSED 		/* poczenie odrzucone przez usera */
+	GG_ERROR_DCC_HANDSHAKE,		/**< Błąd negocjacji */
+	GG_ERROR_DCC_FILE,		/**< Błąd odczytu/zapisu pliku */
+	GG_ERROR_DCC_EOF,		/**< Przedwczesny koniec pliku */
+	GG_ERROR_DCC_NET,		/**< Błąd wysyłania/odbierania */
+	GG_ERROR_DCC_REFUSED, 		/**< Połączenie odrzucone */
+
+	GG_ERROR_DCC7_HANDSHAKE,	/**< Błąd negocjacji */
+	GG_ERROR_DCC7_FILE,		/**< Błąd odczytu/zapisu pliku */
+	GG_ERROR_DCC7_EOF,		/**< Przedwczesny koniec pliku */
+	GG_ERROR_DCC7_NET,		/**< Błąd wysyłania/odbierania */
+	GG_ERROR_DCC7_REFUSED 		/**< Połączenie odrzucone */
 };
 
-/*
- * struktury dotyczce wyszukiwania w GG 5.0. NIE NALEY SI DO NICH
- * ODWOYWA BEZPOREDNIO! do dostpu do nich su funkcje gg_pubdir50_*()
+/**
+ * Pole zapytania lub odpowiedzi katalogu publicznego.
  */
 struct gg_pubdir50_entry {
-	int num;
-	char *field;
-	char *value;
-};
+	int num;	/**< Numer wyniku */
+	char *field;	/**< Nazwa pola */
+	char *value;	/**< Wartość pola */
+} /* GG_DEPRECATED */;
 
+/**
+ * Zapytanie lub odpowiedź katalogu publicznego.
+ *
+ * Patrz \c gg_pubdir50_t.
+ */
 struct gg_pubdir50_s {
-	int count;
-	uin_t next;
-	int type;
-	uint32_t seq;
-	struct gg_pubdir50_entry *entries;
-	int entries_count;
-};
+	int count;	/**< Liczba wyników odpowiedzi */
+	uin_t next;	/**< Numer początkowy następnego zapytania */
+	int type;	/**< Rodzaj zapytania */
+	uint32_t seq;	/**< Numer sekwencyjny */
+	struct gg_pubdir50_entry *entries;	/**< Pola zapytania lub odpowiedzi */
+	int entries_count;	/**< Liczba pól */
+} /* GG_DEPRECATED */;
 
-/*
- * typedef gg_pubdir_50_t
+/**
+ * Zapytanie lub odpowiedź katalogu publicznego.
  *
- * typ opisujcy zapytanie lub wynik zapytania katalogu publicznego
- * z protokou GG 5.0. nie naley si odwoywa bezporednio do jego
- * pl -- su do tego funkcje gg_pubdir50_*()
+ * Do pól nie należy się odwoływać bezpośrednio -- wszystkie niezbędne
+ * informacje są dostępne za pomocą funkcji \c gg_pubdir50_*
  */
 typedef struct gg_pubdir50_s *gg_pubdir50_t;
 
-/*
- * struct gg_event
+/**
+ * Opis zdarzenia \c GG_EVENT_MSG.
+ */
+struct gg_event_msg {
+	uin_t sender;		/**< Numer nadawcy */
+	int msgclass;		/**< Klasa wiadomości */
+	time_t time;		/**< Czas nadania */
+	unsigned char *message;	/**< Treść wiadomości */
+
+	int recipients_count;	/**< Liczba odbiorców konferencji */
+	uin_t *recipients;	/**< Odbiorcy konferencji */
+
+	int formats_length;	/**< Długość informacji o formatowaniu tekstu */
+	void *formats;		/**< Informacje o formatowaniu tekstu */
+	uint32_t seq;		/**< Numer sekwencyjny wiadomości */
+
+	char *xhtml_message;	/**< Treść wiadomości w formacie XHTML (może być równe \c NULL, jeśli wiadomość nie zawiera treści XHTML) */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR.
+ */
+struct gg_event_notify_descr {
+	struct gg_notify_reply *notify;	/**< Informacje o liście kontaktów */
+	char *descr;		/**< Opis status */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS.
+ */
+struct gg_event_status {
+	uin_t uin;		/**< Numer Gadu-Gadu */
+	uint32_t status;	/**< Nowy status */
+	char *descr;		/**< Opis */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS60.
+ */
+struct gg_event_status60 {
+	uin_t uin;		/**< Numer Gadu-Gadu */
+	int status;		/**< Nowy status */
+	uint32_t remote_ip;	/**< Adres IP dla połączeń bezpośrednich */
+	uint16_t remote_port;	/**< Port dla połączeń bezpośrednich */
+	int version;		/**< Wersja protokołu */
+	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+	char *descr;		/**< Opis statusu */
+	time_t time;		/**< Czas powrotu */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60.
+ */
+struct gg_event_notify60 {
+	uin_t uin;		/**< Numer Gadu-Gadu */
+	int status;		/**< Nowy status */
+	uint32_t remote_ip;	/**< Adres IP dla połączeń bezpośrednich */
+	uint16_t remote_port;	/**< Port dla połączeń bezpośrednich */
+	int version;		/**< Wersja protokołu */
+	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+	char *descr;		/**< Opis statusu */
+	time_t time;		/**< Czas powrotu */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_ACK.
+ */
+struct gg_event_ack {
+	uin_t recipient;	/**< Numer odbiorcy */
+	int status;		/**< Status doręczenia */
+	int seq;		/**< Numer sekwencyjny wiadomości */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_USERLIST.
+ */
+struct gg_event_userlist {
+	char type;		/**< Rodzaj odpowiedzi */
+	char *reply;		/**< Treść odpowiedzi */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC_VOICE_DATA.
+ */
+struct gg_event_dcc_voice_data {
+	uint8_t *data;		/**< Dane dźwiękowe */
+	int length;		/**< Rozmiar danych dźwiękowych */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST.
+ */
+struct gg_event_image_request {
+	uin_t sender;		/**< Nadawca żądania */
+	uint32_t size;		/**< Rozmiar obrazka */
+	uint32_t crc32;		/**< Suma kontrolna CRC32 */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_IMAGE_REPLY.
+ */
+struct gg_event_image_reply {
+	uin_t sender;		/**< Nadawca obrazka */
+	uint32_t size;		/**< Rozmiar obrazka */
+	uint32_t crc32;		/**< Suma kontrolna CRC32 */
+	char *filename;		/**< Nazwa pliku */
+	char *image;		/**< Bufor z obrazkiem */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_XML_EVENT.
+ */
+struct gg_event_xml_event {
+	char *data;		/**< Bufor z komunikatem */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED.
+ */
+struct gg_event_dcc7_connected {
+	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
+	// XXX czy coś się przyda?
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_REJECT.
+ */
+struct gg_event_dcc7_reject {
+	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
+	int reason;		/**< powód odrzucenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_ACCEPT.
+ */
+struct gg_event_dcc7_accept {
+	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
+	int type;		/**< Sposób połączenia (P2P, przez serwer) */
+	uint32_t remote_ip;	/**< Adres zdalnego klienta */
+	uint16_t remote_port;	/**< Port zdalnego klienta */
+};
+
+/**
+ * Unia wszystkich zdarzeń zwracanych przez funkcje \c gg_watch_fd(), 
+ * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd().
  *
- * struktura opisujca rodzaj zdarzenia. wychodzi z gg_watch_fd() lub
- * z gg_dcc_watch_fd()
+ * \ingroup events
+ */
+union gg_event_union {
+	enum gg_failure_t failure;	/**< Błąd połączenia (\c GG_EVENT_CONN_FAILED) */
+	struct gg_notify_reply *notify;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY) */
+	struct gg_event_notify_descr notify_descr;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY_DESCR) */
+	struct gg_event_status status;	/**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS) */
+	struct gg_event_status60 status60;	/**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS60) */
+	struct gg_event_notify60 *notify60;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY60) */
+	struct gg_event_msg msg;	/**< Otrzymano wiadomość (\c GG_EVENT_MSG) */
+	struct gg_event_ack ack;	/**< Potwierdzenie wiadomości (\c GG_EVENT_ACK) */
+	struct gg_event_image_request image_request;	/**< Żądanie wysłania obrazka (\c GG_EVENT_IMAGE_REQUEST) */
+	struct gg_event_image_reply image_reply;	/**< Odpowiedź z obrazkiem (\c GG_EVENT_IMAGE_REPLY) */
+	struct gg_event_userlist userlist;	/**< Odpowiedź listy kontaktów (\c GG_EVENT_USERLIST) */
+	gg_pubdir50_t pubdir50;	/**< Odpowiedź katalogu publicznego (\c GG_EVENT_PUBDIR50_*) */
+	struct gg_event_xml_event xml_event;	/**< Zdarzenie systemowe (\c GG_EVENT_XML_EVENT) */
+	struct gg_dcc *dcc_new;	/**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC_NEW) */
+	enum gg_error_t dcc_error;	/**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC_ERROR) */
+	struct gg_event_dcc_voice_data dcc_voice_data;	/**< Dane połączenia głosowego (\c GG_EVENT_DCC_VOICE_DATA) */
+	struct gg_dcc7 *dcc7_new;	/**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC7_NEW) */
+	enum gg_error_t dcc7_error;	/**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC7_ERROR) */
+	struct gg_event_dcc7_connected dcc7_connected;	/**< Informacja o zestawieniu połączenia bezpośredniego (\c GG_EVENT_DCC7_CONNECTED) */
+	struct gg_event_dcc7_reject dcc7_reject;	/**< Odrzucono połączenia bezpośredniego (\c GG_EVENT_DCC7_REJECT) */
+	struct gg_event_dcc7_accept dcc7_accept;	/**< Zaakceptowano połączenie bezpośrednie (\c GG_EVENT_DCC7_ACCEPT) */
+};
+
+/**
+ * Opis zdarzenia.
+ *
+ * Zwracany przez funkcje \c gg_watch_fd(), \c gg_dcc_watch_fd()
+ * i \c gg_dcc7_watch_fd(). Po przeanalizowaniu należy zwolnić
+ * za pomocą \c gg_event_free().
+ *
+ * \ingroup events
  */
 struct gg_event {
-	int type;	/* rodzaj zdarzenia -- gg_event_t */
-	union {		/* @event */
-		struct gg_notify_reply *notify;	/* informacje o licie kontaktw -- GG_EVENT_NOTIFY */
-
-		enum gg_failure_t failure;	/* bd poczenia -- GG_EVENT_FAILURE */
-
-		struct gg_dcc *dcc_new;		/* nowe poczenie bezporednie -- GG_EVENT_DCC_NEW */
-
-		int dcc_error;			/* bd poczenia bezporedniego -- GG_EVENT_DCC_ERROR */
-
-		gg_pubdir50_t pubdir50;		/* wynik operacji zwizanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */
-
-		struct {			/* @msg odebrano wiadomo -- GG_EVENT_MSG */
-			uin_t sender;		/* numer nadawcy */
-			int msgclass;		/* klasa wiadomoci */
-			time_t time;		/* czas nadania */
-			unsigned char *message;	/* tre wiadomoci */
-
-			int recipients_count;	/* ilo odbiorcw konferencji */
-			uin_t *recipients;	/* odbiorcy konferencji */
-
-			int formats_length;	/* dugo informacji o formatowaniu tekstu */
-			void *formats;		/* informacje o formatowaniu tekstu */
-		} msg;
-
-		struct {			/* @notify_descr informacje o licie kontaktw z opisami stanu -- GG_EVENT_NOTIFY_DESCR */
-			struct gg_notify_reply *notify;	/* informacje o licie kontaktw */
-			char *descr;		/* opis stanu */
-		} notify_descr;
-
-		struct {			/* @status zmiana stanu -- GG_EVENT_STATUS */
-			uin_t uin;		/* numer */
-			uint32_t status;	/* nowy stan */
-			char *descr;		/* opis stanu */
-		} status;
-
-		struct {			/* @status60 zmiana stanu -- GG_EVENT_STATUS60 */
-			uin_t uin;		/* numer */
-			int status;	/* nowy stan */
-			uint32_t remote_ip;	/* adres ip */
-			uint16_t remote_port;	/* port */
-			int version;	/* wersja klienta */
-			int image_size;	/* maksymalny rozmiar grafiki w KiB */
-			char *descr;		/* opis stanu */
-			time_t time;		/* czas powrotu */
-		} status60;
-
-		struct {			/* @notify60 informacja o licie kontaktw -- GG_EVENT_NOTIFY60 */
-			uin_t uin;		/* numer */
-			int status;	/* stan */
-			uint32_t remote_ip;	/* adres ip */
-			uint16_t remote_port;	/* port */
-			int version;	/* wersja klienta */
-			int image_size;	/* maksymalny rozmiar grafiki w KiB */
-			char *descr;		/* opis stanu */
-			time_t time;		/* czas powrotu */
-		} *notify60;
-
-		struct {			/* @ack potwierdzenie wiadomoci -- GG_EVENT_ACK */
-			uin_t recipient;	/* numer odbiorcy */
-			int status;		/* stan dorczenia wiadomoci */
-			int seq;		/* numer sekwencyjny wiadomoci */
-		} ack;
-
-		struct {			/* @dcc_voice_data otrzymano dane dwikowe -- GG_EVENT_DCC_VOICE_DATA */
-			uint8_t *data;		/* dane dwikowe */
-			int length;		/* ilo danych dwikowych */
-		} dcc_voice_data;
-
-		struct {			/* @userlist odpowied listy kontaktw serwera */
-			char type;		/* rodzaj odpowiedzi */
-			char *reply;		/* tre odpowiedzi */
-		} userlist;
-
-		struct {			/* @image_request proba o obrazek */
-			uin_t sender;		/* nadawca proby */
-			uint32_t size;		/* rozmiar obrazka */
-			uint32_t crc32;		/* suma kontrolna */
-		} image_request;
-
-		struct {			/* @image_reply odpowied z obrazkiem */
-			uin_t sender;		/* nadawca odpowiedzi */
-			uint32_t size;		/* rozmiar obrazka */
-			uint32_t crc32;		/* suma kontrolna */
-			char *filename;		/* nazwa pliku */
-			char *image;		/* bufor z obrazkiem */
-		} image_reply;
-	} event;
+	int type;			/**< Rodzaj zdarzenia */
+	union gg_event_union event;	/**< Informacja o zdarzeniu */
 };
 
 struct gg_event *gg_watch_fd(struct gg_session *sess);
 void gg_event_free(struct gg_event *e);
-#define gg_free_event gg_event_free
 
-/*
- * funkcje obsugi listy kontaktw.
- */
 int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count);
 int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
 int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type);
@@ -600,76 +936,11 @@
 int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type);
 int gg_remove_notify(struct gg_session *sess, uin_t uin);
 
-/*
- * funkcje obsugi http.
- */
 struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header);
 int gg_http_watch_fd(struct gg_http *h);
 void gg_http_stop(struct gg_http *h);
 void gg_http_free(struct gg_http *h);
-void gg_http_free_fields(struct gg_http *h);
-#define gg_free_http gg_http_free
 
-/*
- * struktury opisujca kryteria wyszukiwania dla gg_search(). nieaktualne,
- * zastpione przez gg_pubdir50_t. pozostawiono je dla zachowania ABI.
- */
-struct gg_search_request {
-	int active;
-	unsigned int start;
-	char *nickname;
-	char *first_name;
-	char *last_name;
-	char *city;
-	int gender;
-	int min_birth;
-	int max_birth;
-	char *email;
-	char *phone;
-	uin_t uin;
-};
-
-struct gg_search {
-	int count;
-	struct gg_search_result *results;
-};
-
-struct gg_search_result {
-	uin_t uin;
-	char *first_name;
-	char *last_name;
-	char *nickname;
-	int born;
-	int gender;
-	char *city;
-	int active;
-};
-
-#define GG_GENDER_NONE 0
-#define GG_GENDER_FEMALE 1
-#define GG_GENDER_MALE 2
-
-/*
- * funkcje wyszukiwania.
- */
-struct gg_http *gg_search(const struct gg_search_request *r, int async);
-int gg_search_watch_fd(struct gg_http *f);
-void gg_free_search(struct gg_http *f);
-#define gg_search_free gg_free_search
-
-const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start);
-const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start);
-const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start);
-const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start);
-void gg_search_request_free(struct gg_search_request *r);
-
-/*
- * funkcje obsugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje
- * zachowuj pewien poziom abstrakcji, eby unikn zmian ABI przy zmianach
- * w protokole.
- *
- * NIE NALEY SI ODWOYWA DO PL gg_pubdir50_t BEZPOREDNIO!
- */
 uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req);
 gg_pubdir50_t gg_pubdir50_new(int type);
 int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value);
@@ -681,6 +952,8 @@
 uint32_t gg_pubdir50_seq(gg_pubdir50_t res);
 void gg_pubdir50_free(gg_pubdir50_t res);
 
+#ifndef DOXYGEN
+
 #define GG_PUBDIR50_UIN "FmNumber"
 #define GG_PUBDIR50_STATUS "FmStatus"
 #define GG_PUBDIR50_FIRSTNAME "firstname"
@@ -699,110 +972,114 @@
 #define GG_PUBDIR50_FAMILYNAME "familyname"
 #define GG_PUBDIR50_FAMILYCITY "familycity"
 
-int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length);
+#else
 
-/*
- * struct gg_pubdir
+/** 
+ * \ingroup pubdir50
  *
- * operacje na katalogu publicznym.
+ * Rodzaj pola zapytania.
  */
-struct gg_pubdir {
-	int success;		/* czy si udao */
-	uin_t uin;		/* otrzymany numerek. 0 jeli bd */
+enum {
+	GG_PUBDIR50_UIN,	/**< Numer Gadu-Gadu */
+	GG_PUBDIR50_STATUS,	/**< Status (tylko wynik wyszukiwania) */
+	GG_PUBDIR50_FIRSTNAME,	/**< Imię */
+	GG_PUBDIR50_LASTNAME,	/**< Nazwisko */
+	GG_PUBDIR50_NICKNAME,	/**< Pseudonim */
+	GG_PUBDIR50_BIRTHYEAR,	/**< Rok urodzenia lub przedział lat oddzielony spacją */
+	GG_PUBDIR50_CITY,	/**< Miejscowość */
+	GG_PUBDIR50_GENDER,	/**< Płeć */
+	GG_PUBDIR50_ACTIVE,	/**< Osoba dostępna (tylko wyszukiwanie) */
+	GG_PUBDIR50_START,	/**< Numer początkowy wyszukiwania (tylko wyszukiwanie) */
+	GG_PUBDIR50_FAMILYNAME,	/**< Nazwisko rodowe (tylko wysyłanie informacji o sobie) */
+	GG_PUBDIR50_FAMILYCITY,	/**< Miejscowość pochodzenia (tylko wysyłanie informacji o sobie) */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolną płeć.
+ */
+enum {
+	GG_PUBDIR50_GENDER_FEMALE,	/**< Kobieta */
+	GG_PUBDIR50_GENDER_MALE,	/**< Mężczyzna */
 };
 
-/* oglne funkcje, nie powinny by uywane */
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_GENDER przy wysyłaniu informacji o sobie.
+ */
+enum {
+	GG_PUBDIR50_GENDER_SET_FEMALE,	/**< Kobieta */
+	GG_PUBDIR50_GENDER_SET_MALE,	/**< Mężczyzna */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_ACTIVE.
+ */
+enum {
+	GG_PUBDIR50_ACTIVE_TRUE,	/**< Wyszukaj tylko osoby dostępne */
+};
+
+#endif	/* DOXYGEN */
+
+/**
+ * Wynik operacji na katalogu publicznym.
+ *
+ * \ingroup http
+ */
+struct gg_pubdir {
+	int success;		/**< Flaga powodzenia operacji */
+	uin_t uin;		/**< Otrzymany numer lub 0 w przypadku błędu */
+};
+
 int gg_pubdir_watch_fd(struct gg_http *f);
 void gg_pubdir_free(struct gg_http *f);
-#define gg_free_pubdir gg_pubdir_free
 
+/**
+ * Token autoryzacji niektórych operacji HTTP.
+ * 
+ * \ingroup token
+ */
 struct gg_token {
-	int width;		/* szeroko obrazka */
-	int height;		/* wysoko obrazka */
-	int length;		/* ilo znakw w tokenie */
-	char *tokenid;		/* id tokenu */
+	int width;		/**< Szerokość obrazka */
+	int height;		/**< Wysokość obrazka */
+	int length;		/**< Liczba znaków w tokenie */
+	char *tokenid;		/**< Identyfikator tokenu */
 };
 
-/* funkcje dotyczce tokenw */
 struct gg_http *gg_token(int async);
 int gg_token_watch_fd(struct gg_http *h);
 void gg_token_free(struct gg_http *h);
 
-/* rejestracja nowego numerka */
-struct gg_http *gg_register(const char *email, const char *password, int async);
-struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async);
 struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
 #define gg_register_watch_fd gg_pubdir_watch_fd
 #define gg_register_free gg_pubdir_free
-#define gg_free_register gg_pubdir_free
+#endif
 
-struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async);
-struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async);
 struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
 #define gg_unregister_watch_fd gg_pubdir_watch_fd
 #define gg_unregister_free gg_pubdir_free
+#endif
 
-/* przypomnienie hasa e-mailem */
-struct gg_http *gg_remind_passwd(uin_t uin, int async);
-struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async);
 struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
 #define gg_remind_passwd_watch_fd gg_pubdir_watch_fd
 #define gg_remind_passwd_free gg_pubdir_free
-#define gg_free_remind_passwd gg_pubdir_free
-
-/* zmiana hasa */
-struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async);
-struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async);
-struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async);
-struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
-#define gg_change_passwd_free gg_pubdir_free
-#define gg_free_change_passwd gg_pubdir_free
-
-/*
- * struct gg_change_info_request
- *
- * opis dania zmiany informacji w katalogu publicznym.
- */
-struct gg_change_info_request {
-	char *first_name;	/* imi */
-	char *last_name;	/* nazwisko */
-	char *nickname;		/* pseudonim */
-	char *email;		/* email */
-	int born;		/* rok urodzenia */
-	int gender;		/* pe */
-	char *city;		/* miasto */
-};
-
-struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city);
-void gg_change_info_request_free(struct gg_change_info_request *r);
+#endif
 
-struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async);
-#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
-#define gg_change_pubdir_free gg_pubdir_free
-#define gg_free_change_pubdir gg_pubdir_free
-
-/*
- * funkcje dotyczce listy kontaktw na serwerze.
- */
-struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async);
-int gg_userlist_get_watch_fd(struct gg_http *f);
-void gg_userlist_get_free(struct gg_http *f);
+struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
+#define gg_change_passwd_watch_fd gg_pubdir_watch_fd
+#define gg_change_passwd_free gg_pubdir_free
+#endif
 
-struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async);
-int gg_userlist_put_watch_fd(struct gg_http *f);
-void gg_userlist_put_free(struct gg_http *f);
-
-struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async);
-int gg_userlist_remove_watch_fd(struct gg_http *f);
-void gg_userlist_remove_free(struct gg_http *f);
-
-
-
-/*
- * funkcje dotyczce komunikacji midzy klientami.
- */
-extern int gg_dcc_port;			/* port, na ktrym nasuchuje klient */
-extern unsigned long gg_dcc_ip;		/* adres, na ktrym nasuchuje klient */
+extern int gg_dcc_port;
+extern unsigned long gg_dcc_ip;
 
 int gg_dcc_request(struct gg_session *sess, uin_t uin);
 
@@ -814,119 +1091,248 @@
 int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename);
 int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length);
 
-#define GG_DCC_VOICE_FRAME_LENGTH 195
-#define GG_DCC_VOICE_FRAME_LENGTH_505 326
+#define GG_DCC_VOICE_FRAME_LENGTH 195		/**< Rozmiar pakietu głosowego przed wersją Gadu-Gadu 5.0.5 */
+#define GG_DCC_VOICE_FRAME_LENGTH_505 326	/**< Rozmiar pakietu głosowego od wersji Gadu-Gadu 5.0.5 */
 
 struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port);
-#define gg_dcc_socket_free gg_free_dcc
+#ifndef DOXYGEN
+#define gg_dcc_socket_free gg_dcc_free
 #define gg_dcc_socket_watch_fd gg_dcc_watch_fd
+#endif
 
 struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d);
 
 void gg_dcc_free(struct gg_dcc *c);
-#define gg_free_dcc gg_dcc_free
-
-/*
- * jeli chcemy sobie podebugowa, wystarczy ustawi `gg_debug_level'.
- * niestety w miar przybywania wpisw `gg_debug(...)' nie chciao mi
- * si ustawia odpowiednich leveli, wic wikszo sza do _MISC.
- */
-extern int gg_debug_level;	/* poziom debugowania. mapa bitowa staych GG_DEBUG_* */
 
-/*
- * mona poda wskanik do funkcji obsugujcej wywoania gg_debug().
- * nieoficjalne, nieudokumentowane, moe si zmieni. jeli kto jest
- * zainteresowany, niech da zna na ekg-devel.
- */
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d);
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash);
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash);
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset);
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason);
+void gg_dcc7_free(struct gg_dcc7 *d);
+
+extern int gg_debug_level;
+
 extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
+extern void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap);
 
-/*
- * mona poda plik, do ktrego bd zapisywane teksty z gg_debug().
- */
 extern FILE *gg_debug_file;
 
-#define GG_DEBUG_NET 1
-#define GG_DEBUG_TRAFFIC 2
-#define GG_DEBUG_DUMP 4
-#define GG_DEBUG_FUNCTION 8
-#define GG_DEBUG_MISC 16
+/**
+ * \ingroup debug
+ * @{
+ */
+#define GG_DEBUG_NET 1		/**< Rejestracja zdarzeń związanych z siecią */
+#define GG_DEBUG_TRAFFIC 2	/**< Rejestracja ruchu sieciowego */
+#define GG_DEBUG_DUMP 4		/**< Rejestracja zawartości pakietów */
+#define GG_DEBUG_FUNCTION 8	/**< Rejestracja wywołań funkcji */
+#define GG_DEBUG_MISC 16	/**< Rejestracja różnych informacji */
+/** @} */
 
 #ifdef GG_DEBUG_DISABLE
 #define gg_debug(x, y...) do { } while(0)
+#define gg_debug_session(z, x, y...) do { } while(0)
 #else
 void gg_debug(int level, const char *format, ...);
+void gg_debug_session(struct gg_session *sess, int level, const char *format, ...);
 #endif
 
 const char *gg_libgadu_version(void);
 
-/*
- * konfiguracja http proxy.
- */
-extern int gg_proxy_enabled;		/* wcza obsug proxy */
-extern char *gg_proxy_host;		/* okrela adres serwera proxy */
-extern int gg_proxy_port;		/* okrela port serwera proxy */
-extern char *gg_proxy_username;		/* okrela nazw uytkownika przy autoryzacji serwera proxy */
-extern char *gg_proxy_password;		/* okrela haso uytkownika przy autoryzacji serwera proxy */
-extern int gg_proxy_http_only;		/* wcza obsug proxy wycznie dla usug HTTP */
+extern int gg_proxy_enabled;
+extern char *gg_proxy_host;
+extern int gg_proxy_port;
+extern char *gg_proxy_username;
+extern char *gg_proxy_password;
+extern int gg_proxy_http_only;
+
+extern unsigned long gg_local_ip;
+
+#define GG_LOGIN_HASH_GG32 0x01	/**< Algorytm Gadu-Gadu */
+#define GG_LOGIN_HASH_SHA1 0x02	/**< Algorytm SHA1 */
 
+#ifndef DOXYGEN
 
-/*
- * adres, z ktrego lemy pakiety (np czymy si z serwerem)
- * uywany przy gg_connect()
+#define GG_PUBDIR50_WRITE 0x01
+#define GG_PUBDIR50_READ 0x02
+#define GG_PUBDIR50_SEARCH 0x03
+#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
+#define GG_PUBDIR50_SEARCH_REPLY 0x05
+
+#else
+
+/**
+ * \ingroup pubdir50
+ * 
+ * Rodzaj zapytania lub odpowiedzi katalogu publicznego.
  */
-extern unsigned long gg_local_ip;
-/*
- * -------------------------------------------------------------------------
- * poniej znajduj si wewntrzne sprawy biblioteki. zwyky klient nie
- * powinien ich w ogle rusza, bo i nie ma po co. wszystko mona zaatwi
- * procedurami wyszego poziomu, ktrych definicje znajduj si na pocztku
- * tego pliku.
- * -------------------------------------------------------------------------
- */
+enum {
+	GG_PUBDIR50_WRITE,	/**< Wysłanie do serwera informacji o sobie */
+	GG_PUBDIR50_READ,	/**< Pobranie z serwera informacji o sobie */
+	GG_PUBDIR50_SEARCH,	/**< Wyszukiwanie w katalogu publicznym */
+	GG_PUBDIR50_SEARCH_REPLY,	/**< Wynik wyszukiwania w katalogu publicznym */
+};
+
+#endif	/* DOXYGEN */
+
+/** \cond obsolete */
+
+#define gg_free_event gg_event_free
+#define gg_free_http gg_http_free
+#define gg_free_pubdir gg_pubdir_free
+#define gg_free_register gg_pubdir_free
+#define gg_free_remind_passwd gg_pubdir_free
+#define gg_free_dcc gg_dcc_free
+#define gg_free_change_passwd gg_pubdir_free
+
+struct gg_search_request {
+	int active;
+	unsigned int start;
+	char *nickname;
+	char *first_name;
+	char *last_name;
+	char *city;
+	int gender;
+	int min_birth;
+	int max_birth;
+	char *email;
+	char *phone;
+	uin_t uin;
+} /* GG_DEPRECATED */;
+
+struct gg_search {
+	int count;
+	struct gg_search_result *results;
+} GG_DEPRECATED;
+
+struct gg_search_result {
+	uin_t uin;
+	char *first_name;
+	char *last_name;
+	char *nickname;
+	int born;
+	int gender;
+	char *city;
+	int active;
+} GG_DEPRECATED;
+
+#define GG_GENDER_NONE 0
+#define GG_GENDER_FEMALE 1
+#define GG_GENDER_MALE 2
+
+struct gg_http *gg_search(const struct gg_search_request *r, int async) GG_DEPRECATED;
+int gg_search_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_free_search(struct gg_http *f) GG_DEPRECATED;
+#define gg_search_free gg_free_search
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
-#elif defined _WIN32
-int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname);
+const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start) GG_DEPRECATED;
+void gg_search_request_free(struct gg_search_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_register(const char *email, const char *password, int async) GG_DEPRECATED;
+struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async) GG_DEPRECATED;
+struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_remind_passwd(uin_t uin, int async) GG_DEPRECATED;
+struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) GG_DEPRECATED;
+
+struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_change_info_request {
+	char *first_name;
+	char *last_name;
+	char *nickname;
+	char *email;
+	int born;
+	int gender;
+	char *city;
+} /* GG_DEPRECATED */;
+
+struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city) GG_DEPRECATED;
+void gg_change_info_request_free(struct gg_change_info_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) GG_DEPRECATED;
+#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
+#define gg_change_pubdir_free gg_pubdir_free
+#define gg_free_change_pubdir gg_pubdir_free
+
+struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_get_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_get_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async) GG_DEPRECATED;
+int gg_userlist_put_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_put_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_remove_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_remove_free(struct gg_http *f) GG_DEPRECATED;
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) GG_DEPRECATED;
+
+/** \endcond */
+
+int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED;
+
+#ifdef __GNUC__
+char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))) GG_DEPRECATED;
+#else
+char *gg_saprintf(const char *format, ...) GG_DEPRECATED;
 #endif
 
-#ifdef _WIN32
-int gg_thread_socket(int thread_id, int socket);
-#endif
-
-int gg_resolve(int *fd, int *pid, const char *hostname);
-
-#if defined __GNUC__ && !defined _WIN32
-char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
-#else
-char *gg_saprintf(const char *format, ...);
-#endif
-
-char *gg_vsaprintf(const char *format, va_list ap);
+char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED;
 
 #define gg_alloc_sprintf gg_saprintf
 
-char *gg_get_line(char **ptr);
+char *gg_get_line(char **ptr) GG_DEPRECATED;
 
-int gg_connect(void *addr, int port, int async);
-struct in_addr *gg_gethostbyname(const char *hostname);
-char *gg_read_line(int sock, char *buf, int length);
-void gg_chomp(char *line);
-char *gg_urlencode(const char *str);
-int gg_http_hash(const char *format, ...);
-int gg_read(struct gg_session *sess, char *buf, int length);
-int gg_write(struct gg_session *sess, const char *buf, int length);
-void *gg_recv_packet(struct gg_session *sess);
-int gg_send_packet(struct gg_session *sess, int type, ...);
-unsigned int gg_login_hash(const unsigned char *password, unsigned int seed);
-uint32_t gg_fix32(uint32_t x);
-uint16_t gg_fix16(uint16_t x);
+int gg_connect(void *addr, int port, int async) GG_DEPRECATED;
+struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED;
+char *gg_read_line(int sock, char *buf, int length) GG_DEPRECATED;
+void gg_chomp(char *line) GG_DEPRECATED;
+char *gg_urlencode(const char *str) GG_DEPRECATED;
+int gg_http_hash(const char *format, ...) GG_DEPRECATED;
+void gg_http_free_fields(struct gg_http *h) GG_DEPRECATED;
+int gg_read(struct gg_session *sess, char *buf, int length) GG_DEPRECATED;
+int gg_write(struct gg_session *sess, const char *buf, int length) GG_DEPRECATED;
+void *gg_recv_packet(struct gg_session *sess) GG_DEPRECATED;
+int gg_send_packet(struct gg_session *sess, int type, ...) GG_DEPRECATED;
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) GG_DEPRECATED;
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) GG_DEPRECATED;
+uint32_t gg_fix32(uint32_t x) GG_DEPRECATED;
+uint16_t gg_fix16(uint16_t x) GG_DEPRECATED;
 #define fix16 gg_fix16
 #define fix32 gg_fix32
-char *gg_proxy_auth(void);
-char *gg_base64_encode(const char *buf);
-char *gg_base64_decode(const char *buf);
-int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq);
+char *gg_proxy_auth(void) GG_DEPRECATED;
+char *gg_base64_encode(const char *buf) GG_DEPRECATED;
+char *gg_base64_decode(const char *buf) GG_DEPRECATED;
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) GG_DEPRECATED;
+
+/**
+ * Kolejka odbieranych obrazków.
+ */
+struct gg_image_queue {
+	uin_t sender;			/**< Nadawca obrazka */
+	uint32_t size;			/**< Rozmiar obrazka */
+	uint32_t crc32;			/**< Suma kontrolna CRC32 */
+	char *filename;			/**< Nazwa pliku */
+	char *image;			/**< Bufor z odebranymi danymi */
+	uint32_t done;			/**< Rozmiar odebranych danych */
+
+	struct gg_image_queue *next;	/**< Kolejny element listy */
+} GG_DEPRECATED;
+
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
 
 #define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
 #define GG_APPMSG_PORT 80
@@ -941,63 +1347,111 @@
 #define GG_HTTPS_PORT 443
 #define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)"
 
-#define GG_DEFAULT_CLIENT_VERSION "6, 1, 0, 158"
-#define GG_DEFAULT_PROTOCOL_VERSION 0x24
+#define GG_DEFAULT_CLIENT_VERSION "8.0.0.7669"
+#define GG_DEFAULT_PROTOCOL_VERSION 0x2e
 #define GG_DEFAULT_TIMEOUT 30
 #define GG_HAS_AUDIO_MASK 0x40000000
+#define GG_HAS_AUDIO7_MASK 0x20000000
 #define GG_ERA_OMNIX_MASK 0x04000000
-#define GG_LIBGADU_VERSION "1.5.20050718"
+#undef GG_LIBGADU_VERSION
+
+#ifndef DOXYGEN
+
+#define GG_FEATURE_MSG77		0x01
+#define GG_FEATURE_STATUS77		0x02
+#define GG_FEATURE_DND_FFC		0x10
+#define GG_FEATURE_IMAGE_DESCR		0x20
+
+/* Poniższe makra zostały zachowane dla zgodności API */
+#define GG_FEATURE_MSG80		0
+#define GG_FEATURE_STATUS80		0
+#define GG_FEATURE_STATUS80BETA		0
+#define GG_FEATURE_ALL			(GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR)
+
+#else
+
+/** 
+ * \ingroup login
+ *
+ * Flagi opcji protokołu.
+ */
+enum {
+	GG_FEATURE_MSG77,	/**< Klient życzy sobie otrzymywać wiadomości zgodnie z protokołem 7.7 */
+	GG_FEATURE_STATUS77,	/**< Klient życzy sobie otrzymywać zmiany stanu zgodnie z protokołem 7.7 */
+	GG_FEATURE_DND_FFC,	/**< Klient obsługuje statusy "nie przeszkadzać" i "poGGadaj ze mną" */
+	GG_FEATURE_IMAGE_DESCR,	/**< Klient obsługuje opisy graficzne oraz flagę \c GG_STATUS80_DESCR_MASK */
+};
+
+
+#endif
 
 #define GG_DEFAULT_DCC_PORT 1550
 
 struct gg_header {
 	uint32_t type;			/* typ pakietu */
-	uint32_t length;		/* dugo reszty pakietu */
+	uint32_t length;		/* długość reszty pakietu */
 } GG_PACKED;
 
 #define GG_WELCOME 0x0001
 #define GG_NEED_EMAIL 0x0014
 
 struct gg_welcome {
-	uint32_t key;			/* klucz szyfrowania hasa */
+	uint32_t key;			/* klucz szyfrowania hasła */
 } GG_PACKED;
 
 #define GG_LOGIN 0x000c
 
 struct gg_login {
-	uint32_t uin;			/* mj numerek */
-	uint32_t hash;			/* hash hasa */
-	uint32_t status;		/* status na dzie dobry */
+	uint32_t uin;			/* mój numerek */
+	uint32_t hash;			/* hash hasła */
+	uint32_t status;		/* status na dzień dobry */
 	uint32_t version;		/* moja wersja klienta */
-	uint32_t local_ip;		/* mj adres ip */
-	uint16_t local_port;		/* port, na ktrym sucham */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
 } GG_PACKED;
 
 #define GG_LOGIN_EXT 0x0013
 
 struct gg_login_ext {
-	uint32_t uin;			/* mj numerek */
-	uint32_t hash;			/* hash hasa */
-	uint32_t status;		/* status na dzie dobry */
+	uint32_t uin;			/* mój numerek */
+	uint32_t hash;			/* hash hasła */
+	uint32_t status;		/* status na dzień dobry */
 	uint32_t version;		/* moja wersja klienta */
-	uint32_t local_ip;		/* mj adres ip */
-	uint16_t local_port;		/* port, na ktrym sucham */
-	uint32_t external_ip;		/* zewntrzny adres ip */
-	uint16_t external_port;		/* zewntrzny port */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip */
+	uint16_t external_port;		/* zewnętrzny port */
 } GG_PACKED;
 
 #define GG_LOGIN60 0x0015
 
 struct gg_login60 {
-	uint32_t uin;			/* mj numerek */
-	uint32_t hash;			/* hash hasa */
-	uint32_t status;		/* status na dzie dobry */
+	uint32_t uin;			/* mój numerek */
+	uint32_t hash;			/* hash hasła */
+	uint32_t status;		/* status na dzień dobry */
 	uint32_t version;		/* moja wersja klienta */
 	uint8_t dunno1;			/* 0x00 */
-	uint32_t local_ip;		/* mj adres ip */
-	uint16_t local_port;		/* port, na ktrym sucham */
-	uint32_t external_ip;		/* zewntrzny adres ip */
-	uint16_t external_port;		/* zewntrzny port */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip */
+	uint16_t external_port;		/* zewnętrzny port */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno2;			/* 0xbe */
+} GG_PACKED;
+
+#define GG_LOGIN70 0x0019
+
+struct gg_login70 {
+	uint32_t uin;			/* mój numerek */
+	uint8_t hash_type;		/* rodzaj hashowania hasła */
+	uint8_t hash[64];		/* hash hasła dopełniony zerami */
+	uint32_t status;		/* status na dzień dobry */
+	uint32_t version;		/* moja wersja klienta */
+	uint8_t dunno1;			/* 0x00 */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip (???) */
+	uint16_t external_port;		/* zewnętrzny port (???) */
 	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 	uint8_t dunno2;			/* 0xbe */
 } GG_PACKED;
@@ -1008,70 +1462,135 @@
 
 #define GG_PUBDIR50_REQUEST 0x0014
 
-#define GG_PUBDIR50_WRITE 0x01
-#define GG_PUBDIR50_READ 0x02
-#define GG_PUBDIR50_SEARCH 0x03
-#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
-#define GG_PUBDIR50_SEARCH_REPLY 0x05
-
 struct gg_pubdir50_request {
 	uint8_t type;			/* GG_PUBDIR50_* */
-	uint32_t seq;			/* czas wysania zapytania */
+	uint32_t seq;			/* czas wysłania zapytania */
 } GG_PACKED;
 
 #define GG_PUBDIR50_REPLY 0x000e
 
 struct gg_pubdir50_reply {
 	uint8_t type;			/* GG_PUBDIR50_* */
-	uint32_t seq;			/* czas wysania zapytania */
+	uint32_t seq;			/* czas wysłania zapytania */
 } GG_PACKED;
 
 #define GG_NEW_STATUS 0x0002
 
-#define GG_STATUS_NOT_AVAIL 0x0001		/* niedostpny */
-#define GG_STATUS_NOT_AVAIL_DESCR 0x0015	/* niedostpny z opisem (4.8) */
-#define GG_STATUS_AVAIL 0x0002			/* dostpny */
-#define GG_STATUS_AVAIL_DESCR 0x0004		/* dostpny z opisem (4.9) */
-#define GG_STATUS_BUSY 0x0003			/* zajty */
-#define GG_STATUS_BUSY_DESCR 0x0005		/* zajty z opisem (4.8) */
-#define GG_STATUS_INVISIBLE 0x0014		/* niewidoczny (4.6) */
-#define GG_STATUS_INVISIBLE_DESCR 0x0016	/* niewidoczny z opisem (4.9) */
-#define GG_STATUS_BLOCKED 0x0006		/* zablokowany */
+#ifndef DOXYGEN
+
+#define GG_STATUS_NOT_AVAIL 0x0001
+#define GG_STATUS_NOT_AVAIL_DESCR 0x0015
+#define GG_STATUS_FFC 0x0017
+#define GG_STATUS_FFC_DESCR 0x0018
+#define GG_STATUS_AVAIL 0x0002
+#define GG_STATUS_AVAIL_DESCR 0x0004
+#define GG_STATUS_BUSY 0x0003
+#define GG_STATUS_BUSY_DESCR 0x0005
+#define GG_STATUS_DND 0x0021
+#define GG_STATUS_DND_DESCR 0x0022
+#define GG_STATUS_INVISIBLE 0x0014
+#define GG_STATUS_INVISIBLE_DESCR 0x0016
+#define GG_STATUS_BLOCKED 0x0006
+
+#define GG_STATUS_IMAGE_MASK 0x0100
+#define GG_STATUS_DESCR_MASK 0x4000
+#define GG_STATUS_FRIENDS_MASK 0x8000
+
+#else
 
-#define GG_STATUS_FRIENDS_MASK 0x8000		/* tylko dla znajomych (4.6) */
-
-#define GG_STATUS_DESCR_MAXSIZE 70
+/**
+ * Rodzaje statusów użytkownika.
+ *
+ * \ingroup status
+ */
+enum {
+	GG_STATUS_NOT_AVAIL,		/**< Niedostępny */
+	GG_STATUS_NOT_AVAIL_DESCR,	/**< Niedostępny z opisem */
+	GG_STATUS_FFC,			/**< PoGGadaj ze mną */
+	GG_STATUS_FFC_DESCR,		/**< PoGGadaj ze mną z opisem */
+	GG_STATUS_AVAIL,		/**< Dostępny */
+	GG_STATUS_AVAIL_DESCR,		/**< Dostępny z opisem */
+	GG_STATUS_BUSY,			/**< Zajęty */
+	GG_STATUS_BUSY_DESCR,		/**< Zajęty z opisem */
+	GG_STATUS_DND,			/**< Nie przeszkadzać */
+	GG_STATUS_DND_DESCR,		/**< Nie przeszakdzać z opisem */
+	GG_STATUS_INVISIBLE,		/**< Niewidoczny (tylko własny status) */
+	GG_STATUS_INVISIBLE_DESCR,	/**< Niewidoczny z opisem (tylko własny status) */
+	GG_STATUS_BLOCKED,		/**< Zablokowany (tylko status innych) */
+	GG_STATUS_IMAGE_MASK,		/**< Flaga bitowa oznaczająca opis graficzny (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
+	GG_STATUS_DESCR_MASK,		/**< Flaga bitowa oznaczająca status z opisem (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
+	GG_STATUS_FRIENDS_MASK,		/**< Flaga bitowa dostępności tylko dla znajomych */
+};
 
-/*
- * makra do atwego i szybkiego sprawdzania stanu.
+#endif	/* DOXYGEN */
+
+/**
+ * \ingroup status
+ *
+ * Flaga bitowa dostepnosci informujaca ze mozemy voipowac
  */
 
+#define GG_STATUS_VOICE_MASK 0x20000	/**< czy ma wlaczone audio (7.7) */
+
+/**
+ * \ingroup status
+ *
+ * Maksymalna długośc opisu.
+ */
+#define GG_STATUS_DESCR_MAXSIZE 255
+#define GG_STATUS_DESCR_MAXSIZE_PRE_8_0 70
+
+#define GG_STATUS_MASK 0xff
+
 /* GG_S_F() tryb tylko dla znajomych */
 #define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0)
 
-/* GG_S() stan bez uwzgldnienia trybu tylko dla znajomych */
-#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK)
+/* GG_S() stan bez uwzględnienia dodatkowych flag */
+#define GG_S(x) ((x) & GG_STATUS_MASK)
 
-/* GG_S_A() dostpny */
-#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_FF() chętny do rozmowy */
+#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR)
 
-/* GG_S_NA() niedostpny */
-#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
+/* GG_S_AV() dostępny */
+#define GG_S_AV(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_AW() zaraz wracam */
+#define GG_S_AW(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
 
-/* GG_S_B() zajty */
-#define GG_S_B(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
+/* GG_S_DD() nie przeszkadzać */
+#define GG_S_DD(x) (GG_S(x) == GG_STATUS_DND || GG_S(x) == GG_STATUS_DND_DESCR)
+
+/* GG_S_NA() niedostępny */
+#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
 
 /* GG_S_I() niewidoczny */
 #define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
 
+
+/* GG_S_A() dostępny lub chętny do rozmowy */
+#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x))
+
+/* GG_S_B() zajęty lub nie przeszkadzać */
+#define GG_S_B(x) (GG_S_AW(x) || GG_S_DD(x))
+
+
 /* GG_S_D() stan opisowy */
-#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
+#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || \
+		   GG_S(x) == GG_STATUS_FFC_DESCR || \
+		   GG_S(x) == GG_STATUS_AVAIL_DESCR || \
+		   GG_S(x) == GG_STATUS_BUSY_DESCR || \
+		   GG_S(x) == GG_STATUS_DND_DESCR || \
+		   GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
 
-/* GG_S_BL() blokowany lub blokujcy */
+/* GG_S_BL() blokowany lub blokujący */
 #define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED)
 
+/**
+ * Zmiana statusu (pakiet \c GG_NEW_STATUS i \c GG_NEW_STATUS80BETA)
+ */
 struct gg_new_status {
-	uint32_t status;			/* na jaki zmieni? */
+	uint32_t status;			/**< Nowy status */
 } GG_PACKED;
 
 #define GG_NOTIFY_FIRST 0x000f
@@ -1081,12 +1600,29 @@
 
 struct gg_notify {
 	uint32_t uin;				/* numerek danej osoby */
-	uint8_t dunno1;				/* rodzaj wpisu w licie */
+	uint8_t dunno1;				/* rodzaj wpisu w liście */
 } GG_PACKED;
 
-#define GG_USER_OFFLINE 0x01	/* bdziemy niewidoczni dla uytkownika */
-#define GG_USER_NORMAL 0x03	/* zwyky uytkownik */
-#define GG_USER_BLOCKED 0x04	/* zablokowany uytkownik */
+#ifndef DOXYGEN
+
+#define GG_USER_OFFLINE 0x01
+#define GG_USER_NORMAL 0x03
+#define GG_USER_BLOCKED 0x04
+
+#else
+
+/**
+ * \ingroup contacts
+ *
+ * Rodzaj kontaktu.
+ */
+enum {
+	GG_USER_NORMAL,		/**< Zwykły kontakt */
+	GG_USER_BLOCKED,	/**< Zablokowany */
+	GG_USER_OFFLINE,	/**< Niewidoczny dla kontaktu */
+};
+
+#endif	/* DOXYGEN */
 
 #define GG_LIST_EMPTY 0x0012
 
@@ -1096,7 +1632,7 @@
 	uint32_t uin;			/* numerek */
 	uint32_t status;		/* status danej osoby */
 	uint32_t remote_ip;		/* adres ip delikwenta */
-	uint16_t remote_port;		/* port, na ktrym sucha klient */
+	uint16_t remote_port;		/* port, na którym słucha klient */
 	uint32_t version;		/* wersja klienta */
 	uint16_t dunno2;		/* znowu port? */
 } GG_PACKED;
@@ -1107,7 +1643,7 @@
 	uint32_t uin;			/* numerek plus flagi w MSB */
 	uint8_t status;			/* status danej osoby */
 	uint32_t remote_ip;		/* adres ip delikwenta */
-	uint16_t remote_port;		/* port, na ktrym sucha klient */
+	uint16_t remote_port;		/* port, na którym słucha klient */
 	uint8_t version;		/* wersja klienta */
 	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 	uint8_t dunno1;			/* 0x00 */
@@ -1119,12 +1655,38 @@
 	uint32_t uin;			/* numerek plus flagi w MSB */
 	uint8_t status;			/* status danej osoby */
 	uint32_t remote_ip;		/* adres ip delikwenta */
-	uint16_t remote_port;		/* port, na ktrym sucha klient */
+	uint16_t remote_port;		/* port, na którym słucha klient */
 	uint8_t version;		/* wersja klienta */
 	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 	uint8_t dunno1;			/* 0x00 */
 } GG_PACKED;
 
+#define GG_NOTIFY_REPLY77 0x0018
+
+struct gg_notify_reply77 {
+	uint32_t uin;			/* numerek plus flagi w MSB */
+	uint8_t status;			/* status danej osoby */
+	uint32_t remote_ip;		/* adres ip delikwenta */
+	uint16_t remote_port;		/* port, na którym słucha klient */
+	uint8_t version;		/* wersja klienta */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno1;			/* 0x00 */
+	uint32_t dunno2;		/* ? */
+} GG_PACKED;
+
+#define GG_STATUS77 0x0017
+
+struct gg_status77 {
+	uint32_t uin;			/* numerek plus flagi w MSB */
+	uint8_t status;			/* status danej osoby */
+	uint32_t remote_ip;		/* adres ip delikwenta */
+	uint16_t remote_port;		/* port, na którym słucha klient */
+	uint8_t version;		/* wersja klienta */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno1;			/* 0x00 */
+	uint32_t dunno2;		/* ? */
+} GG_PACKED;
+
 #define GG_ADD_NOTIFY 0x000d
 #define GG_REMOVE_NOTIFY 0x000e
 
@@ -1142,15 +1704,41 @@
 
 #define GG_SEND_MSG 0x000b
 
+#ifndef DOXYGEN
+
 #define GG_CLASS_QUEUED 0x0001
 #define GG_CLASS_OFFLINE GG_CLASS_QUEUED
 #define GG_CLASS_MSG 0x0004
 #define GG_CLASS_CHAT 0x0008
 #define GG_CLASS_CTCP 0x0010
 #define GG_CLASS_ACK 0x0020
-#define GG_CLASS_EXT GG_CLASS_ACK	/* kompatybilno wstecz */
+#define GG_CLASS_EXT GG_CLASS_ACK	/**< Dla kompatybilności wstecz */
+
+#else
 
-#define GG_MSG_MAXSIZE 2000
+/**
+ * Klasy wiadomości. Wartości są maskami bitowymi, które w większości
+ * przypadków można łączyć (połączenie \c GG_CLASS_MSG i \c GG_CLASS_CHAT
+ * nie ma sensu).
+ *
+ * \ingroup messages
+ */
+enum {
+	GG_CLASS_MSG,		/**< Wiadomość ma pojawić się w osobnym oknie */
+	GG_CLASS_CHAT,		/**< Wiadomość ma pojawić się w oknie rozmowy */
+	GG_CLASS_CTCP,		/**< Wiadomość przeznaczona dla klienta Gadu-Gadu */
+	GG_CLASS_ACK,		/**< Klient nie życzy sobie potwierdzenia */
+	GG_CLASS_QUEUED,	/**< Wiadomość zakolejkowana na serwerze (tylko przy odbieraniu) */
+};
+
+#endif	/* DOXYGEN */
+
+/**
+ * Maksymalna długość wiadomości.
+ *
+ * \ingroup messages
+ */
+#define GG_MSG_MAXSIZE 1989
 
 struct gg_send_msg {
 	uint32_t recipient;
@@ -1163,16 +1751,19 @@
 	uint16_t length;
 } GG_PACKED;
 
+/**
+ * Struktura opisująca formatowanie tekstu. W zależności od wartości pola
+ * \c font, zaraz za tą strukturą może wystąpić \c gg_msg_richtext_color
+ * lub \c gg_msg_richtext_image.
+ *
+ * \ingroup messages
+ */
 struct gg_msg_richtext_format {
-	uint16_t position;
-	uint8_t font;
+	uint16_t position;	/**< Początkowy znak formatowania (liczony od 0) */
+	uint8_t font;		/**< Atrybuty formatowania */
 } GG_PACKED;
 
-struct gg_msg_richtext_image {
-	uint16_t unknown1;
-	uint32_t size;
-	uint32_t crc32;
-} GG_PACKED;
+#ifndef DOXYGEN
 
 #define GG_FONT_BOLD 0x01
 #define GG_FONT_ITALIC 0x02
@@ -1180,10 +1771,44 @@
 #define GG_FONT_COLOR 0x08
 #define GG_FONT_IMAGE 0x80
 
+#else
+
+/**
+ * Atrybuty formatowania wiadomości.
+ *
+ * \ingroup messages
+ */
+enum {
+	GG_FONT_BOLD,
+	GG_FONT_ITALIC,
+	GG_FONT_UNDERLINE,
+	GG_FONT_COLOR,
+	GG_FONT_IMAGE
+};
+
+#endif	/* DOXYGEN */
+
+/**
+ * Struktura opisującą kolor tekstu dla atrybutu \c GG_FONT_COLOR.
+ *
+ * \ingroup messages
+ */
 struct gg_msg_richtext_color {
-	uint8_t red;
-	uint8_t green;
-	uint8_t blue;
+	uint8_t red;		/**< Składowa czerwona koloru */
+	uint8_t green;		/**< Składowa zielona koloru */
+	uint8_t blue;		/**< Składowa niebieska koloru */
+} GG_PACKED;
+
+/**
+ * Strukturya opisująca obrazek wstawiony do wiadomości dla atrubutu
+ * \c GG_FONT_IMAGE.
+ *
+ * \ingroup messages
+ */
+struct gg_msg_richtext_image {
+	uint16_t unknown1;	/**< Nieznane pole o wartości 0x0109 */
+	uint32_t size;		/**< Rozmiar obrazka */
+	uint32_t crc32;		/**< Suma kontrolna CRC32 obrazka */
 } GG_PACKED;
 
 struct gg_msg_recipients {
@@ -1207,12 +1832,32 @@
 
 #define GG_SEND_MSG_ACK 0x0005
 
+#ifndef DOXYGEN
+
 #define GG_ACK_BLOCKED 0x0001
 #define GG_ACK_DELIVERED 0x0002
 #define GG_ACK_QUEUED 0x0003
 #define GG_ACK_MBOXFULL 0x0004
 #define GG_ACK_NOT_DELIVERED 0x0006
 
+#else
+
+/**
+ * Status doręczenia wiadomości.
+ *
+ * \ingroup messages
+ */
+enum
+{
+	GG_ACK_DELIVERED,	/**< Wiadomość dostarczono. */
+	GG_ACK_QUEUED,		/**< Wiadomość zakolejkowano z powodu niedostępności odbiorcy. */
+	GG_ACK_BLOCKED,		/**< Wiadomość zablokowana przez serwer (spam, świąteczne ograniczenia itd.) */
+	GG_ACK_MBOXFULL,	/**< Wiadomości nie dostarczono z powodu zapełnionej kolejki wiadomości odbiorcy. */
+	GG_ACK_NOT_DELIVERED	/**< Wiadomości nie dostarczono (tylko dla \c GG_CLASS_CTCP). */
+};
+
+#endif	/* DOXYGEN */
+
 struct gg_send_msg_ack {
 	uint32_t status;
 	uint32_t recipient;
@@ -1236,29 +1881,59 @@
 
 #define GG_USERLIST_REQUEST 0x0016
 
+#define GG_XML_EVENT 0x0027
+
+#ifndef DOXYGEN
+
 #define GG_USERLIST_PUT 0x00
 #define GG_USERLIST_PUT_MORE 0x01
 #define GG_USERLIST_GET 0x02
 
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj zapytania.
+ */
+enum {
+	GG_USERLIST_PUT,	/**< Eksport listy kontaktów. */
+	GG_USERLIST_GET,	/**< Import listy kontaktów. */
+};
+
+#endif	/* DOXYGEN */
+
 struct gg_userlist_request {
 	uint8_t type;
 } GG_PACKED;
 
 #define GG_USERLIST_REPLY 0x0010
 
+#ifndef DOXYGEN
+
 #define GG_USERLIST_PUT_REPLY 0x00
 #define GG_USERLIST_PUT_MORE_REPLY 0x02
 #define GG_USERLIST_GET_REPLY 0x06
 #define GG_USERLIST_GET_MORE_REPLY 0x04
 
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj odpowiedzi.
+ */
+enum {
+	GG_USERLIST_PUT_REPLY,	/**< Wyeksportowano listy kontaktów. */
+	GG_USERLIST_GET_REPLY,	/**< Zaimportowano listę kontaktów. */
+};
+
+#endif	/* DOXYGEN */
+
 struct gg_userlist_reply {
 	uint8_t type;
 } GG_PACKED;
 
-/*
- * pakiety, stae, struktury dla DCC
- */
-
 struct gg_dcc_tiny_packet {
 	uint8_t type;		/* rodzaj pakietu */
 } GG_PACKED;
@@ -1274,14 +1949,14 @@
 } GG_PACKED;
 
 /*
- * pki co, nie znamy dokadnie protokou. nie wiemy, co czemu odpowiada.
- * nazwy s niepowane i tymczasowe.
+ * póki co, nie znamy dokładnie protokołu. nie wiemy, co czemu odpowiada.
+ * nazwy są niepoważne i tymczasowe.
  */
 #define GG_DCC_WANT_FILE 0x0003		/* peer chce plik */
-#define GG_DCC_HAVE_FILE 0x0001		/* wic mu damy */
+#define GG_DCC_HAVE_FILE 0x0001		/* więc mu damy */
 #define GG_DCC_HAVE_FILEINFO 0x0003	/* niech ma informacje o pliku */
 #define GG_DCC_GIMME_FILE 0x0006	/* peer jest pewny */
-#define GG_DCC_CATCH_FILE 0x0002	/* wysyamy plik */
+#define GG_DCC_CATCH_FILE 0x0002	/* wysyłamy plik */
 
 #define GG_DCC_FILEATTR_READONLY 0x0020
 
@@ -1290,11 +1965,88 @@
 #define GG_DCC_TIMEOUT_FILE_ACK 300	/* 5 minut */
 #define GG_DCC_TIMEOUT_VOICE_ACK 300	/* 5 minut */
 
+#define GG_DCC7_INFO 0x1f
+
+struct gg_dcc7_info {
+	uint32_t uin;			/* numer nadawcy */
+	uint32_t type;			/* sposób połączenia */
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	char info[GG_DCC7_INFO_LEN];	/* informacje o połączeniu "ip port" */
+} GG_PACKED;
+
+#define GG_DCC7_NEW 0x20
+
+struct gg_dcc7_new {
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	uint32_t uin_from;		/* numer nadawcy */
+	uint32_t uin_to;		/* numer odbiorcy */
+	uint32_t type;			/* rodzaj transmisji */
+	unsigned char filename[GG_DCC7_FILENAME_LEN];	/* nazwa pliku */
+	uint32_t size;			/* rozmiar pliku */
+	uint32_t size_hi;		/* rozmiar pliku (starsze bajty) */
+	unsigned char hash[GG_DCC7_HASH_LEN];	/* hash SHA1 */
+} GG_PACKED;
+
+#define GG_DCC7_ACCEPT 0x21
+
+struct gg_dcc7_accept {
+	uint32_t uin;			/* numer przyjmującego połączenie */
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	uint32_t offset;		/* offset przy wznawianiu transmisji */
+	uint32_t dunno1;		/* 0x00000000 */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_TYPE_P2P 0x00000001	/**< Połączenie bezpośrednie */
+#define GG_DCC7_TYPE_SERVER 0x00000002	/**< Połączenie przez serwer */
+
+#define GG_DCC7_REJECT 0x22
+
+struct gg_dcc7_reject {
+	uint32_t uin;			/**< Numer odrzucającego połączenie */
+	gg_dcc7_id_t id;		/**< Identyfikator połączenia */
+	uint32_t reason;		/**< Powód rozłączenia */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_REJECT_BUSY 0x00000001	/**< Połączenie bezpośrednie już trwa, nie umiem obsłużyć więcej */
+#define GG_DCC7_REJECT_USER 0x00000002	/**< Użytkownik odrzucił połączenie */
+#define GG_DCC7_REJECT_VERSION 0x00000006	/**< Druga strona ma wersję klienta nieobsługującą połączeń bezpośrednich tego typu */
+
+#define GG_DCC7_ID_REQUEST 0x23
+
+struct gg_dcc7_id_request {
+	uint32_t type;			/**< Rodzaj tranmisji */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_TYPE_VOICE 0x00000001	/**< Transmisja głosu */
+#define GG_DCC7_TYPE_FILE 0x00000004	/**< transmisja pliku */
+
+#define GG_DCC7_ID_REPLY 0x23
+
+struct gg_dcc7_id_reply {
+	uint32_t type;			/** Rodzaj transmisji */
+	gg_dcc7_id_t id;		/** Przyznany identyfikator */
+} GG_PACKED;
+
+#define GG_DCC7_DUNNO1 0x24
+
+struct gg_dcc7_dunno1 {
+	// XXX
+} GG_PACKED;
+
+#define GG_DCC7_TIMEOUT_CONNECT 10	/* 10 sekund */
+#define GG_DCC7_TIMEOUT_SEND 1800	/* 30 minut */
+#define GG_DCC7_TIMEOUT_GET 1800	/* 30 minut */
+#define GG_DCC7_TIMEOUT_FILE_ACK 300	/* 5 minut */
+#define GG_DCC7_TIMEOUT_VOICE_ACK 300	/* 5 minut */
+
 #ifdef __cplusplus
-#ifdef _MSC_VER
+}
+#ifdef _WIN32
 #pragma pack(pop)
 #endif
-}
 #endif
 
 #endif /* __GG_LIBGADU_H */
--- a/libpurple/protocols/gg/lib/obsolete.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/obsolete.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1,4 +1,4 @@
-/* $Id: obsolete.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -14,16 +14,23 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-/*
- * Plik zawiera deklaracje funkcji, ktre s ju nieaktualne ze wzgldu
- * na zmiany w protokole, ale s wymagane przez aplikacje linkowane ze
- * starszymi wersjami bibliotek.
+/**
+ * \file obsolete.c
+ *
+ * \brief Nieaktualne funkcje
+ *
+ * Plik zawiera definicje funkcji, które są już nieaktualne ze względu
+ * na zmiany w protokole. Programy konsolidowane ze starszych wersjami
+ * bibliotek powinny nadal mieć możliwość działania, mimo ograniczonej
+ * funkcjonalności.
  */
 
+/** \cond obsolete */
+
 #include <errno.h>
 
 #include "libgadu.h"
@@ -205,3 +212,25 @@
 {
 
 }
+
+int gg_resolve(int *fd, int *pid, const char *hostname)
+{
+	return -1;
+}
+
+void gg_resolve_pthread_cleanup(void *arg, int kill)
+{
+
+}
+
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
+{
+	return -1;
+}
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+{
+	return -1;
+}
+
+/** \endcond */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/protocol.h	Wed Mar 10 17:58:21 2010 +0000
@@ -0,0 +1,165 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser 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.
+ */
+
+#ifndef LIBGADU_PROTOCOL_H
+#define LIBGADU_PROTOCOL_H
+
+#include "libgadu.h"
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#endif
+
+#define GG_LOGIN80BETA 0x0029
+
+#define GG_LOGIN80 0x0031
+
+#undef GG_FEATURE_STATUS80BETA
+#undef GG_FEATURE_MSG80
+#undef GG_FEATURE_STATUS80
+#define GG_FEATURE_STATUS80BETA		0x01
+#define GG_FEATURE_MSG80		0x02
+#define GG_FEATURE_STATUS80 		0x05
+
+#define GG8_LANG	"pl"
+#define GG8_VERSION	"Gadu-Gadu Client Build 8.0.0.8731"
+
+struct gg_login80 {
+	uint32_t uin;			/* mój numerek */
+	uint8_t language[2];		/* język: GG8_LANG */
+	uint8_t hash_type;		/* rodzaj hashowania hasła */
+	uint8_t hash[64];		/* hash hasła dopełniony zerami */
+	uint32_t status;		/* status na dzień dobry */
+	uint32_t flags;			/* flagi (przeznaczenie nieznane) */
+	uint32_t features;		/* opcje protokołu (GG8_FEATURES) */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip (???) */
+	uint16_t external_port;		/* zewnętrzny port (???) */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno2;			/* 0x64 */
+} GG_PACKED;
+
+#define GG_LOGIN_HASH_TYPE_INVALID 0x0016
+
+#define GG_LOGIN80_OK 0x0035
+
+#define GG_NEW_STATUS80BETA 0x0028
+
+#define GG_NEW_STATUS80 0x0038
+
+/**
+ * Zmiana stanu (pakiet \c GG_NEW_STATUS80)
+ */
+struct gg_new_status80 {
+	uint32_t status;			/**< Nowy status */
+	uint32_t flags;				/**< flagi (nieznane przeznaczenie) */
+	uint32_t description_size;		/**< rozmiar opisu */
+} GG_PACKED;
+
+#define GG_STATUS80BETA 0x002a
+#define GG_NOTIFY_REPLY80BETA 0x002b
+
+#define GG_STATUS80 0x0036
+#define GG_NOTIFY_REPLY80 0x0037
+
+struct gg_notify_reply80 {
+	uint32_t uin;		/* numerek plus flagi w najstarszym bajcie */
+	uint32_t status;	/* status danej osoby */
+	uint32_t flags;		/* flagi (przeznaczenie nieznane) */
+	uint32_t remote_ip;	/* adres IP bezpośrednich połączeń */
+	uint16_t remote_port;	/* port bezpośrednich połączeń */
+	uint8_t image_size;	/* maksymalny rozmiar obrazków w KB */
+	uint8_t unknown2;	/* 0x00 */
+	uint32_t unknown3;	/* 0x00000000 */
+	uint32_t descr_len;	/* rozmiar opisu */
+} GG_PACKED;
+
+#define GG_SEND_MSG80 0x002d
+
+struct gg_send_msg80 {
+	uint32_t recipient;
+	uint32_t seq;
+	uint32_t msgclass;
+	uint32_t offset_plain;
+	uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_RECV_MSG80 0x002e
+
+struct gg_recv_msg80 {
+	uint32_t sender;
+	uint32_t seq;
+	uint32_t time;
+	uint32_t msgclass;
+	uint32_t offset_plain;
+	uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_DISCONNECT_ACK 0x000d
+
+#define GG_DCC7_VOICE_RETRIES 0x11	/* 17 powtorzen */
+
+#define GG_DCC7_RESERVED1		0xdeadc0de
+#define GG_DCC7_RESERVED2		0xdeadbeaf
+
+struct gg_dcc7_voice_auth {
+	uint8_t type;			/* 0x00 -> wysylanie ID
+					   0x01 -> potwierdzenie ID
+					*/
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */
+	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */
+} GG_PACKED;
+
+struct gg_dcc7_voice_nodata {	/* wyciszony mikrofon, ten pakiet jest wysylany co 1s (jesli chcemy podtrzymac polaczenie) */
+	uint8_t type;			/* 0x02 */
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */
+	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */
+} GG_PACKED;
+
+struct gg_dcc7_voice_data {
+	uint8_t type;			/* 0x03 */
+	uint32_t did;			/* XXX: co ile zwieksza sie u nas id pakietu [uzywac 0x28] */
+	uint32_t len;			/* rozmiar strukturki - 1 (sizeof(type)) */
+	uint32_t packet_id;		/* numerek pakietu */
+	uint32_t datalen;		/* rozmiar danych */
+	/* char data[]; */		/* ramki: albo gsm, albo speex, albo melp, albo inne. */
+} GG_PACKED;
+
+struct gg_dcc7_voice_init {
+	uint8_t type;			/* 0x04 */
+	uint32_t id;			/* nr kroku [0x1 - 0x5] */
+	uint32_t protocol;		/* XXX: wersja protokolu (0x29, 0x2a, 0x2b) */
+	uint32_t len;			/* rozmiar sizeof(protocol)+sizeof(len)+sizeof(data) = 0x08 + sizeof(data) */
+	/* char data[]; */		/* reszta danych */
+} GG_PACKED;
+
+struct gg_dcc7_voice_init_confirm {
+	uint8_t type;			/* 0x05 */
+	uint32_t id;			/* id tego co potwierdzamy [0x1 - 0x5] */
+} GG_PACKED;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+
+#endif /* LIBGADU_PROTOCOL_H */
--- a/libpurple/protocols/gg/lib/pubdir.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/pubdir.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1,8 +1,9 @@
-/* $Id: pubdir.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: pubdir.c 502 2008-01-10 23:25:17Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
  *                          Dawid Jarosz <dawjar@poczta.onet.pl>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,11 +16,18 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file pubdir.c
+ *
+ * \brief Obsługa katalogu publicznego
+ */
+
 #include "libgadu.h"
+#include "libgadu-config.h"
 
 #include <ctype.h>
 #include <errno.h>
@@ -29,20 +37,20 @@
 #include <string.h>
 #include <unistd.h>
 
-/*
- * gg_register3()
+/**
+ * Rejestruje nowego użytkownika.
  *
- * rozpoczyna rejestracj uytkownika protokoem GG 6.0. wymaga wczeniejszego
- * pobrania tokenu za pomoc funkcji gg_token().
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
  *
- *  - email - adres e-mail klienta
- *  - password - haso klienta
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto tokenu
- *  - async - poczenie asynchroniczne
+ * \param email Adres e-mail
+ * \param password Hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_register_free(), albo NULL jeli wystpi bd.
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup register
  */
 struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async)
 {
@@ -121,19 +129,59 @@
 	return h;
 }
 
-/*
- * gg_unregister3()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * usuwa konto uytkownika z serwera protokoem GG 6.0
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup register
+ */
+int gg_register_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
  *
- *  - uin - numerek GG
- *  - password - haso klienta
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto tokenu
- *  - async - poczenie asynchroniczne
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_unregister_free(), albo NULL jeli wystpi bd.
+ * \ingroup register
+ */
+void gg_register_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Usuwa użytkownika.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param password Hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup unregister
  */
 struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async)
 {
@@ -145,7 +193,7 @@
 		errno = EFAULT;
 		return NULL;
 	}
-
+    
 	__pwd = gg_saprintf("%ld", random());
 	__fmpwd = gg_urlencode(password);
 	__tokenid = gg_urlencode(tokenid);
@@ -210,22 +258,61 @@
 	return h;
 }
 
-/*
- * gg_change_passwd4()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
  *
- * wysya danie zmiany hasa zgodnie z protokoem GG 6.0. wymaga
- * wczeniejszego pobrania tokenu za pomoc funkcji gg_token().
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup unregister
+ */
+int gg_unregister_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
  *
- *  - uin - numer
- *  - email - adres e-mail
- *  - passwd - stare haso
- *  - newpasswd - nowe haso
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto tokenu
- *  - async - poczenie asynchroniczne
+ * \param h Struktura połączenia
+ *
+ * \ingroup unregister
+ */
+void gg_unregister_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Zmienia hasło użytkownika.
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_change_passwd_free(), albo NULL jeli wystpi bd.
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail
+ * \param passwd Obecne hasło
+ * \param newpasswd Nowe hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup passwd
  */
 struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async)
 {
@@ -309,19 +396,59 @@
 	return h;
 }
 
-/*
- * gg_remind_passwd3()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * wysya danie przypomnienia hasa e-mailem.
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup passwd
+ */
+int gg_change_passwd_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
  *
- *  - uin - numer
- *  - email - adres e-mail taki, jak ten zapisany na serwerze
- *  - async - poczenie asynchroniczne
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto tokenu
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_remind_passwd_free(), albo NULL jeli wystpi bd.
+ * \ingroup passwd
+ */
+void gg_change_passwd_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Wysyła hasło użytkownika na e-mail.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail (podany przy rejestracji)
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup remind
  */
 struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async)
 {
@@ -396,17 +523,55 @@
 	return h;
 }
 
-/*
- * gg_pubdir_watch_fd()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
  *
- * przy asynchronicznych operacjach na katalogu publicznym naley wywoywa
- * t funkcj przy zmianach na obserwowanym deskryptorze.
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  *
- *  - h - struktura opisujca poczenie
+ * \ingroup remind
+ */
+int gg_remind_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
  *
- * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie
- * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki
- * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error.
+ * \ingroup remind
+ */
+void gg_remind_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
 int gg_pubdir_watch_fd(struct gg_http *h)
 {
@@ -447,7 +612,11 @@
 	
 	gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body);
 
-	if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
+	if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) {
+		p->success = 1;
+		p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0);
+		gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin);
+	} else if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
 		p->success = 1;
 		if (tmp[7] == ':')
 			p->uin = strtol(tmp + 8, NULL, 0);
@@ -458,12 +627,10 @@
 	return 0;
 }
 
-/*
- * gg_pubdir_free()
+/**
+ * Zwalnia zasoby po operacji na katalogu publicznym.
  *
- * zwalnia pami po efektach operacji na katalogu publicznym.
- *
- *  - h - zwalniana struktura
+ * \param h Struktura połączenia
  */
 void gg_pubdir_free(struct gg_http *h)
 {
@@ -474,14 +641,17 @@
 	gg_http_free(h);
 }
 
-/*
- * gg_token()
+/**
+ * Pobiera token do autoryzacji operacji na katalogu publicznym.
+ *
+ * Token jest niezbędny do tworzenia nowego i usuwania użytkownika,
+ * zmiany hasła itd.
  *
- * pobiera z serwera token do autoryzacji zakadania konta, usuwania
- * konta i zmiany hasa.
+ * \param async Flaga połączenia asynchronicznego
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_token_free(), albo NULL jeli wystpi bd.
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup token
  */
 struct gg_http *gg_token(int async)
 {
@@ -511,17 +681,18 @@
 	return h;
 }
 
-/*
- * gg_token_watch_fd()
- *
- * przy asynchronicznych operacjach zwizanych z tokenem naley wywoywa
- * t funkcj przy zmianach na obserwowanym deskryptorze.
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
  *
- *  - h - struktura opisujca poczenie
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
  *
- * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie
- * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki
- * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error.
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup token
  */
 int gg_token_watch_fd(struct gg_http *h)
 {
@@ -547,8 +718,8 @@
 	if (h->state != GG_STATE_PARSING)
 		return 0;
 	
-	/* jeli h->data jest puste, to cigalimy tokenid i url do niego,
-	 * ale jeli co tam jest, to znaczy, e mamy drugi etap polegajcy
+	/* jeśli h->data jest puste, to ściągaliśmy tokenid i url do niego,
+	 * ale jeśli coś tam jest, to znaczy, że mamy drugi etap polegający
 	 * na pobieraniu tokenu. */
 	if (!h->data) {
 		int width, height, length;
@@ -573,8 +744,8 @@
 			return -1;
 		}
 		
-		/* dostalimy tokenid i wszystkie niezbdne informacje,
-		 * wic pobierzmy obrazek z tokenem */
+		/* dostaliśmy tokenid i wszystkie niezbędne informacje,
+		 * więc pobierzmy obrazek z tokenem */
 
 		if (strncmp(url, "http://", 7)) {
 			path = gg_saprintf("%s?tokenid=%s", url, tokenid);
@@ -623,6 +794,8 @@
 		free(path);
 		free(url);
 
+		gg_http_free_fields(h);
+
 		memcpy(h, h2, sizeof(struct gg_http));
 		free(h2);
 
@@ -652,12 +825,12 @@
 	return 0;
 }
 
-/*
- * gg_token_free()
+/**
+ * Zwalnia zasoby po operacji pobierania tokenu.
  *
- * zwalnia pami po efektach pobierania tokenu.
+ * \param h Struktura połączenia
  *
- *  - h - zwalniana struktura
+ * \ingroup token
  */
 void gg_token_free(struct gg_http *h)
 {
--- a/libpurple/protocols/gg/lib/pubdir50.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/gg/lib/pubdir50.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1,4 +1,4 @@
-/* $Id: pubdir50.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: pubdir50.c 854 2009-10-12 21:06:28Z wojtekka $ */
 
 /*
  *  (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -14,23 +14,32 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-#include "libgadu.h"
+/**
+ * \file pubdir50.c
+ *
+ * \brief Obsługa katalogu publicznego od wersji Gadu-Gadu 5.x
+ */
 
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 
-/*
- * gg_pubdir50_new()
+#include "libgadu.h"
+#include "libgadu-internal.h"
+
+/**
+ * Tworzy nowe zapytanie katalogu publicznego.
  *
- * tworzy now zmienn typu gg_pubdir50_t.
+ * \param type Rodzaj zapytania
  *
- * zaalokowana zmienna lub NULL w przypadku braku pamici.
+ * \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku błędu.
+ *
+ * \ingroup pubdir50
  */
 gg_pubdir50_t gg_pubdir50_new(int type)
 {
@@ -50,17 +59,16 @@
 	return res;
 }
 
-/*
- * gg_pubdir50_add_n()  // funkcja wewntrzna
- *
- * funkcja dodaje lub zastpuje istniejce pole do zapytania lub odpowiedzi.
+/**
+ * \internal Dodaje lub zastępuje pole zapytania lub odpowiedzi katalogu
+ * publicznego.
  *
- *  - req - wskanik opisu zapytania,
- *  - num - numer wyniku (0 dla zapytania),
- *  - field - nazwa pola,
- *  - value - warto pola,
+ * \param req Zapytanie lub odpowiedź
+ * \param num Numer wyniku odpowiedzi (0 dla zapytania)
+ * \param field Nazwa pola
+ * \param value Wartość pola
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
 static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
 {
@@ -110,31 +118,31 @@
 	return 0;
 }
 
-/*
- * gg_pubdir50_add()
- *
- * funkcja dodaje pole do zapytania.
+/**
+ * Dodaje pole zapytania.
  *
- *  - req - wskanik opisu zapytania,
- *  - field - nazwa pola,
- *  - value - warto pola,
+ * \param req Zapytanie
+ * \param field Nazwa pola
+ * \param value Wartość pola
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
  */
 int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value)
 {
 	return gg_pubdir50_add_n(req, 0, field, value);
 }
 
-/*
- * gg_pubdir50_seq_set()
- *
- * ustawia numer sekwencyjny zapytania.
+/**
+ * Ustawia numer sekwencyjny zapytania.
  *
- *  - req - zapytanie,
- *  - seq - nowy numer sekwencyjny.
+ * \param req Zapytanie
+ * \param seq Numer sekwencyjny
  *
- * 0/-1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
  */
 int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
 {
@@ -151,12 +159,12 @@
 	return 0;
 }
 
-/*
- * gg_pubdir50_free()
+/**
+ * Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego.
  *
- * zwalnia pami po zapytaniu lub rezultacie szukania uytkownika.
+ * \param s Zapytanie lub odpowiedź
  *
- *  - s - zwalniana zmienna,
+ * \ingroup pubdir50
  */
 void gg_pubdir50_free(gg_pubdir50_t s)
 {
@@ -174,15 +182,15 @@
 	free(s);
 }
 
-/*
- * gg_pubdir50()
- *
- * wysya zapytanie katalogu publicznego do serwera.
+/**
+ * Wysyła zapytanie katalogu publicznego do serwera.
  *
- *  - sess - sesja,
- *  - req - zapytanie.
+ * \param sess Struktura sesji
+ * \param req Zapytanie
  *
- * numer sekwencyjny wyszukiwania lub 0 w przypadku bdu.
+ * \return Numer sekwencyjny zapytania lub 0 w przypadku błędu
+ *
+ * \ingroup pubdir50
  */
 uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
 {
@@ -191,16 +199,16 @@
 	char *buf, *p;
 	struct gg_pubdir50_request *r;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
 	
 	if (!sess || !req) {
-		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
 		errno = EFAULT;
 		return 0;
 	}
 
 	if (sess->state != GG_STATE_CONNECTED) {
-		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
 		errno = ENOTCONN;
 		return 0;
 	}
@@ -210,30 +218,81 @@
 		if (req->entries[i].num)
 			continue;
 		
-		size += strlen(req->entries[i].field) + 1;
-		size += strlen(req->entries[i].value) + 1;
+		if (sess->encoding == GG_ENCODING_CP1250) {
+			size += strlen(req->entries[i].field) + 1;
+			size += strlen(req->entries[i].value) + 1;
+		} else {
+			char *tmp;
+
+			tmp = gg_utf8_to_cp(req->entries[i].field);
+
+			if (tmp == NULL)
+				return -1;
+
+			size += strlen(tmp) + 1;
+
+			free(tmp);
+
+			tmp = gg_utf8_to_cp(req->entries[i].value);
+
+			if (tmp == NULL)
+				return -1;
+
+			size += strlen(tmp) + 1;
+
+			free(tmp);
+		}
 	}
 
 	if (!(buf = malloc(size))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
 		return 0;
 	}
 
+	if (!req->seq)
+		req->seq = time(NULL);
+
+	res = req->seq;
+
 	r = (struct gg_pubdir50_request*) buf;
-	res = time(NULL);
 	r->type = req->type;
-	r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL));
-	req->seq = gg_fix32(r->seq);
+	r->seq = gg_fix32(req->seq);
 
 	for (i = 0, p = buf + 5; i < req->entries_count; i++) {
 		if (req->entries[i].num)
 			continue;
 
-		strcpy(p, req->entries[i].field);
-		p += strlen(p) + 1;
+		if (sess->encoding == GG_ENCODING_CP1250) {
+			strcpy(p, req->entries[i].field);
+			p += strlen(p) + 1;
+
+			strcpy(p, req->entries[i].value);
+			p += strlen(p) + 1;
+		} else {
+			char *tmp;
+
+			tmp = gg_utf8_to_cp(req->entries[i].field);
+
+			if (tmp == NULL) {
+				free(buf);
+				return -1;
+			}
 
-		strcpy(p, req->entries[i].value);
-		p += strlen(p) + 1;
+			strcpy(p, tmp);
+			p += strlen(tmp) + 1;
+			free(tmp);
+
+			tmp = gg_utf8_to_cp(req->entries[i].value);
+
+			if (tmp == NULL) {
+				free(buf);
+				return -1;
+			}
+
+			strcpy(p, tmp);
+			p += strlen(tmp) + 1;
+			free(tmp);
+		}
 	}
 
 	if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1)
@@ -245,26 +304,26 @@
 }
 
 /*
- * gg_pubdir50_handle_reply()  // funkcja wewntrzna
- *
- * analizuje przychodzcy pakiet odpowiedzi i zapisuje wynik w struct gg_event.
+ * \internal Analizuje przychodzący pakiet odpowiedzi i zapisuje wynik
+ * w strukturze \c gg_event.
  *
- *  - e - opis zdarzenia
- *  - packet - zawarto pakietu odpowiedzi
- *  - length - dugo pakietu odpowiedzi
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param packet Pakiet odpowiedzi
+ * \param length Długość pakietu odpowiedzi
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
-int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length)
 {
 	const char *end = packet + length, *p;
 	struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet;
 	gg_pubdir50_t res;
 	int num = 0;
 	
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length);
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);\n", sess, e, packet, length);
 
-	if (!e || !packet) {
+	if (!sess || !e || !packet) {
 		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
 		errno = EFAULT;
 		return -1;
@@ -299,11 +358,11 @@
 			break;
 	}
 
-	/* brak wynikw? */
+	/* brak wyników? */
 	if (length == 5)
 		return 0;
 
-	/* pomi pocztek odpowiedzi */
+	/* pomiń początek odpowiedzi */
 	p = packet + 5;
 
 	while (p < end) {
@@ -311,7 +370,7 @@
 
 		field = p;
 
-		/* sprawd, czy nie mamy podziau na kolejne pole */
+		/* sprawdź, czy nie mamy podziału na kolejne pole */
 		if (!*field) {
 			num++;
 			field++;
@@ -320,22 +379,22 @@
 		value = NULL;
 		
 		for (p = field; p < end; p++) {
-			/* jeli mamy koniec tekstu... */
+			/* jeśli mamy koniec tekstu... */
 			if (!*p) {
-				/* ...i jeszcze nie mielimy wartoci pola to
-				 * wiemy, e po tym zerze jest warto... */
+				/* ...i jeszcze nie mieliśmy wartości pola to
+				 * wiemy, że po tym zerze jest wartość... */
 				if (!value)
 					value = p + 1;
 				else
 					/* ...w przeciwym wypadku koniec
-					 * wartoci i moemy wychodzi
-					 * grzecznie z ptli */
+					 * wartości i możemy wychodzić
+					 * grzecznie z pętli */
 					break;
 			}
 		}
 		
-		/* sprawdmy, czy pole nie wychodzi poza pakiet, eby nie
-		 * mie segfaultw, jeli serwer przestanie zakacza pakietw
+		/* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie
+		 * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów
 		 * przez \0 */
 
 		if (p == end) {
@@ -345,14 +404,30 @@
 
 		p++;
 
-		/* jeli dostalimy namier na nastpne wyniki, to znaczy e
-		 * mamy koniec wynikw i nie jest to kolejna osoba. */
+		/* jeśli dostaliśmy namier na następne wyniki, to znaczy że
+		 * mamy koniec wyników i nie jest to kolejna osoba. */
 		if (!strcasecmp(field, "nextstart")) {
 			res->next = atoi(value);
 			num--;
 		} else {
-			if (gg_pubdir50_add_n(res, num, field, value) == -1)
-				goto failure;
+			if (sess->encoding == GG_ENCODING_CP1250) {
+				if (gg_pubdir50_add_n(res, num, field, value) == -1)
+					goto failure;
+			} else {
+				char *tmp;
+
+				tmp = gg_cp_to_utf8(value);
+
+				if (tmp == NULL)
+					goto failure;
+
+				if (gg_pubdir50_add_n(res, num, field, tmp) == -1) {
+					free(tmp);
+					goto failure;
+				}
+
+				free(tmp);
+			}
 		}
 	}	
 
@@ -365,16 +440,16 @@
 	return -1;
 }
 
-/*
- * gg_pubdir50_get()
- *
- * pobiera informacj z rezultatu wyszukiwania.
+/**
+ * Pobiera pole z odpowiedzi katalogu publicznego.
  *
- *  - res - rezultat wyszukiwania,
- *  - num - numer odpowiedzi,
- *  - field - nazwa pola (wielko liter nie ma znaczenia).
+ * \param res Odpowiedź
+ * \param num Numer wyniku odpowiedzi
+ * \param field Nazwa pola (wielkość liter nie ma znaczenia)
  *
- * warto pola lub NULL, jeli nie znaleziono.
+ * \return Wartość pola lub \c NULL jeśli nie znaleziono
+ *
+ * \ingroup pubdir50
  */
 const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
 {
@@ -399,57 +474,61 @@
 	return value;
 }
 
-/*
- * gg_pubdir50_count()
+/**
+ * Zwraca liczbę wyników odpowiedzi.
  *
- * zwraca ilo wynikw danego zapytania.
+ * \param res Odpowiedź
  *
- *  - res - odpowied
+ * \return Liczba wyników lub -1 w przypadku błędu
  *
- * ilo lub -1 w przypadku bdu.
+ * \ingroup pubdir50
  */
 int gg_pubdir50_count(gg_pubdir50_t res)
 {
 	return (!res) ? -1 : res->count;
 }
 
-/*
- * gg_pubdir50_type()
+/**
+ * Zwraca rodzaj zapytania lub odpowiedzi.
  *
- * zwraca rodzaj zapytania lub odpowiedzi.
+ * \param res Zapytanie lub odpowiedź
  *
- *  - res - zapytanie lub odpowied
+ * \return Rodzaj lub -1 w przypadku błędu
  *
- * ilo lub -1 w przypadku bdu.
+ * \ingroup pubdir50
  */
 int gg_pubdir50_type(gg_pubdir50_t res)
 {
 	return (!res) ? -1 : res->type;
 }
 
-/*
- * gg_pubdir50_next()
+/**
+ * Zwraca numer, od którego należy rozpocząc kolejne wyszukiwanie.
  *
- * zwraca numer, od ktrego naley rozpocz kolejne wyszukiwanie, jeli
- * zaley nam na kolejnych wynikach.
+ * Dłuższe odpowiedzi katalogu publicznego są wysyłane przez serwer
+ * w mniejszych paczkach. Po otrzymaniu odpowiedzi, jeśli numer kolejnego
+ * wyszukiwania jest większy od zera, dalsze wyniki można otrzymać przez
+ * wywołanie kolejnego zapytania z określonym numerem początkowym.
  *
- *  - res - odpowied
+ * \param res Odpowiedź
  *
- * numer lub -1 w przypadku bdu.
+ * \return Numer lub -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
  */
 uin_t gg_pubdir50_next(gg_pubdir50_t res)
 {
 	return (!res) ? (unsigned) -1 : res->next;
 }
 
-/*
- * gg_pubdir50_seq()
+/**
+ * Zwraca numer sekwencyjny zapytania lub odpowiedzi.
  *
- * zwraca numer sekwencyjny zapytania lub odpowiedzi.
+ * \param res Zapytanie lub odpowiedź
  *
- *  - res - zapytanie lub odpowied
+ * \return Numer sekwencyjny lub -1 w przypadku błędu
  *
- * numer lub -1 w przypadku bdu.
+ * \ingroup pubdir50
  */
 uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/resolver.c	Wed Mar 10 17:58:21 2010 +0000
@@ -0,0 +1,1065 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser 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.
+ */
+
+/**
+ * \file resolver.c
+ *
+ * \brief Funkcje rozwiązywania nazw
+ */
+
+#ifndef _WIN32
+#  include <sys/wait.h>
+#  include <netdb.h>
+#  include <signal.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+#include "resolver.h"
+#include "compat.h"
+
+/** Sposób rozwiązywania nazw serwerów */
+static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT;
+
+/** Funkcja rozpoczynająca rozwiązywanie nazwy */
+static int (*gg_global_resolver_start)(int *fd, void **private_data, const char *hostname);
+
+/** Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
+static void (*gg_global_resolver_cleanup)(void **private_data, int force);
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+
+#include <pthread.h>
+
+/**
+ * \internal Funkcja pomocnicza zwalniająca zasoby po rozwiązywaniu nazwy
+ * w wątku.
+ *
+ * \param data Wskaźnik na wskaźnik bufora zaalokowanego w wątku
+ */
+static void gg_gethostbyname_cleaner(void *data)
+{
+	char **buf_ptr = (char**) data;
+
+	if (buf_ptr != NULL) {
+		free(*buf_ptr);
+		*buf_ptr = NULL;
+	}
+}
+
+#endif /* GG_CONFIG_HAVE_PTHREAD */
+
+/**
+ * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
+ *
+ * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
+ * nie, to zwykłej \c gethostbyname.
+ *
+ * \param hostname Nazwa serwera
+ * \param addr Wskaźnik na rezultat rozwiązywania nazwy
+ * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthread)
+{
+#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+	char *buf = NULL;
+	char *new_buf = NULL;
+	struct hostent he;
+	struct hostent *he_ptr = NULL;
+	size_t buf_len = 1024;
+	int result = -1;
+	int h_errnop;
+	int ret = 0;
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	int old_state;
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	pthread_cleanup_push(gg_gethostbyname_cleaner, &buf);
+
+	if (pthread)
+		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+	buf = malloc(buf_len);
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	if (pthread)
+		pthread_setcancelstate(old_state, NULL);
+#endif
+
+	if (buf != NULL) {
+#ifndef sun
+		while ((ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop)) == ERANGE) {
+#else
+		while (((he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop)) == NULL) && (errno == ERANGE)) {
+#endif
+			buf_len *= 2;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+			if (pthread)
+				pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+			new_buf = realloc(buf, buf_len);
+
+			if (new_buf != NULL)
+				buf = new_buf;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+			if (pthread)
+				pthread_setcancelstate(old_state, NULL);
+#endif
+
+			if (new_buf == NULL) {
+				ret = ENOMEM;
+				break;
+			}
+		}
+
+		if (ret == 0 && he_ptr != NULL) {
+			memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr));
+			result = 0;
+		}
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		if (pthread)
+			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+		free(buf);
+		buf = NULL;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		if (pthread)
+			pthread_setcancelstate(old_state, NULL);
+#endif
+	}
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	pthread_cleanup_pop(1);
+#endif
+
+	return result;
+#else
+	struct hostent *he;
+
+	he = gethostbyname(hostname);
+
+	if (he == NULL)
+		return -1;
+
+	memcpy(addr, he->h_addr, sizeof(struct in_addr));
+
+	return 0;
+#endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
+}
+
+/**
+ * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
+ *
+ * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
+ * nie, to zwykłej \c gethostbyname.
+ *
+ * \param hostname Nazwa serwera
+ *
+ * \return Zaalokowana struktura \c in_addr lub NULL w przypadku błędu.
+ */
+struct in_addr *gg_gethostbyname(const char *hostname)
+{
+	struct in_addr *addr;
+
+	if (!(addr = malloc(sizeof(struct in_addr))))
+		return NULL;
+
+	if (gg_gethostbyname_real(hostname, addr, 0)) {
+		free(addr);
+		return NULL;
+	}
+	return addr;
+}
+
+/**
+ * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
+ */
+struct gg_resolver_fork_data {
+	int pid;		/*< Identyfikator procesu */
+};
+
+
+
+#ifdef _WIN32
+/**
+ *  Deal with the fact that you can't select() on a win32 file fd.
+ *  This makes it practically impossible to tie into purple's event loop.
+ *
+ *  -This is thanks to Tor Lillqvist.
+ *  XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu.
+ */
+static int
+socket_pipe (int *fds)
+{
+	SOCKET temp, socket1 = -1, socket2 = -1;
+	struct sockaddr_in saddr;
+	int len;
+	u_long arg;
+	fd_set read_set, write_set;
+	struct timeval tv;
+
+	temp = socket(AF_INET, SOCK_STREAM, 0);
+
+	if (temp == INVALID_SOCKET) {
+		goto out0;
+	}
+
+	arg = 1;
+	if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) {
+		goto out0;
+	}
+
+	memset(&saddr, 0, sizeof(saddr));
+	saddr.sin_family = AF_INET;
+	saddr.sin_port = 0;
+	saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+	if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) {
+		goto out0;
+	}
+
+	if (listen(temp, 1) == SOCKET_ERROR) {
+		goto out0;
+	}
+
+	len = sizeof(saddr);
+	if (getsockname(temp, (struct sockaddr *)&saddr, &len)) {
+		goto out0;
+	}
+
+	socket1 = socket(AF_INET, SOCK_STREAM, 0);
+
+	if (socket1 == INVALID_SOCKET) {
+		goto out0;
+	}
+
+	arg = 1;
+	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
+		goto out1;
+	}
+
+	if (connect(socket1, (struct sockaddr  *)&saddr, len) != SOCKET_ERROR ||
+			WSAGetLastError() != WSAEWOULDBLOCK) {
+		goto out1;
+	}
+
+	FD_ZERO(&read_set);
+	FD_SET(temp, &read_set);
+
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+
+	if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) {
+		goto out1;
+	}
+
+	if (!FD_ISSET(temp, &read_set)) {
+		goto out1;
+	}
+
+	socket2 = accept(temp, (struct sockaddr *) &saddr, &len);
+	if (socket2 == INVALID_SOCKET) {
+		goto out1;
+	}
+
+	FD_ZERO(&write_set);
+	FD_SET(socket1, &write_set);
+
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+
+	if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) {
+		goto out2;
+	}
+
+	if (!FD_ISSET(socket1, &write_set)) {
+		goto out2;
+	}
+
+	arg = 0;
+	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
+		goto out2;
+	}
+
+	arg = 0;
+	if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) {
+		goto out2;
+	}
+
+	fds[0] = socket1;
+	fds[1] = socket2;
+
+	closesocket (temp);
+
+	return 0;
+
+out2:
+	closesocket (socket2);
+out1:
+	closesocket (socket1);
+out0:
+	closesocket (temp);
+	errno = EIO;            /* XXX */
+
+	return -1;
+}
+#endif
+
+
+
+#ifdef _WIN32
+struct gg_resolve_win32thread_data {
+	char *hostname;
+	int fd;
+};
+
+static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg)
+{
+	struct gg_resolve_win32thread_data *d = arg;
+	struct in_addr a;
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() host: %s, fd: %i called\n", d->hostname, d->fd);
+	
+	if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
+		/* W przypadku błędu gg_gethostbyname_real() zwróci -1
+					 * i nie zmieni &addr. Tam jest już INADDR_NONE,
+					 * więc nie musimy robić nic więcej. */
+		gg_gethostbyname_real(d->hostname, &a, 0);
+	}
+	
+	// if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
+		// struct in_addr *hn;
+		
+		// if (!(hn = gg_gethostbyname(d->hostname)))
+			// a.s_addr = INADDR_NONE;
+		// else {
+			// a.s_addr = hn->s_addr;
+			// free(hn);
+		// }
+	// }
+
+	write(d->fd, &a, sizeof(a));
+	close(d->fd);
+
+	free(d->hostname);
+	d->hostname = NULL;
+
+	free(d);
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() done\n");
+	
+	return 0;
+}
+
+
+static int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname)
+{
+	struct gg_resolve_win32thread_data *d = NULL;
+	HANDLE h;
+	DWORD dwTId;
+	int pipes[2], new_errno;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname);
+	
+	if (!resolver || !fd || !hostname) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (socket_pipe(pipes) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+		return -1;
+	}
+
+	if (!(d = malloc(sizeof(*d)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	d->hostname = NULL;
+
+	if (!(d->hostname = strdup(hostname))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	d->fd = pipes[1];
+
+	h = CreateThread(NULL, 0, gg_resolve_win32thread_thread,
+		d, 0, &dwTId);
+
+	if (h == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	*resolver = h;
+	*fd = pipes[0];
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() done\n");
+	
+	return 0;
+
+cleanup:
+	if (d) {
+		free(d->hostname);
+		free(d);
+	}
+
+	close(pipes[0]);
+	close(pipes[1]);
+
+	errno = new_errno;
+
+	return -1;
+
+}
+
+static void gg_resolve_win32thread_cleanup(void **priv_data, int force)
+{
+	struct gg_resolve_win32thread_data *data;
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force: %i called\n", force);
+	
+	if (priv_data == NULL || *priv_data == NULL)
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() priv_data: NULL\n");
+		return;
+
+	data = (struct gg_resolve_win32thread_data*) *priv_data;
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() data: %s called\n", data->hostname);
+	*priv_data = NULL;
+
+	if (force) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force called\n", force);
+		//pthread_cancel(data->thread);
+		//pthread_join(data->thread, NULL);
+	}
+
+	free(data->hostname);
+	data->hostname = NULL;
+
+	if (data->fd != -1) {
+		close(data->fd);
+		data->fd = -1;
+	}
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() done\n");
+	free(data);
+}
+#endif
+
+#ifndef _WIN32
+/**
+ * \internal Rozwiązuje nazwę serwera w osobnym procesie.
+ *
+ * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania
+ * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim
+ * przeprowadzane jest rozwiązywanie nazwy. Deskryptor strony do odczytu 
+ * zapisuje się w strukturze sieci i czeka na dane w postaci struktury
+ * \c in_addr. Jeśli nie znaleziono nazwy, zwracana jest \c INADDR_NONE.
+ *
+ * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor
+ *           potoku
+ * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
+ *                  do numeru procesu potomnego rozwiązującego nazwę
+ * \param hostname Nazwa serwera do rozwiązania
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname)
+{
+	struct gg_resolver_fork_data *data = NULL;
+	struct in_addr addr;
+	int pipes[2], new_errno;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
+
+	if (fd == NULL || priv_data == NULL || hostname == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	data = malloc(sizeof(struct gg_resolver_fork_data));
+
+	if (data == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n");
+		return -1;
+	}
+
+	if (pipe(pipes) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	data->pid = fork();
+
+	if (data->pid == -1) {
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	if (data->pid == 0) {
+		close(pipes[0]);
+
+		if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+			/* W przypadku błędu gg_gethostbyname_real() zwróci -1
+                         * i nie zmieni &addr. Tam jest już INADDR_NONE,
+                         * więc nie musimy robić nic więcej. */
+			gg_gethostbyname_real(hostname, &addr, 0);
+		}
+
+		if (write(pipes[1], &addr, sizeof(addr)) != sizeof(addr))
+			exit(1);
+
+		exit(0);
+	}
+
+	close(pipes[1]);
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data);
+
+	*fd = pipes[0];
+	*priv_data = data;
+
+	return 0;
+
+cleanup:
+	free(data);
+	close(pipes[0]);
+	close(pipes[1]);
+
+	errno = new_errno;
+
+	return -1;
+}
+
+/**
+ * \internal Usuwanie zasobów po procesie rozwiązywaniu nazwy.
+ *
+ * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
+ * zasobów sesji podczas rozwiązywania nazwy.
+ *
+ * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
+ *                  danych
+ * \param force Flaga usuwania zasobów przed zakończeniem działania
+ */
+void gg_resolver_fork_cleanup(void **priv_data, int force)
+{
+	struct gg_resolver_fork_data *data;
+
+	if (priv_data == NULL || *priv_data == NULL)
+		return;
+
+	data = (struct gg_resolver_fork_data*) *priv_data;
+	*priv_data = NULL;
+
+	if (force)
+		kill(data->pid, SIGKILL);
+
+	waitpid(data->pid, NULL, WNOHANG);
+
+	free(data);
+}
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+
+/**
+ * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
+ */
+struct gg_resolver_pthread_data {
+	pthread_t thread;	/*< Identyfikator wątku */
+	char *hostname;		/*< Nazwa serwera */
+	int rfd;		/*< Deskryptor do odczytu */
+	int wfd;		/*< Deskryptor do zapisu */
+};
+
+/**
+ * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy.
+ *
+ * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
+ * zasobów sesji podczas rozwiązywania nazwy.
+ *
+ * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
+ *                  danych
+ * \param force Flaga usuwania zasobów przed zakończeniem działania
+ */
+static void gg_resolver_pthread_cleanup(void **priv_data, int force)
+{
+	struct gg_resolver_pthread_data *data;
+
+	if (priv_data == NULL || *priv_data == NULL)
+		return;
+
+	data = (struct gg_resolver_pthread_data *) *priv_data;
+	*priv_data = NULL;
+
+	if (force) {
+		pthread_cancel(data->thread);
+		pthread_join(data->thread, NULL);
+	}
+
+	free(data->hostname);
+	data->hostname = NULL;
+
+	if (data->wfd != -1) {
+		close(data->wfd);
+		data->wfd = -1;
+	}
+
+	free(data);
+}
+
+/**
+ * \internal Wątek rozwiązujący nazwę.
+ *
+ * \param arg Wskaźnik na strukturę \c gg_resolver_pthread_data
+ */
+static void *gg_resolver_pthread_thread(void *arg)
+{
+	struct gg_resolver_pthread_data *data = arg;
+	struct in_addr addr;
+
+	pthread_detach(pthread_self());
+
+	if ((addr.s_addr = inet_addr(data->hostname)) == INADDR_NONE) {
+		/* W przypadku błędu gg_gethostbyname_real() zwróci -1
+                 * i nie zmieni &addr. Tam jest już INADDR_NONE,
+                 * więc nie musimy robić nic więcej. */
+		gg_gethostbyname_real(data->hostname, &addr, 1);
+	}
+
+	if (write(data->wfd, &addr, sizeof(addr)) == sizeof(addr))
+		pthread_exit(NULL);
+	else 
+		pthread_exit((void*) -1);
+
+	return NULL;	/* żeby kompilator nie marudził */
+}
+
+/**
+ * \internal Rozwiązuje nazwę serwera w osobnym wątku.
+ *
+ * Funkcja działa analogicznie do \c gg_resolver_fork_start(), z tą różnicą,
+ * że działa na wątkach, nie procesach. Jest dostępna wyłącznie gdy podczas
+ * kompilacji włączono odpowiednią opcję.
+ *
+ * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor
+ *           potoku
+ * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
+ *                  do prywatnych danych wątku rozwiązującego nazwę
+ * \param hostname Nazwa serwera do rozwiązania
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_resolver_pthread_start(int *fd, void **priv_data, const char *hostname)
+{
+	struct gg_resolver_pthread_data *data = NULL;
+	int pipes[2], new_errno;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
+
+	if (fd == NULL || priv_data == NULL || hostname == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	data = malloc(sizeof(struct gg_resolver_pthread_data));
+
+	if (data == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory for resolver data\n");
+		return -1;
+	}
+
+	if (pipe(pipes) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	data->hostname = strdup(hostname);
+
+	if (data->hostname == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	data->rfd = pipes[0];
+	data->wfd = pipes[1];
+
+	if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, data)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create thread\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data);
+
+	*fd = pipes[0];
+	*priv_data = data;
+
+	return 0;
+
+cleanup:
+	if (data) {
+		free(data->hostname);
+		free(data);
+	}
+
+	close(pipes[0]);
+	close(pipes[1]);
+
+	errno = new_errno;
+
+	return -1;
+}
+
+#endif /* GG_CONFIG_HAVE_PTHREAD */
+
+/**
+ * Ustawia sposób rozwiązywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)
+{
+	if (gs == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (type == GG_RESOLVER_DEFAULT) {
+		if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
+			gs->resolver_type = gg_global_resolver_type;
+			gs->resolver_start = gg_global_resolver_start;
+			gs->resolver_cleanup = gg_global_resolver_cleanup;
+			return 0;
+		}
+
+#ifdef _WIN32
+		type = GG_RESOLVER_WIN32;
+#else
+		type = GG_RESOLVER_FORK;
+#endif
+
+#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT)
+		type = GG_RESOLVER_PTHREAD;
+#endif
+	}
+
+	switch (type) {
+#ifdef _WIN32
+	case GG_RESOLVER_WIN32:
+			gs->resolver_type = type;
+			gs->resolver_start = gg_resolve_win32thread;
+			gs->resolver_cleanup = gg_resolve_win32thread_cleanup;
+			return 0;
+#else
+	case GG_RESOLVER_FORK:
+			gs->resolver_type = type;
+			gs->resolver_start = gg_resolver_fork_start;
+			gs->resolver_cleanup = gg_resolver_fork_cleanup;
+			return 0;
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		case GG_RESOLVER_PTHREAD:
+			gs->resolver_type = type;
+			gs->resolver_start = gg_resolver_pthread_start;
+			gs->resolver_cleanup = gg_resolver_pthread_cleanup;
+			return 0;
+#endif
+
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+}
+
+/**
+ * Zwraca sposób rozwiązywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ *
+ * \return Sposób rozwiązywania nazw
+ */
+gg_resolver_t gg_session_get_resolver(struct gg_session *gs)
+{
+	if (gs == NULL) {
+		errno = EINVAL;
+		return GG_RESOLVER_INVALID;
+	}
+
+	return gs->resolver_type;
+}
+
+/**
+ * Ustawia własny sposób rozwiązywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniająca zasoby
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+	if (gs == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	gs->resolver_type = GG_RESOLVER_CUSTOM;
+	gs->resolver_start = resolver_start;
+	gs->resolver_cleanup = resolver_cleanup;
+
+	return 0;
+}
+
+/**
+ * Ustawia sposób rozwiązywania nazw połączenia HTTP.
+ *
+ * \param gh Struktura połączenia
+ * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type)
+{
+	if (gh == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (type == GG_RESOLVER_DEFAULT) {
+		if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
+			gh->resolver_type = gg_global_resolver_type;
+			gh->resolver_start = gg_global_resolver_start;
+			gh->resolver_cleanup = gg_global_resolver_cleanup;
+			return 0;
+		}
+
+#ifdef _WIN32
+		type = GG_RESOLVER_WIN32;
+#else
+		type = GG_RESOLVER_FORK;
+#endif
+
+#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT)
+		type = GG_RESOLVER_PTHREAD;
+#endif
+	}
+
+	switch (type) {
+#ifdef _WIN32
+	case GG_RESOLVER_WIN32:
+			gh->resolver_type = type;
+			gh->resolver_start = gg_resolve_win32thread;
+			gh->resolver_cleanup = gg_resolve_win32thread_cleanup;
+			return 0;
+#else
+	case GG_RESOLVER_FORK:
+			gh->resolver_type = type;
+			gh->resolver_start = gg_resolver_fork_start;
+			gh->resolver_cleanup = gg_resolver_fork_cleanup;
+			return 0;
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		case GG_RESOLVER_PTHREAD:
+			gh->resolver_type = type;
+			gh->resolver_start = gg_resolver_pthread_start;
+			gh->resolver_cleanup = gg_resolver_pthread_cleanup;
+			return 0;
+#endif
+
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+}
+
+/**
+ * Zwraca sposób rozwiązywania nazw połączenia HTTP.
+ *
+ * \param gh Struktura połączenia
+ *
+ * \return Sposób rozwiązywania nazw
+ */
+gg_resolver_t gg_http_get_resolver(struct gg_http *gh)
+{
+	if (gh == NULL) {
+		errno = EINVAL;
+		return GG_RESOLVER_INVALID;
+	}
+
+	return gh->resolver_type;
+}
+
+/**
+ * Ustawia własny sposób rozwiązywania nazw połączenia HTTP.
+ *
+ * \param gh Struktura sesji
+ * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniająca zasoby
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+	if (gh == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	gh->resolver_type = GG_RESOLVER_CUSTOM;
+	gh->resolver_start = resolver_start;
+	gh->resolver_cleanup = resolver_cleanup;
+
+	return 0;
+}
+
+/**
+ * Ustawia sposób rozwiązywania nazw globalnie dla biblioteki.
+ *
+ * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_global_set_resolver(gg_resolver_t type)
+{
+	switch (type) {
+		case GG_RESOLVER_DEFAULT:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = NULL;
+			gg_global_resolver_cleanup = NULL;
+			return 0;
+
+#ifndef _WIN32
+		case GG_RESOLVER_FORK:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = gg_resolver_fork_start;
+			gg_global_resolver_cleanup = gg_resolver_fork_cleanup;
+			return 0;
+#endif
+			
+#ifdef _WIN32
+		case GG_RESOLVER_WIN32:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = gg_resolve_win32thread;
+			gg_global_resolver_cleanup = gg_resolve_win32thread_cleanup;
+			return 0;
+#endif			
+		
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		case GG_RESOLVER_PTHREAD:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = gg_resolver_pthread_start;
+			gg_global_resolver_cleanup = gg_resolver_pthread_cleanup;
+			return 0;
+#endif
+
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+}
+
+/**
+ * Zwraca sposób rozwiązywania nazw globalnie dla biblioteki.
+ *
+ * \return Sposób rozwiązywania nazw
+ */
+gg_resolver_t gg_global_get_resolver(void)
+{
+	return gg_global_resolver_type;
+}
+
+/**
+ * Ustawia własny sposób rozwiązywania nazw globalnie dla biblioteki.
+ *
+ * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniająca zasoby
+ *
+ * Parametry funkcji rozpoczynającej rozwiązywanie nazwy wyglądają następująco:
+ *  - \c "int *fd" &mdash; wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku
+ *  - \c "void **priv_data" &mdash; wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy
+ *  - \c "const char *name" &mdash; nazwa serwera do rozwiązania
+ *
+ * Parametry funkcji zwalniającej zasoby wyglądają następująco:
+ *  - \c "void **priv_data" &mdash; wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu
+ *  - \c "int force" &mdash; flaga mówiąca o tym, że zasoby są zwalniane przed zakończeniem rozwiązywania nazwy, np. z powodu zamknięcia sesji.
+ *
+ * Własny kod rozwiązywania nazwy powinien stworzyć potok, parę gniazd lub
+ * inny deskryptor pozwalający na co najmniej jednostronną komunikację i 
+ * przekazać go w parametrze \c fd. Po zakończeniu rozwiązywania nazwy,
+ * powinien wysłać otrzymany adres IP w postaci sieciowej (big-endian) do
+ * deskryptora. Jeśli rozwiązywanie nazwy się nie powiedzie, należy wysłać
+ * \c INADDR_NONE. Następnie zostanie wywołana funkcja zwalniająca zasoby
+ * z parametrem \c force równym \c 0. Gdyby sesja została zakończona przed
+ * rozwiązaniem nazwy, np. za pomocą funkcji \c gg_logoff(), funkcja
+ * zwalniająca zasoby zostanie wywołana z parametrem \c force równym \c 1.
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+	if (resolver_start == NULL || resolver_cleanup == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	gg_global_resolver_type = GG_RESOLVER_CUSTOM;
+	gg_global_resolver_start = resolver_start;
+	gg_global_resolver_cleanup = resolver_cleanup;
+
+	return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/resolver.h	Wed Mar 10 17:58:21 2010 +0000
@@ -0,0 +1,30 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser 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.
+ */
+
+#ifndef LIBGADU_RESOLVER_H
+#define LIBGADU_RESOLVER_H
+
+#ifndef _WIN32
+#  include <arpa/inet.h>
+#endif
+
+int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread);
+
+#endif /* LIBGADU_RESOLVER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/sha1.c	Wed Mar 10 17:58:21 2010 +0000
@@ -0,0 +1,303 @@
+/* $Id: sha1.c 632 2008-07-30 18:40:06Z darkjames $ */
+
+/*
+ *  (C) Copyright 2007 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ *  Public domain SHA-1 implementation by Steve Reid <steve@edmweb.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser 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.
+ */
+
+/**
+ * \file sha1.c
+ *
+ * \brief Funkcje wyznaczania skrĂłtu SHA1
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+
+/** \cond ignore */
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+
+#include <openssl/sha.h>
+
+#else
+
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Modified by Wojtek Kaniewski <wojtekka@toxygen.net> for compatibility
+with libgadu and OpenSSL API.
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <string.h>
+
+typedef struct {
+    uint32_t state[5];
+    uint32_t count[2];
+    unsigned char buffer[64];
+} SHA_CTX;
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]);
+static void SHA1_Init(SHA_CTX* context);
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len);
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef GG_CONFIG_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+uint32_t a, b, c, d, e;
+typedef union {
+    unsigned char c[64];
+    uint32_t l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+static unsigned char workspace[64];
+    block = (CHAR64LONG16*)workspace;
+    memcpy(block, buffer, 64);
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1_Init - Initialize new context */
+
+static void SHA1_Init(SHA_CTX* context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len)
+{
+unsigned int i, j;
+
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+    context->count[1] += (len >> 29);
+    if ((j + len) > 63) {
+        memcpy(&context->buffer[j], data, (i = 64-j));
+        SHA1_Transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            SHA1_Transform(context->state, &data[i]);
+        }
+        j = 0;
+    }
+    else i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context)
+{
+uint32_t i, j;
+unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1_Update(context, (unsigned char *)"\200", 1);
+    while ((context->count[0] & 504) != 448) {
+        SHA1_Update(context, (unsigned char *)"\0", 1);
+    }
+    SHA1_Update(context, finalcount, 8);  /* Should cause a SHA1_Transform() */
+    for (i = 0; i < 20; i++) {
+        digest[i] = (unsigned char)
+         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    i = j = 0;
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF  /* make SHA1_Transform overwrite it's own static vars */
+    SHA1_Transform(context->state, context->buffer);
+#endif
+}
+
+#endif /* GG_CONFIG_HAVE_OPENSSL */
+
+/** \endcond */
+
+/** \cond internal */
+
+/**
+ * \internal Liczy skrót SHA1 z ziarna i hasła.
+ *
+ * \param password Hasło
+ * \param seed Ziarno
+ * \param result Bufor na wynik funkcji skrĂłtu (20 bajtĂłw)
+ */
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)
+{
+	SHA_CTX ctx;
+	
+	SHA1_Init(&ctx);
+	SHA1_Update(&ctx, (const unsigned char*) password, strlen(password));
+	seed = gg_fix32(seed);
+	SHA1_Update(&ctx, (uint8_t*) &seed, 4);
+	
+	SHA1_Final(result, &ctx);
+}
+
+/**
+ * \internal Liczy skrĂłt SHA1 z pliku.
+ *
+ * \param fd Deskryptor pliku
+ * \param result WskaĹşnik na skrĂłt
+ *
+ * \return 0 lub -1
+ */
+int gg_file_hash_sha1(int fd, uint8_t *result)
+{
+	unsigned char buf[4096];
+	SHA_CTX ctx;
+	off_t pos, len;
+	int res;
+
+	if ((pos = lseek(fd, 0, SEEK_CUR)) == (off_t) -1)
+		return -1;
+
+	if ((len = lseek(fd, 0, SEEK_END)) == (off_t) -1)
+		return -1;
+
+	if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+		return -1;
+
+	SHA1_Init(&ctx);
+
+	if (len <= 10485760) {
+		while ((res = read(fd, buf, sizeof(buf))) > 0)
+			SHA1_Update(&ctx, buf, res);
+	} else {
+		int i;
+
+		for (i = 0; i < 9; i++) {
+			int j;
+
+			if (lseek(fd, (len - 1048576) / 9 * i, SEEK_SET) == (off_t) - 1)
+				return -1;
+
+			for (j = 0; j < 1048576 / sizeof(buf); j++) {
+				if ((res = read(fd, buf, sizeof(buf))) != sizeof(buf)) {
+					res = -1;
+					break;
+				}
+
+				SHA1_Update(&ctx, buf, res);
+			}
+
+			if (res == -1)
+				break;
+		}
+	}
+
+	if (res == -1)
+		return -1;
+
+	SHA1_Final(result, &ctx);
+
+	if (lseek(fd, pos, SEEK_SET) == (off_t) -1)
+		return -1;
+
+	return 0;
+}
+
+/** \endcond */
--- a/libpurple/protocols/irc/irc.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/irc/irc.c	Wed Mar 10 17:58:21 2010 +0000
@@ -369,7 +369,7 @@
 	const char *pass = purple_connection_get_password(gc);
 
 	if (pass && *pass) {
-		buf = irc_format(irc, "vv", "PASS", pass);
+		buf = irc_format(irc, "v:", "PASS", pass);
 		if (irc_send(irc, buf) < 0) {
 			g_free(buf);
 			return FALSE;
--- a/libpurple/protocols/jabber/bosh.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/bosh.c	Wed Mar 10 17:58:21 2010 +0000
@@ -521,7 +521,7 @@
 	}
 
 	if (version) {
-		const char *dot = strstr(version, ".");
+		const char *dot = strchr(version, '.');
 		int major, minor = 0;
 
 		purple_debug_info("jabber", "BOSH connection manager version %s\n", version);
--- a/libpurple/protocols/jabber/buddy.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Wed Mar 10 17:58:21 2010 +0000
@@ -218,7 +218,7 @@
 	for (l = jb->resources; l; l = l->next)
 	{
 		JabberBuddyResource *jbr = l->data;
-		if (g_str_equal(resource, jbr->name))
+		if (jbr->name && g_str_equal(resource, jbr->name))
 			return jbr;
 	}
 
@@ -1824,7 +1824,8 @@
 	if(!jb)
 		return m;
 
-	if (js->protocol_version == JABBER_PROTO_0_9 && jb != js->user_jb) {
+	if (js->protocol_version.major == 0 && js->protocol_version.minor == 9 &&
+			jb != js->user_jb) {
 		if(jb->invisible & JABBER_INVIS_BUDDY) {
 			act = purple_menu_action_new(_("Un-hide From"),
 			                           PURPLE_CALLBACK(jabber_buddy_make_visible),
--- a/libpurple/protocols/jabber/data.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/data.c	Wed Mar 10 17:58:21 2010 +0000
@@ -39,7 +39,7 @@
 	JabberStream *js)
 {
 	JabberData *data = g_new0(JabberData, 1);
-	gchar *checksum = purple_util_get_image_checksum(rawdata, size);
+	gchar *checksum = jabber_calculate_data_sha1sum(rawdata, size);
 	gchar cid[256];
 
 	g_snprintf(cid, sizeof(cid), "sha1+%s@bob.xmpp.org", checksum);
--- a/libpurple/protocols/jabber/disco.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/disco.c	Wed Mar 10 17:58:21 2010 +0000
@@ -517,8 +517,12 @@
 		const char *category, *type, *name;
 		category = xmlnode_get_attrib(child, "category");
 		type = xmlnode_get_attrib(child, "type");
-		if(category && type && !strcmp(category, "pubsub") && !strcmp(type,"pep"))
+		if(category && type && !strcmp(category, "pubsub") && !strcmp(type,"pep")) {
+			PurpleConnection *gc = js->gc;
 			js->pep = TRUE;
+			gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS |
+				PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES;
+		}
 		if (!category || strcmp(category, "server"))
 			continue;
 		if (!type || strcmp(type, "im"))
--- a/libpurple/protocols/jabber/jabber.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Wed Mar 10 17:58:21 2010 +0000
@@ -70,9 +70,14 @@
 #include "jingle/jingle.h"
 #include "jingle/rtp.h"
 
+#define PING_TIMEOUT 60
+
 GList *jabber_features = NULL;
 GList *jabber_identities = NULL;
-static GSList *jabber_cmds = NULL;
+
+static GHashTable *jabber_cmds = NULL; /* PurplePlugin * => GSList of ids */
+
+static gint plugin_ref = 0;
 
 static void jabber_unregister_account_cb(JabberStream *js);
 static void try_srv_connect(JabberStream *js);
@@ -521,9 +526,12 @@
 
 void jabber_keepalive(PurpleConnection *gc)
 {
-	JabberStream *js = gc->proto_data;
-
-	if (js->keepalive_timeout == 0) {
+	JabberStream *js = purple_connection_get_protocol_data(gc);
+	time_t now = time(NULL);
+
+	if (js->keepalive_timeout == 0 && (now - js->last_ping) >= PING_TIMEOUT) {
+		js->last_ping = now;
+
 		jabber_keepalive_ping(js);
 		js->keepalive_timeout = purple_timeout_add_seconds(120,
 				(GSourceFunc)(jabber_keepalive_timeout), gc);
@@ -862,7 +870,8 @@
 	js->old_length = 0;
 	js->keepalive_timeout = 0;
 	/* Set the default protocol version to 1.0. Overridden in parser.c. */
-	js->protocol_version = JABBER_PROTO_1_0;
+	js->protocol_version.major = 1;
+	js->protocol_version.minor = 0;
 	js->sessions = NULL;
 	js->stun_ip = NULL;
 	js->stun_port = 0;
@@ -2952,7 +2961,16 @@
 	gchar *error = NULL;
 
 	if (!_jabber_send_buzz(js, username, &error)) {
+		PurpleAccount *account = purple_connection_get_account(gc);
+		PurpleConversation *conv =
+			purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, username, account);
 		purple_debug_error("jabber", "jabber_send_attention: jabber_cmd_buzz failed with error: %s\n", error ? error : "(NULL)");
+
+		if (conv) {
+			purple_conversation_write(conv, username, error, PURPLE_MESSAGE_ERROR,
+			    time(NULL));
+		}
+
 		g_free(error);
 		return FALSE;
 	}
@@ -3301,40 +3319,67 @@
 	}
 }
 
-void jabber_register_commands(void)
+static PurpleCmdRet
+jabber_cmd_mood(PurpleConversation *conv,
+		const char *cmd, char **args, char **error, void *data)
 {
+	JabberStream *js = conv->account->gc->proto_data;
+
+	if (js->pep) {
+		/* if no argument was given, unset mood */
+		if (!args | !args[0]) {
+			jabber_mood_set(js, NULL, NULL);
+		} else if (!args[1]) {
+			jabber_mood_set(js, args[0], NULL);
+		} else {
+			jabber_mood_set(js, args[0], args[1]);
+		}
+
+		return PURPLE_CMD_RET_OK;
+	} else {
+		/* account does not support PEP, can't set a mood */
+		purple_conversation_write(conv, NULL,
+		    _("Account does not support PEP, can't set mood"),
+		    PURPLE_MESSAGE_ERROR, time(NULL));
+		return PURPLE_CMD_RET_FAILED;
+	}
+}
+
+static void jabber_register_commands(PurplePlugin *plugin)
+{
+	GSList *commands = NULL;
 	PurpleCmdId id;
 	id = purple_cmd_register("config", "", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
 	                  "prpl-jabber", jabber_cmd_chat_config,
 	                  _("config:  Configure a chat room."), NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("configure", "", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
 	                  "prpl-jabber", jabber_cmd_chat_config,
 	                  _("configure:  Configure a chat room."), NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("nick", "s", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
 	                  "prpl-jabber", jabber_cmd_chat_nick,
 	                  _("nick &lt;new nickname&gt;:  Change your nickname."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("part", "s", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
 	                  PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
 	                  jabber_cmd_chat_part, _("part [message]:  Leave the room."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("register", "", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
 	                  "prpl-jabber", jabber_cmd_chat_register,
 	                  _("register:  Register with a chat room."), NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	/* XXX: there needs to be a core /topic cmd, methinks */
 	id = purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL,
@@ -3343,7 +3388,7 @@
 	                  jabber_cmd_chat_topic,
 	                  _("topic [new topic]:  View or change the topic."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("ban", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
@@ -3351,7 +3396,7 @@
 	                  jabber_cmd_chat_ban,
 	                  _("ban &lt;user&gt; [reason]:  Ban a user from the room."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
@@ -3359,7 +3404,7 @@
 	                  jabber_cmd_chat_affiliate,
 	                  _("affiliate &lt;owner|admin|member|outcast|none&gt; [nick1] [nick2] ...: Get the users with an affiliation or set users' affiliation with the room."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("role", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
@@ -3367,7 +3412,7 @@
 	                  jabber_cmd_chat_role,
 	                  _("role &lt;moderator|participant|visitor|none&gt; [nick1] [nick2] ...: Get the users with a role or set users' role with the room."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
@@ -3375,7 +3420,7 @@
 	                  jabber_cmd_chat_invite,
 	                  _("invite &lt;user&gt; [message]:  Invite a user to the room."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
@@ -3383,7 +3428,7 @@
 	                  jabber_cmd_chat_join,
 	                  _("join: &lt;room&gt; [password]:  Join a chat on this server."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("kick", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY |
@@ -3391,14 +3436,14 @@
 	                  jabber_cmd_chat_kick,
 	                  _("kick &lt;user&gt; [reason]:  Kick a user from the room."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL,
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY,
 	                  "prpl-jabber", jabber_cmd_chat_msg,
 	                  _("msg &lt;user&gt; &lt;message&gt;:  Send a private message to another user."),
 	                  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("ping", "w", PURPLE_CMD_P_PRPL,
 					  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM |
@@ -3406,24 +3451,39 @@
 					  "prpl-jabber", jabber_cmd_ping,
 					  _("ping &lt;jid&gt;:	Ping a user/component/server."),
 					  NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("buzz", "w", PURPLE_CMD_P_PRPL,
 					  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY |
 					  PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
 					  "prpl-jabber", jabber_cmd_buzz,
 					  _("buzz: Buzz a user to get their attention"), NULL);
-	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
+
+	id = purple_cmd_register("mood", "ws", PURPLE_CMD_P_PRPL,
+	    			  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM |
+	    			  PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
+	    			  "prpl-jabber", jabber_cmd_mood,
+	    			  _("mood: Set current user mood"), NULL);
+	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
+
+	g_hash_table_insert(jabber_cmds, plugin, commands);
 }
 
-void jabber_unregister_commands(void)
+static void cmds_free_func(gpointer value)
 {
-	while (jabber_cmds != NULL) {
-		purple_cmd_unregister(GPOINTER_TO_UINT(jabber_cmds->data));
-		jabber_cmds = g_slist_delete_link(jabber_cmds, jabber_cmds);
+	GSList *commands = value;
+	while (commands) {
+		purple_cmd_unregister(GPOINTER_TO_UINT(commands->data));
+		commands = g_slist_delete_link(commands, commands);
 	}
 }
 
+static void jabber_unregister_commands(PurplePlugin *plugin)
+{
+	g_hash_table_remove(jabber_cmds, plugin);
+}
+
 /* IPC functions */
 
 /**
@@ -3472,14 +3532,46 @@
 	jabber_caps_broadcast_change();
 }
 
-void
-jabber_init_plugin(PurplePlugin *plugin)
+static void
+jabber_do_init(void)
 {
 	GHashTable *ui_info = purple_core_get_ui_info();
 	const gchar *ui_type;
 	const gchar *type = "pc"; /* default client type, if unknown or
 								unspecified */
 	const gchar *ui_name = NULL;
+#ifdef HAVE_CYRUS_SASL
+	/* We really really only want to do this once per process */
+	static gboolean sasl_initialized = FALSE;
+#ifdef _WIN32
+	UINT old_error_mode;
+	gchar *sasldir;
+#endif
+	int ret;
+#endif
+
+	/* XXX - If any other plugin wants SASL this won't be good ... */
+#ifdef HAVE_CYRUS_SASL
+	if (!sasl_initialized) {
+		sasl_initialized = TRUE;
+#ifdef _WIN32
+		sasldir = g_build_filename(wpurple_install_dir(), "sasl2", NULL);
+		sasl_set_path(SASL_PATH_TYPE_PLUGIN, sasldir);
+		g_free(sasldir);
+		/* Suppress error popups for failing to load sasl plugins */
+		old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
+#endif
+		if ((ret = sasl_client_init(NULL)) != SASL_OK) {
+			purple_debug_error("xmpp", "Error (%d) initializing SASL.\n", ret);
+		}
+#ifdef _WIN32
+		/* Restore the original error mode */
+		SetErrorMode(old_error_mode);
+#endif
+	}
+#endif
+
+	jabber_cmds = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, cmds_free_func);
 
 	ui_type = ui_info ? g_hash_table_lookup(ui_info, "client_type") : NULL;
 	if (ui_type) {
@@ -3543,7 +3635,53 @@
 			G_CALLBACK(jabber_caps_broadcast_change), NULL);
 #endif
 
+	/* reverse order of unload_plugin */
+	jabber_iq_init();
+	jabber_presence_init();
+	jabber_caps_init();
+	/* PEP things should be init via jabber_pep_init, not here */
+	jabber_pep_init();
+	jabber_data_init();
+	jabber_bosh_init();
+
+	/* TODO: Implement adding and retrieving own features via IPC API */
+
+	jabber_ibb_init();
+	jabber_si_init();
+
 	jabber_auth_init();
+}
+
+static void
+jabber_do_uninit(void)
+{
+	/* reverse order of jabber_do_init */
+	jabber_bosh_uninit();
+	jabber_data_uninit();
+	jabber_si_uninit();
+	jabber_ibb_uninit();
+	/* PEP things should be uninit via jabber_pep_uninit, not here */
+	jabber_pep_uninit();
+	jabber_caps_uninit();
+	jabber_presence_uninit();
+	jabber_iq_uninit();
+
+	jabber_auth_uninit();
+	jabber_features_destroy();
+	jabber_identities_destroy();
+
+	g_hash_table_destroy(jabber_cmds);
+	jabber_cmds = NULL;
+}
+
+void jabber_plugin_init(PurplePlugin *plugin)
+{
+	++plugin_ref;
+
+	if (plugin_ref == 1)
+		jabber_do_init();
+
+	jabber_register_commands(plugin);
 
 	/* IPC functions */
 	purple_plugin_ipc_register(plugin, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature),
@@ -3558,7 +3696,6 @@
 							 NULL, 1,
 							 purple_value_new(PURPLE_TYPE_STRING));
 
-	/* Modifying these? Look at libxmpp.c:load_plugin for the signal versions */
 	purple_plugin_ipc_register(plugin, "register_namespace_watcher",
 	                           PURPLE_CALLBACK(jabber_iq_signal_register),
 	                           purple_marshal_VOID__POINTER_POINTER,
@@ -3572,14 +3709,95 @@
 	                           NULL, 2,
 	                           purple_value_new(PURPLE_TYPE_STRING),  /* node */
 	                           purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
+
+	purple_signal_register(plugin, "jabber-register-namespace-watcher",
+			purple_marshal_VOID__POINTER_POINTER,
+			NULL, 2,
+			purple_value_new(PURPLE_TYPE_STRING),  /* node */
+			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
+
+	purple_signal_register(plugin, "jabber-unregister-namespace-watcher",
+			purple_marshal_VOID__POINTER_POINTER,
+			NULL, 2,
+			purple_value_new(PURPLE_TYPE_STRING),  /* node */
+			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
+
+	purple_signal_connect(plugin, "jabber-register-namespace-watcher",
+			plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL);
+	purple_signal_connect(plugin, "jabber-unregister-namespace-watcher",
+			plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL);
+
+
+	purple_signal_register(plugin, "jabber-receiving-xmlnode",
+			purple_marshal_VOID__POINTER_POINTER, NULL, 2,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+
+	purple_signal_register(plugin, "jabber-sending-xmlnode",
+			purple_marshal_VOID__POINTER_POINTER, NULL, 2,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+
+	/*
+	 * Do not remove this or the plugin will fail. Completely. You have been
+	 * warned!
+	 */
+	purple_signal_connect_priority(plugin, "jabber-sending-xmlnode",
+			plugin, PURPLE_CALLBACK(jabber_send_signal_cb),
+			NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST);
+
+	purple_signal_register(plugin, "jabber-sending-text",
+			     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
+			     purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			     purple_value_new_outgoing(PURPLE_TYPE_STRING));
+
+	purple_signal_register(plugin, "jabber-receiving-message",
+			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER,
+			purple_value_new(PURPLE_TYPE_BOOLEAN), 6,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new(PURPLE_TYPE_STRING), /* type */
+			purple_value_new(PURPLE_TYPE_STRING), /* id */
+			purple_value_new(PURPLE_TYPE_STRING), /* from */
+			purple_value_new(PURPLE_TYPE_STRING), /* to */
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+
+	purple_signal_register(plugin, "jabber-receiving-iq",
+			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
+			purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new(PURPLE_TYPE_STRING), /* type */
+			purple_value_new(PURPLE_TYPE_STRING), /* id */
+			purple_value_new(PURPLE_TYPE_STRING), /* from */
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+
+	purple_signal_register(plugin, "jabber-watched-iq",
+			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
+			purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new(PURPLE_TYPE_STRING), /* type */
+			purple_value_new(PURPLE_TYPE_STRING), /* id */
+			purple_value_new(PURPLE_TYPE_STRING), /* from */
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */
+
+	purple_signal_register(plugin, "jabber-receiving-presence",
+			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER,
+			purple_value_new(PURPLE_TYPE_BOOLEAN), 4,
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
+			purple_value_new(PURPLE_TYPE_STRING), /* type */
+			purple_value_new(PURPLE_TYPE_STRING), /* from */
+			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
 }
 
-void
-jabber_uninit_plugin(PurplePlugin *plugin)
+void jabber_plugin_uninit(PurplePlugin *plugin)
 {
+	g_return_if_fail(plugin_ref > 0);
+
+	purple_signals_unregister_by_instance(plugin);
 	purple_plugin_ipc_unregister_all(plugin);
 
-	jabber_auth_uninit();
-	jabber_features_destroy();
-	jabber_identities_destroy();
+	jabber_unregister_commands(plugin);
+
+	--plugin_ref;
+	if (plugin_ref == 0)
+		jabber_do_uninit();
 }
--- a/libpurple/protocols/jabber/jabber.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Wed Mar 10 17:58:21 2010 +0000
@@ -105,9 +105,9 @@
 	xmlParserCtxt *context;
 	xmlnode *current;
 
-	enum {
-		JABBER_PROTO_0_9,
-		JABBER_PROTO_1_0
+	struct {
+		guint8 major;
+		guint8 minor;
 	} protocol_version;
 
 	JabberSaslMech *auth_mech;
@@ -166,6 +166,11 @@
 	time_t idle;
 	time_t old_idle;
 
+	/** When we last pinged the server, so we don't ping more
+	 *  often than once every minute.
+	 */
+	time_t last_ping;
+
 	JabberID *user;
 	JabberBuddy *user_jb;
 
@@ -371,10 +376,7 @@
 PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who);
 gboolean jabber_can_receive_file(PurpleConnection *gc, const gchar *who);
 
-void jabber_register_commands(void);
-void jabber_unregister_commands(void);
-
-void jabber_init_plugin(PurplePlugin *plugin);
-void jabber_uninit_plugin(PurplePlugin *plugin);
+void jabber_plugin_init(PurplePlugin *plugin);
+void jabber_plugin_uninit(PurplePlugin *plugin);
 
 #endif /* PURPLE_JABBER_H_ */
--- a/libpurple/protocols/jabber/jutil.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/jutil.c	Wed Mar 10 17:58:21 2010 +0000
@@ -302,7 +302,7 @@
 	const guchar *c;
 
 	c = (const guchar *)in;
-	while (*c) {
+	for ( ; *c; ++c) {
 		if (*c > 0x7f ||
 				(*c < 0x20 && *c != '\t' && *c != '\n' && *c != '\r'))
 			return NULL;
--- a/libpurple/protocols/jabber/libxmpp.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Wed Mar 10 17:58:21 2010 +0000
@@ -132,104 +132,14 @@
 
 static gboolean load_plugin(PurplePlugin *plugin)
 {
-	purple_signal_register(plugin, "jabber-receiving-xmlnode",
-			purple_marshal_VOID__POINTER_POINTER, NULL, 2,
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
-			purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
-
-	purple_signal_register(plugin, "jabber-sending-xmlnode",
-			purple_marshal_VOID__POINTER_POINTER, NULL, 2,
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
-			purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
-
-	/*
-	 * Do not remove this or the plugin will fail. Completely. You have been
-	 * warned!
-	 */
-	purple_signal_connect_priority(plugin, "jabber-sending-xmlnode",
-			plugin, PURPLE_CALLBACK(jabber_send_signal_cb),
-			NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST);
-
-	purple_signal_register(plugin, "jabber-sending-text",
-			     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
-			     purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
-			     purple_value_new_outgoing(PURPLE_TYPE_STRING));
-
-	purple_signal_register(plugin, "jabber-receiving-message",
-			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER,
-			purple_value_new(PURPLE_TYPE_BOOLEAN), 6,
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
-			purple_value_new(PURPLE_TYPE_STRING), /* type */
-			purple_value_new(PURPLE_TYPE_STRING), /* id */
-			purple_value_new(PURPLE_TYPE_STRING), /* from */
-			purple_value_new(PURPLE_TYPE_STRING), /* to */
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
-
-	purple_signal_register(plugin, "jabber-receiving-iq",
-			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
-			purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
-			purple_value_new(PURPLE_TYPE_STRING), /* type */
-			purple_value_new(PURPLE_TYPE_STRING), /* id */
-			purple_value_new(PURPLE_TYPE_STRING), /* from */
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
-
-	purple_signal_register(plugin, "jabber-watched-iq",
-			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER,
-			purple_value_new(PURPLE_TYPE_BOOLEAN), 5,
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
-			purple_value_new(PURPLE_TYPE_STRING), /* type */
-			purple_value_new(PURPLE_TYPE_STRING), /* id */
-			purple_value_new(PURPLE_TYPE_STRING), /* from */
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */
-
-	/* Modifying these? Look at jabber_init_plugin for the ipc versions */
-	purple_signal_register(plugin, "jabber-register-namespace-watcher",
-			purple_marshal_VOID__POINTER_POINTER,
-			NULL, 2,
-			purple_value_new(PURPLE_TYPE_STRING),  /* node */
-			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
-
-	purple_signal_register(plugin, "jabber-unregister-namespace-watcher",
-			purple_marshal_VOID__POINTER_POINTER,
-			NULL, 2,
-			purple_value_new(PURPLE_TYPE_STRING),  /* node */
-			purple_value_new(PURPLE_TYPE_STRING)); /* namespace */
-
-	purple_signal_connect(plugin, "jabber-register-namespace-watcher",
-			plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL);
-	purple_signal_connect(plugin, "jabber-unregister-namespace-watcher",
-			plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL);
-
-	purple_signal_register(plugin, "jabber-receiving-presence",
-			purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER,
-			purple_value_new(PURPLE_TYPE_BOOLEAN), 4,
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
-			purple_value_new(PURPLE_TYPE_STRING), /* type */
-			purple_value_new(PURPLE_TYPE_STRING), /* from */
-			purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE));
+	jabber_plugin_init(plugin);
 
 	return TRUE;
 }
 
 static gboolean unload_plugin(PurplePlugin *plugin)
 {
-	purple_signals_unregister_by_instance(plugin);
-
-	/* reverse order of init_plugin */
-	jabber_bosh_uninit();
-	jabber_data_uninit();
-	jabber_si_uninit();
-	jabber_ibb_uninit();
-	/* PEP things should be uninit via jabber_pep_uninit, not here */
-	jabber_pep_uninit();
-	jabber_caps_uninit();
-	jabber_iq_uninit();
-
-	jabber_unregister_commands();
-
-	/* Stay on target...stay on target... Almost there... */
-	jabber_uninit_plugin(plugin);
+	jabber_plugin_uninit(plugin);
 
 	return TRUE;
 }
@@ -339,13 +249,6 @@
 static void
 init_plugin(PurplePlugin *plugin)
 {
-#ifdef HAVE_CYRUS_SASL
-#ifdef _WIN32
-	UINT old_error_mode;
-	gchar *sasldir;
-#endif
-	int ret;
-#endif
 	PurpleAccountUserSplit *split;
 	PurpleAccountOption *option;
 
@@ -402,42 +305,9 @@
 		option);
 
 	my_protocol = plugin;
-	jabber_init_plugin(plugin);
 
 	purple_prefs_remove("/plugins/prpl/jabber");
 
-	/* XXX - If any other plugin wants SASL this won't be good ... */
-#ifdef HAVE_CYRUS_SASL
-#ifdef _WIN32
-	sasldir = g_build_filename(wpurple_install_dir(), "sasl2", NULL);
-	sasl_set_path(SASL_PATH_TYPE_PLUGIN, sasldir);
-	g_free(sasldir);
-	/* Suppress error popups for failing to load sasl plugins */
-	old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
-#endif
-	if ((ret = sasl_client_init(NULL)) != SASL_OK) {
-		purple_debug_error("xmpp", "Error (%d) initializing SASL.\n", ret);
-	}
-#ifdef _WIN32
-	/* Restore the original error mode */
-	SetErrorMode(old_error_mode);
-#endif
-#endif
-	jabber_register_commands();
-
-	/* reverse order of unload_plugin */
-	jabber_iq_init();
-	jabber_caps_init();
-	/* PEP things should be init via jabber_pep_init, not here */
-	jabber_pep_init();
-	jabber_data_init();
-	jabber_bosh_init();
-
-	/* TODO: Implement adding and retrieving own features via IPC API */
-
-	jabber_ibb_init();
-	jabber_si_init();
-
 	purple_signal_connect(purple_get_core(), "uri-handler", plugin,
 		PURPLE_CALLBACK(xmpp_uri_handler), NULL);
 }
--- a/libpurple/protocols/jabber/message.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/message.c	Wed Mar 10 17:58:21 2010 +0000
@@ -74,24 +74,6 @@
 	jb = jabber_buddy_find(jm->js, jm->from, TRUE);
 	jbr = jabber_buddy_find_resource(jb, jid->resource);
 
-	if (jid->resource) {
-		/*
-		 * We received a message from a specific resource, so we probably want a
-		 * reply to go to this specific resource (i.e. bind/lock the
-		 * conversation to this resource).
-		 *
-		 * This works because purple_conv_im_send gets the name from
-		 * purple_conversation_get_name()
-		 */
-		PurpleConversation *conv;
-
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, jm->from, account);
-		if (conv && !g_str_equal(jm->from, purple_conversation_get_name(conv))) {
-			purple_debug_info("jabber", "Binding conversation to %s\n", jm->from);
-			purple_conversation_set_name(conv, jm->from);
-		}
-	}
-
 	if(!jm->xhtml && !jm->body) {
 		if (jbr) {
 			if (jm->chat_state != JM_STATE_NONE)
@@ -137,6 +119,28 @@
 			serv_got_typing_stopped(gc, jm->from);
 		}
 	} else {
+		if (jid->resource) {
+			/*
+			 * We received a message from a specific resource, so
+			 * we probably want a reply to go to this specific
+			 * resource (i.e. bind/lock the conversation to this
+			 * resource).
+			 *
+			 * This works because purple_conv_im_send gets the name
+			 * from purple_conversation_get_name()
+			 */
+			PurpleConversation *conv;
+
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+			                                             jm->from, account);
+			if (conv && !g_str_equal(jm->from,
+			                         purple_conversation_get_name(conv))) {
+				purple_debug_info("jabber", "Binding conversation to %s\n",
+				                  jm->from);
+				purple_conversation_set_name(conv, jm->from);
+			}
+		}
+
 		if(jbr) {
 			if (jm->chat_state != JM_STATE_NONE)
 				jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
--- a/libpurple/protocols/jabber/parser.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/parser.c	Wed Mar 10 17:58:21 2010 +0000
@@ -44,14 +44,28 @@
 	if(!element_name) {
 		return;
 	} else if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
-		js->protocol_version = JABBER_PROTO_0_9;
+		js->protocol_version.major = 0;
+		js->protocol_version.minor = 9;
 		for(i=0; i < nb_attributes * 5; i += 5) {
 			int attrib_len = attributes[i+4] - attributes[i+3];
 			char *attrib = g_strndup((gchar *)attributes[i+3], attrib_len);
 
-			if(!xmlStrcmp(attributes[i], (xmlChar*) "version")
-					&& !strcmp(attrib, "1.0")) {
-				js->protocol_version = JABBER_PROTO_1_0;
+			if(!xmlStrcmp(attributes[i], (xmlChar*) "version")) {
+				const char *dot = strchr(attrib, '.');
+
+				js->protocol_version.major = atoi(attrib);
+				js->protocol_version.minor = dot ? atoi(dot + 1) : 0;
+
+				if (js->protocol_version.major > 1)
+					/* TODO: Send <unsupported-version/> error */
+					purple_connection_error_reason(js->gc,
+							PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+							_("XMPP Version Mismatch"));
+
+				if (js->protocol_version.major == 0 && js->protocol_version.minor != 9) {
+					purple_debug_warning("jabber", "Treating version %s as 0.9 for backward "
+					                     "compatibility\n", attrib);
+				}
 				g_free(attrib);
 			} else if(!xmlStrcmp(attributes[i], (xmlChar*) "id")) {
 				g_free(js->stream_id);
@@ -255,7 +269,8 @@
 		}
 	}
 
-	if (js->protocol_version == JABBER_PROTO_0_9 && !js->gc->disconnect_timeout &&
+	if (js->protocol_version.major == 0 && js->protocol_version.minor == 9 &&
+			!js->gc->disconnect_timeout &&
 			(js->state == JABBER_STREAM_INITIALIZING ||
 			 js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION)) {
 		/*
--- a/libpurple/protocols/jabber/pep.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/pep.c	Wed Mar 10 17:58:21 2010 +0000
@@ -56,7 +56,6 @@
 
 void jabber_pep_init_actions(GList **m) {
 	/* register the PEP-specific actions */
-	jabber_mood_init_action(m);
 	jabber_nick_init_action(m);
 }
 
@@ -89,10 +88,11 @@
 	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
 	xmlnode *pubsub, *items;
 
-	xmlnode_set_attrib(iq->node,"to",to);
+	if (to)
+		xmlnode_set_attrib(iq->node, "to", to);
+
 	pubsub = xmlnode_new_child(iq->node,"pubsub");
-
-	xmlnode_set_namespace(pubsub,"http://jabber.org/protocol/pubsub");
+	xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub");
 
 	items = xmlnode_new_child(pubsub, "items");
 	xmlnode_set_attrib(items,"node",node);
--- a/libpurple/protocols/jabber/presence.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/presence.c	Wed Mar 10 17:58:21 2010 +0000
@@ -43,6 +43,37 @@
 #include "usermood.h"
 #include "usertune.h"
 
+static GHashTable *presence_handlers = NULL;
+
+static const struct {
+	const char *name;
+	JabberPresenceType type;
+} jabber_presence_types[] = {
+	{ "error", JABBER_PRESENCE_ERROR },
+	{ "probe", JABBER_PRESENCE_PROBE },
+	{ "unavailable", JABBER_PRESENCE_UNAVAILABLE },
+	{ "subscribe", JABBER_PRESENCE_SUBSCRIBE },
+	{ "subscribed", JABBER_PRESENCE_SUBSCRIBED },
+	{ "unsubscribe", JABBER_PRESENCE_UNSUBSCRIBE },
+	{ "unsubscribed", JABBER_PRESENCE_UNSUBSCRIBED }
+	/* { NULL, JABBER_PRESENCE_AVAILABLE } the default */
+};
+
+static JabberPresenceType
+str_to_presence_type(const char *type)
+{
+	int i;
+
+	if (type == NULL)
+		return JABBER_PRESENCE_AVAILABLE;
+
+	for (i = 0; i < G_N_ELEMENTS(jabber_presence_types); ++i)
+		if (g_str_equal(type, jabber_presence_types[i].name))
+			return jabber_presence_types[i].type;
+
+	purple_debug_warning("jabber", "Unknown presence type '%s'\n", type);
+	return JABBER_PRESENCE_AVAILABLE;
+}
 
 static void chats_send_presence_foreach(gpointer key, gpointer val,
 		gpointer user_data)
@@ -137,7 +168,9 @@
 	if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) {
 		const char *mood =
 			purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
-		jabber_mood_set(js, mood, NULL);
+		const char *mood_text =
+			purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT);
+		jabber_mood_set(js, mood, mood_text);
 		return;
 	}
 
@@ -474,7 +507,6 @@
 	purple_prpl_got_media_caps(
 			purple_connection_get_account(userdata->js->gc),
 			userdata->from);
-
 	if (info == NULL)
 		goto out;
 
@@ -506,573 +538,498 @@
 	g_free(userdata);
 }
 
+gboolean
+handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet)
+{
+	static int i = 1;
+	PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
+	JabberChat *chat = presence->chat;
+
+	if (presence->state == JABBER_BUDDY_STATE_ERROR) {
+		char *title, *msg = jabber_parse_error(js, packet, NULL);
+
+		if (!chat->conv) {
+			title = g_strdup_printf(_("Error joining chat %s"), presence->from);
+			purple_serv_got_join_chat_failed(js->gc, chat->components);
+		} else {
+			title = g_strdup_printf(_("Error in chat %s"), presence->from);
+			if (g_hash_table_size(chat->members) == 0)
+				serv_got_chat_left(js->gc, chat->id);
+		}
+		purple_notify_error(js->gc, title, title, msg);
+		g_free(title);
+		g_free(msg);
+
+		if (g_hash_table_size(chat->members) == 0)
+			/* Only destroy the chat if the error happened while joining */
+			jabber_chat_destroy(chat);
+		return FALSE;
+	}
+
+	if (presence->type == JABBER_PRESENCE_AVAILABLE) {
+		const char *jid = NULL;
+		const char *affiliation = NULL;
+		const char *role = NULL;
+		gboolean is_our_resource = FALSE; /* Is the presence about us? */
+		JabberBuddyResource *jbr;
+
+		/*
+		 * XEP-0045 mandates the presence to include a resource (which is
+		 * treated as the chat nick). Some non-compliant servers allow
+		 * joining without a nick.
+		 */
+		if (!presence->jid_from->resource)
+			return FALSE;
+
+		if (presence->chat_info.item) {
+			jid = xmlnode_get_attrib(presence->chat_info.item, "jid");
+			affiliation = xmlnode_get_attrib(presence->chat_info.item, "affiliation");
+			role = xmlnode_get_attrib(presence->chat_info.item, "role");
+		}
+
+		if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110)))
+			is_our_resource = TRUE;
+
+		if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(201))) {
+			chat->config_dialog_type = PURPLE_REQUEST_ACTION;
+			chat->config_dialog_handle =
+				purple_request_action(js->gc,
+						_("Create New Room"),
+						_("Create New Room"),
+						_("You are creating a new room.  Would"
+							" you like to configure it, or"
+							" accept the default settings?"),
+						/* Default Action */ 1,
+						purple_connection_get_account(js->gc), NULL, chat->conv,
+						chat, 2,
+						_("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
+						_("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
+		}
+
+		if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(210))) {
+			/* server rewrote room-nick */
+			g_free(chat->handle);
+			chat->handle = g_strdup(presence->jid_from->resource);
+		}
+
+		if (purple_strequal(affiliation, "owner"))
+			flags |= PURPLE_CBFLAGS_FOUNDER;
+		if (role) {
+			if (g_str_equal(role, "moderator"))
+				flags |= PURPLE_CBFLAGS_OP;
+			else if (g_str_equal(role, "participant"))
+				flags |= PURPLE_CBFLAGS_VOICE;
+		}
+
+		if(!chat->conv) {
+			char *room_jid = g_strdup_printf("%s@%s", presence->jid_from->node, presence->jid_from->domain);
+			chat->id = i++;
+			chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid);
+			purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle);
+
+			jabber_chat_disco_traffic(chat);
+			g_free(room_jid);
+		}
+
+		jbr = jabber_buddy_track_resource(presence->jb, presence->jid_from->resource, presence->priority, presence->state, presence->status);
+		jbr->commands_fetched = TRUE;
+
+		jabber_chat_track_handle(chat, presence->jid_from->resource, jid, affiliation, role);
+
+		if(!jabber_chat_find_buddy(chat->conv, presence->jid_from->resource))
+			purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource,
+					jid, flags, !presence->delayed);
+		else
+			purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource,
+					flags);
+	} else if (presence->type == JABBER_PRESENCE_UNAVAILABLE) {
+		gboolean nick_change = FALSE;
+		gboolean kick = FALSE;
+		gboolean is_our_resource = FALSE; /* Is the presence about us? */
+
+		const char *jid = NULL;
+
+		/* If the chat nick is invalid, we haven't yet joined, or we've
+		 * already left (it was probably us leaving after we closed the
+		 * chat), we don't care.
+		 */
+		if (!presence->jid_from->resource || !chat->conv || chat->left) {
+			if (chat->left &&
+					presence->jid_from->resource && chat->handle && !strcmp(presence->jid_from->resource, chat->handle))
+				jabber_chat_destroy(chat);
+			return FALSE;
+		}
+
+		is_our_resource = (0 == g_utf8_collate(presence->jid_from->resource, chat->handle));
+
+		jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource);
+
+		if (presence->chat_info.item)
+			jid = xmlnode_get_attrib(presence->chat_info.item, "jid");
+
+		if (chat->muc) {
+			if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110)))
+				is_our_resource = TRUE;
+			
+			if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(301))) {
+				/* XXX: We got banned.  YAY! (No GIR, that's bad) */
+			}
+
+
+			if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(303))) {
+				const char *nick = NULL;
+				if (presence->chat_info.item)
+					nick = xmlnode_get_attrib(presence->chat_info.item, "nick");
+
+				/* nick change */
+				if (nick) {
+					purple_debug_warning("jabber", "Chat presence indicating a nick change, but no new nickname!\n");
+				} else {
+					nick_change = TRUE;
+
+					if (g_str_equal(presence->jid_from->resource, chat->handle)) {
+						/* Changing our own nickname */
+						g_free(chat->handle);
+						chat->handle = g_strdup(nick);
+					}
+
+					purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv),
+					                             presence->jid_from->resource,
+					                             nick);
+					jabber_chat_remove_handle(chat,
+					                          presence->jid_from->resource);
+				}
+			}
+
+			if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(307))) {
+				/* Someone was kicked from the room */
+				const char *actor = NULL;
+				char *reason = NULL;
+				char *tmp;
+
+				kick = TRUE;
+
+				if (presence->chat_info.item) {
+					xmlnode *node;
+
+					node = xmlnode_get_child(presence->chat_info.item, "actor");
+					if (node)
+						actor = xmlnode_get_attrib(node, "jid");
+					node = xmlnode_get_child(presence->chat_info.item, "reason");
+					if (node)
+						reason = xmlnode_get_data(node);
+				}
+
+				if (reason == NULL)
+					reason = g_strdup(_("No reason"));
+
+				if (is_our_resource) {
+					if (actor)
+						tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"),
+								actor, reason);
+					else
+						tmp = g_strdup_printf(_("You have been kicked: (%s)"),
+								reason);
+				} else {
+					if (actor)
+						tmp = g_strdup_printf(_("Kicked by %s (%s)"),
+								actor, reason);
+					else
+						tmp = g_strdup_printf(_("Kicked (%s)"),
+								reason);
+				}
+
+				g_free(presence->status);
+				presence->status = tmp;
+
+				g_free(reason);
+			}
+			
+			if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(321))) {
+				/* XXX: removed due to an affiliation change */
+			}
+			
+			if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(322))) {
+				/* XXX: removed because room is now members-only */
+			}
+			
+			if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(332))) {
+				/* XXX: removed due to system shutdown */
+			}
+		}
+
+		/*
+		 * Possibly another connected resource of our JID (see XEP-0045
+		 * v1.24 section 7.1.10) being disconnected. Should be
+		 * distinguished by the item_jid.
+		 * Also possibly works around bits of an Openfire bug. See
+		 * #8319.
+		 */
+		if (is_our_resource && jid && !purple_strequal(presence->to, jid)) {
+			/* TODO: When the above is a loop, this needs to still act
+			 * sanely for all cases (this code is a little fragile). */
+			if (!kick && !nick_change)
+				/* Presumably, kicks and nick changes also affect us. */
+				is_our_resource = FALSE;
+		}
+
+		if(!nick_change) {
+			if (is_our_resource) {
+				if (kick)
+					purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource,
+							presence->status, PURPLE_MESSAGE_SYSTEM, time(NULL));
+
+				serv_got_chat_left(js->gc, chat->id);
+				jabber_chat_destroy(chat);
+			} else {
+				purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource,
+						presence->status);
+				jabber_chat_remove_handle(chat, presence->jid_from->resource);
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+gboolean
+handle_presence_contact(JabberStream *js, JabberPresence *presence)
+{
+	JabberBuddyResource *jbr;
+	PurpleAccount *account;
+	PurpleBuddy *b;
+	char *buddy_name;
+	PurpleConversation *conv;
+
+	buddy_name = jabber_id_get_bare_jid(presence->jid_from);
+
+	account = purple_connection_get_account(js->gc);
+	b = purple_find_buddy(account, buddy_name);
+
+	/*
+	 * Unbind/unlock from sending messages to a specific resource on
+	 * presence changes.  This is locked to a specific resource when
+	 * receiving a message (in message.c).
+	 */
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+			buddy_name, account);
+	if (conv) {
+		purple_debug_info("jabber", "Changed conversation binding from %s to %s\n",
+				purple_conversation_get_name(conv), buddy_name);
+		purple_conversation_set_name(conv, buddy_name);
+	}
+
+	if (b == NULL) {
+		if (presence->jb != js->user_jb) {
+			purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n",
+					buddy_name, purple_account_get_username(account), account);
+			return FALSE;
+		} else {
+			/* this is a different resource of our own account. Resume even when this account isn't on our blist */
+		}
+	}
+
+	if(b && presence->vcard_avatar_hash) {
+		const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b);
+		if(!avatar_hash2 || strcmp(presence->vcard_avatar_hash, avatar_hash2)) {
+			JabberIq *iq;
+			xmlnode *vcard;
+
+			/* XXX this is a crappy way of trying to prevent
+			 * someone from spamming us with presence packets
+			 * and causing us to DoS ourselves...what we really
+			 * need is a queue system that can throttle itself,
+			 * but i'm too tired to write that right now */
+			if(!g_slist_find(js->pending_avatar_requests, presence->jb)) {
+
+				js->pending_avatar_requests = g_slist_prepend(js->pending_avatar_requests, presence->jb);
+
+				iq = jabber_iq_new(js, JABBER_IQ_GET);
+				xmlnode_set_attrib(iq->node, "to", buddy_name);
+				vcard = xmlnode_new_child(iq->node, "vCard");
+				xmlnode_set_namespace(vcard, "vcard-temp");
+
+				jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL);
+				jabber_iq_send(iq);
+			}
+		}
+	}
+
+	if (presence->state == JABBER_BUDDY_STATE_ERROR ||
+			presence->type == JABBER_PRESENCE_UNAVAILABLE ||
+			presence->type == JABBER_PRESENCE_UNSUBSCRIBED) {
+		jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource);
+	} else {
+		jbr = jabber_buddy_track_resource(presence->jb,
+				presence->jid_from->resource, presence->priority,
+				presence->state, presence->status);
+		jbr->idle = presence->idle ? time(NULL) - presence->idle : 0;
+	}
+
+	jbr = jabber_buddy_find_resource(presence->jb, NULL);
+	if (jbr) {
+		jabber_google_presence_incoming(js, buddy_name, jbr);
+		purple_prpl_got_user_status(account, buddy_name,
+				jabber_buddy_state_get_status_id(jbr->state),
+				"priority", jbr->priority,
+				"message", jbr->status,
+				NULL);
+		purple_prpl_got_user_idle(account, buddy_name,
+				jbr->idle, jbr->idle);
+		if (presence->nickname)
+			serv_got_alias(js->gc, buddy_name, presence->nickname);
+	} else {
+		purple_prpl_got_user_status(account, buddy_name,
+				jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE),
+				presence->status ? "message" : NULL, presence->status,
+				NULL);
+	}
+	g_free(buddy_name);
+
+	return TRUE;
+}
+
 void jabber_presence_parse(JabberStream *js, xmlnode *packet)
 {
-	const char *from;
 	const char *type;
-	char *status = NULL;
-	int priority = 0;
-	JabberID *jid;
-	JabberChat *chat;
-	JabberBuddy *jb;
-	JabberBuddyResource *jbr = NULL, *found_jbr = NULL;
-	PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
-	gboolean delayed = FALSE;
-	const gchar *stamp = NULL; /* from <delayed/> element */
-	PurpleAccount *account;
-	PurpleBuddy *b = NULL;
-	char *buddy_name;
-	JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN;
-	xmlnode *y;
-	char *avatar_hash = NULL;
-	xmlnode *caps = NULL;
-	int idle = 0;
-	gchar *nickname = NULL;
-	gboolean signal_return;
+	JabberBuddyResource *jbr = NULL;
+	gboolean signal_return, ret;
+	JabberPresence presence;
+	xmlnode *child;
 
-	from = xmlnode_get_attrib(packet, "from");
+	memset(&presence, 0, sizeof(presence));
+	/* defaults */
+	presence.state = JABBER_BUDDY_STATE_UNKNOWN;
+	presence.sent = time(NULL);
+	/* interesting values */
+	presence.from = xmlnode_get_attrib(packet, "from");
+	presence.to   = xmlnode_get_attrib(packet, "to");
 	type = xmlnode_get_attrib(packet, "type");
-
-	jb = jabber_buddy_find(js, from, TRUE);
-	g_return_if_fail(jb != NULL);
+	presence.type = str_to_presence_type(type);
 
-	signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc),
-			"jabber-receiving-presence", js->gc, type, from, packet));
-	if (signal_return)
-		return;
+	presence.jb = jabber_buddy_find(js, presence.from, TRUE);
+	g_return_if_fail(presence.jb != NULL);
 
-	account = purple_connection_get_account(js->gc);
-
-	jid = jabber_id_new(from);
-	if (jid == NULL) {
+	presence.jid_from = jabber_id_new(presence.from);
+	if (presence.jid_from == NULL) {
 		purple_debug_error("jabber", "Ignoring presence with malformed 'from' "
-		                   "JID: %s\n", from);
+		                   "JID: %s\n", presence.from);
 		return;
 	}
 
-	if(jb->error_msg) {
-		g_free(jb->error_msg);
-		jb->error_msg = NULL;
+	signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc),
+			"jabber-receiving-presence", js->gc, type, presence.from, packet));
+	if (signal_return) {
+		goto out;
+	}
+
+	presence.chat = jabber_chat_find(js, presence.jid_from->node,
+	                                 presence.jid_from->domain);
+	if(presence.jb->error_msg) {
+		g_free(presence.jb->error_msg);
+		presence.jb->error_msg = NULL;
 	}
 
-	if (type == NULL) {
-		xmlnode *show;
-		char *show_data = NULL;
-
-		state = JABBER_BUDDY_STATE_ONLINE;
-
-		show = xmlnode_get_child(packet, "show");
-		if (show) {
-			show_data = xmlnode_get_data(show);
-			if (show_data) {
-				state = jabber_buddy_show_get_state(show_data);
-				g_free(show_data);
-			} else
-				purple_debug_warning("jabber", "<show/> present on presence, "
-				                     "but no contents!\n");
-		}
-	} else if (g_str_equal(type, "error")) {
+	if (presence.type == JABBER_PRESENCE_AVAILABLE) {
+		presence.state = JABBER_BUDDY_STATE_ONLINE;
+	} else if (presence.type == JABBER_PRESENCE_ERROR) {
+		/* TODO: Is this handled properly?  Should it be treated as per-jbr? */
 		char *msg = jabber_parse_error(js, packet, NULL);
-
-		state = JABBER_BUDDY_STATE_ERROR;
-		jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence"));
-	} else if (g_str_equal(type, "subscribe")) {
+		presence.state = JABBER_BUDDY_STATE_ERROR;
+		presence.jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence"));
+	} else if (presence.type == JABBER_PRESENCE_SUBSCRIBE) {
+		/* TODO: Move to handle_subscribe() (so nick is extracted by the
+		 * PresenceHandler */
 		struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1);
 		gboolean onlist = FALSE;
+		PurpleAccount *account;
 		PurpleBuddy *buddy;
-		JabberBuddy *jb = NULL;
 		xmlnode *nick;
 
-		buddy = purple_find_buddy(account, from);
+		account = purple_connection_get_account(js->gc);
+		buddy = purple_find_buddy(account, presence.from);
 		nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick");
 		if (nick)
-			nickname = xmlnode_get_data(nick);
+			presence.nickname = xmlnode_get_data(nick);
 
 		if (buddy) {
-			jb = jabber_buddy_find(js, from, TRUE);
-			if ((jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING)))
+			if ((presence.jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING)))
 				onlist = TRUE;
 		}
 
 		jap->gc = js->gc;
-		jap->who = g_strdup(from);
+		jap->who = g_strdup(presence.from);
 		jap->js = js;
 
-		purple_account_request_authorization(account, from, NULL, nickname,
+		purple_account_request_authorization(account, presence.from, NULL, presence.nickname,
 				NULL, onlist, authorize_add_cb, deny_add_cb, jap);
 
-		g_free(nickname);
-		jabber_id_free(jid);
-		return;
-	} else if (g_str_equal(type, "subscribed")) {
-		/* we've been allowed to see their presence, but we don't care */
-		jabber_id_free(jid);
-		return;
-	} else if (g_str_equal(type, "unsubscribe")) {
+		goto out;
+	} else if (presence.type == JABBER_PRESENCE_SUBSCRIBED) {
+		/* This case (someone has approved our subscribe request) is handled
+		 * by the roster push the server sends along with this.
+		 */
+		goto out;
+	} else if (presence.type == JABBER_PRESENCE_UNSUBSCRIBE) {
 		/* XXX I'm not sure this is the right way to handle this, it
 		 * might be better to add "unsubscribe" to the presence status
 		 * if lower down, but I'm not sure. */
 		/* they are unsubscribing from our presence, we don't care */
 		/* Well, maybe just a little, we might want/need to start
 		 * acknowledging this (and the others) at some point. */
-		jabber_id_free(jid);
-		return;
-	} else if (g_str_equal(type, "probe")) {
+		goto out;
+	} else if (presence.type == JABBER_PRESENCE_PROBE) {
 		purple_debug_warning("jabber", "Ignoring presence probe\n");
-		jabber_id_free(jid);
-		return;
-	} else if (g_str_equal(type, "unavailable")) {
-		state = JABBER_BUDDY_STATE_UNAVAILABLE;
-	} else if (g_str_equal(type, "unsubscribed")) {
-		state = JABBER_BUDDY_STATE_UNKNOWN;
+		goto out;
+	} else if (presence.type == JABBER_PRESENCE_UNAVAILABLE) {
+		presence.state = JABBER_BUDDY_STATE_UNAVAILABLE;
+	} else if (presence.type == JABBER_PRESENCE_UNSUBSCRIBED) {
+		presence.state = JABBER_BUDDY_STATE_UNKNOWN;
 	} else {
 		purple_debug_warning("jabber", "Ignoring presence with invalid type "
 		                     "'%s'\n", type);
-		jabber_id_free(jid);
-		return;
+		goto out;
 	}
 
-
-	for(y = packet->child; y; y = y->next) {
-		const char *xmlns;
-		if(y->type != XMLNODE_TYPE_TAG)
-			continue;
-		xmlns = xmlnode_get_namespace(y);
-
-		if(!strcmp(y->name, "status")) {
-			g_free(status);
-			status = xmlnode_get_data(y);
-		} else if(!strcmp(y->name, "priority")) {
-			char *p = xmlnode_get_data(y);
-			if(p) {
-				priority = atoi(p);
-				g_free(p);
-			}
-		} else if(xmlns == NULL) {
-			/* The rest of the cases used to check xmlns individually. */
+	for (child = packet->child; child; child = child->next) {
+		char *key;
+		JabberPresenceHandler *pih;
+		if (child->type != XMLNODE_TYPE_TAG)
 			continue;
-		} else if(!strcmp(y->name, "delay") && !strcmp(xmlns, NS_DELAYED_DELIVERY)) {
-			/* XXX: compare the time.  jabber:x:delay can happen on presence packets that aren't really and truly delayed */
-			delayed = TRUE;
-			stamp = xmlnode_get_attrib(y, "stamp");
-		} else if(!strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) {
-			caps = y; /* store for later, when creating buddy resource */
-		} else if (g_str_equal(y->name, "nick") && g_str_equal(xmlns, "http://jabber.org/protocol/nick")) {
-			nickname = xmlnode_get_data(y);
-		} else if(!strcmp(y->name, "x")) {
-			if(!strcmp(xmlns, NS_DELAYED_DELIVERY_LEGACY)) {
-				/* XXX: compare the time.  jabber:x:delay can happen on presence packets that aren't really and truly delayed */
-				delayed = TRUE;
-				stamp = xmlnode_get_attrib(y, "stamp");
-			} else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) {
-			} else if(!strcmp(xmlns, "vcard-temp:x:update")) {
-				xmlnode *photo = xmlnode_get_child(y, "photo");
-				if(photo) {
-					g_free(avatar_hash);
-					avatar_hash = xmlnode_get_data(photo);
-				}
-			}
-		} else if (!strcmp(y->name, "query") &&
-			!strcmp(xmlnode_get_namespace(y), NS_LAST_ACTIVITY)) {
-			/* resource has specified idle */
-			const gchar *seconds = xmlnode_get_attrib(y, "seconds");
-			if (seconds) {
-				/* we may need to take "delayed" into account here */
-				idle = atoi(seconds);
-			}
-		}
-	}
-
-	if (idle && delayed && stamp) {
-		/* if we have a delayed presence, we need to add the delay to the idle
-		 value */
-		time_t offset = time(NULL) - purple_str_to_time(stamp, TRUE, NULL, NULL,
-			NULL);
-		purple_debug_info("jabber", "got delay %s yielding %ld s offset\n",
-			stamp, offset);
-		idle += offset;
+	
+		key = g_strdup_printf("%s %s", child->name, xmlnode_get_namespace(child));
+		pih = g_hash_table_lookup(presence_handlers, key);
+		g_free(key);
+		if (pih)
+			pih(js, &presence, child);
 	}
 
-	/* DEALING WITH CHATS */
-	if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) {
-		static int i = 1;
-
-		if(state == JABBER_BUDDY_STATE_ERROR) {
-			char *title, *msg = jabber_parse_error(js, packet, NULL);
-
-			if (!chat->conv) {
-				title = g_strdup_printf(_("Error joining chat %s"), from);
-				purple_serv_got_join_chat_failed(js->gc, chat->components);
-			} else {
-				title = g_strdup_printf(_("Error in chat %s"), from);
-				if (g_hash_table_size(chat->members) == 0)
-					serv_got_chat_left(js->gc, chat->id);
-			}
-			purple_notify_error(js->gc, title, title, msg);
-			g_free(title);
-			g_free(msg);
-
-			if (g_hash_table_size(chat->members) == 0)
-				/* Only destroy the chat if the error happened while joining */
-				jabber_chat_destroy(chat);
-			jabber_id_free(jid);
-			g_free(status);
-			g_free(avatar_hash);
-			g_free(nickname);
-			return;
-		}
-
-		if (type == NULL) {
-			xmlnode *x;
-			const char *real_jid = NULL;
-			const char *affiliation = NULL;
-			const char *role = NULL;
-			gboolean is_our_resource = FALSE; /* Is the presence about us? */
-
-			/*
-			 * XEP-0045 mandates the presence to include a resource (which is
-			 * treated as the chat nick). Some non-compliant servers allow
-			 * joining without a nick.
-			 */
-			if (!jid->resource) {
-				jabber_id_free(jid);
-				g_free(avatar_hash);
-				g_free(nickname);
-				g_free(status);
-				return;
-			}
-
-			x = xmlnode_get_child_with_namespace(packet, "x",
-					"http://jabber.org/protocol/muc#user");
-			if (x) {
-				xmlnode *status_node;
-				xmlnode *item_node;
-
-				for (status_node = xmlnode_get_child(x, "status"); status_node;
-						status_node = xmlnode_get_next_twin(status_node)) {
-					const char *code = xmlnode_get_attrib(status_node, "code");
-					if (!code)
-						continue;
-
-					if (g_str_equal(code, "110")) {
-						is_our_resource = TRUE;
-					} else if (g_str_equal(code, "201")) {
-						if ((chat = jabber_chat_find(js, jid->node, jid->domain))) {
-							chat->config_dialog_type = PURPLE_REQUEST_ACTION;
-							chat->config_dialog_handle =
-								purple_request_action(js->gc,
-										_("Create New Room"),
-										_("Create New Room"),
-										_("You are creating a new room.  Would"
-											" you like to configure it, or"
-											" accept the default settings?"),
-										/* Default Action */ 1,
-										account, NULL, chat->conv,
-										chat, 2,
-										_("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
-										_("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
-						}
-					} else if (g_str_equal(code, "210")) {
-						/* server rewrote room-nick */
-						if((chat = jabber_chat_find(js, jid->node, jid->domain))) {
-							g_free(chat->handle);
-							chat->handle = g_strdup(jid->resource);
-						}
-					}
-				}
-
-				item_node = xmlnode_get_child(x, "item");
-				if (item_node) {
-					real_jid    = xmlnode_get_attrib(item_node, "jid");
-					affiliation = xmlnode_get_attrib(item_node, "affiliation");
-					role        = xmlnode_get_attrib(item_node, "role");
-
-					if (purple_strequal(affiliation, "owner"))
-						flags |= PURPLE_CBFLAGS_FOUNDER;
-					if (role) {
-						if (g_str_equal(role, "moderator"))
-							flags |= PURPLE_CBFLAGS_OP;
-						else if (g_str_equal(role, "participant"))
-							flags |= PURPLE_CBFLAGS_VOICE;
-					}
-				}
-			}
-
-			if(!chat->conv) {
-				char *room_jid = g_strdup_printf("%s@%s", jid->node, jid->domain);
-				chat->id = i++;
-				chat->muc = (x != NULL);
-				chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid);
-				purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle);
-
-				jabber_chat_disco_traffic(chat);
-				g_free(room_jid);
-			}
-
-			jbr = jabber_buddy_track_resource(jb, jid->resource, priority, state,
-					status);
-			jbr->commands_fetched = TRUE;
-
-			jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role);
-
-			if(!jabber_chat_find_buddy(chat->conv, jid->resource))
-				purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), jid->resource,
-						real_jid, flags, !delayed);
-			else
-				purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource,
-						flags);
-		} else if (g_str_equal(type, "unavailable")) {
-			xmlnode *x;
-			gboolean nick_change = FALSE;
-			gboolean kick = FALSE;
-			gboolean is_our_resource = FALSE; /* Is the presence about us? */
-
-			/* If the chat nick is invalid, we haven't yet joined, or we've
-			 * already left (it was probably us leaving after we closed the
-			 * chat), we don't care.
-			 */
-			if (!jid->resource || !chat->conv || chat->left) {
-				if (chat->left &&
-						jid->resource && chat->handle && !strcmp(jid->resource, chat->handle))
-					jabber_chat_destroy(chat);
-				jabber_id_free(jid);
-				g_free(status);
-				g_free(avatar_hash);
-				g_free(nickname);
-				return;
-			}
-
-			is_our_resource = (0 == g_utf8_collate(jid->resource, chat->handle));
-
-			jabber_buddy_remove_resource(jb, jid->resource);
-
-			x = xmlnode_get_child_with_namespace(packet, "x",
-					"http://jabber.org/protocol/muc#user");
-			if (chat->muc && x) {
-				const char *nick;
-				const char *item_jid = NULL;
-				const char *to;
-				xmlnode *stat;
-				xmlnode *item;
-
-				item = xmlnode_get_child(x, "item");
-				if (item)
-					item_jid = xmlnode_get_attrib(item, "jid");
-
-				for (stat = xmlnode_get_child(x, "status"); stat;
-						stat = xmlnode_get_next_twin(stat)) {
-					const char *code = xmlnode_get_attrib(stat, "code");
-
-					if (!code)
-						continue;
-
-					if (g_str_equal(code, "110")) {
-						is_our_resource = TRUE;
-					} else if(!strcmp(code, "301")) {
-						/* XXX: we got banned */
-					} else if(!strcmp(code, "303") && item &&
-							(nick = xmlnode_get_attrib(item, "nick"))) {
-						nick_change = TRUE;
-						if(!strcmp(jid->resource, chat->handle)) {
-							g_free(chat->handle);
-							chat->handle = g_strdup(nick);
-						}
-
-						/* TODO: This should probably be moved out of the loop */
-						purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, nick);
-						jabber_chat_remove_handle(chat, jid->resource);
-						continue;
-					} else if(!strcmp(code, "307")) {
-						/* Someone was kicked from the room */
-						xmlnode *reason = NULL, *actor = NULL;
-						const char *actor_name = NULL;
-						char *reason_text = NULL;
-						char *tmp;
-
-						kick = TRUE;
-
-						if (item) {
-							reason = xmlnode_get_child(item, "reason");
-							actor = xmlnode_get_child(item, "actor");
-
-							if (reason != NULL)
-								reason_text = xmlnode_get_data(reason);
-							if (actor != NULL)
-								actor_name = xmlnode_get_attrib(actor, "jid");
-						}
-
-						if (reason_text == NULL)
-							reason_text = g_strdup(_("No reason"));
-
-						if (is_our_resource) {
-							if (actor_name != NULL)
-								tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"),
-										actor_name, reason_text);
-							else
-								tmp = g_strdup_printf(_("You have been kicked: (%s)"),
-										reason_text);
-						} else {
-							if (actor_name != NULL)
-								tmp = g_strdup_printf(_("Kicked by %s (%s)"),
-										actor_name, reason_text);
-							else
-								tmp = g_strdup_printf(_("Kicked (%s)"),
-										reason_text);
-						}
-
-						g_free(reason_text);
-						g_free(status);
-						status = tmp;
-					} else if(!strcmp(code, "321")) {
-						/* XXX: removed due to an affiliation change */
-					} else if(!strcmp(code, "322")) {
-						/* XXX: removed because room is now members-only */
-					} else if(!strcmp(code, "332")) {
-						/* XXX: removed due to system shutdown */
-					}
-				}
-
-				/*
-				 * Possibly another connected resource of our JID (see XEP-0045
-				 * v1.24 section 7.1.10) being disconnected. Should be
-				 * distinguished by the item_jid.
-				 * Also possibly works around bits of an Openfire bug. See
-				 * #8319.
-				 */
-				to = xmlnode_get_attrib(packet, "to");
-				if (is_our_resource && item_jid && !purple_strequal(to, item_jid)) {
-					/* TODO: When the above is a loop, this needs to still act
-					 * sanely for all cases (this code is a little fragile). */
-					if (!kick && !nick_change)
-						/* Presumably, kicks and nick changes also affect us. */
-						is_our_resource = FALSE;
-				}
-			}
-			if(!nick_change) {
-				if (is_our_resource) {
-					if (kick)
-						purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), jid->resource,
-								status, PURPLE_MESSAGE_SYSTEM, time(NULL));
-
-					serv_got_chat_left(js->gc, chat->id);
-					jabber_chat_destroy(chat);
-				} else {
-					purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), jid->resource,
-							status);
-					jabber_chat_remove_handle(chat, jid->resource);
-				}
-			}
-		} else {
-			/* A type that isn't available or unavailable */
-			purple_debug_error("jabber", "MUC presence with bad type: %s\n",
-			                   type);
-
-			jabber_id_free(jid);
-			g_free(avatar_hash);
-			g_free(status);
-			g_free(nickname);
-			g_return_if_reached();
-		}
-		/* End of DEALING WITH CHATS...about 5000 lines ago */
-	} else {
-		/* DEALING WITH CONTACT (i.e. not a chat) */
-		PurpleConversation *conv;
-
-		buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "",
-									 jid->node ? "@" : "", jid->domain);
-
-		/*
-		 * Unbind/unlock from sending messages to a specific resource on
-		 * presence changes.  This is locked to a specific resource when
-		 * receiving a message (in message.c).
-		 */
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-				buddy_name, account);
-		if (conv) {
-			purple_debug_info("jabber", "Changed conversation binding from %s to %s\n",
-					purple_conversation_get_name(conv), buddy_name);
-			purple_conversation_set_name(conv, buddy_name);
-		}
-
-		if((b = purple_find_buddy(account, buddy_name)) == NULL) {
-			if (jb != js->user_jb) {
-				purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n",
-									 buddy_name, purple_account_get_username(account), account);
-				jabber_id_free(jid);
-				g_free(avatar_hash);
-				g_free(buddy_name);
-				g_free(nickname);
-				g_free(status);
-				return;
-			} else {
-				/* this is a different resource of our own account. Resume even when this account isn't on our blist */
-			}
-		}
-
-		if(b && avatar_hash) {
-			const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b);
-			if(!avatar_hash2 || strcmp(avatar_hash, avatar_hash2)) {
-				JabberIq *iq;
-				xmlnode *vcard;
-
-				/* XXX this is a crappy way of trying to prevent
-				 * someone from spamming us with presence packets
-				 * and causing us to DoS ourselves...what we really
-				 * need is a queue system that can throttle itself,
-				 * but i'm too tired to write that right now */
-				if(!g_slist_find(js->pending_avatar_requests, jb)) {
-
-					js->pending_avatar_requests = g_slist_prepend(js->pending_avatar_requests, jb);
-
-					iq = jabber_iq_new(js, JABBER_IQ_GET);
-					xmlnode_set_attrib(iq->node, "to", buddy_name);
-					vcard = xmlnode_new_child(iq->node, "vCard");
-					xmlnode_set_namespace(vcard, "vcard-temp");
-
-					jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL);
-					jabber_iq_send(iq);
-				}
-			}
-		}
-
-		if(state == JABBER_BUDDY_STATE_ERROR ||
-				(type && (g_str_equal(type, "unavailable") ||
-				          g_str_equal(type, "unsubscribed")))) {
-			jabber_buddy_remove_resource(jb, jid->resource);
-		} else {
-			jbr = jabber_buddy_track_resource(jb, jid->resource, priority,
-					state, status);
-			if (idle) {
-				jbr->idle = time(NULL) - idle;
-			} else {
-				jbr->idle = 0;
-			}
-		}
-
-		if((found_jbr = jabber_buddy_find_resource(jb, NULL))) {
-			jabber_google_presence_incoming(js, buddy_name, found_jbr);
-			purple_prpl_got_user_status(account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL);
-			purple_prpl_got_user_idle(account, buddy_name, found_jbr->idle, found_jbr->idle);
-			if (nickname)
-				serv_got_alias(js->gc, buddy_name, nickname);
-		} else {
-			purple_prpl_got_user_status(account, buddy_name, "offline", status ? "message" : NULL, status, NULL);
-		}
-		g_free(buddy_name);
+	if (presence.delayed && presence.idle) {
+		/* Delayed and idle, so update idle time */
+		presence.idle = presence.idle + (time(NULL) - presence.sent);
 	}
 
-	if (caps && !type) {
+	/* TODO: Handle tracking jb(r) here? */
+
+	if (presence.chat)
+		ret = handle_presence_chat(js, &presence, packet);
+	else
+		ret = handle_presence_contact(js, &presence);
+	if (!ret)
+		goto out;
+
+	if (presence.caps && presence.type == JABBER_PRESENCE_AVAILABLE) {
 		/* handle Entity Capabilities (XEP-0115) */
-		const char *node = xmlnode_get_attrib(caps, "node");
-		const char *ver  = xmlnode_get_attrib(caps, "ver");
-		const char *hash = xmlnode_get_attrib(caps, "hash");
-		const char *ext  = xmlnode_get_attrib(caps, "ext");
+		const char *node = xmlnode_get_attrib(presence.caps, "node");
+		const char *ver  = xmlnode_get_attrib(presence.caps, "ver");
+		const char *hash = xmlnode_get_attrib(presence.caps, "hash");
+		const char *ext  = xmlnode_get_attrib(presence.caps, "ext");
 
 		/* v1.3 uses: node, ver, and optionally ext.
 		 * v1.5 uses: node, ver, and hash. */
 		if (node && *node && ver && *ver) {
 			gchar **exts = ext && *ext ? g_strsplit(ext, " ", -1) : NULL;
-			jbr = jabber_buddy_find_resource(jb, jid->resource);
+			jbr = jabber_buddy_find_resource(presence.jb, presence.jid_from->resource);
 
 			/* Look it up if we don't already have all this information */
 			if (!jbr || !jbr->caps.info ||
@@ -1082,9 +1039,9 @@
 					!jabber_caps_exts_known(jbr->caps.info, (gchar **)exts)) {
 				JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1);
 				userdata->js = js;
-				userdata->jb = jb;
-				userdata->from = g_strdup(from);
-				jabber_caps_get_info(js, from, node, ver, hash, exts,
+				userdata->jb = presence.jb;
+				userdata->from = g_strdup(presence.from);
+				jabber_caps_get_info(js, presence.from, node, ver, hash, exts,
 				    (jabber_caps_get_info_cb)jabber_presence_set_capabilities,
 				    userdata);
 			} else {
@@ -1094,10 +1051,12 @@
 		}
 	}
 
-	g_free(nickname);
-	g_free(status);
-	jabber_id_free(jid);
-	g_free(avatar_hash);
+out:
+	g_free(presence.nickname);
+	g_free(presence.status);
+	jabber_id_free(presence.jid_from);
+	g_free(presence.nickname);
+	g_free(presence.vcard_avatar_hash);
 }
 
 void jabber_presence_subscription_set(JabberStream *js, const char *who, const char *type)
@@ -1140,3 +1099,170 @@
 			*priority = purple_status_get_attr_int(status, "priority");
 	}
 }
+
+/* Incoming presence handlers */
+static void
+parse_priority(JabberStream *js, JabberPresence *presence, xmlnode *priority)
+{
+	char *p = xmlnode_get_data(priority);
+
+	if (presence->priority != 0)
+		purple_debug_warning("jabber", "presence stanza received with multiple "
+		                     "priority children!?\n");
+
+	if (p) {
+		presence->priority = atoi(p);
+		g_free(p);
+	} else
+		purple_debug_warning("jabber", "Empty <priority/> in presence!\n");
+}
+
+static void
+parse_show(JabberStream *js, JabberPresence *presence, xmlnode *show)
+{
+	char *cdata;
+
+	if (presence->type != JABBER_PRESENCE_AVAILABLE) {
+		purple_debug_warning("jabber", "<show/> present on presence, but "
+		                               "type is not default ('available')\n");
+		return;
+	}
+
+	cdata = xmlnode_get_data(show);
+	if (cdata) {
+		presence->state = jabber_buddy_show_get_state(cdata);
+		g_free(cdata);
+	} else
+		purple_debug_warning("jabber", "<show/> present on presence, but "
+		                               "no contents!\n");
+}
+
+static void
+parse_status(JabberStream *js, JabberPresence *presence, xmlnode *status)
+{
+	/* TODO: Check/track language attribute? */
+
+	g_free(presence->status);
+	presence->status = xmlnode_get_data(status);
+}
+
+static void
+parse_delay(JabberStream *js, JabberPresence *presence, xmlnode *delay)
+{
+	/* XXX: compare the time.  Can happen on presence stanzas that aren't
+	 * actually delayed.
+	 */
+	const char *stamp = xmlnode_get_attrib(delay, "stamp");
+	presence->delayed = TRUE;
+	presence->sent = purple_str_to_time(stamp, TRUE, NULL, NULL, NULL);
+}
+
+static void
+parse_idle(JabberStream *js, JabberPresence *presence, xmlnode *query)
+{
+	const gchar *seconds = xmlnode_get_attrib(query, "seconds");
+	if (seconds) {
+		presence->idle = atoi(seconds);
+		if (presence->idle < 0) {
+			purple_debug_warning("jabber", "Received bogus idle time %s\n", seconds);
+			presence->idle = 0;
+		}
+	}
+}
+
+static void
+parse_caps(JabberStream *js, JabberPresence *presence, xmlnode *c)
+{
+	/* TODO: Move the rest of the caps handling in here, after changing the
+	 * the "do we have details about this (node, ver) and exts" to not
+	 * require the jbr to be present (since that happens later).
+	 */
+	presence->caps = c;
+}
+
+static void
+parse_nickname(JabberStream *js, JabberPresence *presence, xmlnode *nick)
+{
+	g_free(presence->nickname);
+	presence->nickname = xmlnode_get_data(nick);
+}
+
+static void
+parse_vcard_avatar(JabberStream *js, JabberPresence *presence, xmlnode *x)
+{
+	xmlnode *photo = xmlnode_get_child(x, "photo");
+	if (photo) {
+		g_free(presence->vcard_avatar_hash);
+		presence->vcard_avatar_hash = xmlnode_get_data(photo);
+	}
+}
+
+static void
+parse_muc_user(JabberStream *js, JabberPresence *presence, xmlnode *x)
+{
+	xmlnode *status;
+
+	if (presence->chat == NULL) {
+		purple_debug_warning("jabber", "Ignoring MUC gloop on non-MUC presence\n");
+		return;
+	}
+
+	if (presence->chat->conv == NULL)
+		presence->chat->muc = TRUE;
+
+	for (status = xmlnode_get_child(x, "status"); status;
+			status = xmlnode_get_next_twin(status)) {
+		const char *code = xmlnode_get_attrib(status, "code");
+		int val;
+		if (!code)
+			continue;
+
+		val = atoi(code);
+		if (val == 0 || val < 0) {
+			purple_debug_warning("jabber", "Ignoring bogus status code '%s'\n",
+			                               code);
+			continue;
+		}
+
+		presence->chat_info.codes = g_slist_prepend(presence->chat_info.codes, GINT_TO_POINTER(val));
+	}
+
+	presence->chat_info.item = xmlnode_get_child(x, "item");
+}
+
+void jabber_presence_register_handler(const char *node, const char *xmlns,
+                                      JabberPresenceHandler *handler)
+{
+	/*
+	 * This is valid because nodes nor namespaces cannot have spaces in them
+	 * (see http://www.w3.org/TR/2006/REC-xml-20060816/ and
+	 * http://www.w3.org/TR/REC-xml-names/)
+	 */
+	char *key = g_strdup_printf("%s %s", node, xmlns);
+	g_hash_table_replace(presence_handlers, key, handler);
+}
+
+void jabber_presence_init(void)
+{
+	presence_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+	/* Core RFC things */
+	jabber_presence_register_handler("priority", "jabber:client", parse_priority);
+	jabber_presence_register_handler("show", "jabber:client", parse_show);
+	jabber_presence_register_handler("status", "jabber:client", parse_status);
+
+	/* XEPs */
+	jabber_presence_register_handler("c", "http://jabber.org/protocol/caps", parse_caps);
+	jabber_presence_register_handler("delay", NS_DELAYED_DELIVERY, parse_delay);
+	jabber_presence_register_handler("nick", "http://jabber.org/protocol/nick", parse_nickname);
+	jabber_presence_register_handler("query", NS_LAST_ACTIVITY, parse_idle);
+	jabber_presence_register_handler("x", NS_DELAYED_DELIVERY_LEGACY, parse_delay);
+	jabber_presence_register_handler("x", "http://jabber.org/protocol/muc#user", parse_muc_user);
+	jabber_presence_register_handler("x", "vcard-temp:x:update", parse_vcard_avatar);
+}
+
+void jabber_presence_uninit(void)
+{
+	g_hash_table_destroy(presence_handlers);
+	presence_handlers = NULL;
+}
--- a/libpurple/protocols/jabber/presence.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/presence.h	Wed Mar 10 17:58:21 2010 +0000
@@ -24,10 +24,63 @@
 #ifndef PURPLE_JABBER_PRESENCE_H_
 #define PURPLE_JABBER_PRESENCE_H_
 
+typedef enum {
+	JABBER_PRESENCE_ERROR = -2,
+	JABBER_PRESENCE_PROBE = -1,
+	JABBER_PRESENCE_AVAILABLE,
+	JABBER_PRESENCE_UNAVAILABLE,
+	JABBER_PRESENCE_SUBSCRIBE,
+	JABBER_PRESENCE_SUBSCRIBED,
+	JABBER_PRESENCE_UNSUBSCRIBE,
+	JABBER_PRESENCE_UNSUBSCRIBED
+} JabberPresenceType;
+
+typedef struct _JabberPresenceChatInfo JabberPresenceChatInfo;
+typedef struct _JabberPresence JabberPresence;
+
 #include "buddy.h"
+#include "chat.h"
 #include "jabber.h"
+#include "jutil.h"
 #include "xmlnode.h"
 
+struct _JabberPresenceChatInfo {
+	GSList *codes;
+	xmlnode *item;
+};
+
+struct _JabberPresence {
+	JabberPresenceType type;
+	JabberID *jid_from;
+	const char *from;
+	const char *to;
+	const char *id;
+
+	JabberBuddy *jb;
+	JabberChat *chat;
+	JabberPresenceChatInfo chat_info;
+	xmlnode *caps; /* TODO: Temporary, see presence.c:parse_caps */
+
+	JabberBuddyState state;
+	gchar *status;
+	int priority;
+
+	char *vcard_avatar_hash;
+	char *nickname;
+
+	gboolean delayed;
+	time_t sent;
+	int idle;
+};
+
+typedef void (JabberPresenceHandler)(JabberStream *js, JabberPresence *presence,
+                                     xmlnode *child);
+void jabber_presence_register_handler(const char *node, const char *xmlns,
+                                      JabberPresenceHandler *handler);
+
+void jabber_presence_init(void);
+void jabber_presence_uninit(void);
+
 void jabber_set_status(PurpleAccount *account, PurpleStatus *status);
 
 /**
--- a/libpurple/protocols/jabber/useravatar.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/useravatar.c	Wed Mar 10 17:58:21 2010 +0000
@@ -239,16 +239,12 @@
 
 void jabber_avatar_fetch_mine(JabberStream *js)
 {
-	char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
-
 	if (js->initial_avatar_hash) {
-		jabber_pep_request_item(js, jid, NS_AVATAR_0_12_METADATA, NULL,
+		jabber_pep_request_item(js, NULL, NS_AVATAR_0_12_METADATA, NULL,
 		                        do_got_own_avatar_0_12_cb);
-		jabber_pep_request_item(js, jid, NS_AVATAR_1_1_METADATA, NULL,
+		jabber_pep_request_item(js, NULL, NS_AVATAR_1_1_METADATA, NULL,
 		                        do_got_own_avatar_cb);
 	}
-
-	g_free(jid);
 }
 
 typedef struct _JabberBuddyAvatarUpdateURLInfo {
--- a/libpurple/protocols/jabber/usermood.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/usermood.c	Wed Mar 10 17:58:21 2010 +0000
@@ -75,8 +75,8 @@
 	{"hungry", N_("Hungry"), NULL},
 	{"hurt", N_("Hurt"), NULL},
 	{"impressed", N_("Impressed"), NULL},
-	{"in_awe", N_("In_awe"), NULL},
-	{"in_love", N_("In_love"), NULL},
+	{"in_awe", N_("In awe"), NULL},
+	{"in_love", N_("In love"), NULL},
 	{"indignant", N_("Indignant"), NULL},
 	{"interested", N_("Interested"), NULL},
 	{"intoxicated", N_("Intoxicated"), NULL},
@@ -119,10 +119,6 @@
 	{NULL, NULL, NULL}
 };
 
-static PurpleMood empty_moods[] = {
-	{NULL, NULL, NULL}
-};
-
 static void jabber_mood_cb(JabberStream *js, const char *from, xmlnode *items) {
 	/* it doesn't make sense to have more than one item here, so let's just pick the first one */
 	xmlnode *item = xmlnode_get_child(items, "item");
@@ -174,78 +170,17 @@
 	jabber_pep_register_handler("http://jabber.org/protocol/mood", jabber_mood_cb);
 }
 
-static void do_mood_set_from_fields(PurpleConnection *gc, PurpleRequestFields *fields) {
-	JabberStream *js;
-	const int max_mood_idx = sizeof(moods) / sizeof(moods[0]) - 1;
-	int selected_mood = purple_request_fields_get_choice(fields, "mood");
-
-	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
-		purple_debug_error("jabber", "Unable to set mood; account offline.\n");
-		return;
-	}
-
-	js = gc->proto_data;
-
-	if (selected_mood < 0 || selected_mood >= max_mood_idx) {
-		purple_debug_error("jabber", "Invalid mood index (%d) selected.\n", selected_mood);
-		return;
-	}
-
-	jabber_mood_set(js, moods[selected_mood].mood, purple_request_fields_get_string(fields, "text"));
-}
-
-static void do_mood_set_mood(PurplePluginAction *action) {
-	PurpleConnection *gc = (PurpleConnection *) action->context;
-
-	PurpleRequestFields *fields;
-	PurpleRequestFieldGroup *group;
-	PurpleRequestField *field;
-	int i;
-
-	fields = purple_request_fields_new();
-	group = purple_request_field_group_new(NULL);
-	purple_request_fields_add_group(fields, group);
-
-	field = purple_request_field_choice_new("mood",
-											_("Mood"), 0);
-
-	for(i = 0; moods[i].mood; ++i)
-		purple_request_field_choice_add(field, _(moods[i].description));
-
-	purple_request_field_set_required(field, TRUE);
-	purple_request_field_group_add_field(group, field);
-
-	field = purple_request_field_string_new("text",
-											_("Description"), NULL,
-											FALSE);
-	purple_request_field_group_add_field(group, field);
-
-	purple_request_fields(gc, _("Edit User Mood"),
-						  _("Edit User Mood"),
-						  _("Please select your mood from the list."),
-						  fields,
-						  _("Set"), G_CALLBACK(do_mood_set_from_fields),
-						  _("Cancel"), NULL,
-						  purple_connection_get_account(gc), NULL, NULL,
-						  gc);
-
-}
-
-void jabber_mood_init_action(GList **m) {
-	PurplePluginAction *act = purple_plugin_action_new(_("Set Mood..."), do_mood_set_mood);
-	*m = g_list_append(*m, act);
-}
-
 void jabber_mood_set(JabberStream *js, const char *mood, const char *text) {
 	xmlnode *publish, *moodnode;
 
-	g_return_if_fail(mood != NULL);
-
 	publish = xmlnode_new("publish");
 	xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/mood");
 	moodnode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "mood");
 	xmlnode_set_namespace(moodnode, "http://jabber.org/protocol/mood");
-	xmlnode_new_child(moodnode, mood);
+	if (mood) {
+		/* if mood is NULL, set an empty mood node, meaning: unset mood */
+	    xmlnode_new_child(moodnode, mood);
+	}
 
 	if (text && text[0] != '\0') {
 		xmlnode *textnode = xmlnode_new_child(moodnode, "text");
@@ -259,14 +194,5 @@
 
 PurpleMood *jabber_get_moods(PurpleAccount *account)
 {
-	PurpleConnection *gc = purple_account_get_connection(account);
-	JabberStream *js = (JabberStream *) gc->proto_data;
-
-	if (js->pep) {
-		purple_debug_info("jabber", "get_moods: account supports PEP\n");
-		return moods;
-	} else {
-		purple_debug_info("jabber", "get_moods: account doesn't support PEP\n");
-		return empty_moods;
-	}
+	return moods;
 }
\ No newline at end of file
--- a/libpurple/protocols/jabber/usermood.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/usermood.h	Wed Mar 10 17:58:21 2010 +0000
@@ -30,8 +30,6 @@
 
 void jabber_mood_init(void);
 
-void jabber_mood_init_action(GList **m);
-
 void jabber_mood_set(JabberStream *js,
 		     const char *mood, /* must be one of the valid strings defined in the XEP */
 		     const char *text /* might be NULL */);
--- a/libpurple/protocols/jabber/usernick.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/jabber/usernick.c	Wed Mar 10 17:58:21 2010 +0000
@@ -86,14 +86,12 @@
 }
 
 static void do_nick_set_nick(PurplePluginAction *action) {
-	PurpleConnection *gc = (PurpleConnection *) action->context;
-	JabberStream *js = gc->proto_data;
-	char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
+	PurpleConnection *gc = action->context;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 
 	/* since the nickname might have been changed by another resource of this account, we always have to request the old one
 		from the server to present as the default for the new one */
-	jabber_pep_request_item(js, jid, "http://jabber.org/protocol/nick", NULL, do_nick_got_own_nick_cb);
-	g_free(jid);
+	jabber_pep_request_item(js, NULL, "http://jabber.org/protocol/nick", NULL, do_nick_got_own_nick_cb);
 }
 
 void jabber_nick_init(void) {
--- a/libpurple/protocols/msn/slp.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/msn/slp.c	Wed Mar 10 17:58:21 2010 +0000
@@ -308,8 +308,6 @@
 	return NULL;
 }
 
-#define MAX_FILE_NAME_LEN 0x226
-
 static void
 got_sessionreq(MsnSlpCall *slpcall, const char *branch,
 			   const char *euf_guid, const char *context)
@@ -382,7 +380,7 @@
 		/* File Transfer */
 		PurpleAccount *account;
 		PurpleXfer *xfer;
-		char *bin;
+		MsnFileContext *header;
 		gsize bin_len;
 		guint32 file_size;
 		char *file_name;
@@ -396,16 +394,18 @@
 
 		xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE,
 							 slpcall->slplink->remote_user);
-		if (xfer)
-		{
-			bin = (char *)purple_base64_decode(context, &bin_len);
-			file_size = GUINT32_FROM_LE(*(gsize *)(bin + 8));
 
-			file_name = g_convert(bin + 20, MAX_FILE_NAME_LEN, "UTF-8", "UTF-16LE",
+		header = (MsnFileContext *)purple_base64_decode(context, &bin_len);
+		if (bin_len >= sizeof(MsnFileContext) - 1 &&
+			(header->version == 2 ||
+			 (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) {
+			file_size = GUINT64_FROM_LE(header->file_size);
+
+			file_name = g_convert((const gchar *)&header->file_name,
+			                      MAX_FILE_NAME_LEN * 2,
+			                      "UTF-8", "UTF-16LE",
 			                      NULL, NULL, NULL);
 
-			g_free(bin);
-
 			purple_xfer_set_filename(xfer, file_name ? file_name : "");
 			g_free(file_name);
 			purple_xfer_set_size(xfer, file_size);
@@ -424,6 +424,7 @@
 
 			purple_xfer_request(xfer);
 		}
+		g_free(header);
 
 		accepted = TRUE;
 
--- a/libpurple/protocols/msn/slp.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/msn/slp.h	Wed Mar 10 17:58:21 2010 +0000
@@ -30,6 +30,25 @@
 #include "session.h"
 #include "slpcall.h"
 
+#define MAX_FILE_NAME_LEN 260 /* MAX_PATH in Windows */
+
+/**
+ * The context data for a file transfer request
+ */
+#pragma pack(push,1) /* Couldn't they have made it the right size? */
+typedef struct
+{
+	guint32   length;       /*< Length of header */
+	guint32   version;      /*< MSN version */
+	guint64   file_size;    /*< Size of file */
+	guint32   type;         /*< Transfer type */
+	gunichar2 file_name[MAX_FILE_NAME_LEN]; /*< Self-explanatory */
+	gchar     unknown1[30]; /*< Used somehow for background sharing */
+	guint32   unknown2;     /*< Possibly for background sharing as well */
+	gchar     preview[1];   /*< File preview data, 96x96 PNG */
+} MsnFileContext;
+#pragma pack(pop)
+
 MsnSlpCall * msn_slp_sip_recv(MsnSlpLink *slplink,
 							  const char *body);
 
--- a/libpurple/protocols/msn/slplink.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/msn/slplink.c	Wed Mar 10 17:58:21 2010 +0000
@@ -307,6 +307,8 @@
 
 	slpmsg->offset += msg->msnslp_header.length;
 
+	slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
+
 	if (slpmsg->offset < real_size)
 	{
 		if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED)
@@ -331,8 +333,6 @@
 			}
 		}
 	}
-
-	slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
 }
 
 /* We have received the message nak. */
@@ -657,74 +657,51 @@
 	}
 }
 
-typedef struct
-{
-	guint32 length;
-	guint32 unk1;
-	guint32 file_size;
-	guint32 unk2;
-	guint32 unk3;
-} MsnContextHeader;
-
-#define MAX_FILE_NAME_LEN 0x226
-
 static gchar *
 gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
 {
 	gsize size = 0;
-	MsnContextHeader header;
+	MsnFileContext header;
 	gchar *u8 = NULL;
-	guchar *base;
-	guchar *n;
 	gchar *ret;
 	gunichar2 *uni = NULL;
 	glong currentChar = 0;
-	glong uni_len = 0;
-	gsize len;
+	glong len = 0;
 
 	size = purple_xfer_get_size(xfer);
 
-	if(!file_name) {
+	if (!file_name) {
 		gchar *basename = g_path_get_basename(file_path);
 		u8 = purple_utf8_try_convert(basename);
 		g_free(basename);
 		file_name = u8;
 	}
 
-	uni = g_utf8_to_utf16(file_name, -1, NULL, &uni_len, NULL);
+	uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL);
 
-	if(u8) {
+	if (u8) {
 		g_free(u8);
 		file_name = NULL;
 		u8 = NULL;
 	}
 
-	len = sizeof(MsnContextHeader) + MAX_FILE_NAME_LEN + 4;
-
-	header.length = GUINT32_TO_LE(len);
-	header.unk1 = GUINT32_TO_LE(2);
-	header.file_size = GUINT32_TO_LE(size);
-	header.unk2 = GUINT32_TO_LE(0);
-	header.unk3 = GUINT32_TO_LE(0);
-
-	base = g_malloc(len + 1);
-	n = base;
+	header.length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1);
+	header.version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */
+	header.file_size = GUINT64_TO_LE(size);
+	header.type = GUINT32_TO_LE(1);    /* No file preview */
 
-	memcpy(n, &header, sizeof(MsnContextHeader));
-	n += sizeof(MsnContextHeader);
+	len = MIN(len, MAX_FILE_NAME_LEN);
+	for (currentChar = 0; currentChar < len; currentChar++) {
+		header.file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]);
+	}
+	memset(&header.file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2);
 
-	memset(n, 0x00, MAX_FILE_NAME_LEN);
-	for(currentChar = 0; currentChar < uni_len; currentChar++) {
-		*((gunichar2 *)n + currentChar) = GUINT16_TO_LE(uni[currentChar]);
-	}
-	n += MAX_FILE_NAME_LEN;
-
-	memset(n, 0xFF, 4);
-	n += 4;
+	memset(&header.unknown1, 0, sizeof(header.unknown1));
+	header.unknown2 = GUINT32_TO_LE(0xffffffff);
+	header.preview[0] = '\0';
 
 	g_free(uni);
-	ret = purple_base64_encode(base, len);
-	g_free(base);
+	ret = purple_base64_encode((const guchar *)&header, sizeof(MsnFileContext));
 	return ret;
 }
 
--- a/libpurple/protocols/msnp9/Makefile.am	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-EXTRA_DIST = \
-	Makefile.mingw
-
-pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
-
-MSNP9SOURCES = \
-	cmdproc.c \
-	cmdproc.h \
-	command.c \
-	command.h \
-	dialog.c \
-	dialog.h \
-	directconn.c \
-	directconn.h \
-	error.c \
-	error.h \
-	group.c \
-	group.h \
-	history.c \
-	history.h \
-	httpconn.c \
-	httpconn.h \
-	msg.c \
-	msg.h \
-	msn.c \
-	msn.h \
-	nexus.c \
-	nexus.h \
-	notification.c \
-	notification.h \
-	object.c \
-	object.h \
-	page.c \
-	page.h \
-	servconn.c \
-	servconn.h \
-	session.c \
-	session.h \
-	slp.c \
-	slp.h \
-	slpcall.c \
-	slpcall.h \
-	slplink.c \
-	slplink.h \
-	slpmsg.c \
-	slpmsg.h \
-	slpsession.c \
-	slpsession.h \
-	state.c \
-	state.h \
-	switchboard.c \
-	switchboard.h \
-	sync.c \
-	sync.h \
-	table.c \
-	table.h \
-	transaction.c \
-	transaction.h \
-	user.c \
-	user.h \
-	userlist.c \
-	userlist.h \
-	msn-utils.c \
-	msn-utils.h
-
-AM_CFLAGS = $(st)
-
-libmsn_la_LDFLAGS = -module -avoid-version
-
-if STATIC_MSN
-
-st = -DPURPLE_STATIC_PRPL
-noinst_LTLIBRARIES = libmsn.la
-libmsn_la_SOURCES  = $(MSNP9SOURCES)
-libmsn_la_CFLAGS   = $(AM_CFLAGS)
-
-else
-
-st =
-pkg_LTLIBRARIES   = libmsn.la
-libmsn_la_SOURCES = $(MSNP9SOURCES)
-libmsn_la_LIBADD  = $(GLIB_LIBS)
-
-endif
-
-AM_CPPFLAGS = \
-	-I$(top_srcdir)/libpurple \
-	-I$(top_builddir)/libpurple \
-	$(GLIB_CFLAGS) \
-	$(DEBUG_CFLAGS)
--- a/libpurple/protocols/msnp9/Makefile.mingw	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-#
-# Makefile.mingw
-#
-# Description: Makefile for win32 (mingw) version of libmsn
-#
-
-PIDGIN_TREE_TOP := ../../..
-include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
-
-TARGET = libmsn
-TYPE = PLUGIN
-
-# Static or Plugin...
-ifeq ($(TYPE),STATIC)
-  DEFINES += -DSTATIC
-  DLL_INSTALL_DIR =	$(PURPLE_INSTALL_DIR)
-else
-ifeq ($(TYPE),PLUGIN)
-  DLL_INSTALL_DIR =	$(PURPLE_INSTALL_PLUGINS_DIR)
-endif
-endif
-
-##
-## INCLUDE PATHS
-##
-INCLUDE_PATHS +=	-I. \
-			-I$(GTK_TOP)/include \
-			-I$(GTK_TOP)/include/glib-2.0 \
-			-I$(GTK_TOP)/lib/glib-2.0/include \
-			-I$(PURPLE_TOP) \
-			-I$(PURPLE_TOP)/win32 \
-			-I$(PIDGIN_TREE_TOP)
-
-LIB_PATHS +=		-L$(GTK_TOP)/lib \
-			-L$(PURPLE_TOP)
-
-##
-##  SOURCES, OBJECTS
-##
-C_SRC =			cmdproc.c \
-			command.c \
-			dialog.c \
-			directconn.c \
-			error.c \
-			group.c \
-			history.c \
-			httpconn.c \
-			msg.c \
-			msn.c \
-			nexus.c \
-			notification.c \
-			object.c \
-			page.c \
-			servconn.c \
-			session.c \
-			slp.c \
-			slpcall.c \
-			slplink.c \
-			slpmsg.c \
-			slpsession.c \
-			state.c \
-			switchboard.c \
-			sync.c \
-			table.c \
-			transaction.c \
-			user.c \
-			userlist.c \
-			msn-utils.c
-
-OBJECTS = $(C_SRC:%.c=%.o)
-
-##
-## LIBRARIES
-##
-LIBS =	\
-			-lglib-2.0 \
-			-lintl \
-			-lws2_32 \
-			-lpurple
-
-include $(PIDGIN_COMMON_RULES)
-
-##
-## TARGET DEFINITIONS
-##
-.PHONY: all install clean
-
-all: $(TARGET).dll
-
-install: all $(DLL_INSTALL_DIR)
-	cp $(TARGET).dll $(DLL_INSTALL_DIR)
-
-$(OBJECTS): $(PURPLE_CONFIG_H)
-
-$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS)
-	$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll
-
-##
-## CLEAN RULES
-##
-clean:
-	rm -f $(OBJECTS)
-	rm -f $(TARGET).dll
-
-include $(PIDGIN_COMMON_TARGETS)
--- a/libpurple/protocols/msnp9/cmdproc.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,344 +0,0 @@
-/**
- * @file cmdproc.c MSN command processor functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "cmdproc.h"
-
-MsnCmdProc *
-msn_cmdproc_new(MsnSession *session)
-{
-	MsnCmdProc *cmdproc;
-
-	cmdproc = g_new0(MsnCmdProc, 1);
-
-	cmdproc->session = session;
-	cmdproc->txqueue = g_queue_new();
-	cmdproc->history = msn_history_new();
-
-	return cmdproc;
-}
-
-void
-msn_cmdproc_destroy(MsnCmdProc *cmdproc)
-{
-	MsnTransaction *trans;
-
-	while ((trans = g_queue_pop_head(cmdproc->txqueue)) != NULL)
-		msn_transaction_destroy(trans);
-
-	g_queue_free(cmdproc->txqueue);
-
-	msn_history_destroy(cmdproc->history);
-
-	if (cmdproc->last_cmd != NULL)
-		msn_command_destroy(cmdproc->last_cmd);
-
-	g_free(cmdproc);
-}
-
-void
-msn_cmdproc_process_queue(MsnCmdProc *cmdproc)
-{
-	MsnTransaction *trans;
-
-	while ((trans = g_queue_pop_head(cmdproc->txqueue)) != NULL)
-		msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-void
-msn_cmdproc_queue_trans(MsnCmdProc *cmdproc, MsnTransaction *trans)
-{
-	g_return_if_fail(cmdproc != NULL);
-	g_return_if_fail(trans   != NULL);
-
-	g_queue_push_tail(cmdproc->txqueue, trans);
-}
-
-static void
-show_debug_cmd(MsnCmdProc *cmdproc, gboolean incoming, const char *command)
-{
-	MsnServConn *servconn;
-	const char *names[] = { "NS", "SB" };
-	char *show;
-	char tmp;
-	size_t len;
-
-	servconn = cmdproc->servconn;
-	len = strlen(command);
-	show = g_strdup(command);
-
-	tmp = (incoming) ? 'S' : 'C';
-
-	if ((show[len - 1] == '\n') && (show[len - 2] == '\r'))
-	{
-		show[len - 2] = '\0';
-	}
-
-	purple_debug_misc("msn", "%c: %s %03d: %s\n", tmp,
-					names[servconn->type], servconn->num, show);
-
-	g_free(show);
-}
-
-void
-msn_cmdproc_send_trans(MsnCmdProc *cmdproc, MsnTransaction *trans)
-{
-	MsnServConn *servconn;
-	char *data;
-	size_t len;
-
-	g_return_if_fail(cmdproc != NULL);
-	g_return_if_fail(trans != NULL);
-
-	servconn = cmdproc->servconn;
-
-	if (!servconn->connected)
-		return;
-
-	msn_history_add(cmdproc->history, trans);
-
-	data = msn_transaction_to_string(trans);
-
-	len = strlen(data);
-
-	show_debug_cmd(cmdproc, FALSE, data);
-
-	if (trans->callbacks == NULL)
-		trans->callbacks = g_hash_table_lookup(cmdproc->cbs_table->cmds,
-											   trans->command);
-
-	if (trans->payload != NULL)
-	{
-		data = g_realloc(data, len + trans->payload_len);
-		memcpy(data + len, trans->payload, trans->payload_len);
-		len += trans->payload_len;
-
-		/*
-		 * We're done with trans->payload.  Free it so that the memory
-		 * doesn't sit around in cmdproc->history.
-		 */
-		g_free(trans->payload);
-		trans->payload = NULL;
-		trans->payload_len = 0;
-	}
-
-	msn_servconn_write(servconn, data, len);
-
-	g_free(data);
-}
-
-void
-msn_cmdproc_send_quick(MsnCmdProc *cmdproc, const char *command,
-					   const char *format, ...)
-{
-	MsnServConn *servconn;
-	char *data;
-	char *params = NULL;
-	va_list arg;
-	size_t len;
-
-	g_return_if_fail(cmdproc != NULL);
-	g_return_if_fail(command != NULL);
-
-	servconn = cmdproc->servconn;
-
-	if (!servconn->connected)
-		return;
-
-	if (format != NULL)
-	{
-		va_start(arg, format);
-		params = g_strdup_vprintf(format, arg);
-		va_end(arg);
-	}
-
-	if (params != NULL)
-		data = g_strdup_printf("%s %s\r\n", command, params);
-	else
-		data = g_strdup_printf("%s\r\n", command);
-
-	g_free(params);
-
-	len = strlen(data);
-
-	show_debug_cmd(cmdproc, FALSE, data);
-
-	msn_servconn_write(servconn, data, len);
-
-	g_free(data);
-}
-
-void
-msn_cmdproc_send(MsnCmdProc *cmdproc, const char *command,
-				 const char *format, ...)
-{
-	MsnTransaction *trans;
-	va_list arg;
-
-	g_return_if_fail(cmdproc != NULL);
-	g_return_if_fail(command != NULL);
-
-	if (!cmdproc->servconn->connected)
-		return;
-
-	trans = g_new0(MsnTransaction, 1);
-
-	trans->command = g_strdup(command);
-
-	if (format != NULL)
-	{
-		va_start(arg, format);
-		trans->params = g_strdup_vprintf(format, arg);
-		va_end(arg);
-	}
-
-	msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-void
-msn_cmdproc_process_payload(MsnCmdProc *cmdproc, char *payload,
-							int payload_len)
-{
-	MsnCommand *last;
-
-	g_return_if_fail(cmdproc != NULL);
-
-	last = cmdproc->last_cmd;
-	last->payload = g_memdup(payload, payload_len);
-	last->payload_len = payload_len;
-
-	if (last->payload_cb != NULL)
-		last->payload_cb(cmdproc, last, payload, payload_len);
-}
-
-void
-msn_cmdproc_process_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	MsnMsgTypeCb cb;
-
-	if (msn_message_get_content_type(msg) == NULL)
-	{
-		purple_debug_misc("msn", "failed to find message content\n");
-		return;
-	}
-
-	cb = g_hash_table_lookup(cmdproc->cbs_table->msgs,
-							 msn_message_get_content_type(msg));
-
-	if (cb == NULL)
-	{
-		purple_debug_warning("msn", "Unhandled content-type '%s'\n",
-						   msn_message_get_content_type(msg));
-
-		return;
-	}
-
-	cb(cmdproc, msg);
-}
-
-void
-msn_cmdproc_process_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnTransCb cb = NULL;
-	MsnTransaction *trans = NULL;
-
-	if (cmd->trId)
-		trans = msn_history_find(cmdproc->history, cmd->trId);
-
-	if (trans != NULL)
-		if (trans->timer)
-			purple_timeout_remove(trans->timer);
-
-	if (g_ascii_isdigit(cmd->command[0]))
-	{
-		if (trans != NULL)
-		{
-			MsnErrorCb error_cb = NULL;
-			int error;
-
-			error = atoi(cmd->command);
-
-			if (trans->error_cb != NULL)
-				error_cb = trans->error_cb;
-
-			if (error_cb == NULL && cmdproc->cbs_table->errors != NULL)
-				error_cb = g_hash_table_lookup(cmdproc->cbs_table->errors, trans->command);
-
-			if (error_cb != NULL)
-			{
-				error_cb(cmdproc, trans, error);
-			}
-			else
-			{
-#if 1
-				msn_error_handle(cmdproc->session, error);
-#else
-				purple_debug_warning("msn", "Unhandled error '%s'\n",
-								   cmd->command);
-#endif
-			}
-
-			return;
-		}
-	}
-
-	if (cmdproc->cbs_table->async != NULL)
-		cb = g_hash_table_lookup(cmdproc->cbs_table->async, cmd->command);
-
-	if (cb == NULL && trans != NULL)
-	{
-		cmd->trans = trans;
-
-		if (trans->callbacks != NULL)
-			cb = g_hash_table_lookup(trans->callbacks, cmd->command);
-	}
-
-	if (cb == NULL && cmdproc->cbs_table->fallback != NULL)
-		cb = g_hash_table_lookup(cmdproc->cbs_table->fallback, cmd->command);
-
-	if (cb != NULL)
-	{
-		cb(cmdproc, cmd);
-	}
-	else
-	{
-		purple_debug_warning("msn", "Unhandled command '%s'\n",
-						   cmd->command);
-	}
-
-	if (trans != NULL && trans->pendent_cmd != NULL)
-		msn_transaction_unqueue_cmd(trans, cmdproc);
-}
-
-void
-msn_cmdproc_process_cmd_text(MsnCmdProc *cmdproc, const char *command)
-{
-	show_debug_cmd(cmdproc, TRUE, command);
-
-	if (cmdproc->last_cmd != NULL)
-		msn_command_destroy(cmdproc->last_cmd);
-
-	cmdproc->last_cmd = msn_command_from_string(command);
-
-	msn_cmdproc_process_cmd(cmdproc, cmdproc->last_cmd);
-}
--- a/libpurple/protocols/msnp9/cmdproc.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/**
- * @file cmdproc.h MSN command processor functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_CMDPROC_H_
-#define _MSN_CMDPROC_H_
-
-typedef struct _MsnCmdProc MsnCmdProc;
-
-#include "session.h"
-#include "servconn.h"
-#include "error.h"
-#include "command.h"
-#include "table.h"
-#include "history.h"
-
-struct _MsnCmdProc
-{
-	MsnSession *session;
-	MsnServConn *servconn;
-
-	GQueue *txqueue;
-
-	MsnCommand *last_cmd;
-
-	MsnTable *cbs_table;
-
-	MsnHistory *history;
-
-	void *data; /**< Extra data, like the switchboard. */
-};
-
-MsnCmdProc *msn_cmdproc_new(MsnSession *session);
-void msn_cmdproc_destroy(MsnCmdProc *cmdproc);
-
-void msn_cmdproc_process_queue(MsnCmdProc *cmdproc);
-
-void msn_cmdproc_send_trans(MsnCmdProc *cmdproc, MsnTransaction *trans);
-void msn_cmdproc_queue_trans(MsnCmdProc *cmdproc,
-							 MsnTransaction *trans);
-void msn_cmdproc_send(MsnCmdProc *cmdproc, const char *command,
-					  const char *format, ...);
-void msn_cmdproc_send_quick(MsnCmdProc *cmdproc, const char *command,
-							const char *format, ...);
-
-void msn_cmdproc_process_msg(MsnCmdProc *cmdproc,
-							 MsnMessage *msg);
-void msn_cmdproc_process_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd);
-void msn_cmdproc_process_cmd_text(MsnCmdProc *cmdproc, const char *command);
-void msn_cmdproc_process_payload(MsnCmdProc *cmdproc,
-								 char *payload, int payload_len);
-
-void msn_cmdproc_disconnect(MsnCmdProc *cmdproc);
-
-#endif /* _MSN_CMDPROC_H_ */
--- a/libpurple/protocols/msnp9/command.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/**
- * @file command.c MSN command functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "command.h"
-
-static gboolean
-is_num(char *str)
-{
-	char *c;
-	for (c = str; *c; c++) {
-		if (!(g_ascii_isdigit(*c)))
-			return FALSE;
-	}
-
-	return TRUE;
-}
-
-MsnCommand *
-msn_command_from_string(const char *string)
-{
-	MsnCommand *cmd;
-	char *tmp;
-	char *param_start;
-
-	g_return_val_if_fail(string != NULL, NULL);
-
-	tmp = g_strdup(string);
-	param_start = strchr(tmp, ' ');
-
-	cmd = g_new0(MsnCommand, 1);
-	cmd->command = tmp;
-
-	if (param_start)
-	{
-		*param_start++ = '\0';
-		cmd->params = g_strsplit(param_start, " ", 0);
-	}
-
-	if (cmd->params != NULL)
-	{
-		int c;
-
-		for (c = 0; cmd->params[c]; c++);
-		cmd->param_count = c;
-
-		if (cmd->param_count) {
-			char *param = cmd->params[0];
-			cmd->trId = is_num(param) ? atoi(param) : 0;
-		} else {
-			cmd->trId = 0;
-		}
-	}
-	else
-		cmd->trId = 0;
-
-	msn_command_ref(cmd);
-
-	return cmd;
-}
-
-void
-msn_command_destroy(MsnCommand *cmd)
-{
-	g_return_if_fail(cmd != NULL);
-
-	if (cmd->ref_count > 0)
-	{
-		msn_command_unref(cmd);
-		return;
-	}
-
-	if (cmd->payload != NULL)
-		g_free(cmd->payload);
-
-	g_free(cmd->command);
-	g_strfreev(cmd->params);
-	g_free(cmd);
-}
-
-MsnCommand *
-msn_command_ref(MsnCommand *cmd)
-{
-	g_return_val_if_fail(cmd != NULL, NULL);
-
-	cmd->ref_count++;
-	return cmd;
-}
-
-MsnCommand *
-msn_command_unref(MsnCommand *cmd)
-{
-	g_return_val_if_fail(cmd != NULL, NULL);
-	g_return_val_if_fail(cmd->ref_count > 0, NULL);
-
-	cmd->ref_count--;
-
-	if (cmd->ref_count == 0)
-	{
-		msn_command_destroy(cmd);
-		return NULL;
-	}
-
-	return cmd;
-}
--- a/libpurple/protocols/msnp9/command.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/**
- * @file command.h MSN command functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_COMMAND_H
-#define _MSN_COMMAND_H
-
-typedef struct _MsnCommand MsnCommand;
-
-#include "cmdproc.h"
-#include "transaction.h"
-
-typedef void (*MsnPayloadCb)(MsnCmdProc *cmdproc, MsnCommand *cmd,
-							 char *payload, size_t len);
-
-/**
- * A received command.
- */
-struct _MsnCommand
-{
-	unsigned int trId;
-
-	char *command;
-	char **params;
-	int param_count;
-
-	int ref_count;
-
-	MsnTransaction *trans;
-
-	char *payload;
-	size_t payload_len;
-
-	MsnPayloadCb payload_cb;
-};
-
-MsnCommand *msn_command_from_string(const char *string);
-void msn_command_destroy(MsnCommand *cmd);
-MsnCommand *msn_command_ref(MsnCommand *cmd);
-MsnCommand *msn_command_unref(MsnCommand *cmd);
-
-#endif /* _MSN_COMMAND_H */
--- a/libpurple/protocols/msnp9/dialog.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-/**
- * @file dialog.c Dialog functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-
-#include "msn.h"
-#include "dialog.h"
-
-typedef struct
-{
-	PurpleConnection *gc;
-	char *who;
-	char *group;
-	gboolean add;
-
-} MsnAddRemData;
-
-/* Remove the buddy referenced by the MsnAddRemData before the serverside list is changed.
- * If the buddy will be added, he'll be added back; if he will be removed, he won't be. */
-static void
-msn_complete_sync_issue(MsnAddRemData *data)
-{
-	PurpleBuddy *buddy;
-	PurpleGroup *group = NULL;
-
-	if (data->group != NULL)
-		group = purple_find_group(data->group);
-	
-	if (group != NULL)
-		buddy = purple_find_buddy_in_group(purple_connection_get_account(data->gc), data->who, group);
-	else
-		buddy = purple_find_buddy(purple_connection_get_account(data->gc), data->who);
-	
-	if (buddy != NULL)
-		purple_blist_remove_buddy(buddy);
-}
-
-static void
-msn_add_cb(MsnAddRemData *data)
-{
-	MsnSession *session;
-	MsnUserList *userlist;
-
-	msn_complete_sync_issue(data);
-
-	session = data->gc->proto_data;
-	userlist = session->userlist;
-
-	msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, data->group);
-
-	g_free(data->group);
-	g_free(data->who);
-	g_free(data);
-}
-
-static void
-msn_rem_cb(MsnAddRemData *data)
-{
-	MsnSession *session;
-	MsnUserList *userlist;
-
-	msn_complete_sync_issue(data);
-
-	session = data->gc->proto_data;
-	userlist = session->userlist;
-
-	msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->group);
-
-	g_free(data->group);
-	g_free(data->who);
-	g_free(data);
-}
-
-void
-msn_show_sync_issue(MsnSession *session, const char *passport,
-					const char *group_name)
-{
-	PurpleConnection *gc;
-	PurpleAccount *account;
-	MsnAddRemData *data;
-	char *msg, *reason;
-
-	account = session->account;
-	gc = purple_account_get_connection(account);
-
-	data        = g_new0(MsnAddRemData, 1);
-	data->who   = g_strdup(passport);
-	data->group = g_strdup(group_name);
-	data->gc    = gc;
-
-	msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"),
-						  purple_account_get_username(account),
-						  purple_account_get_protocol_name(account));
-
-	if (group_name != NULL)
-	{
-		reason = g_strdup_printf(_("%s on the local list is "
-								   "inside the group \"%s\" but not on "
-								   "the server list. "
-								   "Do you want this buddy to be added?"),
-								 passport, group_name);
-	}
-	else
-	{
-		reason = g_strdup_printf(_("%s is on the local list but "
-								   "not on the server list. "
-								   "Do you want this buddy to be added?"),
-								 passport);
-	}
-
-	purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE, 
-						purple_connection_get_account(gc), data->who, NULL,
-						data, 2,
-						_("Yes"), G_CALLBACK(msn_add_cb),
-						_("No"), G_CALLBACK(msn_rem_cb));
-
-	g_free(reason);
-	g_free(msg);
-}
--- a/libpurple/protocols/msnp9/dialog.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/**
- * @file dialog.h Dialog functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_DIALOG_H_
-#define _MSN_DIALOG_H_
-
-void msn_show_sync_issue(MsnSession *session, const char *passport,
-						 const char *group_name);
-
-#endif /* _MSN_DIALOG_H_ */
--- a/libpurple/protocols/msnp9/directconn.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,505 +0,0 @@
-/**
- * @file directconn.c MSN direct connection functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "directconn.h"
-
-#include "slp.h"
-#include "slpmsg.h"
-
-/**************************************************************************
- * Directconn Specific
- **************************************************************************/
-
-void
-msn_directconn_send_handshake(MsnDirectConn *directconn)
-{
-	MsnSlpLink *slplink;
-	MsnSlpMessage *slpmsg;
-
-	g_return_if_fail(directconn != NULL);
-
-	slplink = directconn->slplink;
-
-	slpmsg = msn_slpmsg_new(slplink);
-	slpmsg->flags = 0x100;
-
-	if (directconn->nonce != NULL)
-	{
-		guint32 t1;
-		guint16 t2;
-		guint16 t3;
-		guint16 t4;
-		guint64 t5;
-
-		sscanf (directconn->nonce, "%08X-%04hX-%04hX-%04hX-%012" G_GINT64_MODIFIER "X", &t1, &t2, &t3, &t4, &t5);
-
-		t1 = GUINT32_TO_LE(t1);
-		t2 = GUINT16_TO_LE(t2);
-		t3 = GUINT16_TO_LE(t3);
-		t4 = GUINT16_TO_BE(t4);
-		t5 = GUINT64_TO_BE(t5);
-
-		slpmsg->ack_id     = t1;
-		slpmsg->ack_sub_id = t2 | (t3 << 16);
-		slpmsg->ack_size   = t4 | t5;
-	}
-
-	g_free(directconn->nonce);
-
-	msn_slplink_send_slpmsg(slplink, slpmsg);
-
-	directconn->acked =TRUE;
-}
-
-/**************************************************************************
- * Connection Functions
- **************************************************************************/
-
-#if 0
-static int
-create_listener(int port)
-{
-	int fd;
-	int flags;
-	const int on = 1;
-
-#if 0
-	struct addrinfo hints;
-	struct addrinfo *c, *res;
-	char port_str[5];
-
-	snprintf(port_str, sizeof(port_str), "%d", port);
-
-	memset(&hints, 0, sizeof(hints));
-
-	hints.ai_flags = AI_PASSIVE;
-	hints.ai_family = AF_UNSPEC;
-	hints.ai_socktype = SOCK_STREAM;
-
-	if (getaddrinfo(NULL, port_str, &hints, &res) != 0)
-	{
-		purple_debug_error("msn", "Could not get address info: %s.\n",
-						 port_str);
-		return -1;
-	}
-
-	for (c = res; c != NULL; c = c->ai_next)
-	{
-		fd = socket(c->ai_family, c->ai_socktype, c->ai_protocol);
-
-		if (fd < 0)
-			continue;
-
-		setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
-		if (bind(fd, c->ai_addr, c->ai_addrlen) == 0)
-			break;
-
-		close(fd);
-	}
-
-	if (c == NULL)
-	{
-		purple_debug_error("msn", "Could not find socket: %s.\n", port_str);
-		return -1;
-	}
-
-	freeaddrinfo(res);
-#else
-	struct sockaddr_in sockin;
-
-	fd = socket(AF_INET, SOCK_STREAM, 0);
-
-	if (fd < 0)
-		return -1;
-
-	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0)
-	{
-		close(fd);
-		return -1;
-	}
-
-	memset(&sockin, 0, sizeof(struct sockaddr_in));
-	sockin.sin_family = AF_INET;
-	sockin.sin_port = htons(port);
-
-	if (bind(fd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0)
-	{
-		close(fd);
-		return -1;
-	}
-#endif
-
-	if (listen (fd, 4) != 0)
-	{
-		close (fd);
-		return -1;
-	}
-
-	flags = fcntl(fd, F_GETFL);
-	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-#ifndef _WIN32
-	fcntl(fd, F_SETFD, FD_CLOEXEC);
-#endif
-
-	return fd;
-}
-#endif
-
-static size_t
-msn_directconn_write(MsnDirectConn *directconn,
-					 const char *data, size_t len)
-{
-	char *buffer, *tmp;
-	size_t buf_size;
-	size_t ret;
-	guint32 sent_len;
-
-	g_return_val_if_fail(directconn != NULL, 0);
-
-	buf_size = len + 4;
-	buffer = tmp = g_malloc(buf_size);
-
-	sent_len = GUINT32_TO_LE(len);
-
-	memcpy(tmp, &sent_len, 4);
-	tmp += 4;
-	memcpy(tmp, data, len);
-	tmp += len;
-
-	ret = write(directconn->fd, buffer, buf_size);
-
-#ifdef DEBUG_DC
-	char *str;
-	str = g_strdup_printf("%s/msntest/w%.4d.bin", g_get_home_dir(), directconn->c);
-
-	FILE *tf = g_fopen(str, "w");
-	fwrite(buffer, 1, buf_size, tf);
-	fclose(tf);
-
-	g_free(str);
-#endif
-
-	g_free(buffer);
-
-#if 0
-	/* Let's write the length of the data. */
-	ret = write(directconn->fd, &len, sizeof(len));
-
-	/* Let's write the data. */
-	ret = write(directconn->fd, data, len);
-
-	char *str;
-	str = g_strdup_printf("/home/revo/msntest/w%.4d.bin", directconn->c);
-
-	FILE *tf = g_fopen(str, "w");
-	fwrite(&len, 1, sizeof(len), tf);
-	fwrite(data, 1, len, tf);
-	fclose(tf);
-
-	g_free(str);
-#endif
-
-	directconn->c++;
-
-	return ret;
-}
-
-#if 0
-void
-msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce)
-{
-	guint32 t1;
-	guint16 t2;
-	guint16 t3;
-	guint16 t4;
-	guint64 t5;
-
-	g_return_if_fail(directconn != NULL);
-	g_return_if_fail(nonce      != NULL);
-
-	sscanf (nonce, "%08X-%04hX-%04hX-%04hX-%012llX", &t1, &t2, &t3, &t4, &t5);
-
-	t1 = GUINT32_TO_LE(t1);
-	t2 = GUINT16_TO_LE(t2);
-	t3 = GUINT16_TO_LE(t3);
-	t4 = GUINT16_TO_BE(t4);
-	t5 = GUINT64_TO_BE(t5);
-
-	directconn->slpheader = g_new0(MsnSlpHeader, 1);
-
-	directconn->slpheader->ack_id     = t1;
-	directconn->slpheader->ack_sub_id = t2 | (t3 << 16);
-	directconn->slpheader->ack_size   = t4 | t5;
-}
-#endif
-
-void
-msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg)
-{
-	char *body;
-	size_t body_len;
-
-	body = msn_message_gen_slp_body(msg, &body_len);
-
-	msn_directconn_write(directconn, body, body_len);
-}
-
-static void
-msn_directconn_process_msg(MsnDirectConn *directconn, MsnMessage *msg)
-{
-	purple_debug_info("msn", "directconn: process_msg\n");
-
-	msn_slplink_process_msg(directconn->slplink, msg);
-}
-
-static void
-read_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnDirectConn* directconn;
-	char *body;
-	size_t len, body_len;
-
-	purple_debug_info("msn", "read_cb: %d, %d\n", source, cond);
-
-	directconn = data;
-
-	/* Let's read the length of the data. */
-	len = read(directconn->fd, &body_len, sizeof(body_len));
-
-	if (len <= 0)
-	{
-		/* ERROR */
-		purple_debug_error("msn", "error reading\n");
-
-		msn_directconn_destroy(directconn);
-
-		return;
-	}
-
-	body_len = GUINT32_FROM_LE(body_len);
-
-	purple_debug_info("msn", "body_len=%" G_GSIZE_FORMAT "\n", body_len);
-
-	if (body_len <= 0)
-	{
-		/* ERROR */
-		purple_debug_error("msn", "error reading\n");
-
-		msn_directconn_destroy(directconn);
-
-		return;
-	}
-
-	body = g_try_malloc(body_len);
-
-	if (body != NULL)
-	{
-		/* Let's read the data. */
-		len = read(directconn->fd, body, body_len);
-
-		purple_debug_info("msn", "len=%" G_GSIZE_FORMAT "\n", len);
-	}
-	else
-	{
-		purple_debug_error("msn", "Failed to allocate memory for read\n");
-		len = 0;
-	}
-
-	if (len > 0)
-	{
-		MsnMessage *msg;
-
-#ifdef DEBUG_DC
-		str = g_strdup_printf("/home/revo/msntest/r%.4d.bin", directconn->c);
-
-		FILE *tf = g_fopen(str, "w");
-		fwrite(body, 1, len, tf);
-		fclose(tf);
-
-		g_free(str);
-#endif
-
-		directconn->c++;
-
-		msg = msn_message_new_msnslp();
-		msn_message_parse_slp_body(msg, body, body_len);
-
-		msn_directconn_process_msg(directconn, msg);
-	}
-	else
-	{
-		/* ERROR */
-		purple_debug_error("msn", "error reading\n");
-
-		msn_directconn_destroy(directconn);
-	}
-
-	g_free(body);
-}
-
-static void
-connect_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnDirectConn* directconn;
-	int fd;
-
-	purple_debug_misc("msn", "directconn: connect_cb: %d\n", source);
-
-	directconn = data;
-	directconn->connect_data = NULL;
-
-	if (TRUE)
-	{
-		fd = source;
-	}
-	else
-	{
-		struct sockaddr_in client_addr;
-		socklen_t client;
-		fd = accept (source, (struct sockaddr *)&client_addr, &client);
-	}
-
-	directconn->fd = fd;
-
-	if (fd > 0)
-	{
-		directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, read_cb,
-										  directconn);
-
-		if (TRUE)
-		{
-			/* Send foo. */
-			msn_directconn_write(directconn, "foo", strlen("foo") + 1);
-
-			/* Send Handshake */
-			msn_directconn_send_handshake(directconn);
-		}
-		else
-		{
-		}
-	}
-	else
-	{
-		/* ERROR */
-		purple_debug_error("msn", "could not add input\n");
-
-		if (directconn->inpa)
-			purple_input_remove(directconn->inpa);
-
-		close(directconn->fd);
-	}
-}
-
-static void
-directconn_connect_cb(gpointer data, gint source, const gchar *error_message)
-{
-	if (error_message)
-		purple_debug_error("msn", "Error making direct connection: %s\n", error_message);
-
-	connect_cb(data, source, PURPLE_INPUT_READ);
-}
-
-gboolean
-msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port)
-{
-	MsnSession *session;
-
-	g_return_val_if_fail(directconn != NULL, FALSE);
-	g_return_val_if_fail(host       != NULL, TRUE);
-	g_return_val_if_fail(port        > 0,    FALSE);
-
-	session = directconn->slplink->session;
-
-#if 0
-	if (session->http_method)
-	{
-		servconn->http_data->gateway_host = g_strdup(host);
-	}
-#endif
-
-	directconn->connect_data = purple_proxy_connect(NULL, session->account,
-			host, port, directconn_connect_cb, directconn);
-
-	return (directconn->connect_data != NULL);
-}
-
-#if 0
-void
-msn_directconn_listen(MsnDirectConn *directconn)
-{
-	int port;
-	int fd;
-
-	port = 7000;
-
-	for (fd = -1; fd < 0;)
-		fd = create_listener(++port);
-
-	directconn->fd = fd;
-
-	directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, connect_cb,
-		directconn);
-
-	directconn->port = port;
-	directconn->c = 0;
-}
-#endif
-
-MsnDirectConn*
-msn_directconn_new(MsnSlpLink *slplink)
-{
-	MsnDirectConn *directconn;
-
-	directconn = g_new0(MsnDirectConn, 1);
-
-	directconn->slplink = slplink;
-
-	if (slplink->directconn != NULL)
-		purple_debug_info("msn", "got_transresp: LEAK\n");
-
-	slplink->directconn = directconn;
-
-	return directconn;
-}
-
-void
-msn_directconn_destroy(MsnDirectConn *directconn)
-{
-	if (directconn->connect_data != NULL)
-		purple_proxy_connect_cancel(directconn->connect_data);
-
-	if (directconn->inpa != 0)
-		purple_input_remove(directconn->inpa);
-
-	if (directconn->fd >= 0)
-		close(directconn->fd);
-
-	if (directconn->nonce != NULL)
-		g_free(directconn->nonce);
-
-	directconn->slplink->directconn = NULL;
-
-	g_free(directconn);
-}
--- a/libpurple/protocols/msnp9/directconn.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/**
- * @file directconn.h MSN direct connection functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_DIRECTCONN_H_
-#define _MSN_DIRECTCONN_H_
-
-typedef struct _MsnDirectConn MsnDirectConn;
-
-#include "slplink.h"
-#include "slp.h"
-#include "msg.h"
-
-struct _MsnDirectConn
-{
-	MsnSlpLink *slplink;
-	MsnSlpCall *initial_call;
-
-	PurpleProxyConnectData *connect_data;
-
-	gboolean acked;
-
-	char *nonce;
-
-	int fd;
-
-	int port;
-	int inpa;
-
-	int c;
-};
-
-MsnDirectConn *msn_directconn_new(MsnSlpLink *slplink);
-gboolean msn_directconn_connect(MsnDirectConn *directconn,
-								const char *host, int port);
-#if 0
-void msn_directconn_listen(MsnDirectConn *directconn);
-#endif
-void msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg);
-void msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce);
-void msn_directconn_destroy(MsnDirectConn *directconn);
-void msn_directconn_send_handshake(MsnDirectConn *directconn);
-
-#endif /* _MSN_DIRECTCONN_H_ */
--- a/libpurple/protocols/msnp9/error.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-/**
- * @file error.c Error functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "error.h"
-
-const char *
-msn_error_get_text(unsigned int type, gboolean *debug)
-{
-	static char msg[MSN_BUF_LEN];
-	*debug = FALSE;
-
-	switch (type) {
-		case 0:
-			g_snprintf(msg, sizeof(msg),
-					   _("Unable to parse message"));
-			*debug = TRUE;
-			break;
-		case 200:
-			g_snprintf(msg, sizeof(msg),
-					   _("Syntax Error (probably a client bug)"));
-			*debug = TRUE;
-			break;
-		case 201:
-			g_snprintf(msg, sizeof(msg),
-					   _("Invalid email address"));
-			break;
-		case 205:
-			g_snprintf(msg, sizeof(msg), _("User does not exist"));
-			break;
-		case 206:
-			g_snprintf(msg, sizeof(msg),
-					   _("Fully qualified domain name missing"));
-			break;
-		case 207:
-			g_snprintf(msg, sizeof(msg), _("Already logged in"));
-			break;
-		case 208:
-			g_snprintf(msg, sizeof(msg), _("Invalid username"));
-			break;
-		case 209:
-			g_snprintf(msg, sizeof(msg), _("Invalid friendly name"));
-			break;
-		case 210:
-			g_snprintf(msg, sizeof(msg), _("List full"));
-			break;
-		case 215:
-			g_snprintf(msg, sizeof(msg), _("Already there"));
-			*debug = TRUE;
-			break;
-		case 216:
-			g_snprintf(msg, sizeof(msg), _("Not on list"));
-			break;
-		case 217:
-			g_snprintf(msg, sizeof(msg), _("User is offline"));
-			break;
-		case 218:
-			g_snprintf(msg, sizeof(msg), _("Already in the mode"));
-			*debug = TRUE;
-			break;
-		case 219:
-			g_snprintf(msg, sizeof(msg), _("Already in opposite list"));
-			*debug = TRUE;
-			break;
-		case 223:
-			g_snprintf(msg, sizeof(msg), _("Too many groups"));
-			break;
-		case 224:
-			g_snprintf(msg, sizeof(msg), _("Invalid group"));
-			break;
-		case 225:
-			g_snprintf(msg, sizeof(msg), _("User not in group"));
-			break;
-		case 229:
-			g_snprintf(msg, sizeof(msg), _("Group name too long"));
-			break;
-		case 230:
-			g_snprintf(msg, sizeof(msg), _("Cannot remove group zero"));
-			*debug = TRUE;
-			break;
-		case 231:
-			g_snprintf(msg, sizeof(msg),
-					   _("Tried to add a user to a group "
-						 "that doesn't exist"));
-			break;
-		case 280:
-			g_snprintf(msg, sizeof(msg), _("Switchboard failed"));
-			*debug = TRUE;
-			break;
-		case 281:
-			g_snprintf(msg, sizeof(msg), _("Notify transfer failed"));
-			*debug = TRUE;
-			break;
-
-		case 300:
-			g_snprintf(msg, sizeof(msg), _("Required fields missing"));
-			*debug = TRUE;
-			break;
-		case 301:
-			g_snprintf(msg, sizeof(msg), _("Too many hits to a FND"));
-			*debug = TRUE;
-			break;
-		case 302:
-			g_snprintf(msg, sizeof(msg), _("Not logged in"));
-			break;
-
-		case 500:
-			g_snprintf(msg, sizeof(msg), _("Service temporarily unavailable"));
-			break;
-		case 501:
-			g_snprintf(msg, sizeof(msg), _("Database server error"));
-			*debug = TRUE;
-			break;
-		case 502:
-			g_snprintf(msg, sizeof(msg), _("Command disabled"));
-			*debug = TRUE;
-			break;
-		case 510:
-			g_snprintf(msg, sizeof(msg), _("File operation error"));
-			*debug = TRUE;
-			break;
-		case 520:
-			g_snprintf(msg, sizeof(msg), _("Memory allocation error"));
-			*debug = TRUE;
-			break;
-		case 540:
-			g_snprintf(msg, sizeof(msg), _("Wrong CHL value sent to server"));
-			*debug = TRUE;
-			break;
-
-		case 600:
-			g_snprintf(msg, sizeof(msg), _("Server busy"));
-			break;
-		case 601:
-			g_snprintf(msg, sizeof(msg), _("Server unavailable"));
-			break;
-		case 602:
-			g_snprintf(msg, sizeof(msg), _("Peer notification server down"));
-			*debug = TRUE;
-			break;
-		case 603:
-			g_snprintf(msg, sizeof(msg), _("Database connect error"));
-			*debug = TRUE;
-			break;
-		case 604:
-			g_snprintf(msg, sizeof(msg),
-					   _("Server is going down (abandon ship)"));
-			break;
-		case 605:
-			g_snprintf(msg, sizeof(msg), _("Server unavailable"));
-			break;
-
-		case 707:
-			g_snprintf(msg, sizeof(msg), _("Error creating connection"));
-			*debug = TRUE;
-			break;
-		case 710:
-			g_snprintf(msg, sizeof(msg),
-					   _("CVR parameters are either unknown or not allowed"));
-			*debug = TRUE;
-			break;
-		case 711:
-			g_snprintf(msg, sizeof(msg), _("Unable to write"));
-			break;
-		case 712:
-			g_snprintf(msg, sizeof(msg), _("Session overload"));
-			*debug = TRUE;
-			break;
-		case 713:
-			g_snprintf(msg, sizeof(msg), _("User is too active"));
-			break;
-		case 714:
-			g_snprintf(msg, sizeof(msg), _("Too many sessions"));
-			break;
-		case 715:
-			g_snprintf(msg, sizeof(msg), _("Passport not verified"));
-			break;
-		case 717:
-			g_snprintf(msg, sizeof(msg), _("Bad friend file"));
-			*debug = TRUE;
-			break;
-		case 731:
-			g_snprintf(msg, sizeof(msg), _("Not expected"));
-			*debug = TRUE;
-			break;
-
-		case 800:
-			g_snprintf(msg, sizeof(msg),
-					   _("Friendly name changes too rapidly"));
-			break;
-
-		case 910:
-		case 912:
-		case 918:
-		case 919:
-		case 921:
-		case 922:
-			g_snprintf(msg, sizeof(msg), _("Server too busy"));
-			break;
-		case 911:
-		case 917:
-			g_snprintf(msg, sizeof(msg), _("Authentication failed"));
-			break;
-		case 913:
-			g_snprintf(msg, sizeof(msg), _("Not allowed when offline"));
-			break;
-		case 914:
-		case 915:
-		case 916:
-			g_snprintf(msg, sizeof(msg), _("Server unavailable"));
-			break;
-		case 920:
-			g_snprintf(msg, sizeof(msg), _("Not accepting new users"));
-			break;
-		case 923:
-			g_snprintf(msg, sizeof(msg),
-					   _("Kids Passport without parental consent"));
-			break;
-		case 924:
-			g_snprintf(msg, sizeof(msg),
-					   _("Passport account not yet verified"));
-			break;
-		case 928:
-			g_snprintf(msg, sizeof(msg), _("Bad ticket"));
-			*debug = TRUE;
-			break;
-
-		default:
-			g_snprintf(msg, sizeof(msg), _("Unknown Error Code %d"), type);
-			*debug = TRUE;
-			break;
-	}
-
-	return msg;
-}
-
-void
-msn_error_handle(MsnSession *session, unsigned int type)
-{
-	char buf[MSN_BUF_LEN];
-	gboolean debug;
-	
-	g_snprintf(buf, sizeof(buf), _("MSN Error: %s\n"),
-			   msn_error_get_text(type, &debug));
-	if (debug)
-		purple_debug_warning("msn", "error %d: %s\n", type, buf);
-	else
-		purple_notify_error(session->account->gc, NULL, buf, NULL);
-}
--- a/libpurple/protocols/msnp9/error.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/**
- * @file error.h Error functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_ERROR_H_
-#define _MSN_ERROR_H_
-
-#include "session.h"
-
-/**
- * Returns the string representation of an error type.
- *
- * @param type The error type.
- * @param debug Whether this should be treated as a debug log message or a user-visible error
- *
- * @return The string representation of the error type.
- */
-const char *msn_error_get_text(unsigned int type, gboolean *debug);
-
-/**
- * Handles an error.
- *
- * @param session The current session.
- * @param type    The error type.
- */
-void msn_error_handle(MsnSession *session, unsigned int type);
-
-#endif /* _MSN_ERROR_H_ */
--- a/libpurple/protocols/msnp9/group.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-/**
- * @file group.c Group functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "group.h"
-
-MsnGroup *
-msn_group_new(MsnUserList *userlist, int id, const char *name)
-{
-	MsnGroup *group;
-
-	g_return_val_if_fail(id >= 0,      NULL);
-	g_return_val_if_fail(name != NULL, NULL);
-
-	group = g_new0(MsnGroup, 1);
-
-	msn_userlist_add_group(userlist, group);
-
-	group->id      = id;
-	group->name    = g_strdup(name);
-
-	return group;
-}
-
-void
-msn_group_destroy(MsnGroup *group)
-{
-	g_return_if_fail(group != NULL);
-
-	g_free(group->name);
-	g_free(group);
-}
-
-void
-msn_group_set_id(MsnGroup *group, int id)
-{
-	g_return_if_fail(group != NULL);
-	g_return_if_fail(id >= 0);
-
-	group->id = id;
-}
-
-void
-msn_group_set_name(MsnGroup *group, const char *name)
-{
-	g_return_if_fail(group != NULL);
-	g_return_if_fail(name  != NULL);
-
-	if (group->name != NULL)
-		g_free(group->name);
-
-	group->name = g_strdup(name);
-}
-
-int
-msn_group_get_id(const MsnGroup *group)
-{
-	g_return_val_if_fail(group != NULL, -1);
-
-	return group->id;
-}
-
-const char *
-msn_group_get_name(const MsnGroup *group)
-{
-	g_return_val_if_fail(group != NULL, NULL);
-
-	return group->name;
-}
--- a/libpurple/protocols/msnp9/group.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/**
- * @file group.h Group functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_GROUP_H_
-#define _MSN_GROUP_H_
-
-typedef struct _MsnGroup  MsnGroup;
-
-#include <stdio.h>
-
-#include "session.h"
-#include "user.h"
-
-#include "userlist.h"
-
-/**
- * A group.
- */
-struct _MsnGroup
-{
-	MsnSession *session;    /**< The MSN session.           */
-
-	int id;                 /**< The group ID.              */
-	char *name;             /**< The name of the group.     */
-};
-
-/**************************************************************************/
-/** @name Group API                                                       */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Creates a new group structure.
- *
- * @param session The MSN session.
- * @param id      The group ID.
- * @param name    The name of the group.
- *
- * @return A new group structure.
- */
-MsnGroup *msn_group_new(MsnUserList *userlist, int id, const char *name);
-
-/**
- * Destroys a group structure.
- *
- * @param group The group to destroy.
- */
-void msn_group_destroy(MsnGroup *group);
-
-/**
- * Sets the ID for a group.
- *
- * @param group The group.
- * @param id    The ID.
- */
-void msn_group_set_id(MsnGroup *group, int id);
-
-/**
- * Sets the name for a group.
- *
- * @param group The group.
- * @param name  The name.
- */
-void msn_group_set_name(MsnGroup *group, const char *name);
-
-/**
- * Returns the ID for a group.
- *
- * @param group The group.
- *
- * @return The ID.
- */
-int msn_group_get_id(const MsnGroup *group);
-
-/**
- * Returns the name for a group.
- *
- * @param group The group.
- *
- * @return The name.
- */
-const char *msn_group_get_name(const MsnGroup *group);
-#endif /* _MSN_GROUP_H_ */
--- a/libpurple/protocols/msnp9/history.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/**
- * @file history.c MSN history functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "history.h"
-
-MsnHistory *
-msn_history_new(void)
-{
-	MsnHistory *history = g_new0(MsnHistory, 1);
-
-	history->trId = 1;
-
-	history->queue = g_queue_new();
-
-	return history;
-}
-
-void
-msn_history_destroy(MsnHistory *history)
-{
-	MsnTransaction *trans;
-
-	while ((trans = g_queue_pop_head(history->queue)) != NULL)
-		msn_transaction_destroy(trans);
-
-	g_queue_free(history->queue);
-	g_free(history);
-}
-
-MsnTransaction *
-msn_history_find(MsnHistory *history, unsigned int trId)
-{
-	MsnTransaction *trans;
-	GList *list;
-
-	for (list = history->queue->head; list != NULL; list = list->next)
-	{
-		trans = list->data;
-		if (trans->trId == trId)
-			return trans;
-	}
-
-	return NULL;
-}
-
-void
-msn_history_add(MsnHistory *history, MsnTransaction *trans)
-{
-	GQueue *queue;
-
-	g_return_if_fail(history != NULL);
-	g_return_if_fail(trans   != NULL);
-
-	queue = history->queue;
-
-	trans->trId = history->trId++;
-
-	g_queue_push_tail(queue, trans);
-
-	if (queue->length > MSN_HIST_ELEMS)
-	{
-		trans = g_queue_pop_head(queue);
-		msn_transaction_destroy(trans);
-	}
-}
--- a/libpurple/protocols/msnp9/history.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/**
- * @file history.h MSN history functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_HISTORY_H
-#define _MSN_HISTORY_H
-
-#define MSN_HIST_ELEMS 0x30
-
-typedef struct _MsnHistory MsnHistory;
-
-#include "transaction.h"
-
-/**
- * The history.
- */
-struct _MsnHistory
-{
-	GQueue *queue;
-	unsigned int trId;
-};
-
-MsnHistory *msn_history_new(void);
-void msn_history_destroy(MsnHistory *history);
-MsnTransaction *msn_history_find(MsnHistory *history, unsigned int triId);
-void msn_history_add(MsnHistory *history, MsnTransaction *trans);
-
-#endif /* _MSN_HISTORY_H */
--- a/libpurple/protocols/msnp9/httpconn.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,775 +0,0 @@
-/**
- * @file httpmethod.c HTTP connection method
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "debug.h"
-#include "httpconn.h"
-
-typedef struct
-{
-	MsnHttpConn *httpconn;
-	char *body;
-	size_t body_len;
-} MsnHttpQueueData;
-
-static void
-msn_httpconn_process_queue(MsnHttpConn *httpconn)
-{
-	httpconn->waiting_response = FALSE;
-
-	if (httpconn->queue != NULL)
-	{
-		MsnHttpQueueData *queue_data;
-
-		queue_data = (MsnHttpQueueData *)httpconn->queue->data;
-
-		httpconn->queue = g_list_remove(httpconn->queue, queue_data);
-
-		msn_httpconn_write(queue_data->httpconn,
-						   queue_data->body,
-						   queue_data->body_len);
-
-		g_free(queue_data->body);
-		g_free(queue_data);
-	}
-}
-
-static gboolean
-msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf,
-						size_t size, char **ret_buf, size_t *ret_size,
-						gboolean *error)
-{
-	const char *s, *c;
-	char *header, *body;
-	const char *body_start;
-	char *tmp;
-	size_t body_len = 0;
-	gboolean wasted = FALSE;
-
-	g_return_val_if_fail(httpconn != NULL, FALSE);
-	g_return_val_if_fail(buf      != NULL, FALSE);
-	g_return_val_if_fail(size      > 0,    FALSE);
-	g_return_val_if_fail(ret_buf  != NULL, FALSE);
-	g_return_val_if_fail(ret_size != NULL, FALSE);
-	g_return_val_if_fail(error    != NULL, FALSE);
-
-#if 0
-	purple_debug_info("msn", "HTTP: parsing data {%s}\n", buf);
-#endif
-
-	/* Healthy defaults. */
-	body = NULL;
-
-	*ret_buf  = NULL;
-	*ret_size = 0;
-	*error    = FALSE;
-
-	/* First, some tests to see if we have a full block of stuff. */
-	if (((strncmp(buf, "HTTP/1.1 200 OK\r\n", 17) != 0) &&
-		 (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) != 0)) &&
-		((strncmp(buf, "HTTP/1.0 200 OK\r\n", 17) != 0) &&
-		 (strncmp(buf, "HTTP/1.0 100 Continue\r\n", 23) != 0)))
-	{
-		*error = TRUE;
-
-		return FALSE;
-	}
-
-	if (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) == 0)
-	{
-		if ((s = strstr(buf, "\r\n\r\n")) == NULL)
-			return FALSE;
-
-		s += 4;
-
-		if (*s == '\0')
-		{
-			*ret_buf = g_strdup("");
-			*ret_size = 0;
-
-			msn_httpconn_process_queue(httpconn);
-
-			return TRUE;
-		}
-
-		buf = s;
-		size -= (s - buf);
-	}
-
-	if ((s = strstr(buf, "\r\n\r\n")) == NULL)
-		/* Need to wait for the full HTTP header to arrive */
-		return FALSE;
-
-	s += 4; /* Skip \r\n */
-	header = g_strndup(buf, s - buf);
-	body_start = s;
-	body_len = size - (body_start - buf);
-
-	if ((s = purple_strcasestr(header, "Content-Length: ")) != NULL)
-	{
-		int tmp_len;
-
-		s += strlen("Content-Length: ");
-
-		if ((c = strchr(s, '\r')) == NULL)
-		{
-			g_free(header);
-
-			return FALSE;
-		}
-
-		tmp = g_strndup(s, c - s);
-		tmp_len = atoi(tmp);
-		g_free(tmp);
-
-		if (body_len != tmp_len)
-		{
-			/* Need to wait for the full packet to arrive */
-
-			g_free(header);
-
-#if 0
-			purple_debug_warning("msn",
-							   "body length (%d) != content length (%d)\n",
-							   body_len, tmp_len);
-#endif
-
-			return FALSE;
-		}
-	}
-
-	body = g_malloc0(body_len + 1);
-	memcpy(body, body_start, body_len);
-
-#ifdef MSN_DEBUG_HTTP
-	purple_debug_misc("msn", "Incoming HTTP buffer (header): {%s\r\n}\n",
-					header);
-#endif
-
-	/* Now we should be able to process the data. */
-	if ((s = purple_strcasestr(header, "X-MSN-Messenger: ")) != NULL)
-	{
-		char *full_session_id, *gw_ip, *session_action;
-		char *t, *session_id;
-		char **elems, **cur, **tokens;
-
-		full_session_id = gw_ip = session_action = NULL;
-
-		s += strlen("X-MSN-Messenger: ");
-
-		if ((c = strchr(s, '\r')) == NULL)
-		{
-			msn_session_set_error(httpconn->session,
-								  MSN_ERROR_HTTP_MALFORMED, NULL);
-			purple_debug_error("msn", "Malformed X-MSN-Messenger field.\n{%s}\n",
-							 buf);
-
-			g_free(body);
-			return FALSE;
-		}
-
-		tmp = g_strndup(s, c - s);
-
-		elems = g_strsplit(tmp, "; ", 0);
-
-		for (cur = elems; *cur != NULL; cur++)
-		{
-			tokens = g_strsplit(*cur, "=", 2);
-
-			if (strcmp(tokens[0], "SessionID") == 0)
-				full_session_id = tokens[1];
-			else if (strcmp(tokens[0], "GW-IP") == 0)
-				gw_ip = tokens[1];
-			else if (strcmp(tokens[0], "Session") == 0)
-				session_action = tokens[1];
-			else
-				g_free(tokens[1]);
-
-			g_free(tokens[0]);
-			/* Don't free each of the tokens, only the array. */
-			g_free(tokens);
-		}
-
-		g_strfreev(elems);
-
-		g_free(tmp);
-
-		if ((session_action != NULL) && (strcmp(session_action, "close") == 0))
-			wasted = TRUE;
-
-		g_free(session_action);
-
-		t = strchr(full_session_id, '.');
-		session_id = g_strndup(full_session_id, t - full_session_id);
-
-		if (!wasted)
-		{
-			g_free(httpconn->full_session_id);
-			httpconn->full_session_id = full_session_id;
-
-			g_free(httpconn->session_id);
-			httpconn->session_id = session_id;
-
-			g_free(httpconn->host);
-			httpconn->host = gw_ip;
-		}
-		else
-		{
-			MsnServConn *servconn;
-
-			/* It's going to die. */
-			/* poor thing */
-
-			servconn = httpconn->servconn;
-
-			/* I'll be honest, I don't fully understand all this, but this
-			 * causes crashes, Stu. */
-			/* if (servconn != NULL)
-				servconn->wasted = TRUE; */
-
-			g_free(full_session_id);
-			g_free(session_id);
-			g_free(gw_ip);
-		}
-	}
-
-	g_free(header);
-
-	*ret_buf  = body;
-	*ret_size = body_len;
-
-	msn_httpconn_process_queue(httpconn);
-
-	return TRUE;
-}
-
-static void
-read_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnHttpConn *httpconn;
-	MsnServConn *servconn;
-	MsnSession *session;
-	char buf[MSN_BUF_LEN];
-	char *cur, *end, *old_rx_buf;
-	int len, cur_len;
-	char *result_msg = NULL;
-	size_t result_len = 0;
-	gboolean error = FALSE;
-
-	httpconn = data;
-	servconn = NULL;
-	session = httpconn->session;
-
-	len = read(httpconn->fd, buf, sizeof(buf) - 1);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len <= 0)
-	{
-		purple_debug_error("msn", "HTTP: Read error\n");
-		msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ);
-
-		return;
-	}
-
-	buf[len] = '\0';
-
-	httpconn->rx_buf = g_realloc(httpconn->rx_buf, len + httpconn->rx_len + 1);
-	memcpy(httpconn->rx_buf + httpconn->rx_len, buf, len + 1);
-	httpconn->rx_len += len;
-
-	if (!msn_httpconn_parse_data(httpconn, httpconn->rx_buf, httpconn->rx_len,
-								 &result_msg, &result_len, &error))
-	{
-		/* Either we must wait for more input, or something went wrong */
-		if (error)
-			msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ);
-
-		return;
-	}
-
-	httpconn->servconn->processing = FALSE;
-
-	servconn = httpconn->servconn;
-
-	if (error)
-	{
-		purple_debug_error("msn", "HTTP: Special error\n");
-		msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ);
-
-		return;
-	}
-
-	g_free(httpconn->rx_buf);
-	httpconn->rx_buf = NULL;
-	httpconn->rx_len = 0;
-
-	if (result_len == 0)
-	{
-		/* Nothing to do here */
-#if 0
-		purple_debug_info("msn", "HTTP: nothing to do here\n");
-#endif
-		g_free(result_msg);
-		return;
-	}
-
-	g_free(servconn->rx_buf);
-	servconn->rx_buf = result_msg;
-	servconn->rx_len = result_len;
-
-	end = old_rx_buf = servconn->rx_buf;
-
-	servconn->processing = TRUE;
-
-	do
-	{
-		cur = end;
-
-		if (servconn->payload_len)
-		{
-			if (servconn->payload_len > servconn->rx_len)
-				/* The payload is still not complete. */
-				break;
-
-			cur_len = servconn->payload_len;
-			end += cur_len;
-		}
-		else
-		{
-			end = strstr(cur, "\r\n");
-
-			if (end == NULL)
-				/* The command is still not complete. */
-				break;
-
-			*end = '\0';
-			end += 2;
-			cur_len = end - cur;
-		}
-
-		servconn->rx_len -= cur_len;
-
-		if (servconn->payload_len)
-		{
-			msn_cmdproc_process_payload(servconn->cmdproc, cur, cur_len);
-			servconn->payload_len = 0;
-		}
-		else
-		{
-			msn_cmdproc_process_cmd_text(servconn->cmdproc, cur);
-		}
-	} while (servconn->connected && servconn->rx_len > 0);
-
-	if (servconn->connected)
-	{
-		if (servconn->rx_len > 0)
-			servconn->rx_buf = g_memdup(cur, servconn->rx_len);
-		else
-			servconn->rx_buf = NULL;
-	}
-
-	servconn->processing = FALSE;
-
-	if (servconn->wasted)
-		msn_servconn_destroy(servconn);
-
-	g_free(old_rx_buf);
-}
-
-static void
-httpconn_write_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnHttpConn *httpconn;
-	int ret, writelen;
-
-	httpconn = data;
-	writelen = purple_circ_buffer_get_max_read(httpconn->tx_buf);
-
-	if (writelen == 0)
-	{
-		purple_input_remove(httpconn->tx_handler);
-		httpconn->tx_handler = 0;
-		return;
-	}
-
-	ret = write(httpconn->fd, httpconn->tx_buf->outptr, writelen);
-	if (ret <= 0)
-	{
-		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
-			/* No worries */
-			return;
-
-		/* Error! */
-		msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE);
-		return;
-	}
-
-	purple_circ_buffer_mark_read(httpconn->tx_buf, ret);
-
-	/* TODO: I don't think these 2 lines are needed.  Remove them? */
-	if (ret == writelen)
-		httpconn_write_cb(data, source, cond);
-}
-
-static gboolean
-write_raw(MsnHttpConn *httpconn, const char *data, size_t data_len)
-{
-	ssize_t res; /* result of the write operation */
-
-	if (httpconn->tx_handler == 0)
-		res = write(httpconn->fd, data, data_len);
-	else
-	{
-		res = -1;
-		errno = EAGAIN;
-	}
-
-	if ((res <= 0) && ((errno != EAGAIN) && (errno != EWOULDBLOCK)))
-	{
-		msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE);
-		return FALSE;
-	}
-
-	if (res < 0 || res < data_len)
-	{
-		if (res < 0)
-			res = 0;
-		if (httpconn->tx_handler == 0 && httpconn->fd)
-			httpconn->tx_handler = purple_input_add(httpconn->fd,
-				PURPLE_INPUT_WRITE, httpconn_write_cb, httpconn);
-		purple_circ_buffer_append(httpconn->tx_buf, data + res,
-			data_len - res);
-	}
-
-	return TRUE;
-}
-
-static char *
-msn_httpconn_proxy_auth(MsnHttpConn *httpconn)
-{
-	PurpleAccount *account;
-	PurpleProxyInfo *gpi;
-	const char *username, *password;
-	char *auth = NULL;
-
-	account = httpconn->session->account;
-
-	gpi = purple_proxy_get_setup(account);
-
-	if (gpi == NULL || !(purple_proxy_info_get_type(gpi) == PURPLE_PROXY_HTTP ||
-						 purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR))
-		return NULL;
-
-	username = purple_proxy_info_get_username(gpi);
-	password = purple_proxy_info_get_password(gpi);
-
-	if (username != NULL) {
-		char *tmp;
-		auth = g_strdup_printf("%s:%s", username, password ? password : "");
-		tmp = purple_base64_encode((const guchar *)auth, strlen(auth));
-		g_free(auth);
-		auth = g_strdup_printf("Proxy-Authorization: Basic %s\r\n", tmp);
-		g_free(tmp);
-	}
-
-	return auth;
-}
-
-static gboolean
-msn_httpconn_poll(gpointer data)
-{
-	MsnHttpConn *httpconn;
-	char *header;
-	char *auth;
-
-	httpconn = data;
-
-	g_return_val_if_fail(httpconn != NULL, FALSE);
-
-	if ((httpconn->host == NULL) || (httpconn->full_session_id == NULL))
-	{
-		/* There's no need to poll if the session is not fully established */
-		return TRUE;
-	}
-
-	if (httpconn->waiting_response)
-	{
-		/* There's no need to poll if we're already waiting for a response */
-		return TRUE;
-	}
-
-	auth = msn_httpconn_proxy_auth(httpconn);
-
-	header = g_strdup_printf(
-		"POST http://%s/gateway/gateway.dll?Action=poll&SessionID=%s HTTP/1.1\r\n"
-		"Accept: */*\r\n"
-		"Accept-Language: en-us\r\n"
-		"User-Agent: MSMSGS\r\n"
-		"Host: %s\r\n"
-		"Proxy-Connection: Keep-Alive\r\n"
-		"%s" /* Proxy auth */
-		"Connection: Keep-Alive\r\n"
-		"Pragma: no-cache\r\n"
-		"Content-Type: application/x-msn-messenger\r\n"
-		"Content-Length: 0\r\n\r\n",
-		httpconn->host,
-		httpconn->full_session_id,
-		httpconn->host,
-		auth ? auth : "");
-
-	g_free(auth);
-
-	if (write_raw(httpconn, header, strlen(header)))
-		httpconn->waiting_response = TRUE;
-
-	g_free(header);
-
-	return TRUE;
-}
-
-ssize_t
-msn_httpconn_write(MsnHttpConn *httpconn, const char *body, size_t body_len)
-{
-	char *params;
-	char *data;
-	int header_len;
-	char *auth;
-	const char *server_types[] = { "NS", "SB" };
-	const char *server_type;
-	char *host;
-	MsnServConn *servconn;
-
-	/* TODO: remove http data from servconn */
-
-	g_return_val_if_fail(httpconn != NULL, 0);
-	g_return_val_if_fail(body != NULL, 0);
-	g_return_val_if_fail(body_len > 0, 0);
-
-	servconn = httpconn->servconn;
-
-	if (httpconn->waiting_response)
-	{
-		MsnHttpQueueData *queue_data = g_new0(MsnHttpQueueData, 1);
-
-		queue_data->httpconn = httpconn;
-		queue_data->body     = g_memdup(body, body_len);
-		queue_data->body_len = body_len;
-
-		httpconn->queue = g_list_append(httpconn->queue, queue_data);
-
-		return body_len;
-	}
-
-	server_type = server_types[servconn->type];
-
-	if (httpconn->virgin)
-	{
-		host = "gateway.messenger.hotmail.com";
-
-		/* The first time servconn->host is the host we should connect to. */
-		params = g_strdup_printf("Action=open&Server=%s&IP=%s",
-								 server_type,
-								 servconn->host);
-		httpconn->virgin = FALSE;
-	}
-	else
-	{
-		/* The rest of the times servconn->host is the gateway host. */
-		host = httpconn->host;
-
-		if (host == NULL || httpconn->full_session_id == NULL)
-		{
-			purple_debug_warning("msn", "Attempted HTTP write before session is established\n");
-			return -1;
-		}
-
-		params = g_strdup_printf("SessionID=%s",
-			httpconn->full_session_id);
-	}
-
-	auth = msn_httpconn_proxy_auth(httpconn);
-
-	data = g_strdup_printf(
-		"POST http://%s/gateway/gateway.dll?%s HTTP/1.1\r\n"
-		"Accept: */*\r\n"
-		"Accept-Language: en-us\r\n"
-		"User-Agent: MSMSGS\r\n"
-		"Host: %s\r\n"
-		"Proxy-Connection: Keep-Alive\r\n"
-		"%s" /* Proxy auth */
-		"Connection: Keep-Alive\r\n"
-		"Pragma: no-cache\r\n"
-		"Content-Type: application/x-msn-messenger\r\n"
-		"Content-Length: %d\r\n\r\n",
-		host,
-		params,
-		host,
-		auth ? auth : "",
-		(int) body_len);
-
-	g_free(params);
-
-	g_free(auth);
-
-	header_len = strlen(data);
-	data = g_realloc(data, header_len + body_len);
-	memcpy(data + header_len, body, body_len);
-
-	if (write_raw(httpconn, data, header_len + body_len))
-		httpconn->waiting_response = TRUE;
-
-	g_free(data);
-
-	return body_len;
-}
-
-MsnHttpConn *
-msn_httpconn_new(MsnServConn *servconn)
-{
-	MsnHttpConn *httpconn;
-
-	g_return_val_if_fail(servconn != NULL, NULL);
-
-	httpconn = g_new0(MsnHttpConn, 1);
-
-	purple_debug_info("msn", "new httpconn (%p)\n", httpconn);
-
-	/* TODO: Remove this */
-	httpconn->session = servconn->session;
-
-	httpconn->servconn = servconn;
-
-	httpconn->tx_buf = purple_circ_buffer_new(MSN_BUF_LEN);
-	httpconn->tx_handler = 0;
-
-	return httpconn;
-}
-
-void
-msn_httpconn_destroy(MsnHttpConn *httpconn)
-{
-	g_return_if_fail(httpconn != NULL);
-
-	purple_debug_info("msn", "destroy httpconn (%p)\n", httpconn);
-
-	if (httpconn->connected)
-		msn_httpconn_disconnect(httpconn);
-
-	g_free(httpconn->full_session_id);
-
-	g_free(httpconn->session_id);
-
-	g_free(httpconn->host);
-
-	purple_circ_buffer_destroy(httpconn->tx_buf);
-	if (httpconn->tx_handler > 0)
-		purple_input_remove(httpconn->tx_handler);
-
-	g_free(httpconn);
-}
-
-static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
-{
-	MsnHttpConn *httpconn;
-
-	httpconn = data;
-	httpconn->connect_data = NULL;
-	httpconn->fd = source;
-
-	if (source >= 0)
-	{
-		httpconn->inpa = purple_input_add(httpconn->fd, PURPLE_INPUT_READ,
-			read_cb, data);
-
-		httpconn->timer = purple_timeout_add_seconds(3, msn_httpconn_poll, httpconn);
-
-		msn_httpconn_process_queue(httpconn);
-	}
-	else
-	{
-		purple_debug_error("msn", "HTTP: Connection error\n");
-		msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_CONNECT);
-	}
-}
-
-gboolean
-msn_httpconn_connect(MsnHttpConn *httpconn, const char *host, int port)
-{
-	g_return_val_if_fail(httpconn != NULL, FALSE);
-	g_return_val_if_fail(host     != NULL, FALSE);
-	g_return_val_if_fail(port      > 0,    FALSE);
-
-	if (httpconn->connected)
-		msn_httpconn_disconnect(httpconn);
-
-	httpconn->connect_data = purple_proxy_connect(NULL, httpconn->session->account,
-		host, 80, connect_cb, httpconn);
-
-	if (httpconn->connect_data != NULL)
-	{
-		httpconn->waiting_response = TRUE;
-		httpconn->connected = TRUE;
-	}
-
-	return httpconn->connected;
-}
-
-void
-msn_httpconn_disconnect(MsnHttpConn *httpconn)
-{
-	g_return_if_fail(httpconn != NULL);
-
-	if (!httpconn->connected)
-		return;
-
-	if (httpconn->connect_data != NULL)
-	{
-		purple_proxy_connect_cancel(httpconn->connect_data);
-		httpconn->connect_data = NULL;
-	}
-
-	if (httpconn->timer)
-	{
-		purple_timeout_remove(httpconn->timer);
-		httpconn->timer = 0;
-	}
-
-	if (httpconn->inpa > 0)
-	{
-		purple_input_remove(httpconn->inpa);
-		httpconn->inpa = 0;
-	}
-
-	close(httpconn->fd);
-	httpconn->fd = -1;
-
-	g_free(httpconn->rx_buf);
-	httpconn->rx_buf = NULL;
-	httpconn->rx_len = 0;
-
-	httpconn->connected = FALSE;
-
-	/* msn_servconn_disconnect(httpconn->servconn); */
-}
--- a/libpurple/protocols/msnp9/httpconn.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/**
- * @file httpconn.h HTTP connection
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_HTTPCONN_H_
-#define _MSN_HTTPCONN_H_
-
-typedef struct _MsnHttpConn MsnHttpConn;
-
-#include "circbuffer.h"
-#include "servconn.h"
-
-/**
- * An HTTP Connection.
- */
-struct _MsnHttpConn
-{
-	MsnSession *session; /**< The MSN Session. */
-	MsnServConn *servconn; /**< The connection object. */
-
-	PurpleProxyConnectData *connect_data;
-
-	char *full_session_id; /**< The full session id. */
-	char *session_id; /**< The trimmed session id. */
-
-	int timer; /**< The timer for polling. */
-
-	gboolean waiting_response; /**< The flag that states if we are waiting
-								 a response from the server. */
-	gboolean connected;        /**< The flag that states if the connection is on. */
-	gboolean virgin;           /**< The flag that states if this connection
-								 should specify the host (not gateway) to
-								 connect to. */
-
-	char *host; /**< The HTTP gateway host. */
-	GList *queue; /**< The queue of data chunks to write. */
-
-	int fd; /**< The connection's file descriptor. */
-	guint inpa; /**< The connection's input handler. */
-
-	char *rx_buf; /**< The receive buffer. */
-	int rx_len; /**< The receive buffer length. */
-
-	PurpleCircBuffer *tx_buf;
-	guint tx_handler;
-};
-
-/**
- * Creates a new HTTP connection object.
- *
- * @param servconn The connection object.
- *
- * @return The new object.
- */
-MsnHttpConn *msn_httpconn_new(MsnServConn *servconn);
-
-/**
- * Destroys an HTTP connection object.
- *
- * @param httpconn The HTTP connection object.
- */
-void msn_httpconn_destroy(MsnHttpConn *httpconn);
-
-/**
- * Writes a chunk of data to the HTTP connection.
- *
- * @param servconn    The server connection.
- * @param data        The data to write.
- * @param data_len    The size of the data to write.
- *
- * @return The number of bytes written.
- */
-ssize_t msn_httpconn_write(MsnHttpConn *httpconn, const char *data, size_t data_len);
-
-/**
- * Connects the HTTP connection object to a host.
- *
- * @param httpconn The HTTP connection object.
- * @param host The host to connect to.
- * @param port The port to connect to.
- */
-gboolean msn_httpconn_connect(MsnHttpConn *httpconn,
-							  const char *host, int port);
-
-/**
- * Disconnects the HTTP connection object.
- *
- * @param httpconn The HTTP connection object.
- */
-void msn_httpconn_disconnect(MsnHttpConn *httpconn);
-
-#endif /* _MSN_HTTPCONN_H_ */
--- a/libpurple/protocols/msnp9/msg.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,787 +0,0 @@
-/**
- * @file msg.c Message functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "msg.h"
-
-MsnMessage *
-msn_message_new(MsnMsgType type)
-{
-	MsnMessage *msg;
-
-	msg = g_new0(MsnMessage, 1);
-	msg->type = type;
-
-#ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message new (%p)(%d)\n", msg, type);
-#endif
-
-	msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal,
-											g_free, g_free);
-
-	msn_message_ref(msg);
-
-	return msg;
-}
-
-void
-msn_message_destroy(MsnMessage *msg)
-{
-	g_return_if_fail(msg != NULL);
-
-	if (msg->ref_count > 0)
-	{
-		msn_message_unref(msg);
-
-		return;
-	}
-
-#ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message destroy (%p)\n", msg);
-#endif
-
-	if (msg->remote_user != NULL)
-		g_free(msg->remote_user);
-
-	if (msg->body != NULL)
-		g_free(msg->body);
-
-	if (msg->content_type != NULL)
-		g_free(msg->content_type);
-
-	if (msg->charset != NULL)
-		g_free(msg->charset);
-
-	g_hash_table_destroy(msg->attr_table);
-	g_list_free(msg->attr_list);
-
-	g_free(msg);
-}
-
-MsnMessage *
-msn_message_ref(MsnMessage *msg)
-{
-	g_return_val_if_fail(msg != NULL, NULL);
-
-	msg->ref_count++;
-
-#ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message ref (%p)[%d]\n", msg, msg->ref_count);
-#endif
-
-	return msg;
-}
-
-MsnMessage *
-msn_message_unref(MsnMessage *msg)
-{
-	g_return_val_if_fail(msg != NULL, NULL);
-	g_return_val_if_fail(msg->ref_count > 0, NULL);
-
-	msg->ref_count--;
-
-#ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message unref (%p)[%d]\n", msg, msg->ref_count);
-#endif
-
-	if (msg->ref_count == 0)
-	{
-		msn_message_destroy(msg);
-
-		return NULL;
-	}
-
-	return msg;
-}
-
-MsnMessage *
-msn_message_new_plain(const char *message)
-{
-	MsnMessage *msg;
-	char *message_cr;
-
-	msg = msn_message_new(MSN_MSG_TEXT);
-	msn_message_set_attr(msg, "User-Agent", PACKAGE_NAME "/" DISPLAY_VERSION);
-	msn_message_set_content_type(msg, "text/plain");
-	msn_message_set_charset(msg, "UTF-8");
-	msn_message_set_flag(msg, 'A');
-	msn_message_set_attr(msg, "X-MMS-IM-Format",
-						 "FN=MS%20Sans%20Serif; EF=; CO=0; PF=0");
-
-	message_cr = purple_str_add_cr(message);
-	msn_message_set_bin_data(msg, message_cr, strlen(message_cr));
-	g_free(message_cr);
-
-	return msg;
-}
-
-MsnMessage *
-msn_message_new_msnslp(void)
-{
-	MsnMessage *msg;
-
-	msg = msn_message_new(MSN_MSG_SLP);
-
-	msn_message_set_attr(msg, "User-Agent", NULL);
-
-	msg->msnslp_message = TRUE;
-
-	msn_message_set_flag(msg, 'D');
-	msn_message_set_content_type(msg, "application/x-msnmsgrp2p");
-
-	return msg;
-}
-
-MsnMessage *
-msn_message_new_nudge(void)
-{
-	MsnMessage *msg;
-
-	msg = msn_message_new(MSN_MSG_NUDGE);
-	msn_message_set_content_type(msg, "text/x-msnmsgr-datacast");
-	msn_message_set_flag(msg, 'N');
-	msn_message_set_bin_data(msg, "ID: 1\r\n", 7);
-
-	return msg;
-}
-
-void
-msn_message_parse_slp_body(MsnMessage *msg, const char *body, size_t len)
-{
-	MsnSlpHeader header;
-	const char *tmp;
-	int body_len;
-
-	tmp = body;
-
-	if (len < sizeof(header)) {
-		g_return_if_reached();
-	}
-
-	/* Import the header. */
-	memcpy(&header, tmp, sizeof(header));
-	tmp += sizeof(header);
-
-	msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id);
-	msg->msnslp_header.id         = GUINT32_FROM_LE(header.id);
-	msg->msnslp_header.offset     = GUINT64_FROM_LE(header.offset);
-	msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size);
-	msg->msnslp_header.length     = GUINT32_FROM_LE(header.length);
-	msg->msnslp_header.flags      = GUINT32_FROM_LE(header.flags);
-	msg->msnslp_header.ack_id     = GUINT32_FROM_LE(header.ack_id);
-	msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id);
-	msg->msnslp_header.ack_size   = GUINT64_FROM_LE(header.ack_size);
-
-	/* Import the body. */
-	body_len = len - (tmp - body);
-	/* msg->body_len = msg->msnslp_header.length; */
-
-	if (body_len > 0) {
-		msg->body_len = len - (tmp - body);
-		msg->body = g_malloc0(msg->body_len + 1);
-		memcpy(msg->body, tmp, msg->body_len);
-		tmp += body_len;
-	}
-}
-
-void
-msn_message_parse_payload(MsnMessage *msg,
-						  const char *payload, size_t payload_len)
-{
-	char *tmp_base, *tmp;
-	const char *content_type;
-	char *end;
-	char **elems, **cur, **tokens;
-
-	g_return_if_fail(payload != NULL);
-
-	tmp_base = tmp = g_malloc0(payload_len + 1);
-	memcpy(tmp_base, payload, payload_len);
-
-	/* Parse the attributes. */
-	end = strstr(tmp, "\r\n\r\n");
-	/* TODO? some clients use \r delimiters instead of \r\n, the official client
-	 * doesn't send such messages, but does handle receiving them. We'll just
-	 * avoid crashing for now */
-	if (end == NULL) {
-		g_free(tmp_base);
-		g_return_if_reached();
-	}
-	*end = '\0';
-
-	elems = g_strsplit(tmp, "\r\n", 0);
-
-	for (cur = elems; *cur != NULL; cur++)
-	{
-		const char *key, *value;
-
-		tokens = g_strsplit(*cur, ": ", 2);
-
-		key = tokens[0];
-		value = tokens[1];
-
-		if (!strcmp(key, "MIME-Version"))
-		{
-			g_strfreev(tokens);
-			continue;
-		}
-
-		if (!strcmp(key, "Content-Type"))
-		{
-			char *charset, *c;
-
-			if ((c = strchr(value, ';')) != NULL)
-			{
-				if ((charset = strchr(c, '=')) != NULL)
-				{
-					charset++;
-					msn_message_set_charset(msg, charset);
-				}
-
-				*c = '\0';
-			}
-
-			msn_message_set_content_type(msg, value);
-		}
-		else
-		{
-			msn_message_set_attr(msg, key, value);
-		}
-
-		g_strfreev(tokens);
-	}
-
-	g_strfreev(elems);
-
-	/* Proceed to the end of the "\r\n\r\n" */
-	tmp = end + 4;
-
-	/* Now we *should* be at the body. */
-	content_type = msn_message_get_content_type(msg);
-
-	if (content_type != NULL &&
-		!strcmp(content_type, "application/x-msnmsgrp2p"))
-	{
-		MsnSlpHeader header;
-		MsnSlpFooter footer;
-		int body_len;
-
-		if (payload_len - (tmp - tmp_base) < sizeof(header)) {
-			g_free(tmp_base);
-			g_return_if_reached();
-		}
-
-		msg->msnslp_message = TRUE;
-
-		/* Import the header. */
-		memcpy(&header, tmp, sizeof(header));
-		tmp += sizeof(header);
-
-		msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id);
-		msg->msnslp_header.id         = GUINT32_FROM_LE(header.id);
-		msg->msnslp_header.offset     = GUINT64_FROM_LE(header.offset);
-		msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size);
-		msg->msnslp_header.length     = GUINT32_FROM_LE(header.length);
-		msg->msnslp_header.flags      = GUINT32_FROM_LE(header.flags);
-		msg->msnslp_header.ack_id     = GUINT32_FROM_LE(header.ack_id);
-		msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id);
-		msg->msnslp_header.ack_size   = GUINT64_FROM_LE(header.ack_size);
-
-		body_len = payload_len - (tmp - tmp_base) - sizeof(footer);
-
-		/* Import the body. */
-		if (body_len > 0) {
-			msg->body_len = body_len;
-			msg->body = g_malloc0(msg->body_len + 1);
-			memcpy(msg->body, tmp, msg->body_len);
-			tmp += body_len;
-		}
-
-		/* Import the footer. */
-		if (body_len >= 0) {
-			memcpy(&footer, tmp, sizeof(footer));
-			tmp += sizeof(footer);
-			msg->msnslp_footer.value = GUINT32_FROM_BE(footer.value);
-		}
-	}
-	else
-	{
-		if (payload_len - (tmp - tmp_base) > 0) {
-			msg->body_len = payload_len - (tmp - tmp_base);
-			msg->body = g_malloc0(msg->body_len + 1);
-			memcpy(msg->body, tmp, msg->body_len);
-		}
-	}
-
-	g_free(tmp_base);
-}
-
-MsnMessage *
-msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd)
-{
-	MsnMessage *msg;
-
-	g_return_val_if_fail(cmd != NULL, NULL);
-
-	msg = msn_message_new(MSN_MSG_UNKNOWN);
-
-	msg->remote_user = g_strdup(cmd->params[0]);
-	/* msg->size = atoi(cmd->params[2]); */
-	msg->cmd = cmd;
-
-	return msg;
-}
-
-char *
-msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size)
-{
-	MsnSlpHeader header;
-
-	char *tmp, *base;
-	const void *body;
-	size_t len, body_len;
-
-	g_return_val_if_fail(msg != NULL, NULL);
-
-	len = MSN_BUF_LEN;
-
-	base = tmp = g_malloc(len + 1);
-
-	body = msn_message_get_bin_data(msg, &body_len);
-
-	header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id);
-	header.id         = GUINT32_TO_LE(msg->msnslp_header.id);
-	header.offset     = GUINT64_TO_LE(msg->msnslp_header.offset);
-	header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size);
-	header.length     = GUINT32_TO_LE(msg->msnslp_header.length);
-	header.flags      = GUINT32_TO_LE(msg->msnslp_header.flags);
-	header.ack_id     = GUINT32_TO_LE(msg->msnslp_header.ack_id);
-	header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id);
-	header.ack_size   = GUINT64_TO_LE(msg->msnslp_header.ack_size);
-
-	memcpy(tmp, &header, 48);
-	tmp += 48;
-
-	if (body != NULL)
-	{
-		memcpy(tmp, body, body_len);
-		tmp += body_len;
-	}
-
-	if (ret_size != NULL)
-		*ret_size = tmp - base;
-
-	return base;
-}
-
-char *
-msn_message_gen_payload(MsnMessage *msg, size_t *ret_size)
-{
-	GList *l;
-	char *n, *base, *end;
-	int len;
-	size_t body_len = 0;
-	const void *body;
-
-	g_return_val_if_fail(msg != NULL, NULL);
-
-	len = MSN_BUF_LEN;
-
-	base = n = end = g_malloc(len + 1);
-	end += len;
-
-	/* Standard header. */
-	if (msg->charset == NULL)
-	{
-		g_snprintf(n, len,
-				   "MIME-Version: 1.0\r\n"
-				   "Content-Type: %s\r\n",
-				   msg->content_type);
-	}
-	else
-	{
-		g_snprintf(n, len,
-				   "MIME-Version: 1.0\r\n"
-				   "Content-Type: %s; charset=%s\r\n",
-				   msg->content_type, msg->charset);
-	}
-
-	n += strlen(n);
-
-	for (l = msg->attr_list; l != NULL; l = l->next)
-	{
-		const char *key;
-		const char *value;
-
-		key = l->data;
-		value = msn_message_get_attr(msg, key);
-
-		g_snprintf(n, end - n, "%s: %s\r\n", key, value);
-		n += strlen(n);
-	}
-
-	n += g_strlcpy(n, "\r\n", end - n);
-
-	body = msn_message_get_bin_data(msg, &body_len);
-
-	if (msg->msnslp_message)
-	{
-		MsnSlpHeader header;
-		MsnSlpFooter footer;
-
-		header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id);
-		header.id         = GUINT32_TO_LE(msg->msnslp_header.id);
-		header.offset     = GUINT64_TO_LE(msg->msnslp_header.offset);
-		header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size);
-		header.length     = GUINT32_TO_LE(msg->msnslp_header.length);
-		header.flags      = GUINT32_TO_LE(msg->msnslp_header.flags);
-		header.ack_id     = GUINT32_TO_LE(msg->msnslp_header.ack_id);
-		header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id);
-		header.ack_size   = GUINT64_TO_LE(msg->msnslp_header.ack_size);
-
-		memcpy(n, &header, 48);
-		n += 48;
-
-		if (body != NULL)
-		{
-			memcpy(n, body, body_len);
-
-			n += body_len;
-		}
-
-		footer.value = GUINT32_TO_BE(msg->msnslp_footer.value);
-
-		memcpy(n, &footer, 4);
-		n += 4;
-	}
-	else
-	{
-		if (body != NULL)
-		{
-			memcpy(n, body, body_len);
-			n += body_len;
-		}
-	}
-
-	if (ret_size != NULL)
-	{
-		*ret_size = n - base;
-
-		if (*ret_size > 1664)
-			*ret_size = 1664;
-	}
-
-	return base;
-}
-
-void
-msn_message_set_flag(MsnMessage *msg, char flag)
-{
-	g_return_if_fail(msg != NULL);
-	g_return_if_fail(flag != 0);
-
-	msg->flag = flag;
-}
-
-char
-msn_message_get_flag(const MsnMessage *msg)
-{
-	g_return_val_if_fail(msg != NULL, 0);
-
-	return msg->flag;
-}
-
-void
-msn_message_set_bin_data(MsnMessage *msg, const void *data, size_t len)
-{
-	g_return_if_fail(msg != NULL);
-
-	/* There is no need to waste memory on data we cannot send anyway */
-	if (len > 1664)
-		len = 1664;
-
-	if (msg->body != NULL)
-		g_free(msg->body);
-
-	if (data != NULL && len > 0)
-	{
-		msg->body = g_malloc0(len + 1);
-		memcpy(msg->body, data, len);
-		msg->body_len = len;
-	}
-	else
-	{
-		msg->body = NULL;
-		msg->body_len = 0;
-	}
-}
-
-const void *
-msn_message_get_bin_data(const MsnMessage *msg, size_t *len)
-{
-	g_return_val_if_fail(msg != NULL, NULL);
-
-	if (len)
-		*len = msg->body_len;
-
-	return msg->body;
-}
-
-void
-msn_message_set_content_type(MsnMessage *msg, const char *type)
-{
-	g_return_if_fail(msg != NULL);
-
-	if (msg->content_type != NULL)
-		g_free(msg->content_type);
-
-	msg->content_type = (type != NULL) ? g_strdup(type) : NULL;
-}
-
-const char *
-msn_message_get_content_type(const MsnMessage *msg)
-{
-	g_return_val_if_fail(msg != NULL, NULL);
-
-	return msg->content_type;
-}
-
-void
-msn_message_set_charset(MsnMessage *msg, const char *charset)
-{
-	g_return_if_fail(msg != NULL);
-
-	if (msg->charset != NULL)
-		g_free(msg->charset);
-
-	msg->charset = (charset != NULL) ? g_strdup(charset) : NULL;
-}
-
-const char *
-msn_message_get_charset(const MsnMessage *msg)
-{
-	g_return_val_if_fail(msg != NULL, NULL);
-
-	return msg->charset;
-}
-
-void
-msn_message_set_attr(MsnMessage *msg, const char *attr, const char *value)
-{
-	const char *temp;
-	char *new_attr;
-
-	g_return_if_fail(msg != NULL);
-	g_return_if_fail(attr != NULL);
-
-	temp = msn_message_get_attr(msg, attr);
-
-	if (value == NULL)
-	{
-		if (temp != NULL)
-		{
-			GList *l;
-
-			for (l = msg->attr_list; l != NULL; l = l->next)
-			{
-				if (!g_ascii_strcasecmp(l->data, attr))
-				{
-					msg->attr_list = g_list_remove(msg->attr_list, l->data);
-
-					break;
-				}
-			}
-
-			g_hash_table_remove(msg->attr_table, attr);
-		}
-
-		return;
-	}
-
-	new_attr = g_strdup(attr);
-
-	g_hash_table_insert(msg->attr_table, new_attr, g_strdup(value));
-
-	if (temp == NULL)
-		msg->attr_list = g_list_append(msg->attr_list, new_attr);
-}
-
-const char *
-msn_message_get_attr(const MsnMessage *msg, const char *attr)
-{
-	g_return_val_if_fail(msg != NULL, NULL);
-	g_return_val_if_fail(attr != NULL, NULL);
-
-	return g_hash_table_lookup(msg->attr_table, attr);
-}
-
-GHashTable *
-msn_message_get_hashtable_from_body(const MsnMessage *msg)
-{
-	GHashTable *table;
-	size_t body_len;
-	const char *body;
-	char **elems, **cur, **tokens, *body_str;
-
-	g_return_val_if_fail(msg != NULL, NULL);
-
-	table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-
-	body = msn_message_get_bin_data(msg, &body_len);
-
-	g_return_val_if_fail(body != NULL, NULL);
-
-	body_str = g_strndup(body, body_len);
-	elems = g_strsplit(body_str, "\r\n", 0);
-	g_free(body_str);
-
-	for (cur = elems; *cur != NULL; cur++)
-	{
-		if (**cur == '\0')
-			break;
-
-		tokens = g_strsplit(*cur, ": ", 2);
-
-		if (tokens[0] != NULL && tokens[1] != NULL)
-			g_hash_table_insert(table, tokens[0], tokens[1]);
-
-		g_free(tokens);
-	}
-
-	g_strfreev(elems);
-
-	return table;
-}
-
-char *
-msn_message_to_string(MsnMessage *msg)
-{
-	size_t body_len;
-	const char *body;
-
-	g_return_val_if_fail(msg != NULL, NULL);
-	g_return_val_if_fail(msg->type == MSN_MSG_TEXT, NULL);
-
-	body = msn_message_get_bin_data(msg, &body_len);
-
-	return g_strndup(body, body_len);
-}
-
-void
-msn_message_show_readable(MsnMessage *msg, const char *info,
-						  gboolean text_body)
-{
-	GString *str;
-	size_t body_len;
-	const char *body;
-	GList *l;
-
-	g_return_if_fail(msg != NULL);
-
-	str = g_string_new(NULL);
-
-	/* Standard header. */
-	if (msg->charset == NULL)
-	{
-		g_string_append_printf(str,
-				   "MIME-Version: 1.0\r\n"
-				   "Content-Type: %s\r\n",
-				   msg->content_type);
-	}
-	else
-	{
-		g_string_append_printf(str,
-				   "MIME-Version: 1.0\r\n"
-				   "Content-Type: %s; charset=%s\r\n",
-				   msg->content_type, msg->charset);
-	}
-
-	for (l = msg->attr_list; l; l = l->next)
-	{
-		char *key;
-		const char *value;
-
-		key = l->data;
-		value = msn_message_get_attr(msg, key);
-
-		g_string_append_printf(str, "%s: %s\r\n", key, value);
-	}
-
-	g_string_append(str, "\r\n");
-
-	body = msn_message_get_bin_data(msg, &body_len);
-
-	if (msg->msnslp_message)
-	{
-		g_string_append_printf(str, "Session ID: %u\r\n", msg->msnslp_header.session_id);
-		g_string_append_printf(str, "ID:         %u\r\n", msg->msnslp_header.id);
-		g_string_append_printf(str, "Offset:     %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.offset);
-		g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.total_size);
-		g_string_append_printf(str, "Length:     %u\r\n", msg->msnslp_header.length);
-		g_string_append_printf(str, "Flags:      0x%x\r\n", msg->msnslp_header.flags);
-		g_string_append_printf(str, "ACK ID:     %u\r\n", msg->msnslp_header.ack_id);
-		g_string_append_printf(str, "SUB ID:     %u\r\n", msg->msnslp_header.ack_sub_id);
-		g_string_append_printf(str, "ACK Size:   %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.ack_size);
-
-#ifdef MSN_DEBUG_SLP_VERBOSE
-		if (body != NULL)
-		{
-			if (text_body)
-			{
-				g_string_append_len(str, body, body_len);
-				if (body[body_len - 1] == '\0')
-				{
-					str->len--;
-					g_string_append(str, " 0x00");
-				}
-				g_string_append(str, "\r\n");
-			}
-			else
-			{
-				int i;
-				for (i = 0; i < msg->body_len; i++)
-				{
-					g_string_append_printf(str, "%.2hhX ", body[i]);
-					if ((i % 16) == 15)
-						g_string_append(str, "\r\n");
-				}
-				g_string_append(str, "\r\n");
-			}
-		}
-#endif
-
-		g_string_append_printf(str, "Footer:     %u\r\n", msg->msnslp_footer.value);
-	}
-	else
-	{
-		if (body != NULL)
-		{
-			g_string_append_len(str, body, body_len);
-			g_string_append(str, "\r\n");
-		}
-	}
-
-	purple_debug_info("msn", "Message %s:\n{%s}\n", info, str->str);
-
-	g_string_free(str, TRUE);
-}
--- a/libpurple/protocols/msnp9/msg.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,349 +0,0 @@
-/**
- * @file msg.h Message functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_MSG_H_
-#define _MSN_MSG_H_
-
-typedef struct _MsnMessage MsnMessage;
-
-#include "session.h"
-#include "user.h"
-
-#include "command.h"
-#include "transaction.h"
-
-typedef void (*MsnMsgCb)(MsnMessage *, void *data);
-
-/*
-typedef enum
-{
-	MSN_MSG_NORMAL,
-	MSN_MSG_SLP_SB,
-	MSN_MSG_SLP_DC
-
-} MsnMsgType;
-*/
-
-typedef enum
-{
-	MSN_MSG_UNKNOWN,
-	MSN_MSG_TEXT,
-	MSN_MSG_TYPING,
-	MSN_MSG_CAPS,
-	MSN_MSG_SLP,
-	MSN_MSG_NUDGE
-
-} MsnMsgType;
-
-typedef enum
-{
-	MSN_MSG_ERROR_NONE, /**< No error. */
-	MSN_MSG_ERROR_TIMEOUT, /**< The message timedout. */
-	MSN_MSG_ERROR_NAK, /**< The message could not be sent. */
-	MSN_MSG_ERROR_SB, /**< The error comes from the switchboard. */
-	MSN_MSG_ERROR_UNKNOWN /**< An unknown error occurred. */
-
-} MsnMsgErrorType;
-
-typedef struct
-{
-	guint32 session_id;
-	guint32 id;
-	guint64 offset;
-	guint64 total_size;
-	guint32 length;
-	guint32 flags;
-	guint32 ack_id;
-	guint32 ack_sub_id;
-	guint64 ack_size;
-
-} MsnSlpHeader;
-
-typedef struct
-{
-	guint32 value;
-
-} MsnSlpFooter;
-
-/**
- * A message.
- */
-struct _MsnMessage
-{
-	size_t ref_count;           /**< The reference count.       */
-
-	MsnMsgType type;
-
-	gboolean msnslp_message;
-
-	char *remote_user;
-	char flag;
-
-	char *content_type;
-	char *charset;
-	char *body;
-	gsize body_len;
-
-	MsnSlpHeader msnslp_header;
-	MsnSlpFooter msnslp_footer;
-
-	GHashTable *attr_table;
-	GList *attr_list;
-
-	gboolean ack_ref;           /**< A flag that states if this message has
-								  been ref'ed for using it in a callback. */
-
-	MsnCommand *cmd;
-	MsnTransaction *trans;
-
-	MsnMsgCb ack_cb; /**< The callback to call when we receive an ACK of this
-					   message. */
-	MsnMsgCb nak_cb; /**< The callback to call when we receive a NAK of this
-					   message. */
-	void *ack_data; /**< The data used by callbacks. */
-
-	MsnMsgErrorType error; /**< The error of the message. */
-};
-
-/**
- * Creates a new, empty message.
- *
- * @return A new message.
- */
-MsnMessage *msn_message_new(MsnMsgType type);
-
-/**
- * Creates a new, empty MSNSLP message.
- *
- * @return A new MSNSLP message.
- */
-MsnMessage *msn_message_new_msnslp(void);
-
-/**
- * Creates a new nudge message.
- *
- * @return A new nudge message.
- */
-MsnMessage *msn_message_new_nudge(void);
-
-/**
- * Creates a new plain message.
- *
- * @return A new plain message.
- */
-MsnMessage *msn_message_new_plain(const char *message);
-
-/**
- * Creates a MSNSLP ack message.
- *
- * @param acked_msg The message to acknowledge.
- *
- * @return A new MSNSLP ack message.
- */
-MsnMessage *msn_message_new_msnslp_ack(MsnMessage *acked_msg);
-
-/**
- * Creates a new message based off a command.
- *
- * @param session The MSN session.
- * @param cmd     The command.
- *
- * @return The new message.
- */
-MsnMessage *msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd);
-
-/**
- * Parses the payload of a message.
- *
- * @param msg         The message.
- * @param payload     The payload.
- * @param payload_len The length of the payload.
- */
-void msn_message_parse_payload(MsnMessage *msg, const char *payload,
-							   size_t payload_len);
-
-/**
- * Destroys a message.
- *
- * @param msg The message to destroy.
- */
-void msn_message_destroy(MsnMessage *msg);
-
-/**
- * Increments the reference count on a message.
- *
- * @param msg The message.
- *
- * @return @a msg
- */
-MsnMessage *msn_message_ref(MsnMessage *msg);
-
-/**
- * Decrements the reference count on a message.
- *
- * This will destroy the structure if the count hits 0.
- *
- * @param msg The message.
- *
- * @return @a msg, or @c NULL if the new count is 0.
- */
-MsnMessage *msn_message_unref(MsnMessage *msg);
-
-/**
- * Generates the payload data of a message.
- *
- * @param msg      The message.
- * @param ret_size The returned size of the payload.
- *
- * @return The payload data of the message.
- */
-char *msn_message_gen_payload(MsnMessage *msg, size_t *ret_size);
-
-/**
- * Sets the flag for an outgoing message.
- *
- * @param msg  The message.
- * @param flag The flag.
- */
-void msn_message_set_flag(MsnMessage *msg, char flag);
-
-/**
- * Returns the flag for an outgoing message.
- *
- * @param msg The message.
- *
- * @return The flag.
- */
-char msn_message_get_flag(const MsnMessage *msg);
-
-#if 0
-/**
- * Sets the body of a message.
- *
- * @param msg  The message.
- * @param body The body of the message.
- */
-void msn_message_set_body(MsnMessage *msg, const char *body);
-
-/**
- * Returns the body of the message.
- *
- * @param msg The message.
- *
- * @return The body of the message.
- */
-const char *msn_message_get_body(const MsnMessage *msg);
-#endif
-/**
- * Sets the binary content of the message.
- *
- * @param msg  The message.
- * @param data The binary data.
- * @param len  The length of the data.
- */
-void msn_message_set_bin_data(MsnMessage *msg, const void *data, size_t len);
-
-/**
- * Returns the binary content of the message.
- *
- * @param msg The message.
- * @param len The returned length of the data.
- *
- * @return The binary data.
- */
-const void *msn_message_get_bin_data(const MsnMessage *msg, size_t *len);
-
-/**
- * Sets the content type in a message.
- *
- * @param msg  The message.
- * @param type The content-type.
- */
-void msn_message_set_content_type(MsnMessage *msg, const char *type);
-
-/**
- * Returns the content type in a message.
- *
- * @param msg The message.
- *
- * @return The content-type.
- */
-const char *msn_message_get_content_type(const MsnMessage *msg);
-
-/**
- * Sets the charset in a message.
- *
- * @param msg     The message.
- * @param charset The charset.
- */
-void msn_message_set_charset(MsnMessage *msg, const char *charset);
-
-/**
- * Returns the charset in a message.
- *
- * @param msg The message.
- *
- * @return The charset.
- */
-const char *msn_message_get_charset(const MsnMessage *msg);
-
-/**
- * Sets an attribute in a message.
- *
- * @param msg   The message.
- * @param attr  The attribute name.
- * @param value The attribute value.
- */
-void msn_message_set_attr(MsnMessage *msg, const char *attr,
-						  const char *value);
-
-/**
- * Returns an attribute from a message.
- *
- * @param msg  The message.
- * @param attr The attribute.
- *
- * @return The value, or @c NULL if not found.
- */
-const char *msn_message_get_attr(const MsnMessage *msg, const char *attr);
-
-/**
- * Parses the body and returns it in the form of a hashtable.
- *
- * @param msg The message.
- *
- * @return The resulting hashtable.
- */
-GHashTable *msn_message_get_hashtable_from_body(const MsnMessage *msg);
-
-void msn_message_show_readable(MsnMessage *msg, const char *info,
-							   gboolean text_body);
-
-void msn_message_parse_slp_body(MsnMessage *msg, const char *body,
-								size_t len);
-
-char *msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size);
-
-char *msn_message_to_string(MsnMessage *msg);
-
-#endif /* _MSN_MSG_H_ */
--- a/libpurple/protocols/msnp9/msn-utils.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,441 +0,0 @@
-/**
- * @file msn-utils.c Utility functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "msn-utils.h"
-
-void
-msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
-{
-	char *cur;
-	GString *pre  = g_string_new(NULL);
-	GString *post = g_string_new(NULL);
-	unsigned int colors[3];
-
-	if (pre_ret  != NULL) *pre_ret  = NULL;
-	if (post_ret != NULL) *post_ret = NULL;
-
-	cur = strstr(mime, "FN=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		pre = g_string_append(pre, "<FONT FACE=\"");
-
-		while (*cur && *cur != ';')
-		{
-			pre = g_string_append_c(pre, *cur);
-			cur++;
-		}
-
-		pre = g_string_append(pre, "\">");
-		post = g_string_prepend(post, "</FONT>");
-	}
-
-	cur = strstr(mime, "EF=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		while (*cur && *cur != ';')
-		{
-			pre = g_string_append_c(pre, '<');
-			pre = g_string_append_c(pre, *cur);
-			pre = g_string_append_c(pre, '>');
-			post = g_string_prepend_c(post, '>');
-			post = g_string_prepend_c(post, *cur);
-			post = g_string_prepend_c(post, '/');
-			post = g_string_prepend_c(post, '<');
-			cur++;
-		}
-	}
-
-	cur = strstr(mime, "CO=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		int i;
-
-		i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
-
-		if (i > 0)
-		{
-			char tag[64];
-
-			if (i == 1)
-			{
-				colors[1] = 0;
-				colors[2] = 0;
-			}
-			else if (i == 2)
-			{
-				unsigned int temp = colors[0];
-
-				colors[0] = colors[1];
-				colors[1] = temp;
-				colors[2] = 0;
-			}
-			else if (i == 3)
-			{
-				unsigned int temp = colors[2];
-
-				colors[2] = colors[0];
-				colors[0] = temp;
-			}
-
-			g_snprintf(tag, sizeof(tag),
-					   "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">",
-					   colors[0], colors[1], colors[2]);
-
-			pre = g_string_append(pre, tag);
-			post = g_string_prepend(post, "</FONT>");
-		}
-	}
-
-	cur = strstr(mime, "RL=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		if (*cur == '1')
-		{
-			/* RTL text was received */
-			pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
-			post = g_string_prepend(post, "</SPAN>");
-		}
-	}
-
-	cur = g_strdup(purple_url_decode(pre->str));
-	g_string_free(pre, TRUE);
-
-	if (pre_ret != NULL)
-		*pre_ret = cur;
-	else
-		g_free(cur);
-
-	cur = g_strdup(purple_url_decode(post->str));
-	g_string_free(post, TRUE);
-
-	if (post_ret != NULL)
-		*post_ret = cur;
-	else
-		g_free(cur);
-}
-
-/*
- * We need this because we're only supposed to encode spaces in the font
- * names. purple_url_encode() isn't acceptable.
- */
-static const char *
-encode_spaces(const char *str)
-{
-	static char buf[BUF_LEN];
-	const char *c;
-	char *d;
-
-	g_return_val_if_fail(str != NULL, NULL);
-
-	for (c = str, d = buf; *c != '\0'; c++)
-	{
-		if (*c == ' ')
-		{
-			*d++ = '%';
-			*d++ = '2';
-			*d++ = '0';
-		}
-		else
-			*d++ = *c;
-	}
-	*d = '\0';
-
-	return buf;
-}
-
-/*
- * Taken from the zephyr plugin.
- * This parses HTML formatting (put out by one of the gtkimhtml widgets
- * and converts it to msn formatting. It doesn't deal with the tag closing,
- * but gtkimhtml widgets give valid html.
- * It currently deals properly with <b>, <u>, <i>, <font face=...>,
- * <font color=...>, <span dir=...>, <span style="direction: ...">.
- * It ignores <font back=...> and <font size=...>
- */
-void
-msn_import_html(const char *html, char **attributes, char **message)
-{
-	int len, retcount = 0;
-	const char *c;
-	char *msg;
-	char *fontface = NULL;
-	char fonteffect[4];
-	char fontcolor[7];
-	char direction = '0';
-
-	gboolean has_bold = FALSE;
-	gboolean has_italic = FALSE;
-	gboolean has_underline = FALSE;
-	gboolean has_strikethrough = FALSE;
-
-	g_return_if_fail(html       != NULL);
-	g_return_if_fail(attributes != NULL);
-	g_return_if_fail(message    != NULL);
-
-	len = strlen(html);
-	msg = g_malloc0(len + 1);
-
-	memset(fontcolor, 0, sizeof(fontcolor));
-	strcat(fontcolor, "0");
-	memset(fonteffect, 0, sizeof(fonteffect));
-
-	for (c = html; *c != '\0';)
-	{
-		if (*c == '<')
-		{
-			if (!g_ascii_strncasecmp(c + 1, "br>", 3))
-			{
-				msg[retcount++] = '\r';
-				msg[retcount++] = '\n';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
-			{
-				if (!has_italic)
-				{
-					strcat(fonteffect, "I");
-					has_italic = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
-			{
-				if (!has_bold)
-				{
-					strcat(fonteffect, "B");
-					has_bold = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
-			{
-				if (!has_underline)
-				{
-					strcat(fonteffect, "U");
-					has_underline = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
-			{
-				if (!has_strikethrough)
-				{
-					strcat(fonteffect, "S");
-					has_strikethrough = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
-			{
-				c += 9;
-
-				if (!g_ascii_strncasecmp(c, "mailto:", 7))
-					c += 7;
-
-				while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
-					msg[retcount++] = *c++;
-
-				if (*c != '\0')
-					c += 2;
-
-				/* ignore descriptive string */
-				while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
-					c++;
-
-				if (*c != '\0')
-					c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "span", 4))
-			{
-				/* Bi-directional text support using CSS properties in span tags */
-				c += 5;
-
-				while (*c != '\0' && *c != '>')
-				{
-					while (*c == ' ')
-						c++;
-					if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
-					{
-						c += 9;
-						direction = '1';
-					}
-					else if (!g_ascii_strncasecmp(c, "style=\"", 7))
-					{
-						/* Parse inline CSS attributes */
-						char *attributes;
-						int attr_len = 0;
-						c += 7;
-						while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
-							attr_len++;
-						if (*(c + attr_len) == '"')
-						{
-							char *attr_dir;
-							attributes = g_strndup(c, attr_len);
-							attr_dir = purple_markup_get_css_property(attributes, "direction");
-							if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
-								direction = '1';
-							g_free(attr_dir);
-							g_free(attributes);
-						}
-
-					}
-					else
-					{
-						c++;
-					}
-				}
-				if (*c == '>')
-					c++;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "font", 4))
-			{
-				c += 5;
-
-				while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
-					c++;
-
-				if (!g_ascii_strncasecmp(c, "color=\"#", 7))
-				{
-					c += 8;
-
-					fontcolor[0] = *(c + 4);
-					fontcolor[1] = *(c + 5);
-					fontcolor[2] = *(c + 2);
-					fontcolor[3] = *(c + 3);
-					fontcolor[4] = *c;
-					fontcolor[5] = *(c + 1);
-
-					c += 8;
-				}
-				else if (!g_ascii_strncasecmp(c, "face=\"", 6))
-				{
-					const char *end = NULL;
-					const char *comma = NULL;
-					unsigned int namelen = 0;
-
-					c += 6;
-					end = strchr(c, '\"');
-					comma = strchr(c, ',');
-
-					if (comma == NULL || comma > end)
-						namelen = (unsigned int)(end - c);
-					else
-						namelen = (unsigned int)(comma - c);
-
-					fontface = g_strndup(c, namelen);
-					c = end + 2;
-				}
-				else
-				{
-					/* Drop all unrecognized/misparsed font tags */
-					while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
-						c++;
-
-					if (*c != '\0')
-						c += 2;
-				}
-			}
-			else
-			{
-				while ((*c != '\0') && (*c != '>'))
-					c++;
-				if (*c != '\0')
-					c++;
-			}
-		}
-		else if (*c == '&')
-		{
-			if (!g_ascii_strncasecmp(c, "&lt;", 4))
-			{
-				msg[retcount++] = '<';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&gt;", 4))
-			{
-				msg[retcount++] = '>';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
-			{
-				msg[retcount++] = ' ';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&quot;", 6))
-			{
-				msg[retcount++] = '"';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&amp;", 5))
-			{
-				msg[retcount++] = '&';
-				c += 5;
-			}
-			else if (!g_ascii_strncasecmp(c, "&apos;", 6))
-			{
-				msg[retcount++] = '\'';
-				c += 6;
-			}
-			else
-				msg[retcount++] = *c++;
-		}
-		else
-			msg[retcount++] = *c++;
-	}
-
-	if (fontface == NULL)
-		fontface = g_strdup("MS Sans Serif");
-
-	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
-								  encode_spaces(fontface),
-								  fonteffect, fontcolor, direction);
-	*message = g_strdup(msg);
-
-	g_free(fontface);
-	g_free(msg);
-}
-
-void
-msn_parse_socket(const char *str, char **ret_host, int *ret_port)
-{
-	char *host;
-	char *c;
-	int port;
-
-	host = g_strdup(str);
-
-	if ((c = strchr(host, ':')) != NULL)
-	{
-		*c = '\0';
-		port = atoi(c + 1);
-	}
-	else
-		port = 1863;
-
-	*ret_host = host;
-	*ret_port = port;
-}
--- a/libpurple/protocols/msnp9/msn-utils.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/**
- * @file msn-utils.h Utility functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_UTILS_H_
-#define _MSN_UTILS_H_
-
-/**
- * Parses the MSN message formatting into a format compatible with Purple.
- *
- * @param mime     The mime header with the formatting.
- * @param pre_ret  The returned prefix string.
- * @param post_ret The returned postfix string.
- *
- * @return The new message.
- */
-void msn_parse_format(const char *mime, char **pre_ret, char **post_ret);
-
-/**
- * Parses the Purple message formatting (html) into the MSN format.
- *
- * @param html			The html message to format.
- * @param attributes	The returned attributes string.
- * @param message		The returned message string.
- *
- * @return The new message.
- */
-void msn_import_html(const char *html, char **attributes, char **message);
-
-void msn_parse_socket(const char *str, char **ret_host, int *ret_port);
-
-#endif /* _MSN_UTILS_H_ */
--- a/libpurple/protocols/msnp9/msn.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2363 +0,0 @@
-/**
- * @file msn.c The MSN protocol plugin
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#define PHOTO_SUPPORT 1
-
-#include <glib.h>
-
-#include "msn.h"
-#include "accountopt.h"
-#include "eventloop.h"
-#include "msg.h"
-#include "page.h"
-#include "pluginpref.h"
-#include "prefs.h"
-#include "session.h"
-#include "smiley.h"
-#include "state.h"
-#include "util.h"
-#include "cmds.h"
-#include "core.h"
-#include "prpl.h"
-#include "msn-utils.h"
-#include "version.h"
-
-#include "switchboard.h"
-#include "notification.h"
-#include "sync.h"
-#include "slplink.h"
-
-#if PHOTO_SUPPORT
-#include "imgstore.h"
-#endif
-
-typedef struct
-{
-	PurpleConnection *gc;
-	const char *passport;
-
-} MsnMobileData;
-
-typedef struct
-{
-	PurpleConnection *gc;
-	char *name;
-
-} MsnGetInfoData;
-
-typedef struct
-{
-	MsnGetInfoData *info_data;
-	char *stripped;
-	char *url_buffer;
-	PurpleNotifyUserInfo *user_info;
-	char *photo_url_text;
-
-} MsnGetInfoStepTwoData;
-
-typedef struct
-{
-	PurpleConnection *gc;
-	const char *who;
-	char *msg;
-	PurpleMessageFlags flags;
-	time_t when;
-} MsnIMData;
-
-typedef struct
-{
-	char *smile;
-	MsnObject *obj;
-} MsnEmoticon;
-
-static const char *
-msn_normalize(const PurpleAccount *account, const char *str)
-{
-	static char buf[BUF_LEN];
-	char *tmp;
-
-	g_return_val_if_fail(str != NULL, NULL);
-
-	g_snprintf(buf, sizeof(buf), "%s%s", str,
-			   (strchr(str, '@') ? "" : "@hotmail.com"));
-
-	tmp = g_utf8_strdown(buf, -1);
-	strncpy(buf, tmp, sizeof(buf));
-	g_free(tmp);
-
-	return buf;
-}
-
-static gboolean
-msn_send_attention(PurpleConnection *gc, const char *username, guint type)
-{
-	MsnMessage *msg;
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-
-	msg = msn_message_new_nudge();
-	session = gc->proto_data;
-	swboard = msn_session_get_swboard(session, username, MSN_SB_FLAG_IM);
-
-	if (swboard == NULL)
-		return FALSE;
-
-	msn_switchboard_send_msg(swboard, msg, TRUE);
-	msn_message_destroy(msg);
-
-	return TRUE;
-}
-
-static GList *
-msn_attention_types(PurpleAccount *account)
-{
-	static GList *list = NULL;
-
-	if (!list) {
-		list = g_list_append(list, purple_attention_type_new("Nudge", _("Nudge"),
-				_("%s has nudged you!"), _("Nudging %s...")));
-	}
-
-	return list;
-}
-
-static GHashTable *
-msn_get_account_text_table(PurpleAccount *unused)
-{
-	GHashTable *table;
-
-	table = g_hash_table_new(g_str_hash, g_str_equal);
-
-	g_hash_table_insert(table, "login_label", (gpointer)_("Email Address..."));
-
-	return table;
-}
-
-static PurpleCmdRet
-msn_cmd_nudge(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data)
-{
-	PurpleAccount *account = purple_conversation_get_account(conv);
-	PurpleConnection *gc = purple_account_get_connection(account);
-	const gchar *username;
-
-	username = purple_conversation_get_name(conv);
-
-	purple_prpl_send_attention(gc, username, MSN_NUDGE);
-
-	return PURPLE_CMD_RET_OK;
-}
-
-static void
-msn_act_id(PurpleConnection *gc, const char *entry)
-{
-	MsnCmdProc *cmdproc;
-	MsnSession *session;
-	PurpleAccount *account;
-	const char *alias;
-
-	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-	account = purple_connection_get_account(gc);
-
-	if(entry && strlen(entry))
-		alias = purple_url_encode(entry);
-	else
-		alias = "";
-
-	if (strlen(alias) > BUDDY_ALIAS_MAXLEN)
-	{
-		purple_notify_error(gc, NULL,
-						  _("Your new MSN friendly name is too long."), NULL);
-		return;
-	}
-
-	msn_cmdproc_send(cmdproc, "REA", "%s %s",
-					 purple_account_get_username(account),
-					 alias);
-}
-
-static void
-msn_set_prp(PurpleConnection *gc, const char *type, const char *entry)
-{
-	MsnCmdProc *cmdproc;
-	MsnSession *session;
-
-	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-
-	if (entry == NULL || *entry == '\0')
-	{
-		msn_cmdproc_send(cmdproc, "PRP", "%s", type);
-	}
-	else
-	{
-		msn_cmdproc_send(cmdproc, "PRP", "%s %s", type,
-						 purple_url_encode(entry));
-	}
-}
-
-static void
-msn_set_home_phone_cb(PurpleConnection *gc, const char *entry)
-{
-	msn_set_prp(gc, "PHH", entry);
-}
-
-static void
-msn_set_work_phone_cb(PurpleConnection *gc, const char *entry)
-{
-	msn_set_prp(gc, "PHW", entry);
-}
-
-static void
-msn_set_mobile_phone_cb(PurpleConnection *gc, const char *entry)
-{
-	msn_set_prp(gc, "PHM", entry);
-}
-
-static void
-enable_msn_pages_cb(PurpleConnection *gc)
-{
-	msn_set_prp(gc, "MOB", "Y");
-}
-
-static void
-disable_msn_pages_cb(PurpleConnection *gc)
-{
-	msn_set_prp(gc, "MOB", "N");
-}
-
-static void
-send_to_mobile(PurpleConnection *gc, const char *who, const char *entry)
-{
-	MsnTransaction *trans;
-	MsnSession *session;
-	MsnCmdProc *cmdproc;
-	MsnPage *page;
-	char *payload;
-	size_t payload_len;
-
-	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-
-	page = msn_page_new();
-	msn_page_set_body(page, entry);
-
-	payload = msn_page_gen_payload(page, &payload_len);
-
-	trans = msn_transaction_new(cmdproc, "PGD", "%s 1 %d", who, payload_len);
-
-	msn_transaction_set_payload(trans, payload, payload_len);
-
-	msn_page_destroy(page);
-
-	msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-static void
-send_to_mobile_cb(MsnMobileData *data, const char *entry)
-{
-	send_to_mobile(data->gc, data->passport, entry);
-	g_free(data);
-}
-
-static void
-close_mobile_page_cb(MsnMobileData *data, const char *entry)
-{
-	g_free(data);
-}
-
-/* -- */
-
-static void
-msn_show_set_friendly_name(PurplePluginAction *action)
-{
-	PurpleConnection *gc;
-
-	gc = (PurpleConnection *) action->context;
-
-	purple_request_input(gc, NULL, _("Set your friendly name."),
-					   _("This is the name that other MSN buddies will "
-						 "see you as."),
-					   purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
-					   _("OK"), G_CALLBACK(msn_act_id),
-					   _("Cancel"), NULL,
-					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
-}
-
-static void
-msn_show_set_home_phone(PurplePluginAction *action)
-{
-	PurpleConnection *gc;
-	MsnSession *session;
-
-	gc = (PurpleConnection *) action->context;
-	session = gc->proto_data;
-
-	purple_request_input(gc, NULL, _("Set your home phone number."), NULL,
-					   msn_user_get_home_phone(session->user), FALSE, FALSE, NULL,
-					   _("OK"), G_CALLBACK(msn_set_home_phone_cb),
-					   _("Cancel"), NULL,
-					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
-}
-
-static void
-msn_show_set_work_phone(PurplePluginAction *action)
-{
-	PurpleConnection *gc;
-	MsnSession *session;
-
-	gc = (PurpleConnection *) action->context;
-	session = gc->proto_data;
-
-	purple_request_input(gc, NULL, _("Set your work phone number."), NULL,
-					   msn_user_get_work_phone(session->user), FALSE, FALSE, NULL,
-					   _("OK"), G_CALLBACK(msn_set_work_phone_cb),
-					   _("Cancel"), NULL,
-					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
-}
-
-static void
-msn_show_set_mobile_phone(PurplePluginAction *action)
-{
-	PurpleConnection *gc;
-	MsnSession *session;
-
-	gc = (PurpleConnection *) action->context;
-	session = gc->proto_data;
-
-	purple_request_input(gc, NULL, _("Set your mobile phone number."), NULL,
-					   msn_user_get_mobile_phone(session->user), FALSE, FALSE, NULL,
-					   _("OK"), G_CALLBACK(msn_set_mobile_phone_cb),
-					   _("Cancel"), NULL,
-					   purple_connection_get_account(gc), NULL, NULL,
-					   gc);
-}
-
-static void
-msn_show_set_mobile_pages(PurplePluginAction *action)
-{
-	PurpleConnection *gc;
-
-	gc = (PurpleConnection *) action->context;
-
-	purple_request_action(gc, NULL, _("Allow MSN Mobile pages?"),
-						_("Do you want to allow or disallow people on "
-						  "your buddy list to send you MSN Mobile pages "
-						  "to your cell phone or other mobile device?"),
-						PURPLE_DEFAULT_ACTION_NONE,
-						purple_connection_get_account(gc), NULL, NULL,
-						gc, 3,
-						_("Allow"), G_CALLBACK(enable_msn_pages_cb),
-						_("Disallow"), G_CALLBACK(disable_msn_pages_cb),
-						_("Cancel"), NULL);
-}
-
-static void
-msn_show_hotmail_inbox(PurplePluginAction *action)
-{
-	PurpleConnection *gc;
-	MsnSession *session;
-
-	gc = (PurpleConnection *) action->context;
-	session = gc->proto_data;
-
-	if (session->passport_info.file == NULL)
-	{
-		purple_notify_error(gc, NULL,
-						  _("This Hotmail account may not be active."), NULL);
-		return;
-	}
-
-	purple_notify_uri(gc, session->passport_info.file);
-}
-
-static void
-show_send_to_mobile_cb(PurpleBlistNode *node, gpointer ignored)
-{
-	PurpleBuddy *buddy;
-	PurpleConnection *gc;
-	MsnSession *session;
-	MsnMobileData *data;
-
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
-	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-
-	session = gc->proto_data;
-
-	data = g_new0(MsnMobileData, 1);
-	data->gc = gc;
-	data->passport = buddy->name;
-
-	purple_request_input(gc, NULL, _("Send a mobile message."), NULL,
-					   NULL, TRUE, FALSE, NULL,
-					   _("Page"), G_CALLBACK(send_to_mobile_cb),
-					   _("Close"), G_CALLBACK(close_mobile_page_cb),
-					   purple_connection_get_account(gc), purple_buddy_get_name(buddy), NULL,
-					   data);
-}
-
-static gboolean
-msn_offline_message(const PurpleBuddy *buddy) {
-	MsnUser *user;
-	if (buddy == NULL)
-		return FALSE;
-	user = buddy->proto_data;
-	return user && user->mobile;
-}
-
-static void
-initiate_chat_cb(PurpleBlistNode *node, gpointer data)
-{
-	PurpleBuddy *buddy;
-	PurpleConnection *gc;
-
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
-
-	buddy = (PurpleBuddy *) node;
-	gc = purple_account_get_connection(buddy->account);
-
-	session = gc->proto_data;
-
-	swboard = msn_switchboard_new(session);
-	msn_switchboard_request(swboard);
-	msn_switchboard_request_add_user(swboard, buddy->name);
-
-	/* TODO: This might move somewhere else, after USR might be */
-	swboard->chat_id = msn_switchboard_get_chat_id();
-	swboard->conv = serv_got_joined_chat(gc, swboard->chat_id, "MSN Chat");
-	swboard->flag = MSN_SB_FLAG_IM;
-
-	purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
-							purple_account_get_username(buddy->account), NULL, PURPLE_CBFLAGS_NONE, TRUE);
-}
-
-static void
-t_msn_xfer_init(PurpleXfer *xfer)
-{
-	MsnSlpLink *slplink = xfer->data;
-	msn_slplink_request_ft(slplink, xfer);
-}
-
-static PurpleXfer*
-msn_new_xfer(PurpleConnection *gc, const char *who)
-{
-	MsnSession *session;
-	MsnSlpLink *slplink;
-	PurpleXfer *xfer;
-
-	session = gc->proto_data;
-
-	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
-	if (xfer)
-	{
-		slplink = msn_session_get_slplink(session, who);
-
-		xfer->data = slplink;
-
-		purple_xfer_set_init_fnc(xfer, t_msn_xfer_init);
-	}
-
-	return xfer;
-}
-
-static void
-msn_send_file(PurpleConnection *gc, const char *who, const char *file)
-{
-	PurpleXfer *xfer = msn_new_xfer(gc, who);
-
-	if (file)
-		purple_xfer_request_accepted(xfer, file);
-	else
-		purple_xfer_request(xfer);
-}
-
-static gboolean
-msn_can_receive_file(PurpleConnection *gc, const char *who)
-{
-	PurpleAccount *account;
-	char *normal;
-	gboolean ret;
-
-	account = purple_connection_get_account(gc);
-
-	normal = g_strdup(msn_normalize(account, purple_account_get_username(account)));
-
-	ret = strcmp(normal, msn_normalize(account, who));
-
-	g_free(normal);
-
-	return ret;
-}
-
-/**************************************************************************
- * Protocol Plugin ops
- **************************************************************************/
-
-static const char *
-msn_list_icon(PurpleAccount *a, PurpleBuddy *b)
-{
-	return "msn";
-}
-
-static char *
-msn_status_text(PurpleBuddy *buddy)
-{
-	PurplePresence *presence;
-	PurpleStatus *status;
-
-	presence = purple_buddy_get_presence(buddy);
-	status = purple_presence_get_active_status(presence);
-
-	if (!purple_presence_is_available(presence) && !purple_presence_is_idle(presence))
-	{
-		return g_strdup(purple_status_get_name(status));
-	}
-
-	return NULL;
-}
-
-static void
-msn_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
-{
-	MsnUser *user;
-	PurplePresence *presence = purple_buddy_get_presence(buddy);
-	PurpleStatus *status = purple_presence_get_active_status(presence);
-
-	user = buddy->proto_data;
-
-
-	if (purple_presence_is_online(presence))
-	{
-		purple_notify_user_info_add_pair(user_info, _("Status"),
-									   (purple_presence_is_idle(presence) ? _("Idle") : purple_status_get_name(status)));
-	}
-
-	if (full && user)
-	{
-		purple_notify_user_info_add_pair(user_info, _("Has you"),
-									   ((user->list_op & (1 << MSN_LIST_RL)) ? _("Yes") : _("No")));
-	}
-
-	/* XXX: This is being shown in non-full tooltips because the
-	 * XXX: blocked icon overlay isn't always accurate for MSN.
-	 * XXX: This can die as soon as purple_privacy_check() knows that
-	 * XXX: this prpl always honors both the allow and deny lists. */
-	/* While the above comment may be strictly correct (the privacy API needs
-	 * rewriteing), purple_privacy_check() is going to be more accurate at
-	 * indicating whether a particular buddy is going to be able to message
-	 * you, which is the important information that this is trying to convey. */
-	if (full && user)
-	{
-		const char *phone;
-
-		purple_notify_user_info_add_pair(user_info, _("Blocked"),
-									   ((user->list_op & (1 << MSN_LIST_BL)) ? _("Yes") : _("No")));
-
-		phone = msn_user_get_home_phone(user);
-		if (phone != NULL)
-			purple_notify_user_info_add_pair(user_info, _("Home Phone Number"), phone);
-
-		phone = msn_user_get_work_phone(user);
-		if (phone != NULL)
-			purple_notify_user_info_add_pair(user_info, _("Work Phone Number"), phone);
-
-		phone = msn_user_get_mobile_phone(user);
-		if (phone != NULL)
-			purple_notify_user_info_add_pair(user_info, _("Mobile Phone Number"), phone);
-	}
-}
-
-static GList *
-msn_status_types(PurpleAccount *account)
-{
-	PurpleStatusType *status;
-	GList *types = NULL;
-
-	status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE,
-			NULL, NULL, FALSE, TRUE, FALSE);
-	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
-			NULL, NULL, FALSE, TRUE, FALSE);
-	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
-			"brb", _("Be Right Back"), FALSE, TRUE, FALSE);
-	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
-			"busy", _("Busy"), FALSE, TRUE, FALSE);
-	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
-			"phone", _("On the Phone"), FALSE, TRUE, FALSE);
-	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
-			"lunch", _("Out to Lunch"), FALSE, TRUE, FALSE);
-	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
-			NULL, NULL, FALSE, TRUE, FALSE);
-	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
-			NULL, NULL, FALSE, TRUE, FALSE);
-	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_MOBILE,
-			"mobile", NULL, FALSE, FALSE, TRUE);
-	types = g_list_append(types, status);
-
-	return types;
-}
-
-static GList *
-msn_actions(PurplePlugin *plugin, gpointer context)
-{
-	PurpleConnection *gc = (PurpleConnection *)context;
-	PurpleAccount *account;
-	const char *user;
-
-	GList *m = NULL;
-	PurplePluginAction *act;
-
-	act = purple_plugin_action_new(_("Set Friendly Name..."),
-								 msn_show_set_friendly_name);
-	m = g_list_append(m, act);
-	m = g_list_append(m, NULL);
-
-	act = purple_plugin_action_new(_("Set Home Phone Number..."),
-								 msn_show_set_home_phone);
-	m = g_list_append(m, act);
-
-	act = purple_plugin_action_new(_("Set Work Phone Number..."),
-			msn_show_set_work_phone);
-	m = g_list_append(m, act);
-
-	act = purple_plugin_action_new(_("Set Mobile Phone Number..."),
-			msn_show_set_mobile_phone);
-	m = g_list_append(m, act);
-	m = g_list_append(m, NULL);
-
-#if 0
-	act = purple_plugin_action_new(_("Enable/Disable Mobile Devices..."),
-			msn_show_set_mobile_support);
-	m = g_list_append(m, act);
-#endif
-
-	act = purple_plugin_action_new(_("Allow/Disallow Mobile Pages..."),
-			msn_show_set_mobile_pages);
-	m = g_list_append(m, act);
-
-	account = purple_connection_get_account(gc);
-	user = msn_normalize(account, purple_account_get_username(account));
-
-	if ((strstr(user, "@hotmail.") != NULL) ||
-		(strstr(user, "@msn.com") != NULL))
-	{
-		m = g_list_append(m, NULL);
-		act = purple_plugin_action_new(_("Open Hotmail Inbox"),
-				msn_show_hotmail_inbox);
-		m = g_list_append(m, act);
-	}
-
-	return m;
-}
-
-static GList *
-msn_buddy_menu(PurpleBuddy *buddy)
-{
-	MsnUser *user;
-
-	GList *m = NULL;
-	PurpleMenuAction *act;
-
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	user = buddy->proto_data;
-
-	if (user != NULL)
-	{
-		if (user->mobile)
-		{
-			act = purple_menu_action_new(_("Send to Mobile"),
-			                           PURPLE_CALLBACK(show_send_to_mobile_cb),
-			                           NULL, NULL);
-			m = g_list_append(m, act);
-		}
-	}
-
-	if (g_ascii_strcasecmp(buddy->name,
-	                       purple_account_get_username(buddy->account)))
-	{
-		act = purple_menu_action_new(_("Initiate _Chat"),
-		                           PURPLE_CALLBACK(initiate_chat_cb),
-		                           NULL, NULL);
-		m = g_list_append(m, act);
-	}
-
-	return m;
-}
-
-static GList *
-msn_blist_node_menu(PurpleBlistNode *node)
-{
-	if(PURPLE_BLIST_NODE_IS_BUDDY(node))
-	{
-		return msn_buddy_menu((PurpleBuddy *) node);
-	}
-	else
-	{
-		return NULL;
-	}
-}
-
-static void
-msn_login(PurpleAccount *account)
-{
-	PurpleConnection *gc;
-	MsnSession *session;
-	const char *username;
-	const char *host;
-	gboolean http_method = FALSE;
-	int port;
-
-	gc = purple_account_get_connection(account);
-
-	if (!purple_ssl_is_supported())
-	{
-		purple_connection_error_reason(gc,
-			PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
-
-			_("SSL support is needed for MSN. Please install a supported "
-			  "SSL library."));
-		return;
-	}
-
-	http_method = purple_account_get_bool(account, "http_method", FALSE);
-
-	if (http_method)
-		host = purple_account_get_string(account, "http_method_server", MSN_HTTPCONN_SERVER);
-	else
-		host = purple_account_get_string(account, "server", MSN_SERVER);
-	port = purple_account_get_int(account, "port", MSN_PORT);
-
-	session = msn_session_new(account);
-
-	gc->proto_data = session;
-	gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR |
-		PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY |
-		PURPLE_CONNECTION_ALLOW_ATTENTION;
-
-	msn_session_set_login_step(session, MSN_LOGIN_STEP_START);
-
-	/* Hmm, I don't like this. */
-	/* XXX shx: Me neither */
-	username = msn_normalize(account, purple_account_get_username(account));
-
-	if (strcmp(username, purple_account_get_username(account)))
-		purple_account_set_username(account, username);
-
-	if (!msn_session_connect(session, host, port, http_method))
-		purple_connection_error_reason(gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Unable to connect"));
-}
-
-static void
-msn_close(PurpleConnection *gc)
-{
-	MsnSession *session;
-
-	session = gc->proto_data;
-
-	g_return_if_fail(session != NULL);
-
-	msn_session_destroy(session);
-
-	gc->proto_data = NULL;
-}
-
-static gboolean
-msn_send_me_im(gpointer data)
-{
-	MsnIMData *imdata = data;
-	serv_got_im(imdata->gc, imdata->who, imdata->msg, imdata->flags, imdata->when);
-	g_free(imdata->msg);
-	g_free(imdata);
-	return FALSE;
-}
-
-static GString*
-msn_msg_emoticon_add(GString *current, MsnEmoticon *emoticon)
-{
-	MsnObject *obj;
-	char *strobj;
-
-	if (emoticon == NULL)
-		return current;
-
-	obj = emoticon->obj;
-
-	if (!obj)
-		return current;
-
-	strobj = msn_object_to_string(obj);
-
-	if (current)
-		g_string_append_printf(current, "\t%s\t%s",
-				emoticon->smile, strobj);
-	else {
-		current = g_string_new("");
-		g_string_printf(current,"%s\t%s",
-					emoticon->smile, strobj);
-	}
-
-	g_free(strobj);
-
-	return current;
-}
-
-static void
-msn_send_emoticons(MsnSwitchBoard *swboard, GString *body)
-{
-	MsnMessage *msg;
-
-	g_return_if_fail(body != NULL);
-
-	msg = msn_message_new(MSN_MSG_SLP);
-	msn_message_set_content_type(msg, "text/x-mms-emoticon");
-	msn_message_set_flag(msg, 'N');
-	msn_message_set_bin_data(msg, body->str, body->len);
-
-	msn_switchboard_send_msg(swboard, msg, TRUE);
-	msn_message_destroy(msg);
-}
-
-static void msn_emoticon_destroy(MsnEmoticon *emoticon)
-{
-	if (emoticon->obj)
-		msn_object_destroy(emoticon->obj);
-	g_free(emoticon->smile);
-	g_free(emoticon);
-}
-
-static GSList* msn_msg_grab_emoticons(const char *msg, const char *username)
-{
-	GSList *list;
-	GList *smileys;
-	PurpleSmiley *smiley;
-	PurpleStoredImage *img;
-	char *ptr;
-	MsnEmoticon *emoticon;
-	int length;
-
-	list = NULL;
-	smileys = purple_smileys_get_all();
-	length = strlen(msg);
-
-	for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
-		smiley = (PurpleSmiley*)smileys->data;
-
-		ptr = g_strstr_len(msg, length, purple_smiley_get_shortcut(smiley));
-
-		if (!ptr)
-			continue;
-
-		img = purple_smiley_get_stored_image(smiley);
-
-		emoticon = g_new0(MsnEmoticon, 1);
-		emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley));
-		emoticon->obj = msn_object_new_from_image(img,
-				purple_imgstore_get_filename(img),
-				username, MSN_OBJECT_EMOTICON);
-
-		purple_imgstore_unref(img);
-		list = g_slist_prepend(list, emoticon);
-	}
-
-	return list;
-}
-
-static int
-msn_send_im(PurpleConnection *gc, const char *who, const char *message,
-			PurpleMessageFlags flags)
-{
-	PurpleAccount *account;
-	PurpleBuddy *buddy = purple_find_buddy(gc->account, who);
-	MsnMessage *msg;
-	char *msgformat;
-	char *msgtext;
-	const char *username;
-
-	account = purple_connection_get_account(gc);
-	username = purple_account_get_username(account);
-
-	if (buddy) {
-		PurplePresence *p = purple_buddy_get_presence(buddy);
-		if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
-			char *text = purple_markup_strip_html(message);
-			send_to_mobile(gc, who, text);
-			g_free(text);
-			return 1;
-		}
-	}
-
-	msn_import_html(message, &msgformat, &msgtext);
-
-	if (strlen(msgtext) + strlen(msgformat) + strlen(DISPLAY_VERSION) > 1564)
-	{
-		g_free(msgformat);
-		g_free(msgtext);
-
-		return -E2BIG;
-	}
-
-	msg = msn_message_new_plain(msgtext);
-	msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
-
-	g_free(msgformat);
-	g_free(msgtext);
-
-	if (g_ascii_strcasecmp(who, username))
-	{
-		MsnSession *session;
-		MsnSwitchBoard *swboard;
-		MsnEmoticon *smile;
-		GSList *smileys;
-		GString *emoticons = NULL;
-
-		session = gc->proto_data;
-		swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM);
-		smileys = msn_msg_grab_emoticons(message, username);
-
-		while (smileys) {
-			smile = (MsnEmoticon*)smileys->data;
-			emoticons = msn_msg_emoticon_add(emoticons,smile);
-			msn_emoticon_destroy(smile);
-			smileys = g_slist_delete_link(smileys, smileys);
-		}
-
-		if (emoticons) {
-			msn_send_emoticons(swboard, emoticons);
-			g_string_free(emoticons, TRUE);
-		}
-
-		msn_switchboard_send_msg(swboard, msg, TRUE);
-	}
-	else
-	{
-		char *body_str, *body_enc, *pre, *post;
-		const char *format;
-		MsnIMData *imdata = g_new0(MsnIMData, 1);
-		/*
-		 * In MSN, you can't send messages to yourself, so
-		 * we'll fake like we received it ;)
-		 */
-		body_str = msn_message_to_string(msg);
-		body_enc = g_markup_escape_text(body_str, -1);
-		g_free(body_str);
-
-		format = msn_message_get_attr(msg, "X-MMS-IM-Format");
-		msn_parse_format(format, &pre, &post);
-		body_str = g_strdup_printf("%s%s%s", pre ? pre :  "",
-								   body_enc ? body_enc : "", post ? post : "");
-		g_free(body_enc);
-		g_free(pre);
-		g_free(post);
-
-		serv_got_typing_stopped(gc, who);
-		imdata->gc = gc;
-		imdata->who = who;
-		imdata->msg = body_str;
-		imdata->flags = flags;
-		imdata->when = time(NULL);
-		purple_timeout_add(0, msn_send_me_im, imdata);
-	}
-
-	msn_message_destroy(msg);
-
-	return 1;
-}
-
-static unsigned int
-msn_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
-{
-	PurpleAccount *account;
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-	MsnMessage *msg;
-
-	account = purple_connection_get_account(gc);
-	session = gc->proto_data;
-
-	/*
-	 * TODO: I feel like this should be "if (state != PURPLE_TYPING)"
-	 *       but this is how it was before, and I don't want to break
-	 *       anything. --KingAnt
-	 */
-	if (state == PURPLE_NOT_TYPING)
-		return 0;
-
-	if (!g_ascii_strcasecmp(who, purple_account_get_username(account)))
-	{
-		/* We'll just fake it, since we're sending to ourself. */
-		serv_got_typing(gc, who, MSN_TYPING_RECV_TIMEOUT, PURPLE_TYPING);
-
-		return MSN_TYPING_SEND_TIMEOUT;
-	}
-
-	swboard = msn_session_find_swboard(session, who);
-
-	if (swboard == NULL || !msn_switchboard_can_send(swboard))
-		return 0;
-
-	swboard->flag |= MSN_SB_FLAG_IM;
-
-	msg = msn_message_new(MSN_MSG_TYPING);
-	msn_message_set_content_type(msg, "text/x-msmsgscontrol");
-	msn_message_set_flag(msg, 'U');
-	msn_message_set_attr(msg, "TypingUser",
-						 purple_account_get_username(account));
-	msn_message_set_bin_data(msg, "\r\n", 2);
-
-	msn_switchboard_send_msg(swboard, msg, FALSE);
-
-	msn_message_destroy(msg);
-
-	return MSN_TYPING_SEND_TIMEOUT;
-}
-
-static void
-msn_set_status(PurpleAccount *account, PurpleStatus *status)
-{
-	PurpleConnection *gc;
-	MsnSession *session;
-
-	gc = purple_account_get_connection(account);
-
-	if (gc != NULL)
-	{
-		session = gc->proto_data;
-		msn_change_status(session);
-	}
-}
-
-static void
-msn_set_idle(PurpleConnection *gc, int idle)
-{
-	MsnSession *session;
-
-	session = gc->proto_data;
-
-	msn_change_status(session);
-}
-
-#if 0
-static void
-fake_userlist_add_buddy(MsnUserList *userlist,
-					   const char *who, int list_id,
-					   const char *group_name)
-{
-	MsnUser *user;
-	static int group_id_c = 1;
-	int group_id;
-
-	group_id = -1;
-
-	if (group_name != NULL)
-	{
-		MsnGroup *group;
-		group = msn_group_new(userlist, group_id_c, group_name);
-		group_id = group_id_c++;
-	}
-
-	user = msn_userlist_find_user(userlist, who);
-
-	if (user == NULL)
-	{
-		user = msn_user_new(userlist, who, NULL);
-		msn_userlist_add_user(userlist, user);
-	}
-	else
-		if (user->list_op & (1 << list_id))
-		{
-			if (list_id == MSN_LIST_FL)
-			{
-				if (group_id >= 0)
-					if (g_list_find(user->group_ids,
-									GINT_TO_POINTER(group_id)))
-						return;
-			}
-			else
-				return;
-		}
-
-	if (group_id >= 0)
-	{
-		user->group_ids = g_list_append(user->group_ids,
-										GINT_TO_POINTER(group_id));
-	}
-
-	user->list_op |= (1 << list_id);
-}
-#endif
-
-static void
-msn_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
-{
-	MsnSession *session;
-	MsnUserList *userlist;
-	const char *who;
-
-	session = gc->proto_data;
-	userlist = session->userlist;
-	who = msn_normalize(gc->account, buddy->name);
-
-	if (!session->logged_in)
-	{
-#if 0
-		fake_userlist_add_buddy(session->sync_userlist, who, MSN_LIST_FL,
-								group ? group->name : NULL);
-#else
-		purple_debug_error("msn", "msn_add_buddy called before connected\n");
-#endif
-
-		return;
-	}
-
-#if 0
-	if (group != NULL && group->name != NULL)
-		purple_debug_info("msn", "msn_add_buddy: %s, %s\n", who, group->name);
-	else
-		purple_debug_info("msn", "msn_add_buddy: %s\n", who);
-#endif
-
-#if 0
-	/* Which is the max? */
-	if (session->fl_users_count >= 150)
-	{
-		purple_debug_info("msn", "Too many buddies\n");
-		/* Buddy list full */
-		/* TODO: purple should be notified of this */
-		return;
-	}
-#endif
-
-	/* XXX - Would group ever be NULL here?  I don't think so...
-	 * shx: Yes it should; MSN handles non-grouped buddies, and this is only
-	 * internal. */
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_FL,
-						   group ? group->name : NULL);
-}
-
-static void
-msn_rem_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
-{
-	MsnSession *session;
-	MsnUserList *userlist;
-
-	session = gc->proto_data;
-	userlist = session->userlist;
-
-	if (!session->logged_in)
-		return;
-
-	/* XXX - Does buddy->name need to be msn_normalize'd here?  --KingAnt */
-	msn_userlist_rem_buddy(userlist, buddy->name, MSN_LIST_FL, group->name);
-}
-
-static void
-msn_add_permit(PurpleConnection *gc, const char *who)
-{
-	MsnSession *session;
-	MsnUserList *userlist;
-	MsnUser *user;
-
-	session = gc->proto_data;
-	userlist = session->userlist;
-	user = msn_userlist_find_user(userlist, who);
-
-	if (!session->logged_in)
-		return;
-
-	if (user != NULL && user->list_op & MSN_LIST_BL_OP)
-		msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
-
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
-}
-
-static void
-msn_add_deny(PurpleConnection *gc, const char *who)
-{
-	MsnSession *session;
-	MsnUserList *userlist;
-	MsnUser *user;
-
-	session = gc->proto_data;
-	userlist = session->userlist;
-	user = msn_userlist_find_user(userlist, who);
-
-	if (!session->logged_in)
-		return;
-
-	if (user != NULL && user->list_op & MSN_LIST_AL_OP)
-		msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
-
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
-}
-
-static void
-msn_rem_permit(PurpleConnection *gc, const char *who)
-{
-	MsnSession *session;
-	MsnUserList *userlist;
-	MsnUser *user;
-
-	session = gc->proto_data;
-	userlist = session->userlist;
-
-	if (!session->logged_in)
-		return;
-
-	user = msn_userlist_find_user(userlist, who);
-
-	msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
-
-	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
-		msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
-}
-
-static void
-msn_rem_deny(PurpleConnection *gc, const char *who)
-{
-	MsnSession *session;
-	MsnUserList *userlist;
-	MsnUser *user;
-
-	session = gc->proto_data;
-	userlist = session->userlist;
-
-	if (!session->logged_in)
-		return;
-
-	user = msn_userlist_find_user(userlist, who);
-
-	msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
-
-	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
-		msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
-}
-
-static void
-msn_set_permit_deny(PurpleConnection *gc)
-{
-	PurpleAccount *account;
-	MsnSession *session;
-	MsnCmdProc *cmdproc;
-
-	account = purple_connection_get_account(gc);
-	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-
-	if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
-		account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
-	{
-		msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
-	}
-	else
-	{
-		msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
-	}
-}
-
-static void
-msn_chat_invite(PurpleConnection *gc, int id, const char *msg,
-				const char *who)
-{
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-
-	session = gc->proto_data;
-
-	swboard = msn_session_find_swboard_with_id(session, id);
-
-	if (swboard == NULL)
-	{
-		/* if we have no switchboard, everyone else left the chat already */
-		swboard = msn_switchboard_new(session);
-		msn_switchboard_request(swboard);
-		swboard->chat_id = id;
-		swboard->conv = purple_find_chat(gc, id);
-	}
-
-	swboard->flag |= MSN_SB_FLAG_IM;
-
-	msn_switchboard_request_add_user(swboard, who);
-}
-
-static void
-msn_chat_leave(PurpleConnection *gc, int id)
-{
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-	PurpleConversation *conv;
-
-	session = gc->proto_data;
-
-	swboard = msn_session_find_swboard_with_id(session, id);
-
-	/* if swboard is NULL we were the only person left anyway */
-	if (swboard == NULL)
-		return;
-
-	conv = swboard->conv;
-
-	msn_switchboard_release(swboard, MSN_SB_FLAG_IM);
-
-	/* If other switchboards managed to associate themselves with this
-	 * conv, make sure they know it's gone! */
-	if (conv != NULL)
-	{
-		while ((swboard = msn_session_find_swboard_with_conv(session, conv)) != NULL)
-			swboard->conv = NULL;
-	}
-}
-
-static int
-msn_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
-{
-	PurpleAccount *account;
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-	MsnMessage *msg;
-	char *msgformat;
-	char *msgtext;
-
-	account = purple_connection_get_account(gc);
-	session = gc->proto_data;
-	swboard = msn_session_find_swboard_with_id(session, id);
-
-	if (swboard == NULL)
-		return -EINVAL;
-
-	if (!swboard->ready)
-		return 0;
-
-	swboard->flag |= MSN_SB_FLAG_IM;
-
-	msn_import_html(message, &msgformat, &msgtext);
-
-	if (strlen(msgtext) + strlen(msgformat) + strlen(DISPLAY_VERSION) > 1564)
-	{
-		g_free(msgformat);
-		g_free(msgtext);
-
-		return -E2BIG;
-	}
-
-	msg = msn_message_new_plain(msgtext);
-	msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
-	msn_switchboard_send_msg(swboard, msg, FALSE);
-	msn_message_destroy(msg);
-
-	g_free(msgformat);
-	g_free(msgtext);
-
-	serv_got_chat_in(gc, id, purple_account_get_username(account), flags,
-					 message, time(NULL));
-
-	return 0;
-}
-
-static void
-msn_keepalive(PurpleConnection *gc)
-{
-	MsnSession *session;
-
-	session = gc->proto_data;
-
-	if (!session->http_method)
-	{
-		MsnCmdProc *cmdproc;
-
-		cmdproc = session->notification->cmdproc;
-
-		msn_cmdproc_send_quick(cmdproc, "PNG", NULL, NULL);
-	}
-}
-
-static void
-msn_group_buddy(PurpleConnection *gc, const char *who,
-				const char *old_group_name, const char *new_group_name)
-{
-	MsnSession *session;
-	MsnUserList *userlist;
-
-	session = gc->proto_data;
-	userlist = session->userlist;
-
-	msn_userlist_move_buddy(userlist, who, old_group_name, new_group_name);
-}
-
-static void
-msn_rename_group(PurpleConnection *gc, const char *old_name,
-				 PurpleGroup *group, GList *moved_buddies)
-{
-	MsnSession *session;
-	MsnCmdProc *cmdproc;
-	int old_gid;
-	const char *enc_new_group_name;
-
-	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-	enc_new_group_name = purple_url_encode(group->name);
-
-	old_gid = msn_userlist_find_group_id(session->userlist, old_name);
-
-	if (old_gid >= 0)
-	{
-		msn_cmdproc_send(cmdproc, "REG", "%d %s 0", old_gid,
-						 enc_new_group_name);
-	}
-	else
-	{
-		msn_cmdproc_send(cmdproc, "ADG", "%s 0", enc_new_group_name);
-	}
-}
-
-static void
-msn_convo_closed(PurpleConnection *gc, const char *who)
-{
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-	PurpleConversation *conv;
-
-	session = gc->proto_data;
-
-	swboard = msn_session_find_swboard(session, who);
-
-	/*
-	 * Don't perform an assertion here. If swboard is NULL, then the
-	 * switchboard was either closed by the other party, or the person
-	 * is talking to himself.
-	 */
-	if (swboard == NULL)
-		return;
-
-	conv = swboard->conv;
-
-	/* If we release the switchboard here, it may still have messages
-	   pending ACK which would result in incorrect unsent message errors.
-	   Just let it timeout... This is *so* going to screw with people who
-	   use dumb clients that report "User has closed the conversation window" */
-	/* msn_switchboard_release(swboard, MSN_SB_FLAG_IM); */
-	swboard->conv = NULL;
-
-	/* If other switchboards managed to associate themselves with this
-	 * conv, make sure they know it's gone! */
-	if (conv != NULL)
-	{
-		while ((swboard = msn_session_find_swboard_with_conv(session, conv)) != NULL)
-			swboard->conv = NULL;
-	}
-}
-
-static void
-msn_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
-{
-	MsnSession *session;
-	MsnUser *user;
-
-	session = gc->proto_data;
-	user = session->user;
-
-	msn_user_set_buddy_icon(user, img);
-
-	msn_change_status(session);
-}
-
-static void
-msn_remove_group(PurpleConnection *gc, PurpleGroup *group)
-{
-	MsnSession *session;
-	MsnCmdProc *cmdproc;
-	int group_id;
-
-	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-
-	if ((group_id = msn_userlist_find_group_id(session->userlist, group->name)) >= 0)
-	{
-		msn_cmdproc_send(cmdproc, "RMG", "%d", group_id);
-	}
-}
-
-/**
- * Extract info text from info_data and add it to user_info
- */
-static gboolean
-msn_tooltip_extract_info_text(PurpleNotifyUserInfo *user_info, MsnGetInfoData *info_data)
-{
-	PurpleBuddy *b;
-
-	b = purple_find_buddy(purple_connection_get_account(info_data->gc),
-						info_data->name);
-
-	if (b)
-	{
-		char *tmp;
-
-		if (b->alias && b->alias[0])
-		{
-			char *aliastext = g_markup_escape_text(b->alias, -1);
-			purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext);
-			g_free(aliastext);
-		}
-
-		if (b->server_alias)
-		{
-			char *nicktext = g_markup_escape_text(b->server_alias, -1);
-			tmp = g_strdup_printf("<font sml=\"msn\">%s</font><br>", nicktext);
-			purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
-			g_free(tmp);
-			g_free(nicktext);
-		}
-
-		/* Add the tooltip information */
-		msn_tooltip_text(b, user_info, TRUE);
-
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-#if PHOTO_SUPPORT
-
-static char *
-msn_get_photo_url(const char *url_text)
-{
-	char *p, *q;
-
-	if ((p = strstr(url_text, " contactparams:photopreauthurl=\"")) != NULL)
-	{
-		p += strlen(" contactparams:photopreauthurl=\"");
-	}
-
-	if (p && (strncmp(p, "http://", 8) == 0) && ((q = strchr(p, '"')) != NULL))
-			return g_strndup(p, q - p);
-
-	return NULL;
-}
-
-static void msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
-		const gchar *url_text, size_t len, const gchar *error_message);
-
-#endif
-
-#if 0
-static char *msn_info_date_reformat(const char *field, size_t len)
-{
-	char *tmp = g_strndup(field, len);
-	time_t t = purple_str_to_time(tmp, FALSE, NULL, NULL, NULL);
-
-	g_free(tmp);
-	return g_strdup(purple_date_format_short(localtime(&t)));
-}
-#endif
-
-#define MSN_GOT_INFO_GET_FIELD(a, b) \
-	found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \
-			"\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, NULL); \
-	if (found) \
-		sect_info = TRUE;
-
-#define MSN_GOT_INFO_GET_FIELD_NO_SEARCH(a, b) \
-	found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \
-			"\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, msn_info_strip_search_link); \
-	if (found) \
-		sect_info = TRUE;
-
-static char *
-msn_info_strip_search_link(const char *field, size_t len)
-{
-	const char *c;
-	if ((c = strstr(field, " (http://")) == NULL)
-		return g_strndup(field, len);
-	return g_strndup(field, c - field);
-}
-
-static void
-msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
-		const gchar *url_text, size_t len, const gchar *error_message)
-{
-	MsnGetInfoData *info_data = (MsnGetInfoData *)data;
-	PurpleNotifyUserInfo *user_info;
-	char *stripped, *p, *q, *tmp;
-	char *user_url = NULL;
-	gboolean found;
-	gboolean has_tooltip_text = FALSE;
-	gboolean has_info = FALSE;
-	gboolean sect_info = FALSE;
-	gboolean has_contact_info = FALSE;
-	char *url_buffer;
-	int stripped_len;
-#if PHOTO_SUPPORT
-	char *photo_url_text = NULL;
-	MsnGetInfoStepTwoData *info2_data = NULL;
-#endif
-
-	purple_debug_info("msn", "In msn_got_info\n");
-
-	/* Make sure the connection is still valid */
-	if (g_list_find(purple_connections_get_all(), info_data->gc) == NULL)
-	{
-		purple_debug_warning("msn", "invalid connection. ignoring buddy info.\n");
-		g_free(info_data->name);
-		g_free(info_data);
-		return;
-	}
-
-	user_info = purple_notify_user_info_new();
-	has_tooltip_text = msn_tooltip_extract_info_text(user_info, info_data);
-
-	if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0)
-	{
-		tmp = g_strdup_printf("<b>%s</b>", _("Error retrieving profile"));
-		purple_notify_user_info_add_pair(user_info, NULL, tmp);
-		g_free(tmp);
-
-		purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL);
-		purple_notify_user_info_destroy(user_info);
-
-		g_free(info_data->name);
-		g_free(info_data);
-		return;
-	}
-
-	url_buffer = g_strdup(url_text);
-
-	/* If they have a homepage link, MSN masks it such that we need to
-	 * fetch the url out before purple_markup_strip_html() nukes it */
-	/* I don't think this works with the new spaces profiles - Stu 3/2/06 */
-	if ((p = strstr(url_text,
-			"Take a look at my </font><A class=viewDesc title=\"")) != NULL)
-	{
-		p += 50;
-
-		if ((q = strchr(p, '"')) != NULL)
-			user_url = g_strndup(p, q - p);
-	}
-
-	/*
-	 * purple_markup_strip_html() doesn't strip out character entities like &nbsp;
-	 * and &#183;
-	 */
-	while ((p = strstr(url_buffer, "&nbsp;")) != NULL)
-	{
-		*p = ' '; /* Turn &nbsp;'s into ordinary blanks */
-		p += 1;
-		memmove(p, p + 5, strlen(p + 5));
-		url_buffer[strlen(url_buffer) - 5] = '\0';
-	}
-
-	while ((p = strstr(url_buffer, "&#183;")) != NULL)
-	{
-		memmove(p, p + 6, strlen(p + 6));
-		url_buffer[strlen(url_buffer) - 6] = '\0';
-	}
-
-	/* Nuke the nasty \r's that just get in the way */
-	purple_str_strip_char(url_buffer, '\r');
-
-	/* MSN always puts in &#39; for apostrophes...replace them */
-	while ((p = strstr(url_buffer, "&#39;")) != NULL)
-	{
-		*p = '\'';
-		memmove(p + 1, p + 5, strlen(p + 5));
-		url_buffer[strlen(url_buffer) - 4] = '\0';
-	}
-
-	/* Nuke the html, it's easier than trying to parse the horrid stuff */
-	stripped = purple_markup_strip_html(url_buffer);
-	stripped_len = strlen(stripped);
-
-	purple_debug_misc("msn", "stripped = %p\n", stripped);
-	purple_debug_misc("msn", "url_buffer = %p\n", url_buffer);
-
-	/* General section header */
-	if (has_tooltip_text)
-		purple_notify_user_info_add_section_break(user_info);
-
-	purple_notify_user_info_add_section_header(user_info, _("General"));
-
-	/* Extract their Name and put it in */
-	MSN_GOT_INFO_GET_FIELD("Name", _("Name"));
-
-	/* General */
-	MSN_GOT_INFO_GET_FIELD("Nickname", _("Nickname"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Age", _("Age"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Gender", _("Gender"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Occupation", _("Occupation"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Location", _("Location"));
-
-	/* Extract their Interests and put it in */
-	found = purple_markup_extract_info_field(stripped, stripped_len, user_info,
-			"\nInterests\t", 0, " (/default.aspx?page=searchresults", 0,
-			"Undisclosed", _("Hobbies and Interests") /* _("Interests") */,
-			0, NULL, NULL);
-
-	if (found)
-		sect_info = TRUE;
-
-	MSN_GOT_INFO_GET_FIELD("More about me", _("A Little About Me"));
-
-	if (sect_info)
-	{
-		has_info = TRUE;
-		sect_info = FALSE;
-	}
-    else
-    {
-		/* Remove the section header */
-		purple_notify_user_info_remove_last_item(user_info);
-		if (has_tooltip_text)
-			purple_notify_user_info_remove_last_item(user_info);
-	}
-
-	/* Social */
-	purple_notify_user_info_add_section_break(user_info);
-	purple_notify_user_info_add_section_header(user_info, _("Social"));
-
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Marital status", _("Marital Status"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Interested in", _("Interests"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Pets", _("Pets"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Hometown", _("Hometown"));
-	MSN_GOT_INFO_GET_FIELD("Places lived", _("Places Lived"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Fashion", _("Fashion"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Humor", _("Humor"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Music", _("Music"));
-	MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Favorite quote", _("Favorite Quote"));
-
-	if (sect_info)
-	{
-		has_info = TRUE;
-		sect_info = FALSE;
-	}
-    else
-    {
-		/* Remove the section header */
-		purple_notify_user_info_remove_last_item(user_info);
-		purple_notify_user_info_remove_last_item(user_info);
-	}
-
-	/* Contact Info */
-	/* Personal */
-	purple_notify_user_info_add_section_break(user_info);
-	purple_notify_user_info_add_section_header(user_info, _("Contact Info"));
-	purple_notify_user_info_add_section_header(user_info, _("Personal"));
-
-	MSN_GOT_INFO_GET_FIELD("Name", _("Name"));
-	MSN_GOT_INFO_GET_FIELD("Significant other", _("Significant Other"));
-	MSN_GOT_INFO_GET_FIELD("Home phone", _("Home Phone"));
-	MSN_GOT_INFO_GET_FIELD("Home phone 2", _("Home Phone 2"));
-	MSN_GOT_INFO_GET_FIELD("Home address", _("Home Address"));
-	MSN_GOT_INFO_GET_FIELD("Personal Mobile", _("Personal Mobile"));
-	MSN_GOT_INFO_GET_FIELD("Home fax", _("Home Fax"));
-	MSN_GOT_INFO_GET_FIELD("Personal email", _("Personal Email"));
-	MSN_GOT_INFO_GET_FIELD("Personal IM", _("Personal IM"));
-	MSN_GOT_INFO_GET_FIELD("Birthday", _("Birthday"));
-	MSN_GOT_INFO_GET_FIELD("Anniversary", _("Anniversary"));
-	MSN_GOT_INFO_GET_FIELD("Notes", _("Notes"));
-
-	if (sect_info)
-	{
-		has_info = TRUE;
-		sect_info = FALSE;
-		has_contact_info = TRUE;
-	}
-    else
-    {
-		/* Remove the section header */
-		purple_notify_user_info_remove_last_item(user_info);
-	}
-
-	/* Business */
-	purple_notify_user_info_add_section_header(user_info, _("Work"));
-	MSN_GOT_INFO_GET_FIELD("Name", _("Name"));
-	MSN_GOT_INFO_GET_FIELD("Job title", _("Job Title"));
-	MSN_GOT_INFO_GET_FIELD("Company", _("Company"));
-	MSN_GOT_INFO_GET_FIELD("Department", _("Department"));
-	MSN_GOT_INFO_GET_FIELD("Profession", _("Profession"));
-	MSN_GOT_INFO_GET_FIELD("Work phone 1", _("Work Phone"));
-	MSN_GOT_INFO_GET_FIELD("Work phone 2", _("Work Phone 2"));
-	MSN_GOT_INFO_GET_FIELD("Work address", _("Work Address"));
-	MSN_GOT_INFO_GET_FIELD("Work mobile", _("Work Mobile"));
-	MSN_GOT_INFO_GET_FIELD("Work pager", _("Work Pager"));
-	MSN_GOT_INFO_GET_FIELD("Work fax", _("Work Fax"));
-	MSN_GOT_INFO_GET_FIELD("Work email", _("Work Email"));
-	MSN_GOT_INFO_GET_FIELD("Work IM", _("Work IM"));
-	MSN_GOT_INFO_GET_FIELD("Start date", _("Start Date"));
-	MSN_GOT_INFO_GET_FIELD("Notes", _("Notes"));
-
-	if (sect_info)
-	{
-		has_info = TRUE;
-		sect_info = FALSE;
-		has_contact_info = TRUE;
-	}
-    else
-    {
-		/* Remove the section header */
-		purple_notify_user_info_remove_last_item(user_info);
-	}
-
-	if (!has_contact_info)
-	{
-		/* Remove the Contact Info section header */
-		purple_notify_user_info_remove_last_item(user_info);
-	}
-
-#if 0 /* these probably don't show up any more */
-	/*
-	 * The fields, 'A Little About Me', 'Favorite Things', 'Hobbies
-	 * and Interests', 'Favorite Quote', and 'My Homepage' may or may
-	 * not appear, in any combination. However, they do appear in
-	 * certain order, so we can successively search to pin down the
-	 * distinct values.
-	 */
-
-	/* Check if they have A Little About Me */
-	found = purple_markup_extract_info_field(stripped, stripped_len, s,
-			" A Little About Me \n\n", 0, "Favorite Things", '\n', NULL,
-			_("A Little About Me"), 0, NULL, NULL);
-
-	if (!found)
-	{
-		found = purple_markup_extract_info_field(stripped, stripped_len, s,
-				" A Little About Me \n\n", 0, "Hobbies and Interests", '\n',
-				NULL, _("A Little About Me"), 0, NULL, NULL);
-	}
-
-	if (!found)
-	{
-		found = purple_markup_extract_info_field(stripped, stripped_len, s,
-				" A Little About Me \n\n", 0, "Favorite Quote", '\n', NULL,
-				_("A Little About Me"), 0, NULL, NULL);
-	}
-
-	if (!found)
-	{
-		found = purple_markup_extract_info_field(stripped, stripped_len, s,
-				" A Little About Me \n\n", 0, "My Homepage \n\nTake a look",
-				'\n',
-				NULL, _("A Little About Me"), 0, NULL, NULL);
-	}
-
-	if (!found)
-	{
-		purple_markup_extract_info_field(stripped, stripped_len, s,
-				" A Little About Me \n\n", 0, "last updated", '\n', NULL,
-				_("A Little About Me"), 0, NULL, NULL);
-	}
-
-	if (found)
-		has_info = TRUE;
-
-	/* Check if they have Favorite Things */
-	found = purple_markup_extract_info_field(stripped, stripped_len, s,
-			" Favorite Things \n\n", 0, "Hobbies and Interests", '\n', NULL,
-			_("Favorite Things"), 0, NULL, NULL);
-
-	if (!found)
-	{
-		found = purple_markup_extract_info_field(stripped, stripped_len, s,
-				" Favorite Things \n\n", 0, "Favorite Quote", '\n', NULL,
-				_("Favorite Things"), 0, NULL, NULL);
-	}
-
-	if (!found)
-	{
-		found = purple_markup_extract_info_field(stripped, stripped_len, s,
-				" Favorite Things \n\n", 0, "My Homepage \n\nTake a look", '\n',
-				NULL, _("Favorite Things"), 0, NULL, NULL);
-	}
-
-	if (!found)
-	{
-		purple_markup_extract_info_field(stripped, stripped_len, s,
-				" Favorite Things \n\n", 0, "last updated", '\n', NULL,
-				_("Favorite Things"), 0, NULL, NULL);
-	}
-
-	if (found)
-		has_info = TRUE;
-
-	/* Check if they have Hobbies and Interests */
-	found = purple_markup_extract_info_field(stripped, stripped_len, s,
-			" Hobbies and Interests \n\n", 0, "Favorite Quote", '\n', NULL,
-			_("Hobbies and Interests"), 0, NULL, NULL);
-
-	if (!found)
-	{
-		found = purple_markup_extract_info_field(stripped, stripped_len, s,
-				" Hobbies and Interests \n\n", 0, "My Homepage \n\nTake a look",
-				'\n', NULL, _("Hobbies and Interests"), 0, NULL, NULL);
-	}
-
-	if (!found)
-	{
-		purple_markup_extract_info_field(stripped, stripped_len, s,
-				" Hobbies and Interests \n\n", 0, "last updated", '\n', NULL,
-				_("Hobbies and Interests"), 0, NULL, NULL);
-	}
-
-	if (found)
-		has_info = TRUE;
-
-	/* Check if they have Favorite Quote */
-	found = purple_markup_extract_info_field(stripped, stripped_len, s,
-			"Favorite Quote \n\n", 0, "My Homepage \n\nTake a look", '\n', NULL,
-			_("Favorite Quote"), 0, NULL, NULL);
-
-	if (!found)
-	{
-		purple_markup_extract_info_field(stripped, stripped_len, s,
-				"Favorite Quote \n\n", 0, "last updated", '\n', NULL,
-				_("Favorite Quote"), 0, NULL, NULL);
-	}
-
-	if (found)
-		has_info = TRUE;
-
-	/* Extract the last updated date and put it in */
-	found = purple_markup_extract_info_field(stripped, stripped_len, s,
-			" last updated:", 1, "\n", 0, NULL, _("Last Updated"), 0,
-			NULL, msn_info_date_reformat);
-
-	if (found)
-		has_info = TRUE;
-#endif
-
-	/* If we were able to fetch a homepage url earlier, stick it in there */
-	if (user_url != NULL)
-	{
-		tmp = g_strdup_printf("<a href=\"%s\">%s</a>", user_url, user_url);
-		purple_notify_user_info_add_pair(user_info, _("Homepage"), tmp);
-		g_free(tmp);
-		g_free(user_url);
-
-		has_info = TRUE;
-	}
-
-	if (!has_info)
-	{
-		/* MSN doesn't actually distinguish between "unknown member" and
-		 * a known member with an empty profile. Try to explain this fact.
-		 * Note that if we have a nonempty tooltip_text, we know the user
-		 * exists.
-		 */
-		/* This doesn't work with the new spaces profiles - Stu 3/2/06
-		char *p = strstr(url_buffer, "Unknown Member </TITLE>");
-		 * This might not work for long either ... */
-		/* Nope, it failed some time before 5/2/07 :(
-		char *p = strstr(url_buffer, "form id=\"SpacesSearch\" name=\"SpacesSearch\"");
-		 * Let's see how long this one holds out for ... */
-		char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http&#58;&#47;&#47;spaces.live.com&#47;profile.aspx&#63;cid&#61;0\"");
-		PurpleBuddy *b = purple_find_buddy
-				(purple_connection_get_account(info_data->gc), info_data->name);
-		purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"),
-									   ((p && b) ? _("The user has not created a public profile.") :
-										(p ? _("MSN reported not being able to find the user's profile. "
-											   "This either means that the user does not exist, "
-											   "or that the user exists "
-											   "but has not created a public profile.") :
-										 _("Could not find "	/* This should never happen */
-										   "any information in the user's profile. "
-										   "The user most likely does not exist."))));
-	}
-
-	/* put a link to the actual profile URL */
-	tmp = g_strdup_printf("<a href=\"%s%s\">%s%s</a>",
-					PROFILE_URL, info_data->name, PROFILE_URL, info_data->name);
-	purple_notify_user_info_add_pair(user_info, _("Profile URL"), tmp);
-	g_free(tmp);
-
-#if PHOTO_SUPPORT
-	/* Find the URL to the photo; must be before the marshalling [Bug 994207] */
-	photo_url_text = msn_get_photo_url(url_text);
-
-	/* Marshall the existing state */
-	info2_data = g_malloc0(sizeof(MsnGetInfoStepTwoData));
-	info2_data->info_data = info_data;
-	info2_data->stripped = stripped;
-	info2_data->url_buffer = url_buffer;
-	info2_data->user_info = user_info;
-	info2_data->photo_url_text = photo_url_text;
-
-	/* Try to put the photo in there too, if there's one */
-	if (photo_url_text)
-	{
-		purple_util_fetch_url(photo_url_text, FALSE, NULL, FALSE, msn_got_photo,
-					   info2_data);
-	}
-	else
-	{
-		/* Emulate a callback */
-		/* TODO: Huh? */
-		msn_got_photo(NULL, info2_data, NULL, 0, NULL);
-	}
-}
-
-static void
-msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer user_data,
-		const gchar *url_text, size_t len, const gchar *error_message)
-{
-	MsnGetInfoStepTwoData *info2_data = (MsnGetInfoStepTwoData *)user_data;
-	int id = -1;
-
-	/* Unmarshall the saved state */
-	MsnGetInfoData *info_data = info2_data->info_data;
-	char *stripped = info2_data->stripped;
-	char *url_buffer = info2_data->url_buffer;
-	PurpleNotifyUserInfo *user_info = info2_data->user_info;
-	char *photo_url_text = info2_data->photo_url_text;
-
-	/* Make sure the connection is still valid if we got here by fetching a photo url */
-	if (url_text && (error_message != NULL ||
-					 g_list_find(purple_connections_get_all(), info_data->gc) == NULL))
-	{
-		purple_debug_warning("msn", "invalid connection. ignoring buddy photo info.\n");
-		g_free(stripped);
-		g_free(url_buffer);
-		purple_notify_user_info_destroy(user_info);
-		g_free(info_data->name);
-		g_free(info_data);
-		g_free(photo_url_text);
-		g_free(info2_data);
-
-		return;
-	}
-
-	/* Try to put the photo in there too, if there's one and is readable */
-	if (user_data && url_text && len != 0)
-	{
-		if (strstr(url_text, "400 Bad Request")
-			|| strstr(url_text, "403 Forbidden")
-			|| strstr(url_text, "404 Not Found"))
-		{
-
-			purple_debug_info("msn", "Error getting %s: %s\n",
-					photo_url_text, url_text);
-		}
-		else
-		{
-			char buf[1024];
-			purple_debug_info("msn", "%s is %" G_GSIZE_FORMAT
-					" bytes\n", photo_url_text, len);
-			id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL);
-			g_snprintf(buf, sizeof(buf), "<img id=\"%d\"><br>", id);
-			purple_notify_user_info_prepend_pair(user_info, NULL, buf);
-		}
-	}
-
-	/* We continue here from msn_got_info, as if nothing has happened */
-#endif
-	purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL);
-
-	g_free(stripped);
-	g_free(url_buffer);
-	purple_notify_user_info_destroy(user_info);
-	g_free(info_data->name);
-	g_free(info_data);
-#if PHOTO_SUPPORT
-	g_free(photo_url_text);
-	g_free(info2_data);
-	if (id != -1)
-		purple_imgstore_unref_by_id(id);
-#endif
-}
-
-static void
-msn_get_info(PurpleConnection *gc, const char *name)
-{
-	MsnGetInfoData *data;
-	char *url;
-
-	data       = g_new0(MsnGetInfoData, 1);
-	data->gc   = gc;
-	data->name = g_strdup(name);
-
-	url = g_strdup_printf("%s%s", PROFILE_URL, name);
-
-	purple_util_fetch_url(url, FALSE,
-				   "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
-				   TRUE, msn_got_info, data);
-
-	g_free(url);
-}
-
-static gboolean msn_load(PurplePlugin *plugin)
-{
-	msn_notification_init();
-	msn_switchboard_init();
-	msn_sync_init();
-
-	return TRUE;
-}
-
-static gboolean msn_unload(PurplePlugin *plugin)
-{
-	msn_notification_end();
-	msn_switchboard_end();
-	msn_sync_end();
-
-	return TRUE;
-}
-
-static PurpleAccount *find_acct(const char *prpl, const char *acct_id)
-{
-	PurpleAccount *acct = NULL;
-
-	/* If we have a specific acct, use it */
-	if (acct_id) {
-		acct = purple_accounts_find(acct_id, prpl);
-		if (acct && !purple_account_is_connected(acct))
-			acct = NULL;
-	} else { /* Otherwise find an active account for the protocol */
-		GList *l = purple_accounts_get_all();
-		while (l) {
-			if (!strcmp(prpl, purple_account_get_protocol_id(l->data))
-					&& purple_account_is_connected(l->data)) {
-				acct = l->data;
-				break;
-			}
-			l = l->next;
-		}
-	}
-
-	return acct;
-}
-
-static gboolean msn_uri_handler(const char *proto, const char *cmd, GHashTable *params)
-{
-	char *acct_id = g_hash_table_lookup(params, "account");
-	PurpleAccount *acct;
-
-	if (g_ascii_strcasecmp(proto, "msnim"))
-		return FALSE;
-
-	acct = find_acct("prpl-msn", acct_id);
-
-	if (!acct)
-		return FALSE;
-
-	/* msnim:chat?contact=user@domain.tld */
-	if (!g_ascii_strcasecmp(cmd, "Chat")) {
-		char *sname = g_hash_table_lookup(params, "contact");
-		if (sname) {
-			PurpleConversation *conv = purple_find_conversation_with_account(
-				PURPLE_CONV_TYPE_IM, sname, acct);
-			if (conv == NULL)
-				conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, sname);
-			purple_conversation_present(conv);
-		}
-		/*else
-			**If pidgindialogs_im() was in the core, we could use it here.
-			 * It is all purple_request_* based, but I'm not sure it really belongs in the core
-			pidgindialogs_im();*/
-
-		return TRUE;
-	}
-	/* msnim:add?contact=user@domain.tld */
-	else if (!g_ascii_strcasecmp(cmd, "Add")) {
-		char *name = g_hash_table_lookup(params, "contact");
-		purple_blist_request_add_buddy(acct, name, NULL, NULL);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-
-static PurplePluginProtocolInfo prpl_info =
-{
-	OPT_PROTO_MAIL_CHECK,
-	NULL,					/* user_splits */
-	NULL,					/* protocol_options */
-	{"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND},	/* icon_spec */
-	msn_list_icon,			/* list_icon */
-	NULL,				/* list_emblems */
-	msn_status_text,		/* status_text */
-	msn_tooltip_text,		/* tooltip_text */
-	msn_status_types,		/* away_states */
-	msn_blist_node_menu,		/* blist_node_menu */
-	NULL,					/* chat_info */
-	NULL,					/* chat_info_defaults */
-	msn_login,			/* login */
-	msn_close,			/* close */
-	msn_send_im,			/* send_im */
-	NULL,					/* set_info */
-	msn_send_typing,		/* send_typing */
-	msn_get_info,			/* get_info */
-	msn_set_status,			/* set_away */
-	msn_set_idle,			/* set_idle */
-	NULL,					/* change_passwd */
-	msn_add_buddy,			/* add_buddy */
-	NULL,					/* add_buddies */
-	msn_rem_buddy,			/* remove_buddy */
-	NULL,					/* remove_buddies */
-	msn_add_permit,			/* add_permit */
-	msn_add_deny,			/* add_deny */
-	msn_rem_permit,			/* rem_permit */
-	msn_rem_deny,			/* rem_deny */
-	msn_set_permit_deny,		/* set_permit_deny */
-	NULL,					/* join_chat */
-	NULL,					/* reject chat invite */
-	NULL,					/* get_chat_name */
-	msn_chat_invite,		/* chat_invite */
-	msn_chat_leave,			/* chat_leave */
-	NULL,					/* chat_whisper */
-	msn_chat_send,			/* chat_send */
-	msn_keepalive,			/* keepalive */
-	NULL,					/* register_user */
-	NULL,					/* get_cb_info */
-	NULL,					/* get_cb_away */
-	NULL,					/* alias_buddy */
-	msn_group_buddy,		/* group_buddy */
-	msn_rename_group,		/* rename_group */
-	NULL,					/* buddy_free */
-	msn_convo_closed,		/* convo_closed */
-	msn_normalize,			/* normalize */
-	msn_set_buddy_icon,		/* set_buddy_icon */
-	msn_remove_group,		/* remove_group */
-	NULL,					/* get_cb_real_name */
-	NULL,					/* set_chat_topic */
-	NULL,					/* find_blist_chat */
-	NULL,					/* roomlist_get_list */
-	NULL,					/* roomlist_cancel */
-	NULL,					/* roomlist_expand_category */
-	msn_can_receive_file,	/* can_receive_file */
-	msn_send_file,			/* send_file */
-	msn_new_xfer,			/* new_xfer */
-	msn_offline_message,			/* offline_message */
-	NULL,					/* whiteboard_prpl_ops */
-	NULL,					/* send_raw */
-	NULL,					/* roomlist_room_serialize */
-	NULL,					/* unregister_user */
-	msn_send_attention,                     /* send_attention */
-	msn_attention_types,                    /* attention_types */
-	sizeof(PurplePluginProtocolInfo),       /* struct_size */
-	msn_get_account_text_table,             /* get_account_text_table */
-	NULL,                                   /* initiate_media */
-	NULL                                    /* can_do_media */
-};
-
-static PurplePluginInfo info =
-{
-	PURPLE_PLUGIN_MAGIC,
-	PURPLE_MAJOR_VERSION,
-	PURPLE_MINOR_VERSION,
-	PURPLE_PLUGIN_PROTOCOL,                             /**< type           */
-	NULL,                                             /**< ui_requirement */
-	0,                                                /**< flags          */
-	NULL,                                             /**< dependencies   */
-	PURPLE_PRIORITY_DEFAULT,                            /**< priority       */
-
-	"prpl-msn",                                       /**< id             */
-	"MSN",                                            /**< name           */
-	DISPLAY_VERSION,                                  /**< version        */
-	                                                  /**  summary        */
-	N_("MSN Protocol Plugin"),
-	                                                  /**  description    */
-	N_("MSN Protocol Plugin"),
-	"Christian Hammond <chipx86@gnupdate.org>",       /**< author         */
-	PURPLE_WEBSITE,                                     /**< homepage       */
-
-	msn_load,                                         /**< load           */
-	msn_unload,                                       /**< unload         */
-	NULL,                                             /**< destroy        */
-
-	NULL,                                             /**< ui_info        */
-	&prpl_info,                                       /**< extra_info     */
-	NULL,                                             /**< prefs_info     */
-	msn_actions,
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
-};
-
-static void
-init_plugin(PurplePlugin *plugin)
-{
-	PurpleAccountOption *option;
-
-	option = purple_account_option_string_new(_("Server"), "server",
-											MSN_SERVER);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-
-	option = purple_account_option_int_new(_("Port"), "port", 1863);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-
-	option = purple_account_option_bool_new(_("Use HTTP Method"),
-										  "http_method", FALSE);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-
-	option = purple_account_option_string_new(_("HTTP Method Server"),
-										  "http_method_server", MSN_HTTPCONN_SERVER);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-
-	option = purple_account_option_bool_new(_("Show custom smileys"),
-										  "custom_smileys", TRUE);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
-
-	purple_cmd_register("nudge", "", PURPLE_CMD_P_PRPL,
-	                  PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY,
-	                 "prpl-msn", msn_cmd_nudge,
-	                  _("nudge: nudge a user to get their attention"), NULL);
-
-	purple_prefs_remove("/plugins/prpl/msn");
-
-	purple_signal_connect(purple_get_core(), "uri-handler", plugin,
-		PURPLE_CALLBACK(msn_uri_handler), NULL);
-}
-
-PURPLE_INIT_PLUGIN(msnp9, init_plugin, info);
--- a/libpurple/protocols/msnp9/msn.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-/**
- * @file msn.h The MSN protocol plugin
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_H_
-#define _MSN_H_
-
-/* #define MSN_DEBUG_MSG 1 */
-/* #define MSN_DEBUG_SLPMSG 1 */
-/* #define MSN_DEBUG_HTTP 1 */
-
-/* #define MSN_DEBUG_SLP 1 */
-/* #define MSN_DEBUG_SLP_VERBOSE 1 */
-/* #define MSN_DEBUG_SLP_FILES 1 */
-
-/* #define MSN_DEBUG_NS 1 */
-/* #define MSN_DEBUG_SB 1 */
-
-#include "internal.h"
-
-#include "account.h"
-#include "accountopt.h"
-#include "blist.h"
-#include "connection.h"
-#include "conversation.h"
-#include "debug.h"
-#include "cipher.h"
-#include "notify.h"
-#include "privacy.h"
-#include "proxy.h"
-#include "prpl.h"
-#include "request.h"
-#include "servconn.h"
-#include "sslconn.h"
-#include "util.h"
-
-#include "ft.h"
-
-#define MSN_BUF_LEN 8192
-
-#define USEROPT_MSNSERVER 3
-#define MSN_SERVER "messenger.hotmail.com"
-#define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com"
-#define USEROPT_MSNPORT 4
-#define MSN_PORT 1863
-
-#define MSN_TYPING_RECV_TIMEOUT 6
-#define MSN_TYPING_SEND_TIMEOUT	4
-
-#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"
-#define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login="
-#define PROFILE_URL "http://spaces.live.com/profile.aspx?mem="
-
-#define USEROPT_HOTMAIL 0
-
-#define BUDDY_ALIAS_MAXLEN 387
-
-#define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}"
-
-#define MSN_CLIENTINFO \
-	"Client-Name: Purple/" VERSION "\r\n" \
-	"Chat-Logging: Y\r\n"
-
-/* Index into attention_types */
-#define MSN_NUDGE 0
-
-typedef enum
-{
-	MSN_LIST_FL_OP = 0x01,
-	MSN_LIST_AL_OP = 0x02,
-	MSN_LIST_BL_OP = 0x04,
-	MSN_LIST_RL_OP = 0x08
-
-} MsnListOp;
-
-typedef enum
-{
-	MSN_CLIENT_CAP_WIN_MOBILE = 0x00001,
-	MSN_CLIENT_CAP_UNKNOWN_1  = 0x00002,
-	MSN_CLIENT_CAP_INK_GIF    = 0x00004,
-	MSN_CLIENT_CAP_INK_ISF    = 0x00008,
-	MSN_CLIENT_CAP_VIDEO_CHAT = 0x00010,
-	MSN_CLIENT_CAP_BASE       = 0x00020,
-	MSN_CLIENT_CAP_MSNMOBILE  = 0x00040,
-	MSN_CLIENT_CAP_MSNDIRECT  = 0x00080,
-	MSN_CLIENT_CAP_WEBMSGR    = 0x00100,
-	MSN_CLIENT_CAP_DIRECTIM   = 0x04000,
-	MSN_CLIENT_CAP_WINKS      = 0x08000,
-	MSN_CLIENT_CAP_SEARCH     = 0x10000
-
-} MsnClientCaps;
-
-typedef enum
-{
-	MSN_CLIENT_VER_5_0 = 0x00,
-	MSN_CLIENT_VER_6_0 = 0x10,	/* MSNC1 */
-	MSN_CLIENT_VER_6_1 = 0x20,	/* MSNC2 */
-	MSN_CLIENT_VER_6_2 = 0x30,	/* MSNC3 */
-	MSN_CLIENT_VER_7_0 = 0x40,	/* MSNC4 */
-	MSN_CLIENT_VER_7_5 = 0x50	/* MSNC5 */
-
-} MsnClientVerId;
-
-#define MSN_CLIENT_ID_VERSION      MSN_CLIENT_VER_7_0
-#define MSN_CLIENT_ID_RESERVED_1   0x00
-#define MSN_CLIENT_ID_RESERVED_2   0x00
-#define MSN_CLIENT_ID_CAPABILITIES MSN_CLIENT_CAP_BASE
-
-#define MSN_CLIENT_ID \
-	((MSN_CLIENT_ID_VERSION    << 24) | \
-	 (MSN_CLIENT_ID_RESERVED_1 << 16) | \
-	 (MSN_CLIENT_ID_RESERVED_2 <<  8) | \
-	 (MSN_CLIENT_ID_CAPABILITIES))
-
-#endif /* _MSN_H_ */
--- a/libpurple/protocols/msnp9/nexus.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,513 +0,0 @@
-/**
- * @file nexus.c MSN Nexus functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "nexus.h"
-#include "notification.h"
-
-/**************************************************************************
- * Main
- **************************************************************************/
-
-MsnNexus *
-msn_nexus_new(MsnSession *session)
-{
-	MsnNexus *nexus;
-
-	nexus = g_new0(MsnNexus, 1);
-	nexus->session = session;
-	nexus->challenge_data = g_hash_table_new_full(g_str_hash,
-		g_str_equal, g_free, g_free);
-
-	return nexus;
-}
-
-void
-msn_nexus_destroy(MsnNexus *nexus)
-{
-	if (nexus->gsc)
-		purple_ssl_close(nexus->gsc);
-
-	g_free(nexus->login_host);
-
-	g_free(nexus->login_path);
-
-	if (nexus->challenge_data != NULL)
-		g_hash_table_destroy(nexus->challenge_data);
-
-	if (nexus->input_handler > 0)
-		purple_input_remove(nexus->input_handler);
-	g_free(nexus->write_buf);
-	g_free(nexus->read_buf);
-
-	g_free(nexus);
-}
-
-/**************************************************************************
- * Util
- **************************************************************************/
-
-static gssize
-msn_ssl_read(MsnNexus *nexus)
-{
-	gssize len;
-	char temp_buf[4096];
-
-	if ((len = purple_ssl_read(nexus->gsc, temp_buf,
-			sizeof(temp_buf))) > 0)
-	{
-		nexus->read_buf = g_realloc(nexus->read_buf,
-			nexus->read_len + len + 1);
-		strncpy(nexus->read_buf + nexus->read_len, temp_buf, len);
-		nexus->read_len += len;
-		nexus->read_buf[nexus->read_len] = '\0';
-	}
-
-	return len;
-}
-
-static void
-nexus_write_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnNexus *nexus = data;
-	int len, total_len;
-
-	total_len = strlen(nexus->write_buf);
-
-	len = purple_ssl_write(nexus->gsc,
-		nexus->write_buf + nexus->written_len,
-		total_len - nexus->written_len);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len <= 0) {
-		purple_input_remove(nexus->input_handler);
-		nexus->input_handler = 0;
-		/* TODO: notify of the error */
-		return;
-	}
-	nexus->written_len += len;
-
-	if (nexus->written_len < total_len)
-		return;
-
-	purple_input_remove(nexus->input_handler);
-	nexus->input_handler = 0;
-
-	g_free(nexus->write_buf);
-	nexus->write_buf = NULL;
-	nexus->written_len = 0;
-
-	nexus->written_cb(nexus, source, 0);
-}
-
-/**************************************************************************
- * Login
- **************************************************************************/
-
-static void
-login_connect_cb(gpointer data, PurpleSslConnection *gsc,
-				 PurpleInputCondition cond);
-
-static void
-login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
-{
-	MsnNexus *nexus;
-	MsnSession *session;
-
-	nexus = data;
-	g_return_if_fail(nexus != NULL);
-
-	nexus->gsc = NULL;
-
-	session = nexus->session;
-	g_return_if_fail(session != NULL);
-
-	msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Unable to connect"));
-	/* the above line will result in nexus being destroyed, so we don't want
-	 * to destroy it here, or we'd crash */
-}
-
-static void
-nexus_login_written_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnNexus *nexus = data;
-	MsnSession *session;
-	int len;
-
-	session = nexus->session;
-	g_return_if_fail(session != NULL);
-
-	if (nexus->input_handler == 0)
-		/* TODO: Use purple_ssl_input_add()? */
-		nexus->input_handler = purple_input_add(nexus->gsc->fd,
-			PURPLE_INPUT_READ, nexus_login_written_cb, nexus);
-
-
-	len = msn_ssl_read(nexus);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len <= 0) {
-		purple_input_remove(nexus->input_handler);
-		nexus->input_handler = 0;
-		g_free(nexus->read_buf);
-		nexus->read_buf = NULL;
-		nexus->read_len = 0;
-		/* TODO: error handling */
-		return;
-	}
-
-	if (g_strstr_len(nexus->read_buf, nexus->read_len,
-			"\r\n\r\n") == NULL)
-		return;
-
-	purple_input_remove(nexus->input_handler);
-	nexus->input_handler = 0;
-
-	purple_ssl_close(nexus->gsc);
-	nexus->gsc = NULL;
-
-	purple_debug_misc("msn", "ssl buffer: {%s}\n", nexus->read_buf);
-
-	if (strstr(nexus->read_buf, "HTTP/1.1 302") != NULL)
-	{
-		/* Redirect. */
-		char *location, *c;
-
-		location = strstr(nexus->read_buf, "Location: ");
-		if (location == NULL)
-		{
-			g_free(nexus->read_buf);
-			nexus->read_buf = NULL;
-			nexus->read_len = 0;
-
-			return;
-		}
-		location = strchr(location, ' ') + 1;
-
-		if ((c = strchr(location, '\r')) != NULL)
-			*c = '\0';
-
-		/* Skip the http:// */
-		if ((c = strchr(location, '/')) != NULL)
-			location = c + 2;
-
-		if ((c = strchr(location, '/')) != NULL)
-		{
-			g_free(nexus->login_path);
-			nexus->login_path = g_strdup(c);
-
-			*c = '\0';
-		}
-
-		g_free(nexus->login_host);
-		nexus->login_host = g_strdup(location);
-
-		nexus->gsc = purple_ssl_connect(session->account,
-				nexus->login_host, PURPLE_SSL_DEFAULT_PORT,
-				login_connect_cb, login_error_cb, nexus);
-	}
-	else if (strstr(nexus->read_buf, "HTTP/1.1 401 Unauthorized") != NULL)
-	{
-		const char *error;
-
-		if ((error = strstr(nexus->read_buf, "WWW-Authenticate")) != NULL)
-		{
-			if ((error = strstr(error, "cbtxt=")) != NULL)
-			{
-				const char *c;
-				char *temp;
-
-				error += strlen("cbtxt=");
-
-				if ((c = strchr(error, '\n')) == NULL)
-					c = error + strlen(error);
-
-				temp = g_strndup(error, c - error);
-				error = purple_url_decode(temp);
-				g_free(temp);
-				if ((temp = strstr(error, " Do one of the following or try again:")) != NULL)
-					*temp = '\0';
-			}
-		}
-
-		msn_session_set_error(session, MSN_ERROR_AUTH, error);
-	}
-	else if (strstr(nexus->read_buf, "HTTP/1.1 503 Service Unavailable"))
-	{
-		msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
-	}
-	else if (strstr(nexus->read_buf, "HTTP/1.1 200 OK"))
-	{
-		char *base, *c;
-		char *login_params;
-
-#if 0
-		/* All your base are belong to us. */
-		base = buffer;
-
-		/* For great cookie! */
-		while ((base = strstr(base, "Set-Cookie: ")) != NULL)
-		{
-			base += strlen("Set-Cookie: ");
-
-			c = strchr(base, ';');
-
-			session->login_cookies =
-				g_list_append(session->login_cookies,
-							  g_strndup(base, c - base));
-		}
-#endif
-
-		base  = strstr(nexus->read_buf, "Authentication-Info: ");
-
-		g_return_if_fail(base != NULL);
-
-		base  = strstr(base, "from-PP='");
-		base += strlen("from-PP='");
-		c     = strchr(base, '\'');
-
-		login_params = g_strndup(base, c - base);
-
-		msn_got_login_params(session, login_params);
-
-		g_free(login_params);
-
-		msn_nexus_destroy(nexus);
-		session->nexus = NULL;
-		return;
-	}
-
-	g_free(nexus->read_buf);
-	nexus->read_buf = NULL;
-	nexus->read_len = 0;
-
-}
-
-/* this guards against missing hash entries */
-static char *
-nexus_challenge_data_lookup(GHashTable *challenge_data, const char *key)
-{
-	char *entry;
-
-	return (entry = (char *)g_hash_table_lookup(challenge_data, key)) ?
-		entry : "(null)";
-}
-
-void
-login_connect_cb(gpointer data, PurpleSslConnection *gsc,
-				 PurpleInputCondition cond)
-{
-	MsnNexus *nexus;
-	MsnSession *session;
-	char *username, *password, *encpass;
-	char *request_str, *head, *tail;
-	char *buffer = NULL;
-	guint32 ctint;
-
-	nexus = data;
-	g_return_if_fail(nexus != NULL);
-
-	session = nexus->session;
-	g_return_if_fail(session != NULL);
-
-	msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
-
-	username =
-		g_strdup(purple_url_encode(purple_account_get_username(session->account)));
-
-	password = g_utf8_strncpy(g_strdup(purple_connection_get_password(session->account->gc)),
-							  purple_connection_get_password(session->account->gc), 16);
-	encpass = g_strdup(purple_url_encode(password));
-	g_free(password);
-
-	ctint = strtoul((char *)g_hash_table_lookup(nexus->challenge_data, "ct"), NULL, 10) + 200;
-
-	head = g_strdup_printf(
-		"GET %s HTTP/1.1\r\n"
-		"Authorization: Passport1.4 OrgVerb=GET,OrgURL=%s,sign-in=%s",
-		nexus->login_path,
-		(char *)g_hash_table_lookup(nexus->challenge_data, "ru"),
-		username);
-
-	tail = g_strdup_printf(
-		"lc=%s,id=%s,tw=%s,fs=%s,ru=%s,ct=%" G_GUINT32_FORMAT ",kpp=%s,kv=%s,ver=%s,tpf=%s\r\n"
-		"User-Agent: MSMSGS\r\n"
-		"Host: %s\r\n"
-		"Connection: Keep-Alive\r\n"
-		"Cache-Control: no-cache\r\n",
-		nexus_challenge_data_lookup(nexus->challenge_data, "lc"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "id"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "tw"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "fs"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "ru"),
-		ctint,
-		nexus_challenge_data_lookup(nexus->challenge_data, "kpp"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "kv"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "ver"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "tpf"),
-		nexus->login_host);
-
-	buffer = g_strdup_printf("%s,pwd=XXXXXXXX,%s\r\n", head, tail);
-	request_str = g_strdup_printf("%s,pwd=%s,%s\r\n", head, encpass, tail);
-
-	purple_debug_misc("msn", "Sending: {%s}\n", buffer);
-
-	g_free(buffer);
-	g_free(head);
-	g_free(tail);
-	g_free(username);
-	g_free(encpass);
-
-	nexus->write_buf = request_str;
-	nexus->written_len = 0;
-
-	nexus->read_len = 0;
-
-	nexus->written_cb = nexus_login_written_cb;
-
-	nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE,
-		nexus_write_cb, nexus);
-
-	nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
-
-	return;
-
-
-}
-
-static void
-nexus_connect_written_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnNexus *nexus = data;
-	int len;
-	char *da_login;
-	char *base, *c;
-
-	if (nexus->input_handler == 0)
-		/* TODO: Use purple_ssl_input_add()? */
-		nexus->input_handler = purple_input_add(nexus->gsc->fd,
-			PURPLE_INPUT_READ, nexus_connect_written_cb, nexus);
-
-	/* Get the PassportURLs line. */
-	len = msn_ssl_read(nexus);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len <= 0) {
-		purple_input_remove(nexus->input_handler);
-		nexus->input_handler = 0;
-		g_free(nexus->read_buf);
-		nexus->read_buf = NULL;
-		nexus->read_len = 0;
-		/* TODO: error handling */
-		return;
-	}
-
-	if (g_strstr_len(nexus->read_buf, nexus->read_len,
-			"\r\n\r\n") == NULL)
-		return;
-
-	purple_input_remove(nexus->input_handler);
-	nexus->input_handler = 0;
-
-	base = strstr(nexus->read_buf, "PassportURLs");
-
-	if (base == NULL)
-	{
-		g_free(nexus->read_buf);
-		nexus->read_buf = NULL;
-		nexus->read_len = 0;
-		return;
-	}
-
-	if ((da_login = strstr(base, "DALogin=")) != NULL)
-	{
-		/* skip over "DALogin=" */
-		da_login += 8;
-
-		if ((c = strchr(da_login, ',')) != NULL)
-			*c = '\0';
-
-		if ((c = strchr(da_login, '/')) != NULL)
-		{
-			nexus->login_path = g_strdup(c);
-			*c = '\0';
-		}
-
-		nexus->login_host = g_strdup(da_login);
-	}
-
-	g_free(nexus->read_buf);
-	nexus->read_buf = NULL;
-	nexus->read_len = 0;
-
-	purple_ssl_close(nexus->gsc);
-
-	/* Now begin the connection to the login server. */
-	nexus->gsc = purple_ssl_connect(nexus->session->account,
-			nexus->login_host, PURPLE_SSL_DEFAULT_PORT,
-			login_connect_cb, login_error_cb, nexus);
-}
-
-
-/**************************************************************************
- * Connect
- **************************************************************************/
-
-static void
-nexus_connect_cb(gpointer data, PurpleSslConnection *gsc,
-				 PurpleInputCondition cond)
-{
-	MsnNexus *nexus;
-	MsnSession *session;
-
-	nexus = data;
-	g_return_if_fail(nexus != NULL);
-
-	session = nexus->session;
-	g_return_if_fail(session != NULL);
-
-	msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH);
-
-	nexus->write_buf = g_strdup("GET /rdr/pprdr.asp\r\n\r\n");
-	nexus->written_len = 0;
-
-	nexus->read_len = 0;
-
-	nexus->written_cb = nexus_connect_written_cb;
-
-	nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE,
-		nexus_write_cb, nexus);
-
-	nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
-}
-
-void
-msn_nexus_connect(MsnNexus *nexus)
-{
-	nexus->gsc = purple_ssl_connect(nexus->session->account,
-			"nexus.passport.com", PURPLE_SSL_DEFAULT_PORT,
-			nexus_connect_cb, login_error_cb, nexus);
-}
--- a/libpurple/protocols/msnp9/nexus.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/**
- * @file nexus.h MSN Nexus functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_NEXUS_H_
-#define _MSN_NEXUS_H_
-
-typedef struct _MsnNexus MsnNexus;
-
-struct _MsnNexus
-{
-	MsnSession *session;
-
-	char *login_host;
-	char *login_path;
-	GHashTable *challenge_data;
-	PurpleSslConnection *gsc;
-
-	guint input_handler;
-
-	char *write_buf;
-	gsize written_len;
-	PurpleInputFunction written_cb;
-
-	char *read_buf;
-	gsize read_len;
-};
-
-void msn_nexus_connect(MsnNexus *nexus);
-MsnNexus *msn_nexus_new(MsnSession *session);
-void msn_nexus_destroy(MsnNexus *nexus);
-
-#endif /* _MSN_NEXUS_H_ */
--- a/libpurple/protocols/msnp9/notification.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1510 +0,0 @@
-/**
- * @file notification.c Notification server functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "notification.h"
-#include "state.h"
-#include "error.h"
-#include "msn-utils.h"
-#include "page.h"
-
-#include "userlist.h"
-#include "sync.h"
-#include "slplink.h"
-
-static MsnTable *cbs_table;
-
-/**************************************************************************
- * Main
- **************************************************************************/
-
-static void
-destroy_cb(MsnServConn *servconn)
-{
-	MsnNotification *notification;
-
-	notification = servconn->cmdproc->data;
-	g_return_if_fail(notification != NULL);
-
-	msn_notification_destroy(notification);
-}
-
-MsnNotification *
-msn_notification_new(MsnSession *session)
-{
-	MsnNotification *notification;
-	MsnServConn *servconn;
-
-	g_return_val_if_fail(session != NULL, NULL);
-
-	notification = g_new0(MsnNotification, 1);
-
-	notification->session = session;
-	notification->servconn = servconn = msn_servconn_new(session, MSN_SERVCONN_NS);
-	msn_servconn_set_destroy_cb(servconn, destroy_cb);
-
-	notification->cmdproc = servconn->cmdproc;
-	notification->cmdproc->data = notification;
-	notification->cmdproc->cbs_table = cbs_table;
-
-	return notification;
-}
-
-void
-msn_notification_destroy(MsnNotification *notification)
-{
-	notification->cmdproc->data = NULL;
-
-	msn_servconn_set_destroy_cb(notification->servconn, NULL);
-
-	msn_servconn_destroy(notification->servconn);
-
-	g_free(notification);
-}
-
-/**************************************************************************
- * Connect
- **************************************************************************/
-
-static void
-connect_cb(MsnServConn *servconn)
-{
-	MsnCmdProc *cmdproc;
-	MsnSession *session;
-	PurpleAccount *account;
-	char **a, **c, *vers;
-	int i;
-
-	g_return_if_fail(servconn != NULL);
-
-	cmdproc = servconn->cmdproc;
-	session = servconn->session;
-	account = session->account;
-
-	/* Allocate an array for CVR0, NULL, and all the versions */
-	a = c = g_new0(char *, session->protocol_ver - 8 + 3);
-
-	for (i = session->protocol_ver; i >= 8; i--)
-		*c++ = g_strdup_printf("MSNP%d", i);
-
-	*c++ = g_strdup("CVR0");
-
-	vers = g_strjoinv(" ", a);
-
-	if (session->login_step == MSN_LOGIN_STEP_START)
-		msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE);
-	else
-		msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE2);
-
-	msn_cmdproc_send(cmdproc, "VER", "%s", vers);
-
-	g_strfreev(a);
-	g_free(vers);
-}
-
-gboolean
-msn_notification_connect(MsnNotification *notification, const char *host, int port)
-{
-	MsnServConn *servconn;
-
-	g_return_val_if_fail(notification != NULL, FALSE);
-
-	servconn = notification->servconn;
-
-	msn_servconn_set_connect_cb(servconn, connect_cb);
-	notification->in_use = msn_servconn_connect(servconn, host, port);
-
-	return notification->in_use;
-}
-
-void
-msn_notification_disconnect(MsnNotification *notification)
-{
-	g_return_if_fail(notification != NULL);
-	g_return_if_fail(notification->in_use);
-
-	msn_servconn_disconnect(notification->servconn);
-
-	notification->in_use = FALSE;
-}
-
-/**************************************************************************
- * Util
- **************************************************************************/
-
-static void
-group_error_helper(MsnSession *session, const char *msg, int group_id, int error)
-{
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	char *reason = NULL;
-	char *title = NULL;
-
-	account = session->account;
-	gc = purple_account_get_connection(account);
-
-	if (error == 224)
-	{
-		if (group_id == 0)
-		{
-			return;
-		}
-		else
-		{
-			const char *group_name;
-			group_name =
-				msn_userlist_find_group_name(session->userlist,
-											 group_id);
-			reason = g_strdup_printf(_("%s is not a valid group."),
-									 group_name);
-		}
-	}
-	else
-	{
-		reason = g_strdup(_("Unknown error."));
-	}
-
-	title = g_strdup_printf(_("%s on %s (%s)"), msg,
-						  purple_account_get_username(account),
-						  purple_account_get_protocol_name(account));
-	purple_notify_error(gc, NULL, title, reason);
-	g_free(title);
-	g_free(reason);
-}
-
-/**************************************************************************
- * Login
- **************************************************************************/
-
-void
-msn_got_login_params(MsnSession *session, const char *login_params)
-{
-	MsnCmdProc *cmdproc;
-
-	cmdproc = session->notification->cmdproc;
-
-	msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END);
-
-	msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params);
-}
-
-static void
-cvr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	PurpleAccount *account;
-
-	account = cmdproc->session->account;
-
-	msn_cmdproc_send(cmdproc, "USR", "TWN I %s",
-					 purple_account_get_username(account));
-}
-
-static void
-usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	PurpleAccount *account;
-	PurpleConnection *gc;
-
-	session = cmdproc->session;
-	account = session->account;
-	gc = purple_account_get_connection(account);
-
-	if (!g_ascii_strcasecmp(cmd->params[1], "OK"))
-	{
-		/* OK */
-		const char *friendly = purple_url_decode(cmd->params[3]);
-
-		session->passport_info.verified = atoi(cmd->params[4]);
-
-		purple_connection_set_display_name(gc, friendly);
-
-		msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN);
-
-		msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
-	}
-	else if (!g_ascii_strcasecmp(cmd->params[1], "TWN"))
-	{
-		/* Passport authentication */
-		char **elems, **cur, **tokens;
-
-		session->nexus = msn_nexus_new(session);
-
-		/* Parse the challenge data. */
-
-		elems = g_strsplit(cmd->params[3], ",", 0);
-
-		for (cur = elems; *cur != NULL; cur++)
-		{
-				tokens = g_strsplit(*cur, "=", 2);
-				g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]);
-				/* Don't free each of the tokens, only the array. */
-				g_free(tokens);
-		}
-
-		g_strfreev(elems);
-
-		msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_START);
-
-		msn_nexus_connect(session->nexus);
-	}
-}
-
-static void
-usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	MsnErrorType msnerr = 0;
-
-	switch (error)
-	{
-		case 500:
-		case 601:
-		case 910:
-		case 921:
-			msnerr = MSN_ERROR_SERV_UNAVAILABLE;
-			break;
-		case 911:
-			msnerr = MSN_ERROR_AUTH;
-			break;
-		default:
-			return;
-			break;
-	}
-
-	msn_session_set_error(cmdproc->session, msnerr, NULL);
-}
-
-static void
-ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	PurpleAccount *account;
-	gboolean protocol_supported = FALSE;
-	char proto_str[8];
-	size_t i;
-
-	session = cmdproc->session;
-	account = session->account;
-
-	g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver);
-
-	for (i = 1; i < cmd->param_count; i++)
-	{
-		if (!strcmp(cmd->params[i], proto_str))
-		{
-			protocol_supported = TRUE;
-			break;
-		}
-	}
-
-	if (!protocol_supported)
-	{
-		msn_session_set_error(session, MSN_ERROR_UNSUPPORTED_PROTOCOL,
-							  NULL);
-		return;
-	}
-
-	msn_cmdproc_send(cmdproc, "CVR",
-					 "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s",
-					 purple_account_get_username(account));
-}
-
-/**************************************************************************
- * Log out
- **************************************************************************/
-
-static void
-out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	if (!g_ascii_strcasecmp(cmd->params[0], "OTH"))
-		msn_session_set_error(cmdproc->session, MSN_ERROR_SIGN_OTHER,
-							  NULL);
-	else if (!g_ascii_strcasecmp(cmd->params[0], "SSD"))
-		msn_session_set_error(cmdproc->session, MSN_ERROR_SERV_DOWN, NULL);
-}
-
-void
-msn_notification_close(MsnNotification *notification)
-{
-	g_return_if_fail(notification != NULL);
-
-	if (!notification->in_use)
-		return;
-
-	msn_cmdproc_send_quick(notification->cmdproc, "OUT", NULL, NULL);
-
-	msn_notification_disconnect(notification);
-}
-
-/**************************************************************************
- * Messages
- **************************************************************************/
-
-static void
-msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
-			 size_t len)
-{
-	MsnMessage *msg;
-
-	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
-
-	msn_message_parse_payload(msg, payload, len);
-#ifdef MSN_DEBUG_NS
-	msn_message_show_readable(msg, "Notification", TRUE);
-#endif
-
-	msn_cmdproc_process_msg(cmdproc, msg);
-
-	msn_message_destroy(msg);
-}
-
-static void
-msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	/* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued
-	 * command and we are processing it */
-
-	if (cmd->payload == NULL)
-	{
-		cmdproc->last_cmd->payload_cb  = msg_cmd_post;
-		cmdproc->servconn->payload_len = atoi(cmd->params[2]);
-	}
-	else
-	{
-		g_return_if_fail(cmd->payload_cb != NULL);
-
-		cmd->payload_cb(cmdproc, cmd, cmd->payload, cmd->payload_len);
-	}
-}
-
-/**************************************************************************
- * Challenges
- **************************************************************************/
-
-static void
-chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnTransaction *trans;
-	char buf[33];
-	const char *challenge_resp;
-	PurpleCipher *cipher;
-	PurpleCipherContext *context;
-	guchar digest[16];
-	int i;
-
-	cipher = purple_ciphers_find_cipher("md5");
-	context = purple_cipher_context_new(cipher, NULL);
-
-	purple_cipher_context_append(context, (const guchar *)cmd->params[1],
-							   strlen(cmd->params[1]));
-
-	challenge_resp = "VT6PX?UQTM4WM%YR";
-
-	purple_cipher_context_append(context, (const guchar *)challenge_resp,
-							   strlen(challenge_resp));
-	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
-	purple_cipher_context_destroy(context);
-
-	for (i = 0; i < 16; i++)
-		g_snprintf(buf + (i*2), 3, "%02x", digest[i]);
-
-	trans = msn_transaction_new(cmdproc, "QRY", "%s 32", "PROD0038W!61ZTF9");
-
-	msn_transaction_set_payload(trans, buf, 32);
-
-	msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-/**************************************************************************
- * Buddy Lists
- **************************************************************************/
-
-static void
-add_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	MsnUser *user;
-	const char *list;
-	const char *passport;
-	const char *friendly;
-	MsnListId list_id;
-	int group_id;
-
-	list     = cmd->params[1];
-	passport = cmd->params[3];
-	friendly = purple_url_decode(cmd->params[4]);
-
-	session = cmdproc->session;
-
-	user = msn_userlist_find_user(session->userlist, passport);
-
-	if (user == NULL)
-	{
-		user = msn_user_new(session->userlist, passport, friendly);
-		msn_userlist_add_user(session->userlist, user);
-	}
-	else
-		msn_user_set_friendly_name(user, friendly);
-
-	list_id = msn_get_list_id(list);
-
-	if (cmd->param_count >= 6)
-		group_id = atoi(cmd->params[5]);
-	else
-		group_id = -1;
-
-	msn_got_add_user(session, user, list_id, group_id);
-	msn_user_update(user);
-}
-
-static void
-add_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	MsnSession *session;
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	const char *list, *passport;
-	char *reason = NULL;
-	char *msg = NULL;
-	char **params;
-
-	session = cmdproc->session;
-	account = session->account;
-	gc = purple_account_get_connection(account);
-	params = g_strsplit(trans->params, " ", 0);
-
-	list     = params[0];
-	passport = params[1];
-
-	if (!strcmp(list, "FL"))
-		msg = g_strdup_printf(_("Unable to add user on %s (%s)"),
-							  purple_account_get_username(account),
-							  purple_account_get_protocol_name(account));
-	else if (!strcmp(list, "BL"))
-		msg = g_strdup_printf(_("Unable to block user on %s (%s)"),
-							  purple_account_get_username(account),
-							  purple_account_get_protocol_name(account));
-	else if (!strcmp(list, "AL"))
-		msg = g_strdup_printf(_("Unable to permit user on %s (%s)"),
-							  purple_account_get_username(account),
-							  purple_account_get_protocol_name(account));
-
-	if (!strcmp(list, "FL"))
-	{
-		if (error == 210)
-		{
-			reason = g_strdup_printf(_("%s could not be added because "
-									   "your buddy list is full."), passport);
-		}
-	}
-
-	if (reason == NULL)
-	{
-		if (error == 208)
-		{
-			reason = g_strdup_printf(_("%s is not a valid passport account."),
-									 passport);
-		}
-		else if (error == 500)
-		{
-			reason = g_strdup(_("Service Temporarily Unavailable."));
-		}
-		else
-		{
-			reason = g_strdup(_("Unknown error."));
-		}
-	}
-
-	if (msg != NULL)
-	{
-		purple_notify_error(gc, NULL, msg, reason);
-		g_free(msg);
-	}
-
-	if (!strcmp(list, "FL"))
-	{
-		PurpleBuddy *buddy;
-
-		buddy = purple_find_buddy(account, passport);
-
-		if (buddy != NULL)
-			purple_blist_remove_buddy(buddy);
-	}
-
-	g_free(reason);
-
-	g_strfreev(params);
-}
-
-static void
-adg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	gint group_id;
-	const char *group_name;
-
-	session = cmdproc->session;
-
-	group_id = atoi(cmd->params[3]);
-
-	group_name = purple_url_decode(cmd->params[2]);
-
-	msn_group_new(session->userlist, group_id, group_name);
-
-	/* There is a user that must me moved to this group */
-	if (cmd->trans->data)
-	{
-		/* msn_userlist_move_buddy(); */
-		MsnUserList *userlist = cmdproc->session->userlist;
-		MsnMoveBuddy *data = cmd->trans->data;
-
-		if (data->old_group_name != NULL)
-		{
-			msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->old_group_name);
-			g_free(data->old_group_name);
-		}
-
-		msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, group_name);
-		g_free(data->who);
-
-	}
-}
-
-static void
-qng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	static int count = 0;
-	const char *passport;
-	PurpleAccount *account;
-
-	session = cmdproc->session;
-	account = session->account;
-
-	if (session->passport_info.file == NULL)
-		return;
-
-	passport = purple_normalize(account, purple_account_get_username(account));
-
-	if ((strstr(passport, "@hotmail.") == NULL) &&
-		(strstr(passport, "@live.com") == NULL) &&
-		(strstr(passport, "@msn.com") == NULL))
-		return;
-
-	if (count++ < 26)
-		return;
-
-	count = 0;
-	msn_cmdproc_send(cmdproc, "URL", "%s", "INBOX");
-}
-
-
-static void
-fln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSlpLink *slplink;
-	MsnUser *user;
-
-	user = msn_userlist_find_user(cmdproc->session->userlist, cmd->params[0]);
-
-	user->status = "offline";
-	msn_user_update(user);
-
-	slplink = msn_session_find_slplink(cmdproc->session, cmd->params[0]);
-
-	if (slplink != NULL)
-		msn_slplink_destroy(slplink);
-
-}
-
-static void
-iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	MsnUser *user;
-	MsnObject *msnobj;
-	const char *state, *passport, *friendly;
-
-	session = cmdproc->session;
-	account = session->account;
-	gc = purple_account_get_connection(account);
-
-	state    = cmd->params[1];
-	passport = cmd->params[2];
-	friendly = purple_url_decode(cmd->params[3]);
-
-	user = msn_userlist_find_user(session->userlist, passport);
-
-	serv_got_alias(gc, passport, friendly);
-
-	msn_user_set_friendly_name(user, friendly);
-
-	if (session->protocol_ver >= 9 && cmd->param_count == 6)
-	{
-		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
-		msn_user_set_object(user, msnobj);
-	}
-
-	msn_user_set_state(user, state);
-	msn_user_update(user);
-}
-
-static void
-ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
-{
-#if 0
-	purple_debug_misc("msn", "Incoming Page: {%s}\n", payload);
-#endif
-}
-
-static void
-ipg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	cmdproc->servconn->payload_len = atoi(cmd->params[0]);
-	cmdproc->last_cmd->payload_cb = ipg_cmd_post;
-}
-
-static void
-nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	MsnUser *user;
-	MsnObject *msnobj;
-	int clientid;
-	const char *state, *passport, *friendly, *old_friendly;
-
-	session = cmdproc->session;
-	account = session->account;
-	gc = purple_account_get_connection(account);
-
-	state    = cmd->params[0];
-	passport = cmd->params[1];
-	friendly = purple_url_decode(cmd->params[2]);
-
-	user = msn_userlist_find_user(session->userlist, passport);
-
-	old_friendly = msn_user_get_friendly_name(user);
-	if (!old_friendly || (old_friendly && (!friendly || strcmp(old_friendly, friendly))))
-	{
-		serv_got_alias(gc, passport, friendly);
-		msn_user_set_friendly_name(user, friendly);
-	}
-
-	if (session->protocol_ver >= 9)
-	{
-		if (cmd->param_count == 5)
-		{
-			msnobj =
-				msn_object_new_from_string(purple_url_decode(cmd->params[4]));
-			msn_user_set_object(user, msnobj);
-		}
-		else
-		{
-			msn_user_set_object(user, NULL);
-		}
-	}
-
-	clientid = atoi(cmd->params[3]);
-	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE);
-
-	msn_user_set_state(user, state);
-	msn_user_update(user);
-}
-
-#if 0
-static void
-chg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	char *state = cmd->params[1];
-	int state_id = 0;
-
-	if (!strcmp(state, "NLN"))
-		state_id = MSN_ONLINE;
-	else if (!strcmp(state, "BSY"))
-		state_id = MSN_BUSY;
-	else if (!strcmp(state, "IDL"))
-		state_id = MSN_IDLE;
-	else if (!strcmp(state, "BRB"))
-		state_id = MSN_BRB;
-	else if (!strcmp(state, "AWY"))
-		state_id = MSN_AWAY;
-	else if (!strcmp(state, "PHN"))
-		state_id = MSN_PHONE;
-	else if (!strcmp(state, "LUN"))
-		state_id = MSN_LUNCH;
-	else if (!strcmp(state, "HDN"))
-		state_id = MSN_HIDDEN;
-
-	cmdproc->session->state = state_id;
-}
-#endif
-
-
-static void
-not_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
-{
-#if 0
-	MSN_SET_PARAMS("NOT %d\r\n%s", cmdproc->servconn->payload, payload);
-	purple_debug_misc("msn", "Notification: {%s}\n", payload);
-#endif
-}
-
-static void
-not_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	cmdproc->servconn->payload_len = atoi(cmd->params[0]);
-	cmdproc->last_cmd->payload_cb = not_cmd_post;
-}
-
-static void
-rea_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	const char *friendly;
-	char *username;
-
-	session = cmdproc->session;
-	account = session->account;
-	username = g_strdup(purple_normalize(account,
-						purple_account_get_username(account)));
-
-	/* Only set display name if our *own* friendly name changed! */
-	if (strcmp(username, purple_normalize(account, cmd->params[2])))
-	{
-		g_free(username);
-		return;
-	}
-
-	g_free(username);
-
-	gc = account->gc;
-	friendly = purple_url_decode(cmd->params[3]);
-
-	purple_connection_set_display_name(gc, friendly);
-}
-
-static void
-prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session = cmdproc->session;
-	const char *type, *value;
-
-	g_return_if_fail(cmd->param_count >= 3);
-
-	type  = cmd->params[2];
-
-	if (cmd->param_count == 4)
-	{
-		value = cmd->params[3];
-		if (!strcmp(type, "PHH"))
-			msn_user_set_home_phone(session->user, purple_url_decode(value));
-		else if (!strcmp(type, "PHW"))
-			msn_user_set_work_phone(session->user, purple_url_decode(value));
-		else if (!strcmp(type, "PHM"))
-			msn_user_set_mobile_phone(session->user, purple_url_decode(value));
-	}
-	else
-	{
-		if (!strcmp(type, "PHH"))
-			msn_user_set_home_phone(session->user, NULL);
-		else if (!strcmp(type, "PHW"))
-			msn_user_set_work_phone(session->user, NULL);
-		else if (!strcmp(type, "PHM"))
-			msn_user_set_mobile_phone(session->user, NULL);
-	}
-}
-
-static void
-bpr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	const char *type, *value, *passport;
-	MsnUser *user;
-
-	passport = cmd->params[1];
-	user = msn_userlist_find_user(cmdproc->session->userlist, passport);
-
-	g_return_if_fail(user != NULL);
-
-	type     = cmd->params[2];
-	value    = cmd->params[3];
-
-	if (value)
-	{
-		if (!strcmp(type, "MOB"))
-		{
-			if (!strcmp(value, "Y"))
-				user->mobile = TRUE;
-			else if (!strcmp(value, "N"))
-				user->mobile = FALSE;
-		}
-		else if (!strcmp(type, "PHH"))
-			msn_user_set_home_phone(user, purple_url_decode(value));
-		else if (!strcmp(type, "PHW"))
-			msn_user_set_work_phone(user, purple_url_decode(value));
-		else if (!strcmp(type, "PHM"))
-			msn_user_set_mobile_phone(user, purple_url_decode(value));
-	}
-}
-
-static void
-reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	int group_id;
-	const char *group_name;
-
-	session = cmdproc->session;
-	group_id = atoi(cmd->params[2]);
-	group_name = purple_url_decode(cmd->params[3]);
-
-	msn_userlist_rename_group_id(session->userlist, group_id, group_name);
-}
-
-static void
-reg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	int group_id;
-	char **params;
-
-	params = g_strsplit(trans->params, " ", 0);
-
-	group_id = atoi(params[0]);
-
-	group_error_helper(cmdproc->session, _("Unable to rename group"), group_id, error);
-
-	g_strfreev(params);
-}
-
-static void
-rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	MsnUser *user;
-	const char *list;
-	const char *passport;
-	MsnListId list_id;
-	int group_id;
-
-	session = cmdproc->session;
-	list = cmd->params[1];
-	passport = cmd->params[3];
-	user = msn_userlist_find_user(session->userlist, passport);
-
-	g_return_if_fail(user != NULL);
-
-	list_id = msn_get_list_id(list);
-
-	if (cmd->param_count == 5)
-		group_id = atoi(cmd->params[4]);
-	else
-		group_id = -1;
-
-	msn_got_rem_user(session, user, list_id, group_id);
-	msn_user_update(user);
-}
-
-static void
-rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	int group_id;
-
-	session = cmdproc->session;
-	group_id = atoi(cmd->params[2]);
-
-	msn_userlist_remove_group_id(session->userlist, group_id);
-}
-
-static void
-rmg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	int group_id;
-	char **params;
-
-	params = g_strsplit(trans->params, " ", 0);
-
-	group_id = atoi(params[0]);
-
-	group_error_helper(cmdproc->session, _("Unable to delete group"), group_id, error);
-
-	g_strfreev(params);
-}
-
-static void
-syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	MsnSync *sync;
-	int total_users;
-
-	session = cmdproc->session;
-
-	if (cmd->param_count == 2)
-	{
-		/*
-		 * This can happen if we sent a SYN with an up-to-date
-		 * buddy list revision, but we send 0 to get a full list.
-		 * So, error out.
-		 */
-
-		msn_session_set_error(cmdproc->session, MSN_ERROR_BAD_BLIST, NULL);
-		return;
-	}
-
-	total_users  = atoi(cmd->params[2]);
-
-	sync = msn_sync_new(session);
-	sync->total_users = total_users;
-	sync->old_cbs_table = cmdproc->cbs_table;
-
-	session->sync = sync;
-	cmdproc->cbs_table = sync->cbs_table;
-}
-
-/**************************************************************************
- * Misc commands
- **************************************************************************/
-
-static void
-url_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	PurpleAccount *account;
-	const char *rru;
-	const char *url;
-	PurpleCipher *cipher;
-	PurpleCipherContext *context;
-	guchar digest[16];
-	FILE *fd;
-	char *buf;
-	char buf2[3];
-	char sendbuf[64];
-	int i;
-
-	session = cmdproc->session;
-	account = session->account;
-
-	rru = cmd->params[1];
-	url = cmd->params[2];
-
-	buf = g_strdup_printf("%s%lu%s",
-			   session->passport_info.mspauth ? session->passport_info.mspauth : "BOGUS",
-			   time(NULL) - session->passport_info.sl,
-			   purple_connection_get_password(account->gc));
-
-	cipher = purple_ciphers_find_cipher("md5");
-	context = purple_cipher_context_new(cipher, NULL);
-
-	purple_cipher_context_append(context, (const guchar *)buf, strlen(buf));
-	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
-	purple_cipher_context_destroy(context);
-
-	g_free(buf);
-
-	memset(sendbuf, 0, sizeof(sendbuf));
-
-	for (i = 0; i < 16; i++)
-	{
-		g_snprintf(buf2, sizeof(buf2), "%02x", digest[i]);
-		strcat(sendbuf, buf2);
-	}
-
-	if (session->passport_info.file != NULL)
-	{
-		g_unlink(session->passport_info.file);
-		g_free(session->passport_info.file);
-	}
-
-	if ((fd = purple_mkstemp(&session->passport_info.file, FALSE)) == NULL)
-	{
-		purple_debug_error("msn",
-						 "Error opening temp passport file: %s\n",
-						 g_strerror(errno));
-	}
-	else
-	{
-#ifdef _WIN32
-		fputs("<!-- saved from url=(0013)about:internet -->\n", fd);
-#endif
-		fputs("<html>\n"
-			  "<head>\n"
-			  "<noscript>\n"
-			  "<meta http-equiv=\"Refresh\" content=\"0; "
-			  "url=http://www.hotmail.com\">\n"
-			  "</noscript>\n"
-			  "</head>\n\n",
-			  fd);
-
-		fprintf(fd, "<body onload=\"document.pform.submit(); \">\n");
-		fprintf(fd, "<form name=\"pform\" action=\"%s\" method=\"POST\">\n\n",
-				url);
-		fprintf(fd, "<input type=\"hidden\" name=\"mode\" value=\"ttl\">\n");
-		fprintf(fd, "<input type=\"hidden\" name=\"login\" value=\"%s\">\n",
-				purple_account_get_username(account));
-		fprintf(fd, "<input type=\"hidden\" name=\"username\" value=\"%s\">\n",
-				purple_account_get_username(account));
-		if (session->passport_info.sid != NULL)
-			fprintf(fd, "<input type=\"hidden\" name=\"sid\" value=\"%s\">\n",
-					session->passport_info.sid);
-		if (session->passport_info.kv != NULL)
-			fprintf(fd, "<input type=\"hidden\" name=\"kv\" value=\"%s\">\n",
-					session->passport_info.kv);
-		fprintf(fd, "<input type=\"hidden\" name=\"id\" value=\"2\">\n");
-		fprintf(fd, "<input type=\"hidden\" name=\"sl\" value=\"%ld\">\n",
-				time(NULL) - session->passport_info.sl);
-		fprintf(fd, "<input type=\"hidden\" name=\"rru\" value=\"%s\">\n",
-				rru);
-		if (session->passport_info.mspauth != NULL)
-			fprintf(fd, "<input type=\"hidden\" name=\"auth\" value=\"%s\">\n",
-					session->passport_info.mspauth);
-		fprintf(fd, "<input type=\"hidden\" name=\"creds\" value=\"%s\">\n",
-				sendbuf); /* TODO Digest me (huh? -- ChipX86) */
-		fprintf(fd, "<input type=\"hidden\" name=\"svc\" value=\"mail\">\n");
-		fprintf(fd, "<input type=\"hidden\" name=\"js\" value=\"yes\">\n");
-		fprintf(fd, "</form></body>\n");
-		fprintf(fd, "</html>\n");
-
-		if (fclose(fd))
-		{
-			purple_debug_error("msn",
-							 "Error closing temp passport file: %s\n",
-							 g_strerror(errno));
-
-			g_unlink(session->passport_info.file);
-			g_free(session->passport_info.file);
-			session->passport_info.file = NULL;
-		}
-#ifdef _WIN32
-		else
-		{
-			/*
-			 * Renaming file with .html extension, so that the
-			 * win32 open_url will work.
-			 */
-			char *tmp;
-
-			if ((tmp =
-				g_strdup_printf("%s.html",
-					session->passport_info.file)) != NULL)
-			{
-				if (g_rename(session->passport_info.file,
-							tmp) == 0)
-				{
-					g_free(session->passport_info.file);
-					session->passport_info.file = tmp;
-				}
-				else
-					g_free(tmp);
-			}
-		}
-#endif
-	}
-}
-/**************************************************************************
- * Switchboards
- **************************************************************************/
-
-static void
-rng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-	const char *session_id;
-	char *host;
-	int port;
-
-	session = cmdproc->session;
-	session_id = cmd->params[0];
-
-	msn_parse_socket(cmd->params[1], &host, &port);
-
-	if (session->http_method)
-		port = 80;
-
-	swboard = msn_switchboard_new(session);
-
-	msn_switchboard_set_invited(swboard, TRUE);
-	msn_switchboard_set_session_id(swboard, cmd->params[0]);
-	msn_switchboard_set_auth_key(swboard, cmd->params[3]);
-	swboard->im_user = g_strdup(cmd->params[4]);
-	/* msn_switchboard_add_user(swboard, cmd->params[4]); */
-
-	if (!msn_switchboard_connect(swboard, host, port))
-		msn_switchboard_destroy(swboard);
-
-	g_free(host);
-}
-
-static void
-xfr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	char *host;
-	int port;
-
-	if (strcmp(cmd->params[1], "SB") && strcmp(cmd->params[1], "NS"))
-	{
-		/* Maybe we can have a generic bad command error. */
-		purple_debug_error("msn", "Bad XFR command (%s)\n", cmd->params[1]);
-		return;
-	}
-
-	msn_parse_socket(cmd->params[2], &host, &port);
-
-	if (!strcmp(cmd->params[1], "SB"))
-	{
-		purple_debug_error("msn", "This shouldn't be handled here.\n");
-	}
-	else if (!strcmp(cmd->params[1], "NS"))
-	{
-		MsnSession *session;
-
-		session = cmdproc->session;
-
-		msn_session_set_login_step(session, MSN_LOGIN_STEP_TRANSFER);
-
-		msn_notification_connect(session->notification, host, port);
-	}
-
-	g_free(host);
-}
-
-/**************************************************************************
- * Message Types
- **************************************************************************/
-
-static void
-profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	MsnSession *session;
-	const char *value;
-
-	session = cmdproc->session;
-
-	if (strcmp(msg->remote_user, "Hotmail"))
-		/* This isn't an official message. */
-		return;
-
-	if ((value = msn_message_get_attr(msg, "kv")) != NULL)
-	{
-		g_free(session->passport_info.kv);
-		session->passport_info.kv = g_strdup(value);
-	}
-
-	if ((value = msn_message_get_attr(msg, "sid")) != NULL)
-	{
-		g_free(session->passport_info.sid);
-		session->passport_info.sid = g_strdup(value);
-	}
-
-	if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL)
-	{
-		g_free(session->passport_info.mspauth);
-		session->passport_info.mspauth = g_strdup(value);
-	}
-
-	if ((value = msn_message_get_attr(msg, "ClientIP")) != NULL)
-	{
-		g_free(session->passport_info.client_ip);
-		session->passport_info.client_ip = g_strdup(value);
-	}
-
-	if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL)
-		session->passport_info.client_port = ntohs(atoi(value));
-
-	if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL)
-		session->passport_info.sl = atol(value);
-}
-
-static void
-initial_email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	MsnSession *session;
-	PurpleConnection *gc;
-	GHashTable *table;
-	const char *unread;
-
-	session = cmdproc->session;
-	gc = session->account->gc;
-
-	if (strcmp(msg->remote_user, "Hotmail"))
-		/* This isn't an official message. */
-		return;
-
-	if (session->passport_info.file == NULL)
-	{
-		MsnTransaction *trans;
-		trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX");
-		msn_transaction_queue_cmd(trans, msg->cmd);
-
-		msn_cmdproc_send_trans(cmdproc, trans);
-
-		return;
-	}
-
-	if (!purple_account_get_check_mail(session->account))
-		return;
-
-	table = msn_message_get_hashtable_from_body(msg);
-
-	unread = g_hash_table_lookup(table, "Inbox-Unread");
-
-	if (unread != NULL)
-	{
-		int count = atoi(unread);
-
-		if (count > 0)
-		{
-			const char *passports[2] = { msn_user_get_passport(session->user) };
-			const char *urls[2] = { session->passport_info.file };
-
-			purple_notify_emails(gc, count, FALSE, NULL, NULL,
-							   passports, urls, NULL, NULL);
-		}
-	}
-
-	g_hash_table_destroy(table);
-}
-
-static void
-email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	MsnSession *session;
-	PurpleConnection *gc;
-	GHashTable *table;
-	char *from, *subject, *tmp;
-
-	session = cmdproc->session;
-	gc = session->account->gc;
-
-	if (strcmp(msg->remote_user, "Hotmail"))
-		/* This isn't an official message. */
-		return;
-
-	if (session->passport_info.file == NULL)
-	{
-		MsnTransaction *trans;
-		trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX");
-		msn_transaction_queue_cmd(trans, msg->cmd);
-
-		msn_cmdproc_send_trans(cmdproc, trans);
-
-		return;
-	}
-
-	if (!purple_account_get_check_mail(session->account))
-		return;
-
-	table = msn_message_get_hashtable_from_body(msg);
-
-	from = subject = NULL;
-
-	tmp = g_hash_table_lookup(table, "From");
-	if (tmp != NULL)
-		from = purple_mime_decode_field(tmp);
-
-	tmp = g_hash_table_lookup(table, "Subject");
-	if (tmp != NULL)
-		subject = purple_mime_decode_field(tmp);
-
-	purple_notify_email(gc,
-					  (subject != NULL ? subject : ""),
-					  (from != NULL ?  from : ""),
-					  msn_user_get_passport(session->user),
-					  session->passport_info.file, NULL, NULL);
-
-	g_free(from);
-	g_free(subject);
-
-	g_hash_table_destroy(table);
-}
-
-static void
-system_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	GHashTable *table;
-	const char *type_s;
-
-	if (strcmp(msg->remote_user, "Hotmail"))
-		/* This isn't an official message. */
-		return;
-
-	table = msn_message_get_hashtable_from_body(msg);
-
-	if ((type_s = g_hash_table_lookup(table, "Type")) != NULL)
-	{
-		int type = atoi(type_s);
-		char buf[MSN_BUF_LEN];
-		int minutes;
-
-		switch (type)
-		{
-			case 1:
-				minutes = atoi(g_hash_table_lookup(table, "Arg1"));
-				g_snprintf(buf, sizeof(buf), dngettext(PACKAGE, 
-							"The MSN server will shut down for maintenance "
-							"in %d minute. You will automatically be "
-							"signed out at that time.  Please finish any "
-							"conversations in progress.\n\nAfter the "
-							"maintenance has been completed, you will be "
-							"able to successfully sign in.",
-							"The MSN server will shut down for maintenance "
-							"in %d minutes. You will automatically be "
-							"signed out at that time.  Please finish any "
-							"conversations in progress.\n\nAfter the "
-							"maintenance has been completed, you will be "
-							"able to successfully sign in.", minutes),
-						minutes);
-			default:
-				break;
-		}
-
-		if (*buf != '\0')
-			purple_notify_info(cmdproc->session->account->gc, NULL, buf, NULL);
-	}
-
-	g_hash_table_destroy(table);
-}
-
-void
-msn_notification_add_buddy(MsnNotification *notification, const char *list,
-						   const char *who, const char *friendly_name,
-						   int group_id)
-{
-	MsnCmdProc *cmdproc;
-	cmdproc = notification->servconn->cmdproc;
-
-	if (group_id < 0 && !strcmp(list, "FL"))
-		group_id = 0;
-
-	if (group_id >= 0)
-	{
-		msn_cmdproc_send(cmdproc, "ADD", "%s %s %s %d",
-						 list, who, friendly_name, group_id);
-	}
-	else
-	{
-		msn_cmdproc_send(cmdproc, "ADD", "%s %s %s", list, who, friendly_name);
-	}
-}
-
-void
-msn_notification_rem_buddy(MsnNotification *notification, const char *list,
-						   const char *who, int group_id)
-{
-	MsnCmdProc *cmdproc;
-	cmdproc = notification->servconn->cmdproc;
-
-	if (group_id >= 0)
-	{
-		msn_cmdproc_send(cmdproc, "REM", "%s %s %d", list, who, group_id);
-	}
-	else
-	{
-		msn_cmdproc_send(cmdproc, "REM", "%s %s", list, who);
-	}
-}
-
-/**************************************************************************
- * Init
- **************************************************************************/
-
-void
-msn_notification_init(void)
-{
-	/* TODO: check prp, blp */
-
-	cbs_table = msn_table_new();
-
-	/* Synchronous */
-	msn_table_add_cmd(cbs_table, "CHG", "CHG", NULL);
-	msn_table_add_cmd(cbs_table, "CHG", "ILN", iln_cmd);
-	msn_table_add_cmd(cbs_table, "ADD", "ADD", add_cmd);
-	msn_table_add_cmd(cbs_table, "ADD", "ILN", iln_cmd);
-	msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd);
-	msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
-	msn_table_add_cmd(cbs_table, "USR", "XFR", xfr_cmd);
-	msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd);
-	msn_table_add_cmd(cbs_table, "CVR", "CVR", cvr_cmd);
-	msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd);
-	msn_table_add_cmd(cbs_table, "REA", "REA", rea_cmd);
-	msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd);
-	/* msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); */
-	msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL);
-	msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd);
-	msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd);
-	msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd);
-	msn_table_add_cmd(cbs_table, "XFR", "XFR", xfr_cmd);
-
-	/* Asynchronous */
-	msn_table_add_cmd(cbs_table, NULL, "IPG", ipg_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "NOT", not_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "BPR", bpr_cmd);
-
-	msn_table_add_cmd(cbs_table, NULL, "CHL", chl_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "REM", rem_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "ADD", add_cmd);
-
-	msn_table_add_cmd(cbs_table, NULL, "QRY", NULL);
-	msn_table_add_cmd(cbs_table, NULL, "QNG", qng_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "FLN", fln_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "NLN", nln_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "ILN", iln_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "RNG", rng_cmd);
-
-	msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd);
-
-	msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd);
-
-	msn_table_add_error(cbs_table, "ADD", add_error);
-	msn_table_add_error(cbs_table, "REG", reg_error);
-	msn_table_add_error(cbs_table, "RMG", rmg_error);
-	/* msn_table_add_error(cbs_table, "REA", rea_error); */
-	msn_table_add_error(cbs_table, "USR", usr_error);
-
-	msn_table_add_msg_type(cbs_table,
-						   "text/x-msmsgsprofile",
-						   profile_msg);
-	msn_table_add_msg_type(cbs_table,
-						   "text/x-msmsgsinitialemailnotification",
-						   initial_email_msg);
-	msn_table_add_msg_type(cbs_table,
-						   "text/x-msmsgsemailnotification",
-						   email_msg);
-	msn_table_add_msg_type(cbs_table,
-						   "application/x-msmsgssystemmessage",
-						   system_msg);
-}
-
-void
-msn_notification_end(void)
-{
-	msn_table_destroy(cbs_table);
-}
--- a/libpurple/protocols/msnp9/notification.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/**
- * @file notification.h Notification server functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_NOTIFICATION_H_
-#define _MSN_NOTIFICATION_H_
-
-typedef struct _MsnNotification MsnNotification;
-
-#include "session.h"
-#include "servconn.h"
-#include "cmdproc.h"
-
-struct _MsnNotification
-{
-	MsnSession *session;
-	MsnCmdProc *cmdproc;
-	MsnServConn *servconn;
-
-	gboolean in_use;
-};
-
-#include "state.h"
-
-void msn_notification_end(void);
-void msn_notification_init(void);
-
-void msn_notification_add_buddy(MsnNotification *notification,
-								const char *list, const char *who,
-								const char *friendly_name, int group_id);
-void msn_notification_rem_buddy(MsnNotification *notification,
-								const char *list, const char *who,
-								int group_id);
-MsnNotification *msn_notification_new(MsnSession *session);
-void msn_notification_destroy(MsnNotification *notification);
-gboolean msn_notification_connect(MsnNotification *notification,
-							  const char *host, int port);
-void msn_notification_disconnect(MsnNotification *notification);
-
-/**
- * Closes a notification.
- *
- * It's first closed, and then disconnected.
- * 
- * @param notification The notification object to close.
- */
-void msn_notification_close(MsnNotification *notification);
-
-void msn_got_login_params(MsnSession *session, const char *login_params);
-
-#endif /* _MSN_NOTIFICATION_H_ */
--- a/libpurple/protocols/msnp9/object.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,417 +0,0 @@
-/**
- * @file object.c MSNObject API
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "object.h"
-#include "debug.h"
-/* Sha1 stuff */
-#include "cipher.h"
-/* Base64 stuff */
-#include "util.h"
-
-#define GET_STRING_TAG(field, id) \
-	if ((tag = strstr(str, id "=\"")) != NULL) \
-	{ \
-		tag += strlen(id "=\""); \
-		c = strchr(tag, '"'); \
-		if (c != NULL) \
-		{ \
-			if (obj->field != NULL) \
-				g_free(obj->field); \
-			obj->field = g_strndup(tag, c - tag); \
-		} \
-	}
-
-#define GET_INT_TAG(field, id) \
-	if ((tag = strstr(str, id "=\"")) != NULL) \
-	{ \
-		char buf[16]; \
-		size_t offset; \
-		tag += strlen(id "=\""); \
-		c = strchr(tag, '"'); \
-		if (c != NULL) \
-		{ \
-			memset(buf, 0, sizeof(buf)); \
-			offset = c - tag; \
-			if (offset >= sizeof(buf)) \
-				offset = sizeof(buf) - 1; \
-			strncpy(buf, tag, offset); \
-			obj->field = atoi(buf); \
-		} \
-	}
-
-static GList *local_objs;
-
-MsnObject *
-msn_object_new(void)
-{
-	MsnObject *obj;
-
-	obj = g_new0(MsnObject, 1);
-
-	msn_object_set_type(obj, MSN_OBJECT_UNKNOWN);
-	msn_object_set_friendly(obj, "AAA=");
-
-	return obj;
-}
-
-MsnObject *
-msn_object_new_from_string(const char *str)
-{
-	MsnObject *obj;
-	char *tag, *c;
-
-	g_return_val_if_fail(str != NULL, NULL);
-
-	if (strncmp(str, "<msnobj ", 8))
-		return NULL;
-
-	obj = msn_object_new();
-
-	GET_STRING_TAG(creator,  "Creator");
-	GET_INT_TAG(size,        "Size");
-	GET_INT_TAG(type,        "Type");
-	GET_STRING_TAG(location, "Location");
-	GET_STRING_TAG(friendly, "Friendly");
-	GET_STRING_TAG(sha1d,    "SHA1D");
-	GET_STRING_TAG(sha1c,    "SHA1C");
-
-	/* If we are missing any of the required elements then discard the object */
-	/* SHA1C is not always sent anymore */
-	if (obj->creator == NULL || obj->size == 0 || obj->type == 0
-			|| obj->location == NULL || obj->friendly == NULL
-			|| obj->sha1d == NULL /*|| obj->sha1c == NULL*/) {
-		purple_debug_error("msn", "Discarding invalid msnobj: '%s'\n", str);
-		msn_object_destroy(obj);
-		obj = NULL;
-	}
-
-	return obj;
-}
-
-MsnObject*
-msn_object_new_from_image(PurpleStoredImage *img, const char *location,
-		const char *creator, MsnObjectType type)
-{
-	MsnObject *msnobj;
-
-	PurpleCipherContext *ctx;
-	char *buf;
-	gconstpointer data;
-	size_t size;
-	char *base64;
-	unsigned char digest[20];
-
-	msnobj = NULL;
-
-	if (img == NULL)
-		return msnobj;
-
-	size = purple_imgstore_get_size(img);
-	data = purple_imgstore_get_data(img);
-
-	/* New object */
-	msnobj = msn_object_new();
-	msn_object_set_local(msnobj);
-	msn_object_set_type(msnobj, type);
-	msn_object_set_location(msnobj, location);
-	msn_object_set_creator(msnobj, creator);
-
-	msn_object_set_image(msnobj, img);
-
-	/* Compute the SHA1D field. */
-	memset(digest, 0, sizeof(digest));
-
-	ctx = purple_cipher_context_new_by_name("sha1", NULL);
-	purple_cipher_context_append(ctx, data, size);
-	purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
-
-	base64 = purple_base64_encode(digest, sizeof(digest));
-	msn_object_set_sha1d(msnobj, base64);
-	g_free(base64);
-
-	msn_object_set_size(msnobj, size);
-
-	/* Compute the SHA1C field. */
-	buf = g_strdup_printf(
-		"Creator%sSize%dType%dLocation%sFriendly%sSHA1D%s",
-		msn_object_get_creator(msnobj),
-		msn_object_get_size(msnobj),
-		msn_object_get_type(msnobj),
-		msn_object_get_location(msnobj),
-		msn_object_get_friendly(msnobj),
-		msn_object_get_sha1d(msnobj));
-
-	memset(digest, 0, sizeof(digest));
-
-	purple_cipher_context_reset(ctx, NULL);
-	purple_cipher_context_append(ctx, (const guchar *)buf, strlen(buf));
-	purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL);
-	purple_cipher_context_destroy(ctx);
-	g_free(buf);
-
-	base64 = purple_base64_encode(digest, sizeof(digest));
-	msn_object_set_sha1c(msnobj, base64);
-	g_free(base64);
-
-	return msnobj;
-}
-
-void
-msn_object_destroy(MsnObject *obj)
-{
-	g_return_if_fail(obj != NULL);
-
-	g_free(obj->creator);
-	g_free(obj->location);
-	g_free(obj->friendly);
-	g_free(obj->sha1d);
-	g_free(obj->sha1c);
-
-	purple_imgstore_unref(obj->img);
-
-	if (obj->local)
-		local_objs = g_list_remove(local_objs, obj);
-
-	g_free(obj);
-}
-
-char *
-msn_object_to_string(const MsnObject *obj)
-{
-	char *str;
-	const char *sha1c;
-
-	g_return_val_if_fail(obj != NULL, NULL);
-
-	sha1c = msn_object_get_sha1c(obj);
-
-	str = g_strdup_printf("<msnobj Creator=\"%s\" Size=\"%d\" Type=\"%d\" "
-						  "Location=\"%s\" Friendly=\"%s\" SHA1D=\"%s\""
-						  "%s%s%s/>",
-						  msn_object_get_creator(obj),
-						  msn_object_get_size(obj),
-						  msn_object_get_type(obj),
-						  msn_object_get_location(obj),
-						  msn_object_get_friendly(obj),
-						  msn_object_get_sha1d(obj),
-						  sha1c ? " SHA1C=\"" : "",
-						  sha1c ? sha1c : "",
-						  sha1c ? "\"" : "");
-
-	return str;
-}
-
-void
-msn_object_set_creator(MsnObject *obj, const char *creator)
-{
-	g_return_if_fail(obj != NULL);
-
-	if (obj->creator != NULL)
-		g_free(obj->creator);
-
-	obj->creator = (creator == NULL ? NULL : g_strdup(creator));
-}
-
-void
-msn_object_set_size(MsnObject *obj, int size)
-{
-	g_return_if_fail(obj != NULL);
-
-	obj->size = size;
-}
-
-void
-msn_object_set_type(MsnObject *obj, MsnObjectType type)
-{
-	g_return_if_fail(obj != NULL);
-
-	obj->type = type;
-}
-
-void
-msn_object_set_location(MsnObject *obj, const char *location)
-{
-	g_return_if_fail(obj != NULL);
-
-	if (obj->location != NULL)
-		g_free(obj->location);
-
-	obj->location = (location == NULL ? NULL : g_strdup(location));
-}
-
-void
-msn_object_set_friendly(MsnObject *obj, const char *friendly)
-{
-	g_return_if_fail(obj != NULL);
-
-	if (obj->friendly != NULL)
-		g_free(obj->friendly);
-
-	obj->friendly = (friendly == NULL ? NULL : g_strdup(friendly));
-}
-
-void
-msn_object_set_sha1d(MsnObject *obj, const char *sha1d)
-{
-	g_return_if_fail(obj != NULL);
-
-	if (obj->sha1d != NULL)
-		g_free(obj->sha1d);
-
-	obj->sha1d = (sha1d == NULL ? NULL : g_strdup(sha1d));
-}
-
-void
-msn_object_set_sha1c(MsnObject *obj, const char *sha1c)
-{
-	g_return_if_fail(obj != NULL);
-
-	if (obj->sha1c != NULL)
-		g_free(obj->sha1c);
-
-	obj->sha1c = (sha1c == NULL ? NULL : g_strdup(sha1c));
-}
-
-const char *
-msn_object_get_creator(const MsnObject *obj)
-{
-	g_return_val_if_fail(obj != NULL, NULL);
-
-	return obj->creator;
-}
-
-int
-msn_object_get_size(const MsnObject *obj)
-{
-	g_return_val_if_fail(obj != NULL, 0);
-
-	return obj->size;
-}
-
-MsnObjectType
-msn_object_get_type(const MsnObject *obj)
-{
-	g_return_val_if_fail(obj != NULL, MSN_OBJECT_UNKNOWN);
-
-	return obj->type;
-}
-
-const char *
-msn_object_get_location(const MsnObject *obj)
-{
-	g_return_val_if_fail(obj != NULL, NULL);
-
-	return obj->location;
-}
-
-const char *
-msn_object_get_friendly(const MsnObject *obj)
-{
-	g_return_val_if_fail(obj != NULL, NULL);
-
-	return obj->friendly;
-}
-
-const char *
-msn_object_get_sha1d(const MsnObject *obj)
-{
-	g_return_val_if_fail(obj != NULL, NULL);
-
-	return obj->sha1d;
-}
-
-const char *
-msn_object_get_sha1c(const MsnObject *obj)
-{
-	g_return_val_if_fail(obj != NULL, NULL);
-
-	return obj->sha1c;
-}
-
-const char *
-msn_object_get_sha1(const MsnObject *obj)
-{
-	g_return_val_if_fail(obj != NULL, NULL);
-
-	if(obj->sha1c != NULL) {
-		return obj->sha1c;
-	} else {
-		return obj->sha1d;
-	}
-}
-
-static MsnObject *
-msn_object_find_local(const char *sha1)
-{
-	GList *l;
-
-	g_return_val_if_fail(sha1 != NULL, NULL);
-
-	for (l = local_objs; l != NULL; l = l->next)
-	{
-		MsnObject *local_obj = l->data;
-
-		if (!strcmp(msn_object_get_sha1(local_obj), sha1))
-			return local_obj;
-	}
-
-	return NULL;
-
-}
-
-void
-msn_object_set_local(MsnObject *obj)
-{
-	g_return_if_fail(obj != NULL);
-
-	obj->local = TRUE;
-
-	local_objs = g_list_append(local_objs, obj);
-}
-
-void
-msn_object_set_image(MsnObject *obj, PurpleStoredImage *img)
-{
-	g_return_if_fail(obj != NULL);
-	g_return_if_fail(img != NULL);
-
-	/* obj->local = TRUE; */
-
-	purple_imgstore_unref(obj->img);
-	obj->img = purple_imgstore_ref(img);
-}
-
-PurpleStoredImage *
-msn_object_get_image(const MsnObject *obj)
-{
-	MsnObject *local_obj;
-
-	g_return_val_if_fail(obj != NULL, NULL);
-
-	local_obj = msn_object_find_local(msn_object_get_sha1(obj));
-
-	if (local_obj != NULL)
-		return local_obj->img;
-
-	return NULL;
-}
--- a/libpurple/protocols/msnp9/object.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,242 +0,0 @@
-/**
- * @file object.h MSNObject API
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_OBJECT_H_
-#define _MSN_OBJECT_H_
-
-#include "imgstore.h"
-
-#include "internal.h"
-
-typedef enum
-{
-	MSN_OBJECT_UNKNOWN    = -1, /**< Unknown object        */
-	MSN_OBJECT_RESERVED1  =  1, /**< Reserved              */
-	MSN_OBJECT_EMOTICON   =  2, /**< Custom Emoticon       */
-	MSN_OBJECT_USERTILE   =  3, /**< UserTile (buddy icon) */
-	MSN_OBJECT_RESERVED2  =  4, /**< Reserved              */
-	MSN_OBJECT_BACKGROUND =  5  /**< Background            */
-
-} MsnObjectType;
-
-typedef struct
-{
-	gboolean local;
-
-	char *creator;
-	int size;
-	MsnObjectType type;
-	PurpleStoredImage *img;
-	char *location;
-	char *friendly;
-	char *sha1d;
-	char *sha1c;
-
-} MsnObject;
-
-/**
- * Creates a MsnObject structure.
- *
- * @return A new MsnObject structure.
- */
-MsnObject *msn_object_new(void);
-
-/**
- * Creates a MsnObject structure from a string.
- *
- * @param str The string.
- *
- * @return The new MsnObject structure.
- */
-MsnObject *msn_object_new_from_string(const char *str);
-
-/**
- * Creates a MsnObject structure from a stored image
- *
- * @param img		The image associated to object
- * @param location	The object location as stored in MsnObject
- * @param creator	The creator of the object
- * @param type		The type of the object
- *
- * @return A new MsnObject structure
- */
-MsnObject *msn_object_new_from_image(PurpleStoredImage *img,
-		const char *location, const char *creator, MsnObjectType type);
-
-/**
- * Destroys an MsnObject structure.
- *
- * @param obj The object structure.
- */
-void msn_object_destroy(MsnObject *obj);
-
-/**
- * Outputs a string representation of an MsnObject.
- *
- * @param obj The object.
- *
- * @return The string representation. This must be freed.
- */
-char *msn_object_to_string(const MsnObject *obj);
-
-/**
- * Sets the creator field in a MsnObject.
- *
- * @param creator The creator value.
- */
-void msn_object_set_creator(MsnObject *obj, const char *creator);
-
-/**
- * Sets the size field in a MsnObject.
- *
- * @param size The size value.
- */
-void msn_object_set_size(MsnObject *obj, int size);
-
-/**
- * Sets the type field in a MsnObject.
- *
- * @param type The type value.
- */
-void msn_object_set_type(MsnObject *obj, MsnObjectType type);
-
-/**
- * Sets the location field in a MsnObject.
- *
- * @param location The location value.
- */
-void msn_object_set_location(MsnObject *obj, const char *location);
-
-/**
- * Sets the friendly name field in a MsnObject.
- *
- * @param friendly The friendly name value.
- */
-void msn_object_set_friendly(MsnObject *obj, const char *friendly);
-
-/**
- * Sets the SHA1D field in a MsnObject.
- *
- * @param sha1d The sha1d value.
- */
-void msn_object_set_sha1d(MsnObject *obj, const char *sha1d);
-
-/**
- * Sets the SHA1C field in a MsnObject.
- *
- * @param sha1c The sha1c value.
- */
-void msn_object_set_sha1c(MsnObject *obj, const char *sha1c);
-
-/**
- * Associates an image with a MsnObject.
- *
- * @param obj The object.
- * @param img The image to associate.
- */
-void msn_object_set_image(MsnObject *obj, PurpleStoredImage *img);
-
-/**
- * Returns a MsnObject's creator value.
- *
- * @param obj The object.
- *
- * @return The creator value.
- */
-const char *msn_object_get_creator(const MsnObject *obj);
-
-/**
- * Returns a MsnObject's size value.
- *
- * @param obj The object.
- *
- * @return The size value.
- */
-int msn_object_get_size(const MsnObject *obj);
-
-/**
- * Returns a MsnObject's type.
- *
- * @param obj The object.
- *
- * @return The object type.
- */
-MsnObjectType msn_object_get_type(const MsnObject *obj);
-
-/**
- * Returns a MsnObject's location value.
- *
- * @param obj The object.
- *
- * @return The location value.
- */
-const char *msn_object_get_location(const MsnObject *obj);
-
-/**
- * Returns a MsnObject's friendly name value.
- *
- * @param obj The object.
- *
- * @return The friendly name value.
- */
-const char *msn_object_get_friendly(const MsnObject *obj);
-
-/**
- * Returns a MsnObject's SHA1D value.
- *
- * @param obj The object.
- *
- * @return The SHA1D value.
- */
-const char *msn_object_get_sha1d(const MsnObject *obj);
-
-/**
- * Returns a MsnObject's SHA1C value.
- *
- * @param obj The object.
- *
- * @return The SHA1C value.
- */
-const char *msn_object_get_sha1c(const MsnObject *obj);
-
-/**
- * Returns a MsnObject's SHA1C value if it exists, otherwise SHA1D.
- *
- * @param obj The object.
- *
- * @return The SHA1C value.
- */
-const char *msn_object_get_sha1(const MsnObject *obj);
-
-/**
- * Returns the image associated with the MsnObject.
- *
- * @param obj The object.
- *
- * @return The associated image.
- */
-PurpleStoredImage *msn_object_get_image(const MsnObject *obj);
-
-void msn_object_set_local(MsnObject *obj);
-
-#endif /* _MSN_OBJECT_H_ */
--- a/libpurple/protocols/msnp9/page.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-/**
- * @file page.c Paging functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "page.h"
-
-MsnPage *
-msn_page_new(void)
-{
-	MsnPage *page;
-
-	page = g_new0(MsnPage, 1);
-
-	return page;
-}
-
-void
-msn_page_destroy(MsnPage *page)
-{
-	g_return_if_fail(page != NULL);
-
-	if (page->body != NULL)
-		g_free(page->body);
-
-	if (page->from_location != NULL)
-		g_free(page->from_location);
-
-	if (page->from_phone != NULL)
-		g_free(page->from_phone);
-
-	g_free(page);
-}
-
-char *
-msn_page_gen_payload(const MsnPage *page, size_t *ret_size)
-{
-	char *str;
-
-	g_return_val_if_fail(page != NULL, NULL);
-
-	str =
-		g_strdup_printf("<TEXT xml:space=\"preserve\" enc=\"utf-8\">%s</TEXT>",
-						msn_page_get_body(page));
-
-	if (ret_size != NULL)
-		*ret_size = strlen(str);
-
-	return str;
-}
-
-void
-msn_page_set_body(MsnPage *page, const char *body)
-{
-	g_return_if_fail(page != NULL);
-	g_return_if_fail(body != NULL);
-
-	if (page->body != NULL)
-		g_free(page->body);
-
-	page->body = g_strdup(body);
-}
-
-const char *
-msn_page_get_body(const MsnPage *page)
-{
-	g_return_val_if_fail(page != NULL, NULL);
-
-	return page->body;
-}
--- a/libpurple/protocols/msnp9/page.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/**
- * @file page.h Paging functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_PAGE_H_
-#define _MSN_PAGE_H_
-
-typedef struct _MsnPage MsnPage;
-
-#include "session.h"
-
-/**
- * A page.
- */
-struct _MsnPage
-{
-	char *from_location;
-	char *from_phone;
-
-	char *body;
-};
-
-/**
- * Creates a new, empty page.
- *
- * @return A new page.
- */
-MsnPage *msn_page_new(void);
-
-/**
- * Destroys a page.
- */
-void msn_page_destroy(MsnPage *page);
-
-/**
- * Generates the payload data of a page.
- *
- * @param page     The page.
- * @param ret_size The returned size of the payload.
- *
- * @return The payload data of a page.
- */
-char *msn_page_gen_payload(const MsnPage *page, size_t *ret_size);
-
-/**
- * Sets the body of a page.
- *
- * @param page  The page.
- * @param body The body of the page.
- */
-void msn_page_set_body(MsnPage *page, const char *body);
-
-/**
- * Returns the body of the page.
- *
- * @param page The page.
- *
- * @return The body of the page.
- */
-const char *msn_page_get_body(const MsnPage *page);
-
-#endif /* _MSN_PAGE_H_ */
--- a/libpurple/protocols/msnp9/servconn.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,556 +0,0 @@
-/**
- * @file servconn.c Server connection functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "servconn.h"
-#include "error.h"
-
-static void read_cb(gpointer data, gint source, PurpleInputCondition cond);
-
-/**************************************************************************
- * Main
- **************************************************************************/
-
-MsnServConn *
-msn_servconn_new(MsnSession *session, MsnServConnType type)
-{
-	MsnServConn *servconn;
-
-	g_return_val_if_fail(session != NULL, NULL);
-
-	servconn = g_new0(MsnServConn, 1);
-
-	servconn->type = type;
-
-	servconn->session = session;
-	servconn->cmdproc = msn_cmdproc_new(session);
-	servconn->cmdproc->servconn = servconn;
-
-	servconn->httpconn = msn_httpconn_new(servconn);
-
-	servconn->num = session->servconns_count++;
-
-	servconn->tx_buf = purple_circ_buffer_new(MSN_BUF_LEN);
-	servconn->tx_handler = 0;
-
-	return servconn;
-}
-
-void
-msn_servconn_destroy(MsnServConn *servconn)
-{
-	g_return_if_fail(servconn != NULL);
-
-	if (servconn->processing)
-	{
-		servconn->wasted = TRUE;
-		return;
-	}
-
-	if (servconn->connected)
-		msn_servconn_disconnect(servconn);
-
-	if (servconn->destroy_cb)
-		servconn->destroy_cb(servconn);
-
-	if (servconn->httpconn != NULL)
-		msn_httpconn_destroy(servconn->httpconn);
-
-	g_free(servconn->host);
-
-	purple_circ_buffer_destroy(servconn->tx_buf);
-	if (servconn->tx_handler > 0)
-		purple_input_remove(servconn->tx_handler);
-
-	msn_cmdproc_destroy(servconn->cmdproc);
-	g_free(servconn);
-}
-
-void
-msn_servconn_set_connect_cb(MsnServConn *servconn,
-							void (*connect_cb)(MsnServConn *))
-{
-	g_return_if_fail(servconn != NULL);
-	servconn->connect_cb = connect_cb;
-}
-
-void
-msn_servconn_set_disconnect_cb(MsnServConn *servconn,
-							   void (*disconnect_cb)(MsnServConn *))
-{
-	g_return_if_fail(servconn != NULL);
-
-	servconn->disconnect_cb = disconnect_cb;
-}
-
-void
-msn_servconn_set_destroy_cb(MsnServConn *servconn,
-							void (*destroy_cb)(MsnServConn *))
-{
-	g_return_if_fail(servconn != NULL);
-
-	servconn->destroy_cb = destroy_cb;
-}
-
-/**************************************************************************
- * Utility
- **************************************************************************/
-
-void
-msn_servconn_got_error(MsnServConn *servconn, MsnServConnError error)
-{
-	char *tmp;
-	const char *reason;
-
-	const char *names[] = { "Notification", "Switchboard" };
-	const char *name;
-
-	name = names[servconn->type];
-
-	switch (error)
-	{
-		case MSN_SERVCONN_ERROR_CONNECT:
-			reason = _("Unable to connect"); break;
-		case MSN_SERVCONN_ERROR_WRITE:
-			reason = _("Writing error"); break;
-		case MSN_SERVCONN_ERROR_READ:
-			reason = _("Reading error"); break;
-		default:
-			reason = _("Unknown error"); break;
-	}
-
-	purple_debug_error("msn", "Connection error from %s server (%s): %s\n",
-					 name, servconn->host, reason);
-	tmp = g_strdup_printf(_("Connection error from %s server:\n%s"),
-						  name, reason);
-
-	if (servconn->type == MSN_SERVCONN_NS)
-	{
-		msn_session_set_error(servconn->session, MSN_ERROR_SERVCONN, tmp);
-	}
-	else if (servconn->type == MSN_SERVCONN_SB)
-	{
-		MsnSwitchBoard *swboard;
-		swboard = servconn->cmdproc->data;
-		if (swboard != NULL)
-			swboard->error = MSN_SB_ERROR_CONNECTION;
-	}
-
-	msn_servconn_disconnect(servconn);
-
-	g_free(tmp);
-}
-
-/**************************************************************************
- * Connect
- **************************************************************************/
-
-static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
-{
-	MsnServConn *servconn;
-
-	servconn = data;
-	servconn->connect_data = NULL;
-	servconn->processing = FALSE;
-
-	if (servconn->wasted)
-	{
-		if (source >= 0)
-			close(source);
-		msn_servconn_destroy(servconn);
-		return;
-	}
-
-	servconn->fd = source;
-
-	if (source >= 0)
-	{
-		servconn->connected = TRUE;
-
-		/* Someone wants to know we connected. */
-		servconn->connect_cb(servconn);
-		servconn->inpa = purple_input_add(servconn->fd, PURPLE_INPUT_READ,
-			read_cb, data);
-	}
-	else
-	{
-		purple_debug_error("msn", "Connection error: %s\n", error_message);
-		msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_CONNECT);
-	}
-}
-
-gboolean
-msn_servconn_connect(MsnServConn *servconn, const char *host, int port)
-{
-	MsnSession *session;
-
-	g_return_val_if_fail(servconn != NULL, FALSE);
-	g_return_val_if_fail(host     != NULL, FALSE);
-	g_return_val_if_fail(port      > 0,    FALSE);
-
-	session = servconn->session;
-
-	if (servconn->connected)
-		msn_servconn_disconnect(servconn);
-
-	g_free(servconn->host);
-	servconn->host = g_strdup(host);
-
-	if (session->http_method)
-	{
-		/* HTTP Connection. */
-
-		if (!servconn->httpconn->connected)
-			if (!msn_httpconn_connect(servconn->httpconn, host, port))
-				return FALSE;
-
-		servconn->connected = TRUE;
-		servconn->httpconn->virgin = TRUE;
-
-		/* Someone wants to know we connected. */
-		servconn->connect_cb(servconn);
-
-		return TRUE;
-	}
-
-	servconn->connect_data = purple_proxy_connect(NULL, session->account,
-			host, port, connect_cb, servconn);
-
-	if (servconn->connect_data != NULL)
-	{
-		servconn->processing = TRUE;
-		return TRUE;
-	}
-	else
-		return FALSE;
-}
-
-void
-msn_servconn_disconnect(MsnServConn *servconn)
-{
-	g_return_if_fail(servconn != NULL);
-
-	if (servconn->connect_data != NULL)
-	{
-		purple_proxy_connect_cancel(servconn->connect_data);
-		servconn->connect_data = NULL;
-	}
-
-	if (!servconn->connected)
-	{
-		/* We could not connect. */
-		if (servconn->disconnect_cb != NULL)
-			servconn->disconnect_cb(servconn);
-
-		return;
-	}
-
-	if (servconn->session->http_method)
-	{
-		/* Fake disconnection. */
-		if (servconn->disconnect_cb != NULL)
-			servconn->disconnect_cb(servconn);
-
-		return;
-	}
-
-	if (servconn->inpa > 0)
-	{
-		purple_input_remove(servconn->inpa);
-		servconn->inpa = 0;
-	}
-
-	close(servconn->fd);
-
-	servconn->rx_buf = NULL;
-	servconn->rx_len = 0;
-	servconn->payload_len = 0;
-
-	servconn->connected = FALSE;
-
-	if (servconn->disconnect_cb != NULL)
-		servconn->disconnect_cb(servconn);
-}
-
-static void
-servconn_write_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnServConn *servconn = data;
-	int ret, writelen;
-
-	writelen = purple_circ_buffer_get_max_read(servconn->tx_buf);
-
-	if (writelen == 0) {
-		purple_input_remove(servconn->tx_handler);
-		servconn->tx_handler = 0;
-		return;
-	}
-
-	ret = write(servconn->fd, servconn->tx_buf->outptr, writelen);
-
-	if (ret < 0 && errno == EAGAIN)
-		return;
-	else if (ret <= 0) {
-		msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_WRITE);
-		return;
-	}
-
-	purple_circ_buffer_mark_read(servconn->tx_buf, ret);
-}
-
-ssize_t
-msn_servconn_write(MsnServConn *servconn, const char *buf, size_t len)
-{
-	ssize_t ret = 0;
-
-	g_return_val_if_fail(servconn != NULL, 0);
-
-	if (!servconn->session->http_method)
-	{
-		if (servconn->tx_handler == 0) {
-			switch (servconn->type)
-			{
-				case MSN_SERVCONN_NS:
-				case MSN_SERVCONN_SB:
-					ret = write(servconn->fd, buf, len);
-					break;
-#if 0
-				case MSN_SERVCONN_DC:
-					ret = write(servconn->fd, &buf, sizeof(len));
-					ret = write(servconn->fd, buf, len);
-					break;
-#endif
-				default:
-					ret = write(servconn->fd, buf, len);
-					break;
-			}
-		} else {
-			ret = -1;
-			errno = EAGAIN;
-		}
-
-		if (ret < 0 && errno == EAGAIN)
-			ret = 0;
-		if (ret >= 0 && ret < len) {
-			if (servconn->tx_handler == 0)
-				servconn->tx_handler = purple_input_add(
-					servconn->fd, PURPLE_INPUT_WRITE,
-					servconn_write_cb, servconn);
-			purple_circ_buffer_append(servconn->tx_buf, buf + ret,
-				len - ret);
-		}
-	}
-	else
-	{
-		ret = msn_httpconn_write(servconn->httpconn, buf, len);
-	}
-
-	if (ret == -1)
-	{
-		msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_WRITE);
-	}
-
-	return ret;
-}
-
-static void
-read_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnServConn *servconn;
-	MsnSession *session;
-	char buf[MSN_BUF_LEN];
-	char *cur, *end, *old_rx_buf;
-	int len, cur_len;
-
-	servconn = data;
-	session = servconn->session;
-
-	len = read(servconn->fd, buf, sizeof(buf) - 1);
-	servconn->session->account->gc->last_received = time(NULL);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len <= 0)
-	{
-		purple_debug_error("msn", "servconn read error, len: %d error: %s\n", len, g_strerror(errno));
-		msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ);
-
-		return;
-	}
-
-	buf[len] = '\0';
-
-	servconn->rx_buf = g_realloc(servconn->rx_buf, len + servconn->rx_len + 1);
-	memcpy(servconn->rx_buf + servconn->rx_len, buf, len + 1);
-	servconn->rx_len += len;
-
-	end = old_rx_buf = servconn->rx_buf;
-
-	servconn->processing = TRUE;
-
-	do
-	{
-		cur = end;
-
-		if (servconn->payload_len)
-		{
-			if (servconn->payload_len > servconn->rx_len)
-				/* The payload is still not complete. */
-				break;
-
-			cur_len = servconn->payload_len;
-			end += cur_len;
-		}
-		else
-		{
-			end = strstr(cur, "\r\n");
-
-			if (end == NULL)
-				/* The command is still not complete. */
-				break;
-
-			*end = '\0';
-			end += 2;
-			cur_len = end - cur;
-		}
-
-		servconn->rx_len -= cur_len;
-
-		if (servconn->payload_len)
-		{
-			msn_cmdproc_process_payload(servconn->cmdproc, cur, cur_len);
-			servconn->payload_len = 0;
-		}
-		else
-		{
-			msn_cmdproc_process_cmd_text(servconn->cmdproc, cur);
-		}
-	} while (servconn->connected && !servconn->wasted && servconn->rx_len > 0);
-
-	if (servconn->connected && !servconn->wasted)
-	{
-		if (servconn->rx_len > 0)
-			servconn->rx_buf = g_memdup(cur, servconn->rx_len);
-		else
-			servconn->rx_buf = NULL;
-	}
-
-	servconn->processing = FALSE;
-
-	if (servconn->wasted)
-		msn_servconn_destroy(servconn);
-
-	g_free(old_rx_buf);
-}
-
-#if 0
-static int
-create_listener(int port)
-{
-	int fd;
-	int flags;
-	const int on = 1;
-
-#if 0
-	struct addrinfo hints;
-	struct addrinfo *c, *res;
-	char port_str[5];
-
-	snprintf(port_str, sizeof(port_str), "%d", port);
-
-	memset(&hints, 0, sizeof(hints));
-
-	hints.ai_flags = AI_PASSIVE;
-	hints.ai_family = AF_UNSPEC;
-	hints.ai_socktype = SOCK_STREAM;
-
-	if (getaddrinfo(NULL, port_str, &hints, &res) != 0)
-	{
-		purple_debug_error("msn", "Could not get address info: %s.\n",
-						 port_str);
-		return -1;
-	}
-
-	for (c = res; c != NULL; c = c->ai_next)
-	{
-		fd = socket(c->ai_family, c->ai_socktype, c->ai_protocol);
-
-		if (fd < 0)
-			continue;
-
-		setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
-		if (bind(fd, c->ai_addr, c->ai_addrlen) == 0)
-			break;
-
-		close(fd);
-	}
-
-	if (c == NULL)
-	{
-		purple_debug_error("msn", "Could not find socket: %s.\n", port_str);
-		return -1;
-	}
-
-	freeaddrinfo(res);
-#else
-	struct sockaddr_in sockin;
-
-	fd = socket(AF_INET, SOCK_STREAM, 0);
-
-	if (fd < 0)
-		return -1;
-
-	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0)
-	{
-		close(fd);
-		return -1;
-	}
-
-	memset(&sockin, 0, sizeof(struct sockaddr_in));
-	sockin.sin_family = AF_INET;
-	sockin.sin_port = htons(port);
-
-	if (bind(fd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0)
-	{
-		close(fd);
-		return -1;
-	}
-#endif
-
-	if (listen (fd, 4) != 0)
-	{
-		close (fd);
-		return -1;
-	}
-
-	flags = fcntl(fd, F_GETFL);
-	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-#ifndef _WIN32
-	fcntl(fd, F_SETFD, FD_CLOEXEC);
-#endif
-
-	return fd;
-}
-#endif
--- a/libpurple/protocols/msnp9/servconn.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-/**
- * @file servconn.h Server connection functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_SERVCONN_H_
-#define _MSN_SERVCONN_H_
-
-typedef struct _MsnServConn MsnServConn;
-
-#include "session.h"
-#include "cmdproc.h"
-
-#include "proxy.h"
-#include "httpconn.h"
-
-/**
- * Connection error types.
- */
-typedef enum
-{
-	MSN_SERVCONN_ERROR_NONE,
-	MSN_SERVCONN_ERROR_CONNECT,
-	MSN_SERVCONN_ERROR_WRITE,
-	MSN_SERVCONN_ERROR_READ,
-
-} MsnServConnError;
-
-/**
- * Connection types.
- */
-typedef enum
-{
-	MSN_SERVCONN_NS,
-	MSN_SERVCONN_SB
-
-} MsnServConnType;
-
-/**
- * A Connection.
- */
-struct _MsnServConn
-{
-	MsnServConnType type; /**< The type of this connection. */
-	MsnSession *session;  /**< The MSN session of this connection. */
-	MsnCmdProc *cmdproc;  /**< The command processor of this connection. */
-
-	PurpleProxyConnectData *connect_data;
-
-	gboolean connected;   /**< A flag that states if it's connected. */
-	gboolean processing;  /**< A flag that states if something is working
-							with this connection. */
-	gboolean wasted;      /**< A flag that states if it should be destroyed. */
-
-	char *host; /**< The host this connection is connected or should be
-				  connected to. */
-	int num; /**< A number id of this connection. */
-
-	MsnHttpConn *httpconn; /**< The HTTP connection this connection should use. */
-
-	int fd; /**< The connection's file descriptor. */
-	int inpa; /**< The connection's input handler. */
-
-	char *rx_buf; /**< The receive buffer. */
-	int rx_len; /**< The receive buffer lenght. */
-
-	size_t payload_len; /**< The length of the payload.
-						  It's only set when we've received a command that
-						  has a payload. */
-
-	PurpleCircBuffer *tx_buf;
-	guint tx_handler;
-
-	void (*connect_cb)(MsnServConn *); /**< The callback to call when connecting. */
-	void (*disconnect_cb)(MsnServConn *); /**< The callback to call when disconnecting. */
-	void (*destroy_cb)(MsnServConn *); /**< The callback to call when destroying. */
-};
-
-/**
- * Creates a new connection object.
- *
- * @param session The session.
- * @param type The type of the connection.
- */
-MsnServConn *msn_servconn_new(MsnSession *session, MsnServConnType type);
-
-/**
- * Destroys a connection object.
- *
- * @param servconn The connection.
- */
-void msn_servconn_destroy(MsnServConn *servconn);
-
-/**
- * Connects to a host.
- *
- * @param servconn The connection.
- * @param host The host.
- * @param port The port.
- */
-gboolean msn_servconn_connect(MsnServConn *servconn, const char *host, int port);
-
-/**
- * Disconnects.
- *
- * @param servconn The connection.
- */
-void msn_servconn_disconnect(MsnServConn *servconn);
-
-/**
- * Sets the connect callback.
- *
- * @param servconn The servconn.
- * @param connect_cb The connect callback.
- */
-void msn_servconn_set_connect_cb(MsnServConn *servconn,
-								 void (*connect_cb)(MsnServConn *));
-/**
- * Sets the disconnect callback.
- *
- * @param servconn The servconn.
- * @param disconnect_cb The disconnect callback.
- */
-void msn_servconn_set_disconnect_cb(MsnServConn *servconn,
-									void (*disconnect_cb)(MsnServConn *));
-/**
- * Sets the destroy callback.
- *
- * @param servconn The servconn that's being destroyed.
- * @param destroy_cb The destroy callback.
- */
-void msn_servconn_set_destroy_cb(MsnServConn *servconn,
-								 void (*destroy_cb)(MsnServConn *));
-
-/**
- * Writes a chunck of data to the servconn.
- *
- * @param servconn The servconn.
- * @param buf The data to write.
- * @param size The size of the data.
- */
-ssize_t msn_servconn_write(MsnServConn *servconn, const char *buf,
-						  size_t size);
-
-/**
- * Function to call whenever an error related to a switchboard occurs.
- *
- * @param servconn The servconn.
- * @param error The error that happened.
- */
-void msn_servconn_got_error(MsnServConn *servconn, MsnServConnError error);
-
-#endif /* _MSN_SERVCONN_H_ */
--- a/libpurple/protocols/msnp9/session.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,432 +0,0 @@
-/**
- * @file session.c MSN session functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "session.h"
-#include "notification.h"
-
-#include "dialog.h"
-
-MsnSession *
-msn_session_new(PurpleAccount *account)
-{
-	MsnSession *session;
-
-	g_return_val_if_fail(account != NULL, NULL);
-
-	session = g_new0(MsnSession, 1);
-
-	session->account = account;
-	session->notification = msn_notification_new(session);
-	session->userlist = msn_userlist_new(session);
-
-	session->user = msn_user_new(session->userlist,
-								 purple_account_get_username(account), NULL);
-
-	session->protocol_ver = 9;
-
-	return session;
-}
-
-void
-msn_session_destroy(MsnSession *session)
-{
-	g_return_if_fail(session != NULL);
-
-	session->destroying = TRUE;
-
-	if (session->connected)
-		msn_session_disconnect(session);
-
-	if (session->notification != NULL)
-		msn_notification_destroy(session->notification);
-
-	while (session->switches != NULL)
-		msn_switchboard_destroy(session->switches->data);
-
-	while (session->slplinks != NULL)
-		msn_slplink_destroy(session->slplinks->data);
-
-	msn_userlist_destroy(session->userlist);
-
-	g_free(session->passport_info.kv);
-	g_free(session->passport_info.sid);
-	g_free(session->passport_info.mspauth);
-	g_free(session->passport_info.client_ip);
-
-	if (session->passport_info.file != NULL)
-	{
-		g_unlink(session->passport_info.file);
-		g_free(session->passport_info.file);
-	}
-
-	if (session->sync != NULL)
-		msn_sync_destroy(session->sync);
-
-	if (session->nexus != NULL)
-		msn_nexus_destroy(session->nexus);
-
-	if (session->user != NULL)
-		msn_user_destroy(session->user);
-
-	g_free(session);
-}
-
-gboolean
-msn_session_connect(MsnSession *session, const char *host, int port,
-					gboolean http_method)
-{
-	g_return_val_if_fail(session != NULL, FALSE);
-	g_return_val_if_fail(!session->connected, TRUE);
-
-	session->connected = TRUE;
-	session->http_method = http_method;
-
-	if (session->notification == NULL)
-	{
-		purple_debug_error("msn", "This shouldn't happen\n");
-		g_return_val_if_reached(FALSE);
-	}
-
-	if (msn_notification_connect(session->notification, host, port))
-	{
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-void
-msn_session_disconnect(MsnSession *session)
-{
-	g_return_if_fail(session != NULL);
-	g_return_if_fail(session->connected);
-
-	session->connected = FALSE;
-
-	while (session->switches != NULL)
-		msn_switchboard_close(session->switches->data);
-
-	if (session->notification != NULL)
-		msn_notification_close(session->notification);
-}
-
-/* TODO: This must go away when conversation is redesigned */
-MsnSwitchBoard *
-msn_session_find_swboard(MsnSession *session, const char *username)
-{
-	GList *l;
-
-	g_return_val_if_fail(session  != NULL, NULL);
-	g_return_val_if_fail(username != NULL, NULL);
-
-	for (l = session->switches; l != NULL; l = l->next)
-	{
-		MsnSwitchBoard *swboard;
-
-		swboard = l->data;
-
-		if ((swboard->im_user != NULL) && !strcmp(username, swboard->im_user))
-			return swboard;
-	}
-
-	return NULL;
-}
-
-MsnSwitchBoard *
-msn_session_find_swboard_with_conv(MsnSession *session, PurpleConversation *conv)
-{
-	GList *l;
-
-	g_return_val_if_fail(session  != NULL, NULL);
-	g_return_val_if_fail(conv != NULL, NULL);
-
-	for (l = session->switches; l != NULL; l = l->next)
-	{
-		MsnSwitchBoard *swboard;
-
-		swboard = l->data;
-
-		if (swboard->conv == conv)
-			return swboard;
-	}
-
-	return NULL;
-}
-
-MsnSwitchBoard *
-msn_session_find_swboard_with_id(const MsnSession *session, int chat_id)
-{
-	GList *l;
-
-	g_return_val_if_fail(session != NULL, NULL);
-	g_return_val_if_fail(chat_id >= 0,    NULL);
-
-	for (l = session->switches; l != NULL; l = l->next)
-	{
-		MsnSwitchBoard *swboard;
-
-		swboard = l->data;
-
-		if (swboard->chat_id == chat_id)
-			return swboard;
-	}
-
-	return NULL;
-}
-
-MsnSwitchBoard *
-msn_session_get_swboard(MsnSession *session, const char *username,
-						MsnSBFlag flag)
-{
-	MsnSwitchBoard *swboard;
-
-	g_return_val_if_fail(session != NULL, NULL);
-
-	swboard = msn_session_find_swboard(session, username);
-
-	if (swboard == NULL)
-	{
-		swboard = msn_switchboard_new(session);
-		swboard->im_user = g_strdup(username);
-		msn_switchboard_request(swboard);
-		msn_switchboard_request_add_user(swboard, username);
-	}
-
-	swboard->flag |= flag;
-
-	return swboard;
-}
-
-static void
-msn_session_sync_users(MsnSession *session)
-{
-	PurpleConnection *gc = purple_account_get_connection(session->account);
-	GList *to_remove = NULL;
-	GSList *buddies;
-
-	g_return_if_fail(gc != NULL);
-
-	/* The core used to use msn_add_buddy to add all buddies before
-	 * being logged in. This no longer happens, so we manually iterate
-	 * over the whole buddy list to identify sync issues. */
-	for (buddies = purple_find_buddies(session->account, NULL); buddies;
-			buddies = g_slist_delete_link(buddies, buddies)) {
-		PurpleBuddy *buddy = buddies->data;
-		const char *buddy_name = purple_buddy_get_name(buddy);
-		const char *group_name = purple_group_get_name(purple_buddy_get_group(buddy));
-		MsnUser *remote_user;
-		gboolean found = FALSE;
-
-		remote_user = msn_userlist_find_user(session->userlist, buddy_name);
-
-		if (remote_user && remote_user->list_op & MSN_LIST_FL_OP) {
-			int group_id;
-			GList *l;
-
-			group_id = msn_userlist_find_group_id(remote_user->userlist,
-					group_name);
-
-			for (l = remote_user->group_ids; l; l = l->next) {
-				if (group_id == GPOINTER_TO_INT(l->data)) {
-					found = TRUE;
-					break;
-				}
-			}
-
-			/* We don't care if they're in a different group, as long as they're on the
-			 * list somewhere. If we check for the group, we cause pain, agony and
-			 * suffering for people who decide to re-arrange their buddy list elsewhere.
-			 */
-			if (!found)
-			{
-				if ((remote_user == NULL) || !(remote_user->list_op & MSN_LIST_FL_OP)) {
-					/* The user is not on the server list */
-					msn_show_sync_issue(session, buddy_name, group_name);
-				} else {
-					/* The user is not in that group on the server list */
-					to_remove = g_list_prepend(to_remove, buddy);
-				}
-			}
-		}
-	}
-
-	if (to_remove != NULL) {
-		g_list_foreach(to_remove, (GFunc)purple_blist_remove_buddy, NULL);
-		g_list_free(to_remove);
-	}
-}
-
-void
-msn_session_set_error(MsnSession *session, MsnErrorType error,
-					  const char *info)
-{
-	PurpleConnection *gc;
-	PurpleConnectionError reason;
-	char *msg;
-
-	gc = purple_account_get_connection(session->account);
-
-	switch (error)
-	{
-		case MSN_ERROR_SERVCONN:
-			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-			msg = g_strdup(info);
-			break;
-		case MSN_ERROR_UNSUPPORTED_PROTOCOL:
-			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-			msg = g_strdup(_("Our protocol is not supported by the "
-							 "server"));
-			break;
-		case MSN_ERROR_HTTP_MALFORMED:
-			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-			msg = g_strdup(_("Error parsing HTTP"));
-			break;
-		case MSN_ERROR_SIGN_OTHER:
-			reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
-			msg = g_strdup(_("You have signed on from another location"));
-			if (!purple_account_get_remember_password(session->account))
-				purple_account_set_password(session->account, NULL);
-			break;
-		case MSN_ERROR_SERV_UNAVAILABLE:
-			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-			msg = g_strdup(_("The MSN servers are temporarily "
-							 "unavailable. Please wait and try "
-							 "again."));
-			break;
-		case MSN_ERROR_SERV_DOWN:
-			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-			msg = g_strdup(_("The MSN servers are going down "
-							 "temporarily"));
-			break;
-		case MSN_ERROR_AUTH:
-			reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
-			msg = g_strdup_printf(_("Unable to authenticate: %s"),
-								  (info == NULL ) ?
-								  _("Unknown error") : info);
-			break;
-		case MSN_ERROR_BAD_BLIST:
-			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-			msg = g_strdup(_("Your MSN buddy list is temporarily "
-							 "unavailable. Please wait and try "
-							 "again."));
-			break;
-		default:
-			reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-			msg = g_strdup(_("Unknown error"));
-			break;
-	}
-
-	msn_session_disconnect(session);
-
-	purple_connection_error_reason(gc, reason, msg);
-
-	g_free(msg);
-}
-
-static const char *
-get_login_step_text(MsnSession *session)
-{
-	const char *steps_text[] = {
-		_("Connecting"),
-		_("Handshaking"),
-		_("Transferring"),
-		_("Handshaking"),
-		_("Starting authentication"),
-		_("Getting cookie"),
-		_("Authenticating"),
-		_("Sending cookie"),
-		_("Retrieving buddy list")
-	};
-
-	return steps_text[session->login_step];
-}
-
-void
-msn_session_set_login_step(MsnSession *session, MsnLoginStep step)
-{
-	PurpleConnection *gc;
-
-	/* Prevent the connection progress going backwards, eg. if we get
-	 * transferred several times during login */
-	if (session->login_step > step)
-		return;
-
-	/* If we're already logged in, we're probably here because of a
-	 * mid-session XFR from the notification server, so we don't want to
-	 * popup the connection progress dialog */
-	if (session->logged_in)
-		return;
-
-	gc = session->account->gc;
-
-	session->login_step = step;
-
-	purple_connection_update_progress(gc, get_login_step_text(session), step,
-									MSN_LOGIN_STEPS);
-}
-
-void
-msn_session_finish_login(MsnSession *session)
-{
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	PurpleStoredImage *img;
-	const char *passport;
-
-	if (session->logged_in) {
-		/* We are probably here because of a mid-session notification server XFR
-		 * We must send a CHG now, otherwise the servers default to invisible,
-		 * and prevent things happening, like sending IMs */
-		msn_change_status(session);
-		return;
-	}
-
-	account = session->account;
-	gc = purple_account_get_connection(account);
-
-	img = purple_buddy_icons_find_account_icon(session->account);
-	msn_user_set_buddy_icon(session->user, img);
-	purple_imgstore_unref(img);
-
-	session->logged_in = TRUE;
-
-	msn_change_status(session);
-
-	purple_connection_set_state(gc, PURPLE_CONNECTED);
-
-	/* Sync users */
-	msn_session_sync_users(session);
-	/* It seems that some accounts that haven't accessed hotmail for a while
-	 * and @msn.com accounts don't automatically get the initial email
-	 * notification so we always request it on login
-	 */
-
-	passport = purple_normalize(account, purple_account_get_username(account));
-
-	if ((strstr(passport, "@hotmail.") != NULL) ||
-		(strstr(passport, "@msn.com") != NULL))
-	{
-		msn_cmdproc_send(session->notification->cmdproc, "URL", "%s", "INBOX");
-	}
-}
--- a/libpurple/protocols/msnp9/session.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,225 +0,0 @@
-/**
- * @file session.h MSN session functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_SESSION_H_
-#define _MSN_SESSION_H_
-
-typedef struct _MsnSession MsnSession;
-
-#include "sslconn.h"
-
-#include "user.h"
-#include "slpcall.h"
-
-#include "notification.h"
-#include "switchboard.h"
-#include "group.h"
-
-#include "cmdproc.h"
-#include "nexus.h"
-#include "httpconn.h"
-
-#include "userlist.h"
-#include "sync.h"
-
-/**
- * Types of errors.
- */
-typedef enum
-{
-	MSN_ERROR_SERVCONN,
-	MSN_ERROR_UNSUPPORTED_PROTOCOL,
-	MSN_ERROR_HTTP_MALFORMED,
-	MSN_ERROR_AUTH,
-	MSN_ERROR_BAD_BLIST,
-	MSN_ERROR_SIGN_OTHER,
-	MSN_ERROR_SERV_DOWN,
-	MSN_ERROR_SERV_UNAVAILABLE
-
-} MsnErrorType;
-
-/**
- * Login steps.
- */
-typedef enum
-{
-	MSN_LOGIN_STEP_START,
-	MSN_LOGIN_STEP_HANDSHAKE,
-	MSN_LOGIN_STEP_TRANSFER,
-	MSN_LOGIN_STEP_HANDSHAKE2,
-	MSN_LOGIN_STEP_AUTH_START,
-	MSN_LOGIN_STEP_AUTH,
-	MSN_LOGIN_STEP_GET_COOKIE,
-	MSN_LOGIN_STEP_AUTH_END,
-	MSN_LOGIN_STEP_SYN,
-	MSN_LOGIN_STEP_END
-
-} MsnLoginStep;
-
-#define MSN_LOGIN_STEPS MSN_LOGIN_STEP_END
-
-struct _MsnSession
-{
-	PurpleAccount *account;
-	MsnUser *user;
-
-	guint protocol_ver;
-
-	MsnLoginStep login_step; /**< The current step in the login process. */
-
-	gboolean connected;
-	gboolean logged_in; /**< A temporal flag to ignore local buddy list adds. */
-	gboolean destroying; /**< A flag that states if the session is being destroyed. */
-	gboolean http_method;
-
-	MsnNotification *notification;
-	MsnNexus *nexus;
-	MsnSync *sync;
-
-	MsnUserList *userlist;
-
-	int servconns_count; /**< The count of server connections. */
-	GList *switches; /**< The list of all the switchboards. */
-	GList *directconns; /**< The list of all the directconnections. */
-	GList *slplinks; /**< The list of all the slplinks. */
-
-	struct
-	{
-		char *kv;
-		char *sid;
-		char *mspauth;
-		unsigned long sl;
-		char *file;
-		char *client_ip;
-		int client_port;
-		int verified;
-	} passport_info;
-};
-
-/**
- * Creates an MSN session.
- *
- * @param account The account.
- *
- * @return The new MSN session.
- */
-MsnSession *msn_session_new(PurpleAccount *account);
-
-/**
- * Destroys an MSN session.
- *
- * @param session The MSN session to destroy.
- */
-void msn_session_destroy(MsnSession *session);
-
-/**
- * Connects to and initiates an MSN session.
- *
- * @param session     The MSN session.
- * @param host        The dispatch server host.
- * @param port        The dispatch server port.
- * @param http_method Whether to use or not http_method.
- *
- * @return @c TRUE on success, @c FALSE on failure.
- */
-gboolean msn_session_connect(MsnSession *session,
-							 const char *host, int port,
-							 gboolean http_method);
-
-/**
- * Disconnects from an MSN session.
- *
- * @param session The MSN session.
- */
-void msn_session_disconnect(MsnSession *session);
-
- /**
- * Finds a switchboard with the given username.
- *
- * @param session The MSN session.
- * @param username The username to search for.
- *
- * @return The switchboard, if found.
- */
-MsnSwitchBoard *msn_session_find_swboard(MsnSession *session,
-										 const char *username);
-
- /**
- * Finds a switchboard with the given conversation.
- *
- * @param session The MSN session.
- * @param conv    The conversation to search for.
- *
- * @return The switchboard, if found.
- */
-MsnSwitchBoard *msn_session_find_swboard_with_conv(MsnSession *session,
-												   PurpleConversation *conv);
-/**
- * Finds a switchboard with the given chat ID.
- *
- * @param session The MSN session.
- * @param chat_id The chat ID to search for.
- *
- * @return The switchboard, if found.
- */
-MsnSwitchBoard *msn_session_find_swboard_with_id(const MsnSession *session,
-												 int chat_id);
-
-/**
- * Returns a switchboard to communicate with certain username.
- *
- * @param session The MSN session.
- * @param username The username to search for.
- * @param flag The flag of the switchboard
- *
- * @return The switchboard.
- */
-MsnSwitchBoard *msn_session_get_swboard(MsnSession *session,
-										const char *username, MsnSBFlag flag);
-
-/**
- * Sets an error for the MSN session.
- *
- * @param session The MSN session.
- * @param error The error.
- * @param info Extra information.
- */
-void msn_session_set_error(MsnSession *session, MsnErrorType error,
-						   const char *info);
-
-/**
- * Sets the current step in the login proccess.
- *
- * @param session The MSN session.
- * @param step The current step.
- */
-void msn_session_set_login_step(MsnSession *session, MsnLoginStep step);
-
-/**
- * Finish the login proccess.
- *
- * @param session The MSN session.
- */
-void msn_session_finish_login(MsnSession *session);
-
-#endif /* _MSN_SESSION_H_ */
--- a/libpurple/protocols/msnp9/slp.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1122 +0,0 @@
-/**
- * @file msnslp.c MSNSLP support
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "slp.h"
-#include "slpcall.h"
-#include "slpmsg.h"
-#include "slpsession.h"
-
-#include "object.h"
-#include "user.h"
-#include "switchboard.h"
-
-#include "smiley.h"
-
-/* Seconds to delay between sending buddy icon requests to the server. */
-#define BUDDY_ICON_DELAY 20
-
-static void send_ok(MsnSlpCall *slpcall, const char *branch,
-					const char *type, const char *content);
-
-static void send_decline(MsnSlpCall *slpcall, const char *branch,
-						 const char *type, const char *content);
-
-void msn_request_user_display(MsnUser *user);
-
-/**************************************************************************
- * Util
- **************************************************************************/
-
-static char *
-get_token(const char *str, const char *start, const char *end)
-{
-	const char *c, *c2;
-
-	if ((c = strstr(str, start)) == NULL)
-		return NULL;
-
-	c += strlen(start);
-
-	if (end != NULL)
-	{
-		if ((c2 = strstr(c, end)) == NULL)
-			return NULL;
-
-		return g_strndup(c, c2 - c);
-	}
-	else
-	{
-		/* This has to be changed */
-		return g_strdup(c);
-	}
-
-}
-
-/**************************************************************************
- * Xfer
- **************************************************************************/
-
-static void
-msn_xfer_init(PurpleXfer *xfer)
-{
-	MsnSlpCall *slpcall;
-	/* MsnSlpLink *slplink; */
-	char *content;
-
-	purple_debug_info("msn", "xfer_init\n");
-
-	slpcall = xfer->data;
-
-	/* Send Ok */
-	content = g_strdup_printf("SessionID: %lu\r\n\r\n",
-							  slpcall->session_id);
-
-	send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
-			content);
-
-	g_free(content);
-	msn_slplink_unleash(slpcall->slplink);
-}
-
-void
-msn_xfer_cancel(PurpleXfer *xfer)
-{
-	MsnSlpCall *slpcall;
-	char *content;
-
-	g_return_if_fail(xfer != NULL);
-	g_return_if_fail(xfer->data != NULL);
-
-	slpcall = xfer->data;
-
-	if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
-	{
-		if (slpcall->started)
-		{
-			msn_slp_call_close(slpcall);
-		}
-		else
-		{
-			content = g_strdup_printf("SessionID: %lu\r\n\r\n",
-									slpcall->session_id);
-
-			send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
-						content);
-
-			g_free(content);
-			msn_slplink_unleash(slpcall->slplink);
-
-			msn_slp_call_destroy(slpcall);
-		}
-	}
-}
-
-void
-msn_xfer_progress_cb(MsnSlpCall *slpcall, gsize total_length, gsize len, gsize offset)
-{
-	PurpleXfer *xfer;
-
-	xfer = slpcall->xfer;
-
-	xfer->bytes_sent = (offset + len);
-	xfer->bytes_remaining = total_length - (offset + len);
-
-	purple_xfer_update_progress(xfer);
-}
-
-void
-msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session)
-{
-	if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) &&
-		(purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) &&
-		(purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL))
-	{
-		purple_xfer_cancel_remote(slpcall->xfer);
-	}
-}
-
-void
-msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
-					  gsize size)
-{
-	PurpleXfer *xfer = slpcall->xfer;
-	purple_xfer_set_completed(xfer, TRUE);
-	purple_xfer_end(xfer);
-}
-
-/**************************************************************************
- * SLP Control
- **************************************************************************/
-
-#if 0
-static void
-got_transresp(MsnSlpCall *slpcall, const char *nonce,
-			  const char *ips_str, int port)
-{
-	MsnDirectConn *directconn;
-	char **ip_addrs, **c;
-
-	directconn = msn_directconn_new(slpcall->slplink);
-
-	directconn->initial_call = slpcall;
-
-	/* msn_directconn_parse_nonce(directconn, nonce); */
-	directconn->nonce = g_strdup(nonce);
-
-	ip_addrs = g_strsplit(ips_str, " ", -1);
-
-	for (c = ip_addrs; *c != NULL; c++)
-	{
-		purple_debug_info("msn", "ip_addr = %s\n", *c);
-		if (msn_directconn_connect(directconn, *c, port))
-			break;
-	}
-
-	g_strfreev(ip_addrs);
-}
-#endif
-
-static void
-send_ok(MsnSlpCall *slpcall, const char *branch,
-		const char *type, const char *content)
-{
-	MsnSlpLink *slplink;
-	MsnSlpMessage *slpmsg;
-
-	slplink = slpcall->slplink;
-
-	/* 200 OK */
-	slpmsg = msn_slpmsg_sip_new(slpcall, 1,
-								"MSNSLP/1.0 200 OK",
-								branch, type, content);
-
-#ifdef MSN_DEBUG_SLP
-	slpmsg->info = "SLP 200 OK";
-	slpmsg->text_body = TRUE;
-#endif
-
-	msn_slplink_queue_slpmsg(slplink, slpmsg);
-
-	msn_slp_call_session_init(slpcall);
-}
-
-static void
-send_decline(MsnSlpCall *slpcall, const char *branch,
-			 const char *type, const char *content)
-{
-	MsnSlpLink *slplink;
-	MsnSlpMessage *slpmsg;
-
-	slplink = slpcall->slplink;
-
-	/* 603 Decline */
-	slpmsg = msn_slpmsg_sip_new(slpcall, 1,
-								"MSNSLP/1.0 603 Decline",
-								branch, type, content);
-
-#ifdef MSN_DEBUG_SLP
-	slpmsg->info = "SLP 603 Decline";
-	slpmsg->text_body = TRUE;
-#endif
-
-	msn_slplink_queue_slpmsg(slplink, slpmsg);
-}
-
-#define MAX_FILE_NAME_LEN 0x226
-
-static void
-got_sessionreq(MsnSlpCall *slpcall, const char *branch,
-			   const char *euf_guid, const char *context)
-{
-	if (!strcmp(euf_guid, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6"))
-	{
-		/* Emoticon or UserDisplay */
-		char *content;
-		gsize len;
-		MsnSlpSession *slpsession;
-		MsnSlpLink *slplink;
-		MsnSlpMessage *slpmsg;
-		MsnObject *obj;
-		char *msnobj_data;
-		PurpleStoredImage *img;
-		int type;
-
-		/* Send Ok */
-		content = g_strdup_printf("SessionID: %lu\r\n\r\n",
-								  slpcall->session_id);
-
-		send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
-				content);
-
-		g_free(content);
-
-		slplink = slpcall->slplink;
-
-		msnobj_data = (char *)purple_base64_decode(context, &len);
-		obj = msn_object_new_from_string(msnobj_data);
-		type = msn_object_get_type(obj);
-		g_free(msnobj_data);
-
-		if ((type != MSN_OBJECT_USERTILE) && (type != MSN_OBJECT_EMOTICON))
-		{
-			purple_debug_error("msn", "Wrong object?\n");
-			msn_object_destroy(obj);
-			g_return_if_reached();
-		}
-
-		if (type == MSN_OBJECT_EMOTICON) {
-			char *path;
-			path = g_build_filename(purple_smileys_get_storing_dir(),
-					obj->location, NULL);
-			img = purple_imgstore_new_from_file(path);
-			g_free(path);
-		} else {
-			img = msn_object_get_image(obj);
-			if (img)
-				purple_imgstore_ref(img);
-		}
-		msn_object_destroy(obj);
-
-		if (img == NULL)
-		{
-			purple_debug_error("msn", "Wrong object.\n");
-			g_return_if_reached();
-		}
-
-		slpsession = msn_slplink_find_slp_session(slplink,
-												  slpcall->session_id);
-
-		/* DATA PREP */
-		slpmsg = msn_slpmsg_new(slplink);
-		slpmsg->slpcall = slpcall;
-		slpmsg->slpsession = slpsession;
-		slpmsg->session_id = slpsession->id;
-		msn_slpmsg_set_body(slpmsg, NULL, 4);
-#ifdef MSN_DEBUG_SLP
-		slpmsg->info = "SLP DATA PREP";
-#endif
-		msn_slplink_queue_slpmsg(slplink, slpmsg);
-
-		/* DATA */
-		slpmsg = msn_slpmsg_new(slplink);
-		slpmsg->slpcall = slpcall;
-		slpmsg->slpsession = slpsession;
-		slpmsg->flags = 0x20;
-#ifdef MSN_DEBUG_SLP
-		slpmsg->info = "SLP DATA";
-#endif
-		msn_slpmsg_set_image(slpmsg, img);
-		msn_slplink_queue_slpmsg(slplink, slpmsg);
-		purple_imgstore_unref(img);
-	}
-	else if (!strcmp(euf_guid, "5D3E02AB-6190-11D3-BBBB-00C04F795683"))
-	{
-		/* File Transfer */
-		PurpleAccount *account;
-		PurpleXfer *xfer;
-		char *bin;
-		gsize bin_len;
-		guint32 file_size;
-		gchar *file_name;
-		gunichar2 *uni_name;
-
-		account = slpcall->slplink->session->account;
-
-		slpcall->cb = msn_xfer_completed_cb;
-		slpcall->end_cb = msn_xfer_end_cb;
-		slpcall->progress_cb = msn_xfer_progress_cb;
-		slpcall->branch = g_strdup(branch);
-
-		slpcall->pending = TRUE;
-
-		xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE,
-							 slpcall->slplink->remote_user);
-		if (xfer)
-		{
-			bin = (char *)purple_base64_decode(context, &bin_len);
-			file_size = GUINT32_FROM_LE(*(gsize *)(bin + 8));
-
-			uni_name = (gunichar2 *)(bin + 20);
-			while(*uni_name != 0 && ((char *)uni_name - (bin + 20)) < MAX_FILE_NAME_LEN) {
-				*uni_name = GUINT16_FROM_LE(*uni_name);
-				uni_name++;
-			}
-
-			file_name = g_utf16_to_utf8((const gunichar2 *)(bin + 20), -1,
-										NULL, NULL, NULL);
-
-			g_free(bin);
-
-			purple_xfer_set_filename(xfer, file_name ? file_name : "");
-			g_free(file_name);
-			purple_xfer_set_size(xfer, file_size);
-			purple_xfer_set_init_fnc(xfer, msn_xfer_init);
-			purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
-			purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
-
-			slpcall->xfer = xfer;
-			purple_xfer_ref(slpcall->xfer);
-
-			xfer->data = slpcall;
-
-			purple_xfer_request(xfer);
-		}
-	}
-}
-
-void
-send_bye(MsnSlpCall *slpcall, const char *type)
-{
-	MsnSlpLink *slplink;
-	MsnSlpMessage *slpmsg;
-	char *header;
-
-	slplink = slpcall->slplink;
-
-	g_return_if_fail(slplink != NULL);
-
-	header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0",
-							 slplink->local_user);
-
-	slpmsg = msn_slpmsg_sip_new(slpcall, 0, header,
-								"A0D624A6-6C0C-4283-A9E0-BC97B4B46D32",
-								type,
-								"\r\n");
-	g_free(header);
-
-#ifdef MSN_DEBUG_SLP
-	slpmsg->info = "SLP BYE";
-	slpmsg->text_body = TRUE;
-#endif
-
-	msn_slplink_queue_slpmsg(slplink, slpmsg);
-}
-
-static void
-got_invite(MsnSlpCall *slpcall,
-		   const char *branch, const char *type, const char *content)
-{
-	MsnSlpLink *slplink;
-
-	slplink = slpcall->slplink;
-
-	if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
-	{
-		char *euf_guid, *context;
-		char *temp;
-
-		euf_guid = get_token(content, "EUF-GUID: {", "}\r\n");
-
-		temp = get_token(content, "SessionID: ", "\r\n");
-		if (temp != NULL)
-			slpcall->session_id = atoi(temp);
-		g_free(temp);
-
-		temp = get_token(content, "AppID: ", "\r\n");
-		if (temp != NULL)
-			slpcall->app_id = atoi(temp);
-		g_free(temp);
-
-		context = get_token(content, "Context: ", "\r\n");
-
-		if (context != NULL)
-			got_sessionreq(slpcall, branch, euf_guid, context);
-
-		g_free(context);
-		g_free(euf_guid);
-	}
-	else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
-	{
-		/* A direct connection? */
-
-		char *listening, *nonce;
-		char *content;
-
-		if (FALSE)
-		{
-#if 0
-			MsnDirectConn *directconn;
-			/* const char *ip_addr; */
-			char *ip_port;
-			int port;
-
-			/* ip_addr = purple_prefs_get_string("/purple/ft/public_ip"); */
-			ip_port = "5190";
-			listening = "true";
-			nonce = rand_guid();
-
-			directconn = msn_directconn_new(slplink);
-
-			/* msn_directconn_parse_nonce(directconn, nonce); */
-			directconn->nonce = g_strdup(nonce);
-
-			msn_directconn_listen(directconn);
-
-			port = directconn->port;
-
-			content = g_strdup_printf(
-				"Bridge: TCPv1\r\n"
-				"Listening: %s\r\n"
-				"Nonce: {%s}\r\n"
-				"Ipv4Internal-Addrs: 192.168.0.82\r\n"
-				"Ipv4Internal-Port: %d\r\n"
-				"\r\n",
-				listening,
-				nonce,
-				port);
-#endif
-		}
-		else
-		{
-			listening = "false";
-			nonce = g_strdup("00000000-0000-0000-0000-000000000000");
-
-			content = g_strdup_printf(
-				"Bridge: TCPv1\r\n"
-				"Listening: %s\r\n"
-				"Nonce: {%s}\r\n"
-				"\r\n",
-				listening,
-				nonce);
-		}
-
-		send_ok(slpcall, branch,
-				"application/x-msnmsgr-transrespbody", content);
-
-		g_free(content);
-		g_free(nonce);
-	}
-	else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
-	{
-#if 0
-		char *ip_addrs;
-		char *temp;
-		char *nonce;
-		int port;
-
-		nonce = get_token(content, "Nonce: {", "}\r\n");
-		ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
-
-		temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
-		if (temp != NULL)
-			port = atoi(temp);
-		else
-			port = -1;
-		g_free(temp);
-
-		if (ip_addrs == NULL)
-			return;
-
-		if (port > 0)
-			got_transresp(slpcall, nonce, ip_addrs, port);
-
-		g_free(nonce);
-		g_free(ip_addrs);
-#endif
-	}
-}
-
-static void
-got_ok(MsnSlpCall *slpcall,
-	   const char *type, const char *content)
-{
-	g_return_if_fail(slpcall != NULL);
-	g_return_if_fail(type    != NULL);
-
-	if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
-	{
-#if 0
-		if (slpcall->type == MSN_SLPCALL_DC)
-		{
-			/* First let's try a DirectConnection. */
-
-			MsnSlpLink *slplink;
-			MsnSlpMessage *slpmsg;
-			char *header;
-			char *content;
-			char *branch;
-
-			slplink = slpcall->slplink;
-
-			branch = rand_guid();
-
-			content = g_strdup_printf(
-				"Bridges: TRUDPv1 TCPv1\r\n"
-				"NetID: 0\r\n"
-				"Conn-Type: Direct-Connect\r\n"
-				"UPnPNat: false\r\n"
-				"ICF: false\r\n"
-			);
-
-			header = g_strdup_printf("INVITE MSNMSGR:%s MSNSLP/1.0",
-									 slplink->remote_user);
-
-			slpmsg = msn_slp_sipmsg_new(slpcall, 0, header, branch,
-										"application/x-msnmsgr-transreqbody",
-										content);
-
-#ifdef MSN_DEBUG_SLP
-			slpmsg->info = "SLP INVITE";
-			slpmsg->text_body = TRUE;
-#endif
-			msn_slplink_send_slpmsg(slplink, slpmsg);
-
-			g_free(header);
-			g_free(content);
-
-			g_free(branch);
-		}
-		else
-		{
-			msn_slp_call_session_init(slpcall);
-		}
-#else
-		msn_slp_call_session_init(slpcall);
-#endif
-	}
-	else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
-	{
-		/* Do we get this? */
-		purple_debug_info("msn", "OK with transreqbody\n");
-	}
-	else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
-	{
-#if 0
-		char *ip_addrs;
-		char *temp;
-		char *nonce;
-		int port;
-
-		nonce = get_token(content, "Nonce: {", "}\r\n");
-		ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
-
-		temp = get_token(content, "IPv4Internal-Port: ", "\r\n");
-		if (temp != NULL)
-			port = atoi(temp);
-		else
-			port = -1;
-		g_free(temp);
-
-		if (ip_addrs == NULL)
-			return;
-
-		if (port > 0)
-			got_transresp(slpcall, nonce, ip_addrs, port);
-
-		g_free(nonce);
-		g_free(ip_addrs);
-#endif
-	}
-}
-
-MsnSlpCall *
-msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
-{
-	MsnSlpCall *slpcall;
-
-	if (body == NULL)
-	{
-		purple_debug_warning("msn", "received bogus message\n");
-		return NULL;
-	}
-
-	if (!strncmp(body, "INVITE", strlen("INVITE")))
-	{
-		char *branch;
-		char *content;
-		char *content_type;
-
-		slpcall = msn_slp_call_new(slplink);
-
-		/* From: <msnmsgr:buddy@hotmail.com> */
-#if 0
-		slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n");
-#endif
-
-		branch = get_token(body, ";branch={", "}");
-
-		slpcall->id = get_token(body, "Call-ID: {", "}");
-
-#if 0
-		long content_len = -1;
-
-		temp = get_token(body, "Content-Length: ", "\r\n");
-		if (temp != NULL)
-			content_len = atoi(temp);
-		g_free(temp);
-#endif
-		content_type = get_token(body, "Content-Type: ", "\r\n");
-
-		content = get_token(body, "\r\n\r\n", NULL);
-
-		got_invite(slpcall, branch, content_type, content);
-
-		g_free(branch);
-		g_free(content_type);
-		g_free(content);
-	}
-	else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 ")))
-	{
-		char *content;
-		char *content_type;
-		/* Make sure this is "OK" */
-		const char *status = body + strlen("MSNSLP/1.0 ");
-		char *call_id;
-
-		call_id = get_token(body, "Call-ID: {", "}");
-		slpcall = msn_slplink_find_slp_call(slplink, call_id);
-		g_free(call_id);
-
-		g_return_val_if_fail(slpcall != NULL, NULL);
-
-		if (strncmp(status, "200 OK", 6))
-		{
-			/* It's not valid. Kill this off. */
-			char temp[32];
-			const char *c;
-
-			/* Eww */
-			if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) ||
-				(c = strchr(status, '\0')))
-			{
-				size_t offset =  c - status;
-				if (offset >= sizeof(temp))
-					offset = sizeof(temp) - 1;
-
-				strncpy(temp, status, offset);
-				temp[offset] = '\0';
-			}
-
-			purple_debug_error("msn", "Received non-OK result: %s\n", temp);
-
-			slpcall->wasted = TRUE;
-
-			/* msn_slp_call_destroy(slpcall); */
-			return slpcall;
-		}
-
-		content_type = get_token(body, "Content-Type: ", "\r\n");
-
-		content = get_token(body, "\r\n\r\n", NULL);
-
-		got_ok(slpcall, content_type, content);
-
-		g_free(content_type);
-		g_free(content);
-	}
-	else if (!strncmp(body, "BYE", strlen("BYE")))
-	{
-		char *call_id;
-
-		call_id = get_token(body, "Call-ID: {", "}");
-		slpcall = msn_slplink_find_slp_call(slplink, call_id);
-		g_free(call_id);
-
-		if (slpcall != NULL)
-			slpcall->wasted = TRUE;
-
-		/* msn_slp_call_destroy(slpcall); */
-	}
-	else
-		slpcall = NULL;
-
-	return slpcall;
-}
-
-/**************************************************************************
- * Msg Callbacks
- **************************************************************************/
-
-void
-msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	MsnSession *session;
-	MsnSlpLink *slplink;
-
-	session = cmdproc->servconn->session;
-	slplink = msn_session_get_slplink(session, msg->remote_user);
-
-	if (slplink->swboard == NULL)
-	{
-		/* We will need this in order to change its flags. */
-		slplink->swboard = (MsnSwitchBoard *)cmdproc->data;
-		/* If swboard is NULL, something has probably gone wrong earlier on
-		 * I didn't want to do this, but MSN 7 is somehow causing us to crash
-		 * here, I couldn't reproduce it to debug more, and people are
-		 * reporting bugs. Hopefully this doesn't cause more crashes. Stu.
-		 */
-		if (slplink->swboard != NULL)
-			slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
-		else
-			purple_debug_error("msn", "msn_p2p_msg, swboard is NULL, ouch!\n");
-	}
-
-	msn_slplink_process_msg(slplink, msg);
-}
-
-static void
-got_emoticon(MsnSlpCall *slpcall,
-			 const guchar *data, gsize size)
-{
-
-	PurpleConversation *conv;
-	PurpleConnection *gc;
-	const char *who;
-
-	gc = slpcall->slplink->session->account->gc;
-	who = slpcall->slplink->remote_user;
-
-	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) {
-
-		/* FIXME: it would be better if we wrote the data as we received it
-		          instead of all at once, calling write multiple times and
-		          close once at the very end
-		*/
-		purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
-		purple_conv_custom_smiley_close(conv, slpcall->data_info);
-	}
-#ifdef MSN_DEBUG_UD
-	purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
-#endif
-}
-
-void
-msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	MsnSession *session;
-	MsnSlpLink *slplink;
-	MsnObject *obj;
-	char **tokens;
-	char *smile, *body_str;
-	const char *body, *who, *sha1;
-	guint tok;
-	size_t body_len;
-
-	PurpleConversation *conv;
-
-	session = cmdproc->servconn->session;
-
-	if (!purple_account_get_bool(session->account, "custom_smileys", TRUE))
-		return;
-
-	body = msn_message_get_bin_data(msg, &body_len);
-	body_str = g_strndup(body, body_len);
-
-	/* MSN Messenger 7 may send more than one MSNObject in a single message...
-	 * Maybe 10 tokens is a reasonable max value. */
-	tokens = g_strsplit(body_str, "\t", 10);
-
-	g_free(body_str);
-
-	for (tok = 0; tok < 9; tok += 2) {
-		if (tokens[tok] == NULL || tokens[tok + 1] == NULL) {
-			break;
-		}
-
-		smile = tokens[tok];
-		obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1]));
-
-		if (obj == NULL)
-			break;
-
-		who = msn_object_get_creator(obj);
-		sha1 = msn_object_get_sha1(obj);
-
-		slplink = msn_session_get_slplink(session, who);
-
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who,
-												   session->account);
-
-		/* If the conversation doesn't exist then this is a custom smiley
-		 * used in the first message in a MSN conversation: we need to create
-		 * the conversation now, otherwise the custom smiley won't be shown.
-		 * This happens because every GtkIMHtml has its own smiley tree: if
-		 * the conversation doesn't exist then we cannot associate the new
-		 * smiley with its GtkIMHtml widget. */
-		if (!conv) {
-			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who);
-		}
-
-		if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) {
-			msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
-		}
-
-		msn_object_destroy(obj);
-		obj =   NULL;
-		who =   NULL;
-		sha1 = NULL;
-	}
-	g_strfreev(tokens);
-}
-
-static gboolean
-buddy_icon_cached(PurpleConnection *gc, MsnObject *obj)
-{
-	PurpleAccount *account;
-	PurpleBuddy *buddy;
-	const char *old;
-	const char *new;
-
-	g_return_val_if_fail(obj != NULL, FALSE);
-
-	account = purple_connection_get_account(gc);
-
-	buddy = purple_find_buddy(account, msn_object_get_creator(obj));
-	if (buddy == NULL)
-		return FALSE;
-
-	old = purple_buddy_icons_get_checksum_for_user(buddy);
-	new = msn_object_get_sha1(obj);
-
-	if (new == NULL)
-		return FALSE;
-
-	/* If the old and new checksums are the same, and the file actually exists,
-	 * then return TRUE */
-	if (old != NULL && !strcmp(old, new))
-		return TRUE;
-
-	return FALSE;
-}
-
-static void
-msn_release_buddy_icon_request(MsnUserList *userlist)
-{
-	MsnUser *user;
-
-	g_return_if_fail(userlist != NULL);
-
-#ifdef MSN_DEBUG_UD
-	purple_debug_info("msn", "Releasing buddy icon request\n");
-#endif
-
-	if (userlist->buddy_icon_window > 0)
-	{
-		GQueue *queue;
-		PurpleAccount *account;
-		const char *username;
-
-		queue = userlist->buddy_icon_requests;
-
-		if (g_queue_is_empty(userlist->buddy_icon_requests))
-			return;
-
-		user = g_queue_pop_head(queue);
-
-		account  = userlist->session->account;
-		username = user->passport;
-
-		userlist->buddy_icon_window--;
-		msn_request_user_display(user);
-
-#ifdef MSN_DEBUG_UD
-		purple_debug_info("msn", "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
-						userlist->buddy_icon_window);
-#endif
-	}
-}
-
-/*
- * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next
- * buddy icon request if there is one.
- */
-static gboolean
-msn_release_buddy_icon_request_timeout(gpointer data)
-{
-	MsnUserList *userlist = (MsnUserList *)data;
-	
-	/* Free one window slot */
-	userlist->buddy_icon_window++;	
-	
-	/* Clear the tag for our former request timer */
-	userlist->buddy_icon_request_timer = 0;
-	
-	msn_release_buddy_icon_request(userlist);
-	
-	return FALSE;
-}
-
-void
-msn_queue_buddy_icon_request(MsnUser *user)
-{
-	PurpleAccount *account;
-	MsnObject *obj;
-	GQueue *queue;
-
-	g_return_if_fail(user != NULL);
-
-	account = user->userlist->session->account;
-
-	obj = msn_user_get_object(user);
-
-	if (obj == NULL)
-	{
-		purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL);
-		return;
-	}
-
-	if (!buddy_icon_cached(account->gc, obj))
-	{
-		MsnUserList *userlist;
-
-		userlist = user->userlist;
-		queue = userlist->buddy_icon_requests;
-
-#ifdef MSN_DEBUG_UD
-		purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
-						user->passport, userlist->buddy_icon_window);
-#endif
-
-		g_queue_push_tail(queue, user);
-
-		if (userlist->buddy_icon_window > 0)
-			msn_release_buddy_icon_request(userlist);
-	}
-}
-
-static void
-got_user_display(MsnSlpCall *slpcall,
-				 const guchar *data, gsize size)
-{
-	MsnUserList *userlist;
-	const char *info;
-	PurpleAccount *account;
-
-	g_return_if_fail(slpcall != NULL);
-
-	info = slpcall->data_info;
-#ifdef MSN_DEBUG_UD
-	purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user);
-#endif
-
-	userlist = slpcall->slplink->session->userlist;
-	account = slpcall->slplink->session->account;
-
-	purple_buddy_icons_set_for_user(account, slpcall->slplink->remote_user,
-								  g_memdup(data, size), size, info);
-
-#if 0
-	/* Free one window slot */
-	userlist->buddy_icon_window++;
-
-	purple_debug_info("msn", "got_user_display(): buddy_icon_window++ yields =%d\n",
-					userlist->buddy_icon_window);
-
-	msn_release_buddy_icon_request(userlist);
-#endif
-}
-
-static void
-end_user_display(MsnSlpCall *slpcall, MsnSession *session)
-{
-	MsnUserList *userlist;
-
-	g_return_if_fail(session != NULL);
-
-#ifdef MSN_DEBUG_UD
-	purple_debug_info("msn", "End User Display\n");
-#endif
-
-	userlist = session->userlist;
-
-	/* If the session is being destroyed we better stop doing anything. */
-	if (session->destroying)
-		return;
-
-	/* Delay before freeing a buddy icon window slot and requesting the next icon, if appropriate.
-	 * If we don't delay, we'll rapidly hit the MSN equivalent of AIM's rate limiting; the server will
-	 * send us an error 800 like so:
-	 *
-	 * C: NS 000: XFR 21 SB
-	 * S: NS 000: 800 21
-	 */
-	if (userlist->buddy_icon_request_timer) {
-		/* Free the window slot used by this previous request */
-		userlist->buddy_icon_window++;
-
-		/* Clear our pending timeout */
-		purple_timeout_remove(userlist->buddy_icon_request_timer);
-	}
-
-	/* Wait BUDDY_ICON_DELAY_S seconds before freeing our window slot and requesting the next icon. */
-	userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY, 
-														  msn_release_buddy_icon_request_timeout, userlist);
-}
-
-void
-msn_request_user_display(MsnUser *user)
-{
-	PurpleAccount *account;
-	MsnSession *session;
-	MsnSlpLink *slplink;
-	MsnObject *obj;
-	const char *info;
-
-	session = user->userlist->session;
-	account = session->account;
-
-	slplink = msn_session_get_slplink(session, user->passport);
-
-	obj = msn_user_get_object(user);
-
-	info = msn_object_get_sha1(obj);
-
-	if (g_ascii_strcasecmp(user->passport,
-						   purple_account_get_username(account)))
-	{
-		msn_slplink_request_object(slplink, info, got_user_display,
-								   end_user_display, obj);
-	}
-	else
-	{
-		MsnObject *my_obj = NULL;
-		gconstpointer data = NULL;
-		size_t len = 0;
-
-#ifdef MSN_DEBUG_UD
-		purple_debug_info("msn", "Requesting our own user display\n");
-#endif
-
-		my_obj = msn_user_get_object(session->user);
-
-		if (my_obj != NULL)
-		{
-			PurpleStoredImage *img = msn_object_get_image(my_obj);
-			data = purple_imgstore_get_data(img);
-			len = purple_imgstore_get_size(img);
-		}
-
-		purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info);
-
-		/* Free one window slot */
-		session->userlist->buddy_icon_window++;
-
-#ifdef MSN_DEBUG_UD
-		purple_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n",
-						session->userlist->buddy_icon_window);
-#endif
-
-		msn_release_buddy_icon_request(session->userlist);
-	}
-}
--- a/libpurple/protocols/msnp9/slp.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/**
- * @file slp.h MSNSLP support
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_SLP_H_
-#define _MSN_SLP_H_
-
-#include "slpcall.h"
-#include "session.h"
-#include "internal.h"
-#include "ft.h"
-
-void msn_xfer_progress_cb(MsnSlpCall *slpcall, gsize total_length, gsize
-						  len, gsize offset);
-
-MsnSlpCall * msn_slp_sip_recv(MsnSlpLink *slplink,
-							  const char *body);
-
-void send_bye(MsnSlpCall *slpcall, const char *type);
-
-void msn_xfer_completed_cb(MsnSlpCall *slpcall,
-						   const guchar *body, gsize size);
-
-void msn_xfer_cancel(PurpleXfer *xfer);
-void msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session);
-
-void msn_queue_buddy_icon_request(MsnUser *user);
-
-#endif /* _MSN_SLP_H_ */
--- a/libpurple/protocols/msnp9/slpcall.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,272 +0,0 @@
-/**
- * @file slpcall.c SLP Call Functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "slpcall.h"
-#include "slpsession.h"
-
-#include "slp.h"
-
-/* #define MSN_DEBUG_SLPCALL */
-
-/**************************************************************************
- * Util
- **************************************************************************/
-
-static char *
-rand_guid(void)
-{
-	return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111);
-}
-
-/**************************************************************************
- * Main
- **************************************************************************/
-
-MsnSlpCall *
-msn_slp_call_new(MsnSlpLink *slplink)
-{
-	MsnSlpCall *slpcall;
-
-	g_return_val_if_fail(slplink != NULL, NULL);
-
-	slpcall = g_new0(MsnSlpCall, 1);
-
-#ifdef MSN_DEBUG_SLPCALL
-	purple_debug_info("msn", "slpcall_new: slpcall(%p)\n", slpcall);
-#endif
-
-	slpcall->slplink = slplink;
-
-	msn_slplink_add_slpcall(slplink, slpcall);
-
-	slpcall->timer = purple_timeout_add_seconds(MSN_SLPCALL_TIMEOUT, msn_slp_call_timeout, slpcall);
-
-	return slpcall;
-}
-
-void
-msn_slp_call_destroy(MsnSlpCall *slpcall)
-{
-	GList *e;
-	MsnSession *session;
-
-#ifdef MSN_DEBUG_SLPCALL
-	purple_debug_info("msn", "slpcall_destroy: slpcall(%p)\n", slpcall);
-#endif
-
-	g_return_if_fail(slpcall != NULL);
-
-	if (slpcall->timer)
-		purple_timeout_remove(slpcall->timer);
-
-	if (slpcall->id != NULL)
-		g_free(slpcall->id);
-
-	if (slpcall->branch != NULL)
-		g_free(slpcall->branch);
-
-	if (slpcall->data_info != NULL)
-		g_free(slpcall->data_info);
-
-	for (e = slpcall->slplink->slp_msgs; e != NULL; )
-	{
-		MsnSlpMessage *slpmsg = e->data;
-		e = e->next;
-
-#ifdef MSN_DEBUG_SLPCALL_VERBOSE
-		purple_debug_info("msn", "slpcall_destroy: trying slpmsg(%p)\n",
-						slpmsg);
-#endif
-
-		if (slpmsg->slpcall == slpcall)
-		{
-			msn_slpmsg_destroy(slpmsg);
-		}
-	}
-
-	session = slpcall->slplink->session;
-
-	msn_slplink_remove_slpcall(slpcall->slplink, slpcall);
-
-	if (slpcall->end_cb != NULL)
-		slpcall->end_cb(slpcall, session);
-
-	if (slpcall->xfer != NULL) {
-		slpcall->xfer->data = NULL;
-		purple_xfer_unref(slpcall->xfer);
-	}
-
-	g_free(slpcall);
-}
-
-void
-msn_slp_call_init(MsnSlpCall *slpcall, MsnSlpCallType type)
-{
-	slpcall->session_id = rand() % 0xFFFFFF00 + 4;
-	slpcall->id = rand_guid();
-	slpcall->type = type;
-}
-
-void
-msn_slp_call_session_init(MsnSlpCall *slpcall)
-{
-	MsnSlpSession *slpsession;
-
-	slpsession = msn_slp_session_new(slpcall);
-
-	if (slpcall->session_init_cb)
-		slpcall->session_init_cb(slpsession);
-
-	slpcall->started = TRUE;
-}
-
-void
-msn_slp_call_invite(MsnSlpCall *slpcall, const char *euf_guid,
-					int app_id, const char *context)
-{
-	MsnSlpLink *slplink;
-	MsnSlpMessage *slpmsg;
-	char *header;
-	char *content;
-
-	g_return_if_fail(slpcall != NULL);
-	g_return_if_fail(context != NULL);
-
-	slplink = slpcall->slplink;
-
-	slpcall->branch = rand_guid();
-
-	content = g_strdup_printf(
-		"EUF-GUID: {%s}\r\n"
-		"SessionID: %lu\r\n"
-		"AppID: %d\r\n"
-		"Context: %s\r\n\r\n",
-		euf_guid,
-		slpcall->session_id,
-		app_id,
-		context);
-
-	header = g_strdup_printf("INVITE MSNMSGR:%s MSNSLP/1.0",
-							 slplink->remote_user);
-
-	slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, slpcall->branch,
-								"application/x-msnmsgr-sessionreqbody", content);
-
-#ifdef MSN_DEBUG_SLP
-	slpmsg->info = "SLP INVITE";
-	slpmsg->text_body = TRUE;
-#endif
-
-	msn_slplink_send_slpmsg(slplink, slpmsg);
-
-	g_free(header);
-	g_free(content);
-}
-
-void
-msn_slp_call_close(MsnSlpCall *slpcall)
-{
-	g_return_if_fail(slpcall != NULL);
-	g_return_if_fail(slpcall->slplink != NULL);
-
-	send_bye(slpcall, "application/x-msnmsgr-sessionclosebody");
-	msn_slplink_unleash(slpcall->slplink);
-	msn_slp_call_destroy(slpcall);
-}
-
-gboolean
-msn_slp_call_timeout(gpointer data)
-{
-	MsnSlpCall *slpcall;
-
-	slpcall = data;
-
-#ifdef MSN_DEBUG_SLPCALL
-	purple_debug_info("msn", "slpcall_timeout: slpcall(%p)\n", slpcall);
-#endif
-
-	if (!slpcall->pending && !slpcall->progress)
-	{
-		msn_slp_call_destroy(slpcall);
-		return FALSE;
-	}
-
-	slpcall->progress = FALSE;
-
-	return TRUE;
-}
-
-MsnSlpCall *
-msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
-{
-	MsnSlpCall *slpcall;
-	const guchar *body;
-	gsize body_len;
-
-	slpcall = NULL;
-	body = slpmsg->buffer;
-	body_len = slpmsg->size;
-
-	if (slpmsg->flags == 0x0)
-	{
-		char *body_str;
-
-		body_str = g_strndup((const char *)body, body_len);
-		slpcall = msn_slp_sip_recv(slplink, body_str);
-		g_free(body_str);
-	}
-	else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
-	{
-		slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id);
-
-		if (slpcall != NULL)
-		{
-			if (slpcall->timer)
-				purple_timeout_remove(slpcall->timer);
-
-			slpcall->cb(slpcall, body, body_len);
-
-			slpcall->wasted = TRUE;
-		}
-	}
-#if 0
-	else if (slpmsg->flags == 0x100)
-	{
-		slpcall = slplink->directconn->initial_call;
-
-		if (slpcall != NULL)
-			msn_slp_call_session_init(slpcall);
-	}
-#endif
-
-	return slpcall;
-}
--- a/libpurple/protocols/msnp9/slpcall.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/**
- * @file slpcall.h SLP Call functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_SLPCALL_H_
-#define _MSN_SLPCALL_H_
-
-#include "internal.h"
-#include "ft.h"
-
-typedef struct _MsnSlpCall MsnSlpCall;
-
-#include "slplink.h"
-#include "slpsession.h"
-
-/* The official client seems to timeout slp calls after 5 minutes */
-#define MSN_SLPCALL_TIMEOUT 300
-
-typedef enum
-{
-	MSN_SLPCALL_ANY,
-	MSN_SLPCALL_DC,
-
-} MsnSlpCallType;
-
-struct _MsnSlpCall
-{
-	/* MsnSession *session; */
-	MsnSlpLink *slplink;
-
-	MsnSlpCallType type;
-
-	/* Call-ID */
-	char *id;
-	char *branch;
-
-	long session_id;
-	long app_id;
-
-	gboolean pending; /**< A flag that states if we should wait for this
-						slpcall to start and do not time out. */
-	gboolean progress; /**< A flag that states if there has been progress since
-						 the last time out. */
-	gboolean wasted; /**< A flag that states if this slpcall is going to be
-					   destroyed. */
-	gboolean started; /**< A flag that states if this slpcall's session has
-						been initiated. */
-
-	void (*progress_cb)(MsnSlpCall *slpcall,
-						gsize total_length, gsize len, gsize offset);
-	void (*session_init_cb)(MsnSlpSession *slpsession);
-
-	/* Can be checksum, or smile */
-	char *data_info;
-
-	PurpleXfer *xfer;
-
-	MsnSlpCb cb;
-	void (*end_cb)(MsnSlpCall *slpcall, MsnSession *session);
-
-	int timer;
-};
-
-MsnSlpCall *msn_slp_call_new(MsnSlpLink *slplink);
-void msn_slp_call_init(MsnSlpCall *slpcall, MsnSlpCallType type);
-void msn_slp_call_session_init(MsnSlpCall *slpcall);
-void msn_slp_call_destroy(MsnSlpCall *slpcall);
-void msn_slp_call_invite(MsnSlpCall *slpcall, const char *euf_guid,
-						 int app_id, const char *context);
-void msn_slp_call_close(MsnSlpCall *slpcall);
-gboolean msn_slp_call_timeout(gpointer data);
-
-#endif /* _MSN_SLPCALL_H_ */
--- a/libpurple/protocols/msnp9/slplink.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,822 +0,0 @@
-/**
- * @file slplink.c MSNSLP Link support
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "slplink.h"
-
-#include "switchboard.h"
-#include "slp.h"
-
-void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
-
-#ifdef MSN_DEBUG_SLP_FILES
-static int m_sc = 0;
-static int m_rc = 0;
-
-static void
-debug_msg_to_file(MsnMessage *msg, gboolean send)
-{
-	char *tmp;
-	char *dir;
-	char *pload;
-	FILE *tf;
-	int c;
-	gsize pload_size;
-
-	dir = send ? "send" : "recv";
-	c = send ? m_sc++ : m_rc++;
-	tmp = g_strdup_printf("%s/msntest/%s/%03d", g_get_home_dir(), dir, c);
-	tf = g_fopen(tmp, "wb");
-	if (tf == NULL)
-	{
-		purple_debug_error("msn", "could not open debug file\n");
-		return;
-	}
-	pload = msn_message_gen_payload(msg, &pload_size);
-	fwrite(pload, 1, pload_size, tf);
-	fclose(tf);
-	g_free(tmp);
-}
-#endif
-
-/**************************************************************************
- * Main
- **************************************************************************/
-
-MsnSlpLink *
-msn_slplink_new(MsnSession *session, const char *username)
-{
-	MsnSlpLink *slplink;
-
-	g_return_val_if_fail(session != NULL, NULL);
-
-	slplink = g_new0(MsnSlpLink, 1);
-
-#ifdef MSN_DEBUG_SLPLINK
-	purple_debug_info("msn", "slplink_new: slplink(%p)\n", slplink);
-#endif
-
-	slplink->session = session;
-	slplink->slp_seq_id = rand() % 0xFFFFFF00 + 4;
-
-	slplink->local_user = g_strdup(msn_user_get_passport(session->user));
-	slplink->remote_user = g_strdup(username);
-
-	slplink->slp_msg_queue = g_queue_new();
-
-	session->slplinks =
-		g_list_append(session->slplinks, slplink);
-
-	return slplink;
-}
-
-void
-msn_slplink_destroy(MsnSlpLink *slplink)
-{
-	MsnSession *session;
-
-#ifdef MSN_DEBUG_SLPLINK
-	purple_debug_info("msn", "slplink_destroy: slplink(%p)\n", slplink);
-#endif
-
-	g_return_if_fail(slplink != NULL);
-
-	if (slplink->swboard != NULL)
-		slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
-
-	session = slplink->session;
-
-	if (slplink->local_user != NULL)
-		g_free(slplink->local_user);
-
-	if (slplink->remote_user != NULL)
-		g_free(slplink->remote_user);
-
-	if (slplink->directconn != NULL)
-		msn_directconn_destroy(slplink->directconn);
-
-	while (slplink->slp_calls != NULL)
-		msn_slp_call_destroy(slplink->slp_calls->data);
-
-	g_queue_free(slplink->slp_msg_queue);
-
-	session->slplinks =
-		g_list_remove(session->slplinks, slplink);
-
-	g_free(slplink);
-}
-
-MsnSlpLink *
-msn_session_find_slplink(MsnSession *session, const char *who)
-{
-	GList *l;
-
-	for (l = session->slplinks; l != NULL; l = l->next)
-	{
-		MsnSlpLink *slplink;
-
-		slplink = l->data;
-
-		if (!strcmp(slplink->remote_user, who))
-			return slplink;
-	}
-
-	return NULL;
-}
-
-MsnSlpLink *
-msn_session_get_slplink(MsnSession *session, const char *username)
-{
-	MsnSlpLink *slplink;
-
-	g_return_val_if_fail(session != NULL, NULL);
-	g_return_val_if_fail(username != NULL, NULL);
-
-	slplink = msn_session_find_slplink(session, username);
-
-	if (slplink == NULL)
-		slplink = msn_slplink_new(session, username);
-
-	return slplink;
-}
-
-MsnSlpSession *
-msn_slplink_find_slp_session(MsnSlpLink *slplink, long session_id)
-{
-	GList *l;
-	MsnSlpSession *slpsession;
-
-	for (l = slplink->slp_sessions; l != NULL; l = l->next)
-	{
-		slpsession = l->data;
-
-		if (slpsession->id == session_id)
-			return slpsession;
-	}
-
-	return NULL;
-}
-
-void
-msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall)
-{
-	if (slplink->swboard != NULL)
-		slplink->swboard->flag |= MSN_SB_FLAG_FT;
-
-	slplink->slp_calls = g_list_append(slplink->slp_calls, slpcall);
-}
-
-void
-msn_slplink_remove_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall)
-{
-	slplink->slp_calls = g_list_remove(slplink->slp_calls, slpcall);
-
-	/* The slplink has no slpcalls in it. If no one is using it, we might
-	 * destroy the switchboard, but we should be careful not to use the slplink
-	 * again. */
-	if (slplink->slp_calls == NULL)
-	{
-		if (slplink->swboard != NULL)
-		{
-			if (msn_switchboard_release(slplink->swboard, MSN_SB_FLAG_FT))
-				/* I'm not sure this is the best thing to do, but it's better
-				 * than nothing. */
-				slpcall->slplink = NULL;
-		}
-	}
-}
-
-MsnSlpCall *
-msn_slplink_find_slp_call(MsnSlpLink *slplink, const char *id)
-{
-	GList *l;
-	MsnSlpCall *slpcall;
-
-	if (!id)
-		return NULL;
-
-	for (l = slplink->slp_calls; l != NULL; l = l->next)
-	{
-		slpcall = l->data;
-
-		if (slpcall->id && !strcmp(slpcall->id, id))
-			return slpcall;
-	}
-
-	return NULL;
-}
-
-MsnSlpCall *
-msn_slplink_find_slp_call_with_session_id(MsnSlpLink *slplink, long id)
-{
-	GList *l;
-	MsnSlpCall *slpcall;
-
-	for (l = slplink->slp_calls; l != NULL; l = l->next)
-	{
-		slpcall = l->data;
-
-		if (slpcall->session_id == id)
-			return slpcall;
-	}
-
-	return NULL;
-}
-
-void
-msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg)
-{
-	if (slplink->directconn != NULL)
-	{
-		msn_directconn_send_msg(slplink->directconn, msg);
-	}
-	else
-	{
-		if (slplink->swboard == NULL)
-		{
-			slplink->swboard = msn_session_get_swboard(slplink->session,
-													   slplink->remote_user, MSN_SB_FLAG_FT);
-
-			if (slplink->swboard == NULL)
-				return;
-
-			/* If swboard is destroyed we will be too */
-			slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
-		}
-
-		msn_switchboard_send_msg(slplink->swboard, msg, TRUE);
-	}
-}
-
-/* We have received the message ack */
-static void
-msg_ack(MsnMessage *msg, void *data)
-{
-	MsnSlpMessage *slpmsg;
-	long long real_size;
-
-	slpmsg = data;
-
-	real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size;
-
-	slpmsg->offset += msg->msnslp_header.length;
-
-	if (slpmsg->offset < real_size)
-	{
-		msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
-	}
-	else
-	{
-		/* The whole message has been sent */
-		if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
-		{
-			if (slpmsg->slpcall != NULL)
-			{
-				if (slpmsg->slpcall->cb)
-					slpmsg->slpcall->cb(slpmsg->slpcall,
-						NULL, 0);
-			}
-		}
-	}
-
-	slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
-}
-
-/* We have received the message nak. */
-static void
-msg_nak(MsnMessage *msg, void *data)
-{
-	MsnSlpMessage *slpmsg;
-
-	slpmsg = data;
-
-	msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
-
-	slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
-}
-
-void
-msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
-{
-	MsnMessage *msg;
-	long long real_size;
-	size_t len = 0;
-
-	/* Maybe we will want to create a new msg for this slpmsg instead of
-	 * reusing the same one all the time. */
-	msg = slpmsg->msg;
-
-	real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size;
-
-	if (slpmsg->offset < real_size)
-	{
-		if (slpmsg->fp)
-		{
-			char data[1202];
-			len = fread(data, 1, sizeof(data), slpmsg->fp);
-			msn_message_set_bin_data(msg, data, len);
-		}
-		else
-		{
-			len = slpmsg->size - slpmsg->offset;
-
-			if (len > 1202)
-				len = 1202;
-
-			msn_message_set_bin_data(msg, slpmsg->buffer + slpmsg->offset, len);
-		}
-
-		msg->msnslp_header.offset = slpmsg->offset;
-		msg->msnslp_header.length = len;
-	}
-
-#ifdef MSN_DEBUG_SLP
-	msn_message_show_readable(msg, slpmsg->info, slpmsg->text_body);
-#endif
-
-#ifdef MSN_DEBUG_SLP_FILES
-	debug_msg_to_file(msg, TRUE);
-#endif
-
-	slpmsg->msgs =
-		g_list_append(slpmsg->msgs, msg);
-	msn_slplink_send_msg(slplink, msg);
-
-	if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) &&
-		(slpmsg->slpcall != NULL))
-	{
-		slpmsg->slpcall->progress = TRUE;
-
-		if (slpmsg->slpcall->progress_cb != NULL)
-		{
-			slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size,
-										 len, slpmsg->offset);
-		}
-	}
-
-	/* slpmsg->offset += len; */
-}
-
-void
-msn_slplink_release_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
-{
-	MsnMessage *msg;
-
-	slpmsg->msg = msg = msn_message_new_msnslp();
-
-	if (slpmsg->flags == 0x0)
-	{
-		msg->msnslp_header.session_id = slpmsg->session_id;
-		msg->msnslp_header.ack_id = rand() % 0xFFFFFF00;
-	}
-	else if (slpmsg->flags == 0x2)
-	{
-		msg->msnslp_header.session_id = slpmsg->session_id;
-		msg->msnslp_header.ack_id = slpmsg->ack_id;
-		msg->msnslp_header.ack_size = slpmsg->ack_size;
-		msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id;
-	}
-	else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
-	{
-		MsnSlpSession *slpsession;
-		slpsession = slpmsg->slpsession;
-
-		g_return_if_fail(slpsession != NULL);
-		msg->msnslp_header.session_id = slpsession->id;
-		msg->msnslp_footer.value = slpsession->app_id;
-		msg->msnslp_header.ack_id = rand() % 0xFFFFFF00;
-	}
-	else if (slpmsg->flags == 0x100)
-	{
-		msg->msnslp_header.ack_id     = slpmsg->ack_id;
-		msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id;
-		msg->msnslp_header.ack_size   = slpmsg->ack_size;
-	}
-
-	msg->msnslp_header.id = slpmsg->id;
-	msg->msnslp_header.flags = slpmsg->flags;
-
-	msg->msnslp_header.total_size = slpmsg->size;
-
-	msn_message_set_attr(msg, "P2P-Dest", slplink->remote_user);
-
-	msg->ack_cb = msg_ack;
-	msg->nak_cb = msg_nak;
-	msg->ack_data = slpmsg;
-
-	msn_slplink_send_msgpart(slplink, slpmsg);
-
-	msn_message_destroy(msg);
-}
-
-void
-msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
-{
-	slpmsg->id = slplink->slp_seq_id++;
-
-	g_queue_push_head(slplink->slp_msg_queue, slpmsg);
-}
-
-void
-msn_slplink_send_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
-{
-	slpmsg->id = slplink->slp_seq_id++;
-
-	msn_slplink_release_slpmsg(slplink, slpmsg);
-}
-
-void
-msn_slplink_unleash(MsnSlpLink *slplink)
-{
-	MsnSlpMessage *slpmsg;
-
-	/* Send the queued msgs in the order they came. */
-
-	while ((slpmsg = g_queue_pop_tail(slplink->slp_msg_queue)) != NULL)
-	{
-		msn_slplink_release_slpmsg(slplink, slpmsg);
-	}
-}
-
-void
-msn_slplink_send_ack(MsnSlpLink *slplink, MsnMessage *msg)
-{
-	MsnSlpMessage *slpmsg;
-
-	slpmsg = msn_slpmsg_new(slplink);
-
-	slpmsg->session_id = msg->msnslp_header.session_id;
-	slpmsg->size       = msg->msnslp_header.total_size;
-	slpmsg->flags      = 0x02;
-	slpmsg->ack_id     = msg->msnslp_header.id;
-	slpmsg->ack_sub_id = msg->msnslp_header.ack_id;
-	slpmsg->ack_size   = msg->msnslp_header.total_size;
-
-#ifdef MSN_DEBUG_SLP
-	slpmsg->info = "SLP ACK";
-#endif
-
-	msn_slplink_send_slpmsg(slplink, slpmsg);
-	msn_slpmsg_destroy(slpmsg);
-}
-
-static void
-send_file_cb(MsnSlpSession *slpsession)
-{
-	MsnSlpCall *slpcall;
-	MsnSlpMessage *slpmsg;
-	struct stat st;
-	PurpleXfer *xfer;
-
-	slpcall = slpsession->slpcall;
-	slpmsg = msn_slpmsg_new(slpcall->slplink);
-	slpmsg->slpcall = slpcall;
-	slpmsg->flags = 0x1000030;
-	slpmsg->slpsession = slpsession;
-#ifdef MSN_DEBUG_SLP
-	slpmsg->info = "SLP FILE";
-#endif
-	xfer = (PurpleXfer *)slpcall->xfer;
-	purple_xfer_start(slpcall->xfer, -1, NULL, 0);
-	slpmsg->fp = xfer->dest_fp;
-	if (g_stat(purple_xfer_get_local_filename(xfer), &st) == 0)
-		slpmsg->size = st.st_size;
-	xfer->dest_fp = NULL; /* Disable double fclose() */
-
-	msn_slplink_send_slpmsg(slpcall->slplink, slpmsg);
-}
-
-void
-msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
-{
-	MsnSlpMessage *slpmsg;
-	const char *data;
-	guint64 offset;
-	gsize len;
-
-#ifdef MSN_DEBUG_SLP
-	msn_slpmsg_show(msg);
-#endif
-
-#ifdef MSN_DEBUG_SLP_FILES
-	debug_msg_to_file(msg, FALSE);
-#endif
-
-	if (msg->msnslp_header.total_size < msg->msnslp_header.length)
-	{
-		purple_debug_error("msn", "This can't be good\n");
-		g_return_if_reached();
-	}
-
-	slpmsg = NULL;
-	data = msn_message_get_bin_data(msg, &len);
-
-	/*
-		OVERHEAD!
-		if (msg->msnslp_header.length < msg->msnslp_header.total_size)
-	 */
-
-	offset = msg->msnslp_header.offset;
-
-	if (offset == 0)
-	{
-		slpmsg = msn_slpmsg_new(slplink);
-		slpmsg->id = msg->msnslp_header.id;
-		slpmsg->session_id = msg->msnslp_header.session_id;
-		slpmsg->size = msg->msnslp_header.total_size;
-		slpmsg->flags = msg->msnslp_header.flags;
-
-		if (slpmsg->session_id)
-		{
-			if (slpmsg->slpcall == NULL)
-				slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id);
-
-			if (slpmsg->slpcall != NULL)
-			{
-				if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030)
-				{
-					PurpleXfer *xfer;
-
-					xfer = slpmsg->slpcall->xfer;
-
-					if (xfer != NULL)
-					{
-						purple_xfer_ref(xfer);
-						purple_xfer_start(xfer,	-1, NULL, 0);
-
-						if (xfer->data == NULL) {
-							purple_xfer_unref(xfer);
-							return;
-						} else {
-							purple_xfer_unref(xfer);
-							slpmsg->fp = xfer->dest_fp;
-							xfer->dest_fp = NULL; /* Disable double fclose() */
-						}
-					}
-				}
-			}
-		}
-		if (!slpmsg->fp && slpmsg->size)
-		{
-			slpmsg->buffer = g_try_malloc(slpmsg->size);
-			if (slpmsg->buffer == NULL)
-			{
-				purple_debug_error("msn", "Failed to allocate buffer for slpmsg\n");
-				msn_slpmsg_destroy(slpmsg);
-				return;
-			}
-		}
-	}
-	else
-	{
-		slpmsg = msn_slplink_message_find(slplink, msg->msnslp_header.session_id, msg->msnslp_header.id);
-	}
-
-	if (slpmsg == NULL)
-	{
-		/* Probably the transfer was canceled */
-		purple_debug_error("msn", "Couldn't find slpmsg\n");
-		return;
-	}
-
-	if (slpmsg->fp)
-	{
-		/* fseek(slpmsg->fp, offset, SEEK_SET); */
-		len = fwrite(data, 1, len, slpmsg->fp);
-	}
-	else if (slpmsg->size && slpmsg->buffer)
-	{
-		if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size)
-		{
-			purple_debug_error("msn", "Oversized slpmsg\n");
-			g_return_if_reached();
-		}
-		else
-			memcpy(slpmsg->buffer + offset, data, len);
-	}
-
-	if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) &&
-		(slpmsg->slpcall != NULL))
-	{
-		slpmsg->slpcall->progress = TRUE;
-
-		if (slpmsg->slpcall->progress_cb != NULL)
-		{
-			slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size,
-										 len, offset);
-		}
-	}
-
-#if 0
-	if (slpmsg->buffer == NULL)
-		return;
-#endif
-
-	if (msg->msnslp_header.offset + msg->msnslp_header.length
-		>= msg->msnslp_header.total_size)
-	{
-		/* All the pieces of the slpmsg have been received */
-		MsnSlpCall *slpcall;
-
-		slpcall = msn_slp_process_msg(slplink, slpmsg);
-
-		if (slpmsg->flags == 0x100)
-		{
-			MsnDirectConn *directconn;
-
-			directconn = slplink->directconn;
-
-			if (!directconn->acked)
-				msn_directconn_send_handshake(directconn);
-		}
-		else if (slpmsg->flags == 0x0 || slpmsg->flags == 0x20 ||
-				 slpmsg->flags == 0x1000030)
-		{
-			/* Release all the messages and send the ACK */
-
-			msn_slplink_send_ack(slplink, msg);
-			msn_slplink_unleash(slplink);
-		}
-
-		msn_slpmsg_destroy(slpmsg);
-
-		if (slpcall != NULL && slpcall->wasted)
-			msn_slp_call_destroy(slpcall);
-	}
-}
-
-MsnSlpMessage *
-msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id)
-{
-	GList *e;
-
-	for (e = slplink->slp_msgs; e != NULL; e = e->next)
-	{
-		MsnSlpMessage *slpmsg = e->data;
-
-		if ((slpmsg->session_id == session_id) && (slpmsg->id == id))
-			return slpmsg;
-	}
-
-	return NULL;
-}
-
-typedef struct
-{
-	guint32 length;
-	guint32 unk1;
-	guint32 file_size;
-	guint32 unk2;
-	guint32 unk3;
-} MsnContextHeader;
-
-#define MAX_FILE_NAME_LEN 0x226
-
-static gchar *
-gen_context(const char *file_name, const char *file_path)
-{
-	struct stat st;
-	gsize size = 0;
-	MsnContextHeader header;
-	gchar *u8 = NULL;
-	guchar *base;
-	guchar *n;
-	gchar *ret;
-	gunichar2 *uni = NULL;
-	glong currentChar = 0;
-	glong uni_len = 0;
-	gsize len;
-
-	if (g_stat(file_path, &st) == 0)
-		size = st.st_size;
-
-	if(!file_name) {
-		u8 = purple_utf8_try_convert(g_basename(file_path));
-		file_name = u8;
-	}
-
-	uni = g_utf8_to_utf16(file_name, -1, NULL, &uni_len, NULL);
-
-	if(u8) {
-		g_free(u8);
-		file_name = NULL;
-		u8 = NULL;
-	}
-
-	len = sizeof(MsnContextHeader) + MAX_FILE_NAME_LEN + 4;
-
-	header.length = GUINT32_TO_LE(len);
-	header.unk1 = GUINT32_TO_LE(2);
-	header.file_size = GUINT32_TO_LE(size);
-	header.unk2 = GUINT32_TO_LE(0);
-	header.unk3 = GUINT32_TO_LE(0);
-
-	base = g_malloc(len + 1);
-	n = base;
-
-	memcpy(n, &header, sizeof(MsnContextHeader));
-	n += sizeof(MsnContextHeader);
-
-	memset(n, 0x00, MAX_FILE_NAME_LEN);
-	for(currentChar = 0; currentChar < uni_len; currentChar++) {
-		*((gunichar2 *)n + currentChar) = GUINT16_TO_LE(uni[currentChar]);
-	}
-	n += MAX_FILE_NAME_LEN;
-
-	memset(n, 0xFF, 4);
-	n += 4;
-
-	g_free(uni);
-	ret = purple_base64_encode(base, len);
-	g_free(base);
-	return ret;
-}
-
-void
-msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer)
-{
-	MsnSlpCall *slpcall;
-	char *context;
-	const char *fn;
-	const char *fp;
-
-	fn = purple_xfer_get_filename(xfer);
-	fp = purple_xfer_get_local_filename(xfer);
-
-	g_return_if_fail(slplink != NULL);
-	g_return_if_fail(fp != NULL);
-
-	slpcall = msn_slp_call_new(slplink);
-	msn_slp_call_init(slpcall, MSN_SLPCALL_DC);
-
-	slpcall->session_init_cb = send_file_cb;
-	slpcall->end_cb = msn_xfer_end_cb;
-	slpcall->progress_cb = msn_xfer_progress_cb;
-	slpcall->cb = msn_xfer_completed_cb;
-	slpcall->xfer = xfer;
-	purple_xfer_ref(slpcall->xfer);
-
-	slpcall->pending = TRUE;
-
-	purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel);
-
-	xfer->data = slpcall;
-
-	context = gen_context(fn, fp);
-
-	msn_slp_call_invite(slpcall, "5D3E02AB-6190-11D3-BBBB-00C04F795683", 2,
-						context);
-
-	g_free(context);
-}
-
-void
-msn_slplink_request_object(MsnSlpLink *slplink,
-						   const char *info,
-						   MsnSlpCb cb,
-						   MsnSlpEndCb end_cb,
-						   const MsnObject *obj)
-{
-	MsnSlpCall *slpcall;
-	char *msnobj_data;
-	char *msnobj_base64;
-
-	g_return_if_fail(slplink != NULL);
-	g_return_if_fail(obj     != NULL);
-
-	msnobj_data = msn_object_to_string(obj);
-	msnobj_base64 = purple_base64_encode((const guchar *)msnobj_data, strlen(msnobj_data));
-	g_free(msnobj_data);
-
-	slpcall = msn_slp_call_new(slplink);
-	msn_slp_call_init(slpcall, MSN_SLPCALL_ANY);
-
-	slpcall->data_info = g_strdup(info);
-	slpcall->cb = cb;
-	slpcall->end_cb = end_cb;
-
-	msn_slp_call_invite(slpcall, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", 1,
-						msnobj_base64);
-
-	g_free(msnobj_base64);
-}
--- a/libpurple/protocols/msnp9/slplink.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-/**
- * @file slplink.h MSNSLP Link support
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_SLPLINK_H_
-#define _MSN_SLPLINK_H_
-
-typedef struct _MsnSlpLink MsnSlpLink;
-
-#include "directconn.h"
-#include "slpcall.h"
-#include "slpmsg.h"
-
-#include "switchboard.h"
-
-#include "ft.h"
-
-#include "session.h"
-
-typedef void (*MsnSlpCb)(MsnSlpCall *slpcall,
-						 const guchar *data, gsize size);
-typedef void (*MsnSlpEndCb)(MsnSlpCall *slpcall, MsnSession *session);
-
-struct _MsnSlpLink
-{
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-
-	char *local_user;
-	char *remote_user;
-
-	int slp_seq_id;
-
-	MsnDirectConn *directconn;
-
-	GList *slp_calls;
-	GList *slp_sessions;
-	GList *slp_msgs;
-
-	GQueue *slp_msg_queue;
-};
-
-MsnSlpLink *msn_slplink_new(MsnSession *session, const char *username);
-void msn_slplink_destroy(MsnSlpLink *slplink);
-MsnSlpLink *msn_session_find_slplink(MsnSession *session,
-									 const char *who);
-MsnSlpLink *msn_session_get_slplink(MsnSession *session, const char *username);
-MsnSlpSession *msn_slplink_find_slp_session(MsnSlpLink *slplink,
-											long session_id);
-void msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall);
-void msn_slplink_remove_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall);
-MsnSlpCall *msn_slplink_find_slp_call(MsnSlpLink *slplink,
-									  const char *id);
-MsnSlpCall *msn_slplink_find_slp_call_with_session_id(MsnSlpLink *slplink, long id);
-void msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg);
-void msn_slplink_release_slpmsg(MsnSlpLink *slplink,
-								MsnSlpMessage *slpmsg);
-void msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
-void msn_slplink_send_slpmsg(MsnSlpLink *slplink,
-							 MsnSlpMessage *slpmsg);
-void msn_slplink_unleash(MsnSlpLink *slplink);
-void msn_slplink_send_ack(MsnSlpLink *slplink, MsnMessage *msg);
-void msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg);
-MsnSlpMessage *msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id);
-void msn_slplink_append_slp_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
-void msn_slplink_remove_slp_msg(MsnSlpLink *slplink,
-								MsnSlpMessage *slpmsg);
-void msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer);
-
-void msn_slplink_request_object(MsnSlpLink *slplink,
-								const char *info,
-								MsnSlpCb cb,
-								MsnSlpEndCb end_cb,
-								const MsnObject *obj);
-
-MsnSlpCall *msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
-
-#endif /* _MSN_SLPLINK_H_ */
--- a/libpurple/protocols/msnp9/slpmsg.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,239 +0,0 @@
-/**
- * @file slpmsg.h SLP Message functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "slpmsg.h"
-#include "slplink.h"
-
-/**************************************************************************
- * SLP Message
- **************************************************************************/
-
-MsnSlpMessage *
-msn_slpmsg_new(MsnSlpLink *slplink)
-{
-	MsnSlpMessage *slpmsg;
-
-	slpmsg = g_new0(MsnSlpMessage, 1);
-
-#ifdef MSN_DEBUG_SLPMSG
-	purple_debug_info("msn", "slpmsg new (%p)\n", slpmsg);
-#endif
-
-	slpmsg->slplink = slplink;
-
-	slplink->slp_msgs =
-		g_list_append(slplink->slp_msgs, slpmsg);
-
-	return slpmsg;
-}
-
-void
-msn_slpmsg_destroy(MsnSlpMessage *slpmsg)
-{
-	MsnSlpLink *slplink;
-	GList *cur;
-
-	g_return_if_fail(slpmsg != NULL);
-
-#ifdef MSN_DEBUG_SLPMSG
-	purple_debug_info("msn", "slpmsg destroy (%p)\n", slpmsg);
-#endif
-
-	slplink = slpmsg->slplink;
-
-	if (slpmsg->fp != NULL)
-		fclose(slpmsg->fp);
-
-	purple_imgstore_unref(slpmsg->img);
-
-	/* We don't want to free the data of the PurpleStoredImage,
-	 * but to avoid code duplication, it's sharing buffer. */
-	if (slpmsg->img == NULL)
-		g_free(slpmsg->buffer);
-
-#ifdef MSN_DEBUG_SLP
-	/*
-	if (slpmsg->info != NULL)
-		g_free(slpmsg->info);
-	*/
-#endif
-
-	for (cur = slpmsg->msgs; cur != NULL; cur = cur->next)
-	{
-		/* Something is pointing to this slpmsg, so we should remove that
-		 * pointer to prevent a crash. */
-		/* Ex: a user goes offline and after that we receive an ACK */
-
-		MsnMessage *msg = cur->data;
-
-#ifdef MSN_DEBUG_SLPMSG
-		purple_debug_info("msn", "Unlink slpmsg callbacks.\n");
-#endif
-
-		msg->ack_cb = NULL;
-		msg->nak_cb = NULL;
-		msg->ack_data = NULL;
-	}
-
-	slplink->slp_msgs = g_list_remove(slplink->slp_msgs, slpmsg);
-
-	g_free(slpmsg);
-}
-
-void
-msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body,
-						 long long size)
-{
-	/* We can only have one data source at a time. */
-	g_return_if_fail(slpmsg->buffer == NULL);
-	g_return_if_fail(slpmsg->img == NULL);
-	g_return_if_fail(slpmsg->fp == NULL);
-
-	if (body != NULL)
-		slpmsg->buffer = g_memdup(body, size);
-	else
-		slpmsg->buffer = g_new0(guchar, size);
-
-	slpmsg->size = size;
-}
-
-void
-msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img)
-{
-	/* We can only have one data source at a time. */
-	g_return_if_fail(slpmsg->buffer == NULL);
-	g_return_if_fail(slpmsg->img == NULL);
-	g_return_if_fail(slpmsg->fp == NULL);
-
-	slpmsg->img = purple_imgstore_ref(img);
-	slpmsg->buffer = (guchar *)purple_imgstore_get_data(img);
-	slpmsg->size = purple_imgstore_get_size(img);
-}
-
-void
-msn_slpmsg_open_file(MsnSlpMessage *slpmsg, const char *file_name)
-{
-	struct stat st;
-
-	/* We can only have one data source at a time. */
-	g_return_if_fail(slpmsg->buffer == NULL);
-	g_return_if_fail(slpmsg->img == NULL);
-	g_return_if_fail(slpmsg->fp == NULL);
-
-	slpmsg->fp = g_fopen(file_name, "rb");
-
-	if (g_stat(file_name, &st) == 0)
-		slpmsg->size = st.st_size;
-}
-
-#ifdef MSN_DEBUG_SLP
-void
-msn_slpmsg_show(MsnMessage *msg)
-{
-	const char *info;
-	gboolean text;
-	guint32 flags;
-
-	text = FALSE;
-
-	flags = GUINT32_TO_LE(msg->msnslp_header.flags);
-
-	switch (flags)
-	{
-		case 0x0:
-			info = "SLP CONTROL";
-			text = TRUE;
-			break;
-		case 0x2:
-			info = "SLP ACK"; break;
-		case 0x20:
-		case 0x1000030:
-			info = "SLP DATA"; break;
-		default:
-			info = "SLP UNKNOWN"; break;
-	}
-
-	msn_message_show_readable(msg, info, text);
-}
-#endif
-
-MsnSlpMessage *
-msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq,
-				   const char *header, const char *branch,
-				   const char *content_type, const char *content)
-{
-	MsnSlpLink *slplink;
-	MsnSlpMessage *slpmsg;
-	char *body;
-	gsize body_len;
-	gsize content_len;
-
-	g_return_val_if_fail(slpcall != NULL, NULL);
-	g_return_val_if_fail(header  != NULL, NULL);
-
-	slplink = slpcall->slplink;
-
-	/* Let's remember that "content" should end with a 0x00 */
-
-	content_len = (content != NULL) ? strlen(content) + 1 : 0;
-
-	body = g_strdup_printf(
-		"%s\r\n"
-		"To: <msnmsgr:%s>\r\n"
-		"From: <msnmsgr:%s>\r\n"
-		"Via: MSNSLP/1.0/TLP ;branch={%s}\r\n"
-		"CSeq: %d\r\n"
-		"Call-ID: {%s}\r\n"
-		"Max-Forwards: 0\r\n"
-		"Content-Type: %s\r\n"
-		"Content-Length: %" G_GSIZE_FORMAT "\r\n"
-		"\r\n",
-		header,
-		slplink->remote_user,
-		slplink->local_user,
-		branch,
-		cseq,
-		slpcall->id,
-		content_type,
-		content_len);
-
-	body_len = strlen(body);
-
-	if (content_len > 0)
-	{
-		body_len += content_len;
-		body = g_realloc(body, body_len);
-		g_strlcat(body, content, body_len);
-	}
-
-	slpmsg = msn_slpmsg_new(slplink);
-	msn_slpmsg_set_body(slpmsg, body, body_len);
-
-	slpmsg->sip = TRUE;
-	slpmsg->slpcall = slpcall;
-
-	g_free(body);
-
-	return slpmsg;
-}
--- a/libpurple/protocols/msnp9/slpmsg.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-/**
- * @file slpmsg.h SLP Message functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_SLPMSG_H_
-#define _MSN_SLPMSG_H_
-
-typedef struct _MsnSlpMessage MsnSlpMessage;
-
-#include "imgstore.h"
-
-#include "slpsession.h"
-#include "slpcall.h"
-#include "slplink.h"
-#include "session.h"
-#include "msg.h"
-
-#include "slp.h"
-
-/**
- * A SLP Message  This contains everything that we will need to send a SLP
- * Message even if has to be sent in several parts.
- */
-struct _MsnSlpMessage
-{
-	MsnSlpSession *slpsession;
-	MsnSlpCall *slpcall; /**< The slpcall to which this slp message belongs (if applicable). */
-	MsnSlpLink *slplink; /**< The slplink through which this slp message is being sent. */
-	MsnSession *session;
-
-	long session_id;
-	long id;
-	long ack_id;
-	long ack_sub_id;
-	long long ack_size;
-	long app_id;
-
-	gboolean sip; /**< A flag that states if this is a SIP slp message. */
-	int ref_count; /**< The reference count. */
-	long flags;
-
-	FILE *fp;
-	PurpleStoredImage *img;
-	guchar *buffer;
-	long long offset;
-	long long size;
-
-	GList *msgs; /**< The real messages. */
-
-#if 1
-	MsnMessage *msg; /**< The temporary real message that will be sent. */
-#endif
-
-#ifdef MSN_DEBUG_SLP
-	char *info;
-	gboolean text_body;
-#endif
-};
-
-/**
- * Creates a new slp message
- *
- * @param slplink The slplink through which this slp message will be sent.
- * @return The created slp message.
- */
-MsnSlpMessage *msn_slpmsg_new(MsnSlpLink *slplink);
-
-/**
- * Destroys a slp message
- *
- * @param slpmsg The slp message to destory.
- */
-void msn_slpmsg_destroy(MsnSlpMessage *slpmsg);
-
-void msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body,
-						 long long size);
-void msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img);
-void msn_slpmsg_open_file(MsnSlpMessage *slpmsg,
-						  const char *file_name);
-MsnSlpMessage * msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq,
-								   const char *header,
-								   const char *branch,
-								   const char *content_type,
-								   const char *content);
-
-#ifdef MSN_DEBUG_SLP
-void msn_slpmsg_show(MsnMessage *msg);
-#endif
-
-#endif /* _MSN_SLPMSG_H_ */
--- a/libpurple/protocols/msnp9/slpsession.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/**
- * @file slpsession.h SLP Session functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "slpsession.h"
-
-/**************************************************************************
- * SLP Session
- **************************************************************************/
-
-MsnSlpSession *
-msn_slp_session_new(MsnSlpCall *slpcall)
-{
-	MsnSlpSession *slpsession;
-
-	g_return_val_if_fail(slpcall != NULL, NULL);
-
-	slpsession = g_new0(MsnSlpSession, 1);
-
-	slpsession->slpcall = slpcall;
-	slpsession->id = slpcall->session_id;
-	slpsession->call_id = slpcall->id;
-	slpsession->app_id = slpcall->app_id;
-
-	slpcall->slplink->slp_sessions =
-		g_list_append(slpcall->slplink->slp_sessions, slpsession);
-
-	return slpsession;
-}
-
-void
-msn_slp_session_destroy(MsnSlpSession *slpsession)
-{
-	g_return_if_fail(slpsession != NULL);
-
-	if (slpsession->call_id != NULL)
-		g_free(slpsession->call_id);
-
-	slpsession->slpcall->slplink->slp_sessions =
-		g_list_remove(slpsession->slpcall->slplink->slp_sessions, slpsession);
-
-	g_free(slpsession);
-}
-
-#if 0
-static void
-msn_slp_session_send_slpmsg(MsnSlpSession *slpsession, MsnSlpMessage *slpmsg)
-{
-	slpmsg->slpsession = slpsession;
-
-#if 0
-	slpmsg->session_id = slpsession->id;
-	slpmsg->app_id = slpsession->app_id;
-#endif
-
-	msn_slplink_send_slpmsg(slpsession->slpcall->slplink, slpmsg);
-}
-#endif
--- a/libpurple/protocols/msnp9/slpsession.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/**
- * @file slpsession.h SLP Session functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_SLPSESSION_H_
-#define _MSN_SLPSESSION_H_
-
-typedef struct _MsnSlpSession MsnSlpSession;
-
-#include "slpcall.h"
-#include "slpsession.h"
-#include "slpmsg.h"
-
-struct _MsnSlpSession
-{
-	/* MsnSlpLink *slplink; */
-	MsnSlpCall *slpcall;
-
-	long id;
-
-	long app_id;
-	char *call_id;
-};
-
-MsnSlpSession *msn_slp_session_new(MsnSlpCall *slpcall);
-void msn_slp_session_destroy(MsnSlpSession *slpsession);
-void msn_slpsession_send_slpmsg(MsnSlpSession *slpsession,
-								MsnSlpMessage *slpmsg);
-#endif /* _MSN_SLPSESSION_H_ */
--- a/libpurple/protocols/msnp9/state.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-/**
- * @file state.c State functions and definitions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "state.h"
-
-static const char *away_text[] =
-{
-	N_("Available"),
-	N_("Available"),
-	N_("Busy"),
-	N_("Idle"),
-	N_("Be Right Back"),
-	N_("Away From Computer"),
-	N_("On The Phone"),
-	N_("Out To Lunch"),
-	N_("Available"),
-	N_("Available")
-};
-
-void
-msn_change_status(MsnSession *session)
-{
-	PurpleAccount *account;
-	MsnCmdProc *cmdproc;
-	MsnUser *user;
-	MsnObject *msnobj;
-	const char *state_text;
-
-	g_return_if_fail(session != NULL);
-	g_return_if_fail(session->notification != NULL);
-
-	account = session->account;
-	cmdproc = session->notification->cmdproc;
-	user = session->user;
-	state_text = msn_state_get_text(msn_state_from_account(account));
-
-	/* If we're not logged in yet, don't send the status to the server,
-	 * it will be sent when login completes
-	 */
-	if (!session->logged_in)
-		return;
-
-	msnobj = msn_user_get_object(user);
-
-	if (msnobj == NULL)
-	{
-		msn_cmdproc_send(cmdproc, "CHG", "%s %d", state_text,
-						 MSN_CLIENT_ID);
-	}
-	else
-	{
-		char *msnobj_str;
-
-		msnobj_str = msn_object_to_string(msnobj);
-
-		msn_cmdproc_send(cmdproc, "CHG", "%s %d %s", state_text,
-						 MSN_CLIENT_ID, purple_url_encode(msnobj_str));
-
-		g_free(msnobj_str);
-	}
-}
-
-const char *
-msn_away_get_text(MsnAwayType type)
-{
-	g_return_val_if_fail(type <= MSN_HIDDEN, NULL);
-
-	return _(away_text[type]);
-}
-
-const char *
-msn_state_get_text(MsnAwayType state)
-{
-	static char *status_text[] =
-	{ "NLN", "NLN", "BSY", "IDL", "BRB", "AWY", "PHN", "LUN", "HDN", "HDN" };
-
-	return status_text[state];
-}
-
-MsnAwayType
-msn_state_from_account(PurpleAccount *account)
-{
-	MsnAwayType msnstatus;
-	PurplePresence *presence;
-	PurpleStatus *status;
-	const char *status_id;
-
-	presence = purple_account_get_presence(account);
-	status = purple_presence_get_active_status(presence);
-	status_id = purple_status_get_id(status);
-
-	if (!strcmp(status_id, "away"))
-		msnstatus = MSN_AWAY;
-	else if (!strcmp(status_id, "brb"))
-		msnstatus = MSN_BRB;
-	else if (!strcmp(status_id, "busy"))
-		msnstatus = MSN_BUSY;
-	else if (!strcmp(status_id, "phone"))
-		msnstatus = MSN_PHONE;
-	else if (!strcmp(status_id, "lunch"))
-		msnstatus = MSN_LUNCH;
-	else if (!strcmp(status_id, "invisible"))
-		msnstatus = MSN_HIDDEN;
-	else
-		msnstatus = MSN_ONLINE;
-
-	if ((msnstatus == MSN_ONLINE) && purple_presence_is_idle(presence))
-		msnstatus = MSN_IDLE;
-
-	return msnstatus;
-}
--- a/libpurple/protocols/msnp9/state.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/**
- * @file state.h State functions and definitions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_STATE_H_
-#define _MSN_STATE_H_
-
-/**
- * Away types.
- */
-typedef enum
-{
-	MSN_ONLINE  = 1,
-	MSN_BUSY    = 2,
-	MSN_IDLE    = 3,
-	MSN_BRB     = 4,
-	MSN_AWAY    = 5,
-	MSN_PHONE   = 6,
-	MSN_LUNCH   = 7,
-	MSN_OFFLINE = 8,
-	MSN_HIDDEN  = 9
-
-} MsnAwayType;
-
-/**
- * Changes the status of the user.
- *
- * @param session The MSN session.
- */
-void msn_change_status(MsnSession *session);
-
-/**
- * Returns the string representation of an away type.
- *
- * @param type The away type.
- *
- * @return The string representation of the away type.
- */
-const char *msn_away_get_text(MsnAwayType type);
-
-const char *msn_state_get_text(MsnAwayType state);
-
-MsnAwayType msn_state_from_account(PurpleAccount *account);
-
-#endif /* _MSN_STATE_H_ */
--- a/libpurple/protocols/msnp9/switchboard.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1305 +0,0 @@
-/**
- * @file switchboard.c MSN switchboard functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "prefs.h"
-#include "switchboard.h"
-#include "notification.h"
-#include "msn-utils.h"
-
-#include "error.h"
-
-static MsnTable *cbs_table;
-
-static void msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg,
-							 MsnMsgErrorType error);
-
-/**************************************************************************
- * Main
- **************************************************************************/
-
-MsnSwitchBoard *
-msn_switchboard_new(MsnSession *session)
-{
-	MsnSwitchBoard *swboard;
-	MsnServConn *servconn;
-
-	g_return_val_if_fail(session != NULL, NULL);
-
-	swboard = g_new0(MsnSwitchBoard, 1);
-
-	swboard->session = session;
-	swboard->servconn = servconn = msn_servconn_new(session, MSN_SERVCONN_SB);
-	swboard->cmdproc = servconn->cmdproc;
-
-	swboard->msg_queue = g_queue_new();
-	swboard->empty = TRUE;
-
-	swboard->cmdproc->data = swboard;
-	swboard->cmdproc->cbs_table = cbs_table;
-
-	session->switches = g_list_append(session->switches, swboard);
-
-	return swboard;
-}
-
-void
-msn_switchboard_destroy(MsnSwitchBoard *swboard)
-{
-	MsnSession *session;
-	MsnMessage *msg;
-	GList *l;
-
-#ifdef MSN_DEBUG_SB
-	purple_debug_info("msn", "switchboard_destroy: swboard(%p)\n", swboard);
-#endif
-
-	g_return_if_fail(swboard != NULL);
-
-	if (swboard->destroying)
-		return;
-
-	swboard->destroying = TRUE;
-
-	/* If it linked us is because its looking for trouble */
-	while (swboard->slplinks != NULL)
-		msn_slplink_destroy(swboard->slplinks->data);
-
-	/* Destroy the message queue */
-	while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
-	{
-		if (swboard->error != MSN_SB_ERROR_NONE)
-		{
-			/* The messages could not be sent due to a switchboard error */
-			msg_error_helper(swboard->cmdproc, msg,
-							 MSN_MSG_ERROR_SB);
-		}
-		msn_message_unref(msg);
-	}
-
-	g_queue_free(swboard->msg_queue);
-
-	/* msg_error_helper will both remove the msg from ack_list and
-	   unref it, so we don't need to do either here */
-	while ((l = swboard->ack_list) != NULL)
-		msg_error_helper(swboard->cmdproc, l->data, MSN_MSG_ERROR_SB);
-
-	g_free(swboard->im_user);
-	g_free(swboard->auth_key);
-	g_free(swboard->session_id);
-
-	for (l = swboard->users; l != NULL; l = l->next)
-		g_free(l->data);
-
-	if (swboard->users != NULL)
-		g_list_free(swboard->users);
-
-	session = swboard->session;
-	session->switches = g_list_remove(session->switches, swboard);
-
-#if 0
-	/* This should never happen or we are in trouble. */
-	if (swboard->servconn != NULL)
-		msn_servconn_destroy(swboard->servconn);
-#endif
-
-	swboard->cmdproc->data = NULL;
-
-	msn_servconn_set_disconnect_cb(swboard->servconn, NULL);
-
-	msn_servconn_destroy(swboard->servconn);
-
-	g_free(swboard);
-}
-
-void
-msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key)
-{
-	g_return_if_fail(swboard != NULL);
-	g_return_if_fail(key != NULL);
-
-	swboard->auth_key = g_strdup(key);
-}
-
-const char *
-msn_switchboard_get_auth_key(MsnSwitchBoard *swboard)
-{
-	g_return_val_if_fail(swboard != NULL, NULL);
-
-	return swboard->auth_key;
-}
-
-void
-msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id)
-{
-	g_return_if_fail(swboard != NULL);
-	g_return_if_fail(id != NULL);
-
-	if (swboard->session_id != NULL)
-		g_free(swboard->session_id);
-
-	swboard->session_id = g_strdup(id);
-}
-
-const char *
-msn_switchboard_get_session_id(MsnSwitchBoard *swboard)
-{
-	g_return_val_if_fail(swboard != NULL, NULL);
-
-	return swboard->session_id;
-}
-
-int
-msn_switchboard_get_chat_id(void)
-{
-	static int chat_id = 1;
-
-	return chat_id++;
-}
-
-void
-msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited)
-{
-	g_return_if_fail(swboard != NULL);
-
-	swboard->invited = invited;
-}
-
-gboolean
-msn_switchboard_is_invited(MsnSwitchBoard *swboard)
-{
-	g_return_val_if_fail(swboard != NULL, FALSE);
-
-	return swboard->invited;
-}
-
-/**************************************************************************
- * Utility
- **************************************************************************/
-
-static void
-send_clientcaps(MsnSwitchBoard *swboard)
-{
-	MsnMessage *msg;
-
-	msg = msn_message_new(MSN_MSG_CAPS);
-	msn_message_set_content_type(msg, "text/x-clientcaps");
-	msn_message_set_flag(msg, 'U');
-	msn_message_set_bin_data(msg, MSN_CLIENTINFO, strlen(MSN_CLIENTINFO));
-
-	msn_switchboard_send_msg(swboard, msg, TRUE);
-
-	msn_message_destroy(msg);
-}
-
-static void
-msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
-{
-	MsnCmdProc *cmdproc;
-	PurpleAccount *account;
-
-	g_return_if_fail(swboard != NULL);
-
-	cmdproc = swboard->cmdproc;
-	account = cmdproc->session->account;
-
-	swboard->users = g_list_prepend(swboard->users, g_strdup(user));
-	swboard->current_users++;
-	swboard->empty = FALSE;
-
-#ifdef MSN_DEBUG_CHAT
-	purple_debug_info("msn", "user=[%s], total=%d\n", user,
-					swboard->current_users);
-#endif
-
-	if (!(swboard->flag & MSN_SB_FLAG_IM) && (swboard->conv != NULL))
-	{
-		/* This is a helper switchboard. */
-		purple_debug_error("msn", "switchboard_add_user: conv != NULL\n");
-		return;
-	}
-
-	if ((swboard->conv != NULL) &&
-		(purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
-	{
-		purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL,
-								PURPLE_CBFLAGS_NONE, TRUE);
-	}
-	else if (swboard->current_users > 1 || swboard->total_users > 1)
-	{
-		if (swboard->conv == NULL ||
-			purple_conversation_get_type(swboard->conv) != PURPLE_CONV_TYPE_CHAT)
-		{
-			GList *l;
-
-#ifdef MSN_DEBUG_CHAT
-			purple_debug_info("msn", "[chat] Switching to chat.\n");
-#endif
-
-#if 0
-			/* this is bad - it causes msn_switchboard_close to be called on the
-			 * switchboard we're in the middle of using :( */
-			if (swboard->conv != NULL)
-				purple_conversation_destroy(swboard->conv);
-#endif
-
-			swboard->chat_id = msn_switchboard_get_chat_id();
-			swboard->flag |= MSN_SB_FLAG_IM;
-			swboard->conv = serv_got_joined_chat(account->gc,
-												 swboard->chat_id,
-												 "MSN Chat");
-
-			for (l = swboard->users; l != NULL; l = l->next)
-			{
-				const char *tmp_user;
-
-				tmp_user = l->data;
-
-#ifdef MSN_DEBUG_CHAT
-				purple_debug_info("msn", "[chat] Adding [%s].\n", tmp_user);
-#endif
-
-				purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
-										tmp_user, NULL, PURPLE_CBFLAGS_NONE, TRUE);
-			}
-
-#ifdef MSN_DEBUG_CHAT
-			purple_debug_info("msn", "[chat] We add ourselves.\n");
-#endif
-
-			purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
-									purple_account_get_username(account),
-									NULL, PURPLE_CBFLAGS_NONE, TRUE);
-
-			g_free(swboard->im_user);
-			swboard->im_user = NULL;
-		}
-	}
-	else if (swboard->conv == NULL)
-	{
-		swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-															user, account);
-	}
-	else
-	{
-		purple_debug_warning("msn", "switchboard_add_user: This should not happen!\n");
-	}
-}
-
-static PurpleConversation *
-msn_switchboard_get_conv(MsnSwitchBoard *swboard)
-{
-	PurpleAccount *account;
-
-	g_return_val_if_fail(swboard != NULL, NULL);
-
-	if (swboard->conv != NULL)
-		return swboard->conv;
-
-	purple_debug_error("msn", "Switchboard with unassigned conversation\n");
-
-	account = swboard->session->account;
-
-	return (swboard->conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
-												  account, swboard->im_user));
-}
-
-static void
-msn_switchboard_report_user(MsnSwitchBoard *swboard, PurpleMessageFlags flags, const char *msg)
-{
-	PurpleConversation *conv;
-
-	g_return_if_fail(swboard != NULL);
-	g_return_if_fail(msg != NULL);
-
-	if ((conv = msn_switchboard_get_conv(swboard)) != NULL)
-	{
-		purple_conversation_write(conv, NULL, msg, flags, time(NULL));
-	}
-}
-
-static void
-swboard_error_helper(MsnSwitchBoard *swboard, int reason, const char *passport)
-{
-	g_return_if_fail(swboard != NULL);
-
-	purple_debug_warning("msg", "Error: Unable to call the user %s for reason %i\n",
-					   passport ? passport : "(null)", reason);
-
-	/* TODO: if current_users > 0, this is probably a chat and an invite failed,
-	 * we should report that in the chat or something */
-	if (swboard->current_users == 0)
-	{
-		swboard->error = reason;
-		msn_switchboard_close(swboard);
-	}
-}
-
-static void
-cal_error_helper(MsnTransaction *trans, int reason)
-{
-	MsnSwitchBoard *swboard;
-	const char *passport;
-	char **params;
-
-	params = g_strsplit(trans->params, " ", 0);
-
-	passport = params[0];
-
-	swboard = trans->data;
-
-	purple_debug_warning("msn", "cal_error_helper: command %s failed for reason %i\n",trans->command,reason);
-
-	swboard_error_helper(swboard, reason, passport);
-
-	g_strfreev(params);
-}
-
-static void
-msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error)
-{
-	MsnSwitchBoard *swboard;
-
-	g_return_if_fail(cmdproc != NULL);
-	g_return_if_fail(msg     != NULL);
-
-	if ((error != MSN_MSG_ERROR_SB) && (msg->nak_cb != NULL))
-		msg->nak_cb(msg, msg->ack_data);
-
-	swboard = cmdproc->data;
-
-	/* This is not good, and should be fixed somewhere else. */
-	g_return_if_fail(swboard != NULL);
-
-	if (msg->type == MSN_MSG_TEXT)
-	{
-		const char *format, *str_reason;
-		char *body_str, *body_enc, *pre, *post;
-
-#if 0
-		if (swboard->conv == NULL)
-		{
-			if (msg->ack_ref)
-				msn_message_unref(msg);
-
-			return;
-		}
-#endif
-
-		if (error == MSN_MSG_ERROR_TIMEOUT)
-		{
-			str_reason = _("Message may have not been sent "
-						   "because a timeout occurred:");
-		}
-		else if (error == MSN_MSG_ERROR_SB)
-		{
-			switch (swboard->error)
-			{
-				case MSN_SB_ERROR_OFFLINE:
-					str_reason = _("Message could not be sent, "
-								   "not allowed while invisible:");
-					break;
-				case MSN_SB_ERROR_USER_OFFLINE:
-					str_reason = _("Message could not be sent "
-								   "because the user is offline:");
-					break;
-				case MSN_SB_ERROR_CONNECTION:
-					str_reason = _("Message could not be sent "
-								   "because a connection error occurred:");
-					break;
-				case MSN_SB_ERROR_TOO_FAST:
-					str_reason = _("Message could not be sent "
-								   "because we are sending too quickly:");
-					break;
-				case MSN_SB_ERROR_AUTHFAILED:
-					str_reason = _("Message could not be sent "
-								   "because we were unable to establish a "
-								   "session with the server. This is "
-								   "likely a server problem, try again in "
-								   "a few minutes:");
-					break;
-				default:
-					str_reason = _("Message could not be sent "
-								   "because an error with "
-								   "the switchboard occurred:");
-					break;
-			}
-		}
-		else
-		{
-			str_reason = _("Message may have not been sent "
-						   "because an unknown error occurred:");
-		}
-
-		body_str = msn_message_to_string(msg);
-		body_enc = g_markup_escape_text(body_str, -1);
-		g_free(body_str);
-
-		format = msn_message_get_attr(msg, "X-MMS-IM-Format");
-		msn_parse_format(format, &pre, &post);
-		body_str = g_strdup_printf("%s%s%s", pre ? pre : "",
-								   body_enc ? body_enc : "", post ? post : "");
-		g_free(body_enc);
-		g_free(pre);
-		g_free(post);
-
-		msn_switchboard_report_user(swboard, PURPLE_MESSAGE_ERROR,
-									str_reason);
-		msn_switchboard_report_user(swboard, PURPLE_MESSAGE_RAW,
-									body_str);
-
-		g_free(body_str);
-	}
-
-	/* If a timeout occures we will want the msg around just in case we
-	 * receive the ACK after the timeout. */
-	if (msg->ack_ref && error != MSN_MSG_ERROR_TIMEOUT)
-	{
-		swboard->ack_list = g_list_remove(swboard->ack_list, msg);
-		msn_message_unref(msg);
-	}
-}
-
-/**************************************************************************
- * Message Stuff
- **************************************************************************/
-
-/** Called when a message times out. */
-static void
-msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
-{
-	MsnMessage *msg;
-
-	msg = trans->data;
-
-	msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT);
-}
-
-/** Called when we receive an error of a message. */
-static void
-msg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	msg_error_helper(cmdproc, trans->data, MSN_MSG_ERROR_UNKNOWN);
-}
-
-#if 0
-/** Called when we receive an ack of a special message. */
-static void
-msg_ack(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnMessage *msg;
-
-	msg = cmd->trans->data;
-
-	if (msg->ack_cb != NULL)
-		msg->ack_cb(msg->ack_data);
-
-	msn_message_unref(msg);
-}
-
-/** Called when we receive a nak of a special message. */
-static void
-msg_nak(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnMessage *msg;
-
-	msg = cmd->trans->data;
-
-	msn_message_unref(msg);
-}
-#endif
-
-static void
-release_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
-{
-	MsnCmdProc *cmdproc;
-	MsnTransaction *trans;
-	char *payload;
-	gsize payload_len;
-
-	g_return_if_fail(swboard != NULL);
-	g_return_if_fail(msg     != NULL);
-
-	cmdproc = swboard->cmdproc;
-
-	payload = msn_message_gen_payload(msg, &payload_len);
-
-#ifdef MSN_DEBUG_SB
-	msn_message_show_readable(msg, "SB SEND", FALSE);
-#endif
-
-	trans = msn_transaction_new(cmdproc, "MSG", "%c %d",
-								msn_message_get_flag(msg), payload_len);
-
-	/* Data for callbacks */
-	msn_transaction_set_data(trans, msg);
-
-	if (msg->type == MSN_MSG_TEXT)
-	{
-		msg->ack_ref = TRUE;
-		msn_message_ref(msg);
-		swboard->ack_list = g_list_append(swboard->ack_list, msg);
-		msn_transaction_set_timeout_cb(trans, msg_timeout);
-	}
-	else if (msg->type == MSN_MSG_SLP)
-	{
-		msg->ack_ref = TRUE;
-		msn_message_ref(msg);
-		swboard->ack_list = g_list_append(swboard->ack_list, msg);
-		msn_transaction_set_timeout_cb(trans, msg_timeout);
-#if 0
-		if (msg->ack_cb != NULL)
-		{
-			msn_transaction_add_cb(trans, "ACK", msg_ack);
-			msn_transaction_add_cb(trans, "NAK", msg_nak);
-		}
-#endif
-	}
-
-	trans->payload = payload;
-	trans->payload_len = payload_len;
-
-	msg->trans = trans;
-
-	msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-static void
-queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
-{
-	g_return_if_fail(swboard != NULL);
-	g_return_if_fail(msg     != NULL);
-
-	purple_debug_info("msn", "Appending message to queue.\n");
-
-	g_queue_push_tail(swboard->msg_queue, msg);
-
-	msn_message_ref(msg);
-}
-
-static void
-process_queue(MsnSwitchBoard *swboard)
-{
-	MsnMessage *msg;
-
-	g_return_if_fail(swboard != NULL);
-
-	purple_debug_info("msn", "Processing queue\n");
-
-	while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
-	{
-		purple_debug_info("msn", "Sending message\n");
-		release_msg(swboard, msg);
-		msn_message_unref(msg);
-	}
-}
-
-gboolean
-msn_switchboard_can_send(MsnSwitchBoard *swboard)
-{
-	g_return_val_if_fail(swboard != NULL, FALSE);
-
-	if (swboard->empty || !g_queue_is_empty(swboard->msg_queue))
-		return FALSE;
-
-	return TRUE;
-}
-
-void
-msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
-						 gboolean queue)
-{
-	g_return_if_fail(swboard != NULL);
-	g_return_if_fail(msg     != NULL);
-
-	if (msn_switchboard_can_send(swboard))
-		release_msg(swboard, msg);
-	else if (queue)
-		queue_msg(swboard, msg);
-}
-
-/**************************************************************************
- * Switchboard Commands
- **************************************************************************/
-
-static void
-ans_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSwitchBoard *swboard;
-
-	swboard = cmdproc->data;
-	swboard->ready = TRUE;
-}
-
-static void
-bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSwitchBoard *swboard;
-	const char *user;
-
-	swboard = cmdproc->data;
-	user = cmd->params[0];
-
-	/* cmdproc->data is set to NULL when the switchboard is destroyed;
-	 * we may get a bye shortly thereafter. */
-	g_return_if_fail(swboard != NULL);
-
-	if (!(swboard->flag & MSN_SB_FLAG_IM) && (swboard->conv != NULL))
-		purple_debug_error("msn_switchboard", "bye_cmd: helper bug\n");
-
-	if (swboard->conv == NULL)
-	{
-		/* This is a helper switchboard */
-		msn_switchboard_destroy(swboard);
-	}
-	else if ((swboard->current_users > 1) ||
-			 (purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
-	{
-		/* This is a switchboard used for a chat */
-		purple_conv_chat_remove_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL);
-		swboard->current_users--;
-		if (swboard->current_users == 0)
-			msn_switchboard_destroy(swboard);
-	}
-	else
-	{
-		/* This is a switchboard used for a im session */
-		msn_switchboard_destroy(swboard);
-	}
-}
-
-static void
-iro_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	MsnSwitchBoard *swboard;
-
-	account = cmdproc->session->account;
-	gc = account->gc;
-	swboard = cmdproc->data;
-
-	swboard->total_users = atoi(cmd->params[2]);
-
-	msn_switchboard_add_user(swboard, cmd->params[3]);
-}
-
-static void
-joi_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session;
-	PurpleAccount *account;
-	PurpleConnection *gc;
-	MsnSwitchBoard *swboard;
-	const char *passport;
-
-	passport = cmd->params[0];
-
-	session = cmdproc->session;
-	account = session->account;
-	gc = account->gc;
-	swboard = cmdproc->data;
-
-	msn_switchboard_add_user(swboard, passport);
-
-	process_queue(swboard);
-
-	if (!session->http_method)
-		send_clientcaps(swboard);
-
-	if (swboard->closed)
-		msn_switchboard_close(swboard);
-}
-
-static void
-msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
-{
-	MsnMessage *msg;
-
-	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
-
-	msn_message_parse_payload(msg, payload, len);
-#ifdef MSN_DEBUG_SB
-	msn_message_show_readable(msg, "SB RECV", FALSE);
-#endif
-
-	if (msg->remote_user != NULL)
-		g_free (msg->remote_user);
-
-	msg->remote_user = g_strdup(cmd->params[0]);
-	msn_cmdproc_process_msg(cmdproc, msg);
-
-	msn_message_destroy(msg);
-}
-
-static void
-msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	cmdproc->servconn->payload_len = atoi(cmd->params[2]);
-	cmdproc->last_cmd->payload_cb = msg_cmd_post;
-}
-
-static void
-nak_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnMessage *msg;
-
-	msg = cmd->trans->data;
-	g_return_if_fail(msg != NULL);
-
-	msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_NAK);
-}
-
-static void
-ack_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSwitchBoard *swboard;
-	MsnMessage *msg;
-
-	msg = cmd->trans->data;
-
-	if (msg->ack_cb != NULL)
-		msg->ack_cb(msg, msg->ack_data);
-
-	swboard = cmdproc->data;
-	if (swboard)
-		swboard->ack_list = g_list_remove(swboard->ack_list, msg);
-	msn_message_unref(msg);
-}
-
-static void
-out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	PurpleConnection *gc;
-	MsnSwitchBoard *swboard;
-
-	gc = cmdproc->session->account->gc;
-	swboard = cmdproc->data;
-
-	if (swboard->current_users > 1)
-		serv_got_chat_left(gc, swboard->chat_id);
-
-	msn_switchboard_disconnect(swboard);
-}
-
-static void
-usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSwitchBoard *swboard;
-
-	swboard = cmdproc->data;
-
-#if 0
-	GList *l;
-
-	for (l = swboard->users; l != NULL; l = l->next)
-	{
-		const char *user;
-		user = l->data;
-
-		msn_cmdproc_send(cmdproc, "CAL", "%s", user);
-	}
-#endif
-
-	swboard->ready = TRUE;
-	msn_cmdproc_process_queue(cmdproc);
-}
-
-/**************************************************************************
- * Message Handlers
- **************************************************************************/
-static void
-plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	PurpleConnection *gc;
-	MsnSwitchBoard *swboard;
-	const char *body;
-	char *body_str;
-	char *body_enc;
-	char *body_final;
-	size_t body_len;
-	const char *passport;
-	const char *value;
-
-	gc = cmdproc->session->account->gc;
-	swboard = cmdproc->data;
-
-	body = msn_message_get_bin_data(msg, &body_len);
-	body_str = g_strndup(body, body_len);
-	body_enc = g_markup_escape_text(body_str, -1);
-	g_free(body_str);
-
-	passport = msg->remote_user;
-
-	if (!strcmp(passport, "messenger@microsoft.com") &&
-		strstr(body, "immediate security update"))
-	{
-		return;
-	}
-
-#if 0
-	if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL)
-	{
-		purple_debug_misc("msn", "User-Agent = '%s'\n", value);
-	}
-#endif
-
-	if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)
-	{
-		char *pre, *post;
-
-		msn_parse_format(value, &pre, &post);
-
-		body_final = g_strdup_printf("%s%s%s", pre ? pre : "",
-									 body_enc ? body_enc : "", post ? post : "");
-
-		g_free(pre);
-		g_free(post);
-		g_free(body_enc);
-	}
-	else
-	{
-		body_final = body_enc;
-	}
-
-	swboard->flag |= MSN_SB_FLAG_IM;
-
-	if (swboard->current_users > 1 ||
-		((swboard->conv != NULL) &&
-		 purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
-	{
-		/* If current_users is always ok as it should then there is no need to
-		 * check if this is a chat. */
-		if (swboard->current_users <= 1)
-			purple_debug_misc("msn", "plain_msg: current_users(%d)\n",
-							swboard->current_users);
-
-		serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final,
-						 time(NULL));
-		if (swboard->conv == NULL)
-		{
-			swboard->conv = purple_find_chat(gc, swboard->chat_id);
-			swboard->flag |= MSN_SB_FLAG_IM;
-		}
-	}
-	else
-	{
-		serv_got_im(gc, passport, body_final, 0, time(NULL));
-		if (swboard->conv == NULL)
-		{
-			swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-									passport, purple_connection_get_account(gc));
-			swboard->flag |= MSN_SB_FLAG_IM;
-		}
-	}
-
-	g_free(body_final);
-}
-
-static void
-control_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	PurpleConnection *gc;
-	MsnSwitchBoard *swboard;
-	char *passport;
-
-	gc = cmdproc->session->account->gc;
-	swboard = cmdproc->data;
-	passport = msg->remote_user;
-
-	if (swboard->current_users == 1 &&
-		msn_message_get_attr(msg, "TypingUser") != NULL)
-	{
-		serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
-						PURPLE_TYPING);
-	}
-}
-
-static void
-clientcaps_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-#if 0
-	MsnSession *session;
-	MsnSwitchBoard *swboard;
-	MsnUser *user;
-	GHashTable *clientcaps;
-	const char *value;
-
-	char *passport = msg->sender;
-
-	session = cmdproc->session;
-	swboard = cmdproc->servconn->swboard;
-
-	clientcaps = msn_message_get_hashtable_from_body(msg);
-#endif
-}
-
-static void
-nudge_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
-	MsnSwitchBoard *swboard;
-	PurpleAccount *account;
-	const char *user;
-
-	swboard = cmdproc->data;
-	account = cmdproc->session->account;
-	user = msg->remote_user;
-
-	serv_got_attention(account->gc, user, MSN_NUDGE);
-}
-
-/**************************************************************************
- * Connect stuff
- **************************************************************************/
-static void
-ans_usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error);
-
-static void
-connect_cb(MsnServConn *servconn)
-{
-	MsnSwitchBoard *swboard;
-	MsnTransaction *trans;
-	MsnCmdProc *cmdproc;
-	PurpleAccount *account;
-
-	cmdproc = servconn->cmdproc;
-	g_return_if_fail(cmdproc != NULL);
-
-	account = cmdproc->session->account;
-	swboard = cmdproc->data;
-	g_return_if_fail(swboard != NULL);
-
-	if (msn_switchboard_is_invited(swboard))
-	{
-		swboard->empty = FALSE;
-
-		trans = msn_transaction_new(cmdproc, "ANS", "%s %s %s",
-									purple_account_get_username(account),
-									swboard->auth_key, swboard->session_id);
-	}
-	else
-	{
-		trans = msn_transaction_new(cmdproc, "USR", "%s %s",
-									purple_account_get_username(account),
-									swboard->auth_key);
-	}
-
-	msn_transaction_set_error_cb(trans, ans_usr_error);
-	msn_transaction_set_data(trans, swboard);
-	msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-static void
-ans_usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	MsnSwitchBoard *swboard;
-	char **params;
-	char *passport;
-	int reason = MSN_SB_ERROR_UNKNOWN;
-
-	if (error == 911)
-	{
-		reason = MSN_SB_ERROR_AUTHFAILED;
-	}
-
-	purple_debug_warning("msn", "ans_usr_error: command %s gave error %i\n", trans->command, error);
-
-	params = g_strsplit(trans->params, " ", 0);
-	passport = params[0];
-	swboard = trans->data;
-
-	swboard_error_helper(swboard, reason, passport);
-
-	g_strfreev(params);
-}
-
-static void
-disconnect_cb(MsnServConn *servconn)
-{
-	MsnSwitchBoard *swboard;
-
-	swboard = servconn->cmdproc->data;
-	g_return_if_fail(swboard != NULL);
-
-	msn_servconn_set_disconnect_cb(swboard->servconn, NULL);
-
-	msn_switchboard_destroy(swboard);
-}
-
-gboolean
-msn_switchboard_connect(MsnSwitchBoard *swboard, const char *host, int port)
-{
-	g_return_val_if_fail(swboard != NULL, FALSE);
-
-	msn_servconn_set_connect_cb(swboard->servconn, connect_cb);
-	msn_servconn_set_disconnect_cb(swboard->servconn, disconnect_cb);
-
-	return msn_servconn_connect(swboard->servconn, host, port);
-}
-
-void
-msn_switchboard_disconnect(MsnSwitchBoard *swboard)
-{
-	g_return_if_fail(swboard != NULL);
-
-	msn_servconn_disconnect(swboard->servconn);
-}
-
-/**************************************************************************
- * Call stuff
- **************************************************************************/
-static void
-got_cal(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-#if 0
-	MsnSwitchBoard *swboard;
-	const char *user;
-
-	swboard = cmdproc->data;
-
-	user = cmd->params[0];
-
-	msn_switchboard_add_user(swboard, user);
-#endif
-}
-
-static void
-cal_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
-{
-	purple_debug_warning("msn", "cal_timeout: command %s timed out\n", trans->command);
-
-	cal_error_helper(trans, MSN_SB_ERROR_UNKNOWN);
-}
-
-static void
-cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	int reason = MSN_SB_ERROR_UNKNOWN;
-
-	if (error == 215)
-	{
-		purple_debug_info("msn", "Invited user already in switchboard\n");
-		return;
-	}
-	else if (error == 217)
-	{
-		reason = MSN_SB_ERROR_USER_OFFLINE;
-	}
-
-	purple_debug_warning("msn", "cal_error: command %s gave error %i\n", trans->command, error);
-
-	cal_error_helper(trans, reason);
-}
-
-void
-msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user)
-{
-	MsnTransaction *trans;
-	MsnCmdProc *cmdproc;
-
-	g_return_if_fail(swboard != NULL);
-
-	cmdproc = swboard->cmdproc;
-
-	trans = msn_transaction_new(cmdproc, "CAL", "%s", user);
-	/* this doesn't do anything, but users seem to think that
-	 * 'Unhandled command' is some kind of error, so we don't report it */
-	msn_transaction_add_cb(trans, "CAL", got_cal);
-
-	msn_transaction_set_data(trans, swboard);
-	msn_transaction_set_timeout_cb(trans, cal_timeout);
-
-	if (swboard->ready)
-		msn_cmdproc_send_trans(cmdproc, trans);
-	else
-		msn_cmdproc_queue_trans(cmdproc, trans);
-}
-
-/**************************************************************************
- * Create & Transfer stuff
- **************************************************************************/
-
-static void
-got_swboard(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSwitchBoard *swboard;
-	char *host;
-	int port;
-	swboard = cmd->trans->data;
-
-	if (g_list_find(cmdproc->session->switches, swboard) == NULL)
-		/* The conversation window was closed. */
-		return;
-
-	msn_switchboard_set_auth_key(swboard, cmd->params[4]);
-
-	msn_parse_socket(cmd->params[2], &host, &port);
-
-	if (!msn_switchboard_connect(swboard, host, port))
-		msn_switchboard_destroy(swboard);
-
-	g_free(host);
-}
-
-static void
-xfr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-	MsnSwitchBoard *swboard;
-	int reason = MSN_SB_ERROR_UNKNOWN;
-
-	if (error == 913)
-		reason = MSN_SB_ERROR_OFFLINE;
-	else if (error == 800)
-		reason = MSN_SB_ERROR_TOO_FAST;
-
-	swboard = trans->data;
-
-	purple_debug_info("msn", "xfr_error %i for %s: trans %p, command %s, reason %i\n",
-					error, (swboard->im_user ? swboard->im_user : "(null)"), trans,
-					(trans->command ? trans->command : "(null)"), reason);
-
-	swboard_error_helper(swboard, reason, swboard->im_user);
-}
-
-void
-msn_switchboard_request(MsnSwitchBoard *swboard)
-{
-	MsnCmdProc *cmdproc;
-	MsnTransaction *trans;
-
-	g_return_if_fail(swboard != NULL);
-
-	cmdproc = swboard->session->notification->cmdproc;
-
-	trans = msn_transaction_new(cmdproc, "XFR", "%s", "SB");
-	msn_transaction_add_cb(trans, "XFR", got_swboard);
-
-	msn_transaction_set_data(trans, swboard);
-	msn_transaction_set_error_cb(trans, xfr_error);
-
-	msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-void
-msn_switchboard_close(MsnSwitchBoard *swboard)
-{
-	g_return_if_fail(swboard != NULL);
-
-	if (swboard->error != MSN_SB_ERROR_NONE)
-	{
-		msn_switchboard_destroy(swboard);
-	}
-	else if (g_queue_is_empty(swboard->msg_queue) ||
-			 !swboard->session->connected)
-	{
-		MsnCmdProc *cmdproc;
-		cmdproc = swboard->cmdproc;
-		msn_cmdproc_send_quick(cmdproc, "OUT", NULL, NULL);
-
-		msn_switchboard_destroy(swboard);
-	}
-	else
-	{
-		swboard->closed = TRUE;
-	}
-}
-
-gboolean
-msn_switchboard_release(MsnSwitchBoard *swboard, MsnSBFlag flag)
-{
-	g_return_val_if_fail(swboard != NULL, FALSE);
-
-	swboard->flag &= ~flag;
-
-	if (flag == MSN_SB_FLAG_IM)
-		/* Forget any conversation that used to be associated with this
-		 * swboard. */
-		swboard->conv = NULL;
-
-	if (swboard->flag == 0)
-	{
-		msn_switchboard_close(swboard);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-/**************************************************************************
- * Init stuff
- **************************************************************************/
-
-void
-msn_switchboard_init(void)
-{
-	cbs_table = msn_table_new();
-
-	msn_table_add_cmd(cbs_table, "ANS", "ANS", ans_cmd);
-	msn_table_add_cmd(cbs_table, "ANS", "IRO", iro_cmd);
-
-	msn_table_add_cmd(cbs_table, "MSG", "ACK", ack_cmd);
-	msn_table_add_cmd(cbs_table, "MSG", "NAK", nak_cmd);
-
-	msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
-
-	msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
-
-#if 0
-	/* They might skip the history */
-	msn_table_add_cmd(cbs_table, NULL, "ACK", NULL);
-#endif
-
-	msn_table_add_error(cbs_table, "MSG", msg_error);
-	msn_table_add_error(cbs_table, "CAL", cal_error);
-
-	/* Register the message type callbacks. */
-	msn_table_add_msg_type(cbs_table, "text/plain",
-						   plain_msg);
-	msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol",
-						   control_msg);
-	msn_table_add_msg_type(cbs_table, "text/x-clientcaps",
-						   clientcaps_msg);
-	msn_table_add_msg_type(cbs_table, "text/x-clientinfo",
-						   clientcaps_msg);
-	msn_table_add_msg_type(cbs_table, "application/x-msnmsgrp2p",
-						   msn_p2p_msg);
-	msn_table_add_msg_type(cbs_table, "text/x-mms-emoticon",
-						   msn_emoticon_msg);
-	msn_table_add_msg_type(cbs_table, "text/x-mms-animemoticon",
-	                                           msn_emoticon_msg);
-	msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast",
-						   nudge_msg);
-#if 0
-	msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite",
-						   msn_invite_msg);
-#endif
-}
-
-void
-msn_switchboard_end(void)
-{
-	msn_table_destroy(cbs_table);
-}
--- a/libpurple/protocols/msnp9/switchboard.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,284 +0,0 @@
-/**
- * @file switchboard.h MSN switchboard functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_SWITCHBOARD_H_
-#define _MSN_SWITCHBOARD_H_
-
-typedef struct _MsnSwitchBoard MsnSwitchBoard;
-
-#include "conversation.h"
-
-#include "msg.h"
-#include "user.h"
-
-#include "servconn.h"
-
-#include "slplink.h"
-
-/**
- * A switchboard error.
- */
-typedef enum
-{
-	MSN_SB_ERROR_NONE, /**< No error. */
-	MSN_SB_ERROR_CAL, /**< The user could not join (answer the call). */
-	MSN_SB_ERROR_OFFLINE, /**< The account is offline. */
-	MSN_SB_ERROR_USER_OFFLINE, /**< The user to call is offline. */
-	MSN_SB_ERROR_CONNECTION, /**< There was a connection error. */
-	MSN_SB_ERROR_TOO_FAST, /**< We are sending too fast */
-	MSN_SB_ERROR_AUTHFAILED, /**< Authentication failed joining the switchboard session */
-	MSN_SB_ERROR_UNKNOWN /**< An unknown error occurred. */
-
-} MsnSBErrorType;
-
-/**
- * A switchboard flag.
- */
-typedef enum
-{
-	MSN_SB_FLAG_IM = 0x01, /**< This switchboard is being used for a conversation. */
-	MSN_SB_FLAG_FT = 0x02, /**< This switchboard is being used for file transfer. */
-
-} MsnSBFlag;
-
-/**
- * A switchboard.
- *
- * A place where a bunch of users send messages to the rest of the users.
- */
-struct _MsnSwitchBoard
-{
-	MsnSession *session;
-	MsnServConn *servconn;
-	MsnCmdProc *cmdproc;
-	char *im_user;
-
-	MsnSBFlag flag;
-	char *auth_key;
-	char *session_id;
-
-	PurpleConversation *conv; /**< The conversation that displays the
-							  messages of this switchboard, or @c NULL if
-							  this is a helper switchboard. */
-
-	gboolean empty;			/**< A flag that states if the swithcboard has no
-							  users in it. */
-	gboolean invited;		/**< A flag that states if we were invited to the
-							  switchboard. */
-	gboolean ready;			/**< A flag that states if this switchboard is
-							  ready to be used. */
-	gboolean closed;		/**< A flag that states if the switchboard has
-							  been closed by the user. */
-	gboolean destroying;	/**< A flag that states if the switchboard is
-							  alredy on the process of destruction. */
-
-	int current_users;
-	int total_users;
-	GList *users;
-
-	int chat_id;
-
-	GQueue *msg_queue; /**< Queue of messages to send. */
-	GList *ack_list; /**< List of messages waiting for an ack. */
-
-	MsnSBErrorType error; /**< The error that occurred in this switchboard
-							(if applicable). */
-	GList *slplinks; /**< The list of slplinks that are using this switchboard. */
-};
-
-/**
- * Initialize the variables for switchboard creation.
- */
-void msn_switchboard_init(void);
-
-/**
- * Destroy the variables for switchboard creation.
- */
-void msn_switchboard_end(void);
-
-/**
- * Creates a new switchboard.
- *
- * @param session The MSN session.
- *
- * @return The new switchboard.
- */
-MsnSwitchBoard *msn_switchboard_new(MsnSession *session);
-
-/**
- * Destroys a switchboard.
- *
- * @param swboard The switchboard to destroy.
- */
-void msn_switchboard_destroy(MsnSwitchBoard *swboard);
-
-/**
- * Sets the auth key the switchboard must use when connecting.
- *
- * @param swboard The switchboard.
- * @param key     The auth key.
- */
-void msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key);
-
-/**
- * Returns the auth key the switchboard must use when connecting.
- *
- * @param swboard The switchboard.
- *
- * @return The auth key.
- */
-const char *msn_switchboard_get_auth_key(MsnSwitchBoard *swboard);
-
-/**
- * Sets the session ID the switchboard must use when connecting.
- *
- * @param swboard The switchboard.
- * @param id      The session ID.
- */
-void msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id);
-
-/**
- * Returns the session ID the switchboard must use when connecting.
- *
- * @param swboard The switchboard.
- *
- * @return The session ID.
- */
-const char *msn_switchboard_get_session_id(MsnSwitchBoard *swboard);
-
-/**
- * Returns the next chat ID for use by a switchboard.
- *
- * @return The chat ID.
- */
-int msn_switchboard_get_chat_id(void);
-
-/**
- * Sets whether or not we were invited to this switchboard.
- *
- * @param swboard The switchboard.
- * @param invite  @c TRUE if invited, @c FALSE otherwise.
- */
-void msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited);
-
-/**
- * Returns whether or not we were invited to this switchboard.
- *
- * @param swboard The switchboard.
- *
- * @return @c TRUE if invited, @c FALSE otherwise.
- */
-gboolean msn_switchboard_is_invited(MsnSwitchBoard *swboard);
-
-/**
- * Connects to a switchboard.
- *
- * @param swboard The switchboard.
- * @param host    The switchboard server host.
- * @param port    The switcbharod server port.
- *
- * @return @c TRUE if able to connect, or @c FALSE otherwise.
- */
-gboolean msn_switchboard_connect(MsnSwitchBoard *swboard,
-								 const char *host, int port);
-
-/**
- * Disconnects from a switchboard.
- *
- * @param swboard The switchboard to disconnect from.
- */
-void msn_switchboard_disconnect(MsnSwitchBoard *swboard);
-
-/**
- * Closes the switchboard.
- *
- * Called when a conversation is closed.
- *
- * @param swboard The switchboard to close.
- */
-void msn_switchboard_close(MsnSwitchBoard *swboard);
-
-/**
- * Release a switchboard from a certain function.
- *
- * @param swboard The switchboard to release.
- * @param flag The flag that states the function.
- *
- * @return @c TRUE if the switchboard was closed, @c FALSE otherwise.
- */
-gboolean msn_switchboard_release(MsnSwitchBoard *swboard, MsnSBFlag flag);
-
-/**
- * Returns whether or not we currently can send a message through this
- * switchboard.
- *
- * @param swboard The switchboard.
- *
- * @return @c TRUE if a message can be sent, @c FALSE otherwise.
- */
-gboolean msn_switchboard_can_send(MsnSwitchBoard *swboard);
-
-/**
- * Sends a message through this switchboard.
- *
- * @param swboard The switchboard.
- * @param msg The message.
- * @param queue A flag that states if we want this message to be queued (in
- * the case it cannot currently be sent).
- *
- * @return @c TRUE if a message can be sent, @c FALSE otherwise.
- */
-void msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
-							  gboolean queue);
-
-gboolean msn_switchboard_chat_leave(MsnSwitchBoard *swboard);
-gboolean msn_switchboard_chat_invite(MsnSwitchBoard *swboard, const char *who);
-
-void msn_switchboard_request(MsnSwitchBoard *swboard);
-void msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user);
-
-/**
- * Processes peer to peer messages.
- *
- * @param cmdproc The command processor.
- * @param msg     The message.
- */
-void msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
-
-/**
- * Processes emoticon messages.
- *
- * @param cmdproc The command processor.
- * @param msg     The message.
- */
-void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
-
-/**
- * Processes INVITE messages.
- *
- * @param cmdproc The command processor.
- * @param msg     The message.
- */
-void msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
-
-#endif /* _MSN_SWITCHBOARD_H_ */
--- a/libpurple/protocols/msnp9/sync.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,260 +0,0 @@
-/**
- * @file sync.c MSN list synchronization functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "sync.h"
-#include "state.h"
-
-static MsnTable *cbs_table;
-
-static void
-blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	PurpleConnection *gc = cmdproc->session->account->gc;
-	const char *list_name;
-
-	list_name = cmd->params[0];
-
-	if (!g_ascii_strcasecmp(list_name, "AL"))
-	{
-		/*
-		 * If the current setting is AL, messages from users who
-		 * are not in BL will be delivered.
-		 *
-		 * In other words, deny some.
-		 */
-		gc->account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
-	}
-	else
-	{
-		/* If the current setting is BL, only messages from people
-		 * who are in the AL will be delivered.
-		 *
-		 * In other words, permit some.
-		 */
-		gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
-	}
-}
-
-static void
-prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session = cmdproc->session;
-	const char *type, *value;
-
-	type  = cmd->params[0];
-	value = cmd->params[1];
-
-	if (cmd->param_count == 2)
-	{
-		if (!strcmp(type, "PHH"))
-			msn_user_set_home_phone(session->user, purple_url_decode(value));
-		else if (!strcmp(type, "PHW"))
-			msn_user_set_work_phone(session->user, purple_url_decode(value));
-		else if (!strcmp(type, "PHM"))
-			msn_user_set_mobile_phone(session->user, purple_url_decode(value));
-	}
-	else
-	{
-		if (!strcmp(type, "PHH"))
-			msn_user_set_home_phone(session->user, NULL);
-		else if (!strcmp(type, "PHW"))
-			msn_user_set_work_phone(session->user, NULL);
-		else if (!strcmp(type, "PHM"))
-			msn_user_set_mobile_phone(session->user, NULL);
-	}
-}
-
-static void
-lsg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session = cmdproc->session;
-	const char *name;
-	int group_id;
-
-	group_id = atoi(cmd->params[0]);
-	name = purple_url_decode(cmd->params[1]);
-
-	msn_group_new(session->userlist, group_id, name);
-
-	/* HACK */
-	if (group_id == 0)
-	{
-		/* Group of ungroupped buddies */
-		if (session->sync->total_users == 0)
-		{
-			cmdproc->cbs_table = session->sync->old_cbs_table;
-
-			msn_session_finish_login(session);
-
-			msn_sync_destroy(session->sync);
-			session->sync = NULL;
-		}
-		return;
-	}
-
-	if ((purple_find_group(name)) == NULL)
-	{
-		PurpleGroup *g = purple_group_new(name);
-		purple_blist_add_group(g, NULL);
-	}
-}
-
-static void
-lst_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSession *session = cmdproc->session;
-	char *passport = NULL;
-	const char *friend = NULL;
-	int list_op;
-	MsnUser *user;
-
-	passport = cmd->params[0];
-	friend   = purple_url_decode(cmd->params[1]);
-	list_op  = atoi(cmd->params[2]);
-
-	user = msn_user_new(session->userlist, passport, friend);
-
-	msn_userlist_add_user(session->userlist, user);
-
-	session->sync->last_user = user;
-
-	/* TODO: This can be improved */
-
-	if (list_op & MSN_LIST_FL_OP)
-	{
-		char **c;
-		char **tokens;
-		const char *group_nums;
-		GSList *group_ids;
-
-		group_nums = cmd->params[3];
-
-		group_ids = NULL;
-
-		tokens = g_strsplit(group_nums, ",", -1);
-
-		for (c = tokens; *c != NULL; c++)
-		{
-			int id;
-
-			id = atoi(*c);
-			group_ids = g_slist_append(group_ids, GINT_TO_POINTER(id));
-		}
-
-		g_strfreev(tokens);
-
-		msn_got_lst_user(session, user, list_op, group_ids);
-
-		g_slist_free(group_ids);
-	}
-	else
-	{
-		msn_got_lst_user(session, user, list_op, NULL);
-	}
-
-	session->sync->num_users++;
-
-	if (session->sync->num_users == session->sync->total_users)
-	{
-		cmdproc->cbs_table = session->sync->old_cbs_table;
-
-		msn_session_finish_login(session);
-
-		msn_sync_destroy(session->sync);
-		session->sync = NULL;
-	}
-}
-
-static void
-bpr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-	MsnSync *sync = cmdproc->session->sync;
-	const char *type, *value;
-	MsnUser *user;
-
-	user = sync->last_user;
-
-	g_return_if_fail(user != NULL);
-
-	type     = cmd->params[0];
-	value    = cmd->params[1];
-
-	if (value)
-	{
-		if (!strcmp(type, "MOB"))
-		{
-			if (!strcmp(value, "Y"))
-				user->mobile = TRUE;
-			else if (!strcmp(value, "N"))
-				user->mobile = FALSE;
-		}
-		else if (!strcmp(type, "PHH"))
-			msn_user_set_home_phone(user, purple_url_decode(value));
-		else if (!strcmp(type, "PHW"))
-			msn_user_set_work_phone(user, purple_url_decode(value));
-		else if (!strcmp(type, "PHM"))
-			msn_user_set_mobile_phone(user, purple_url_decode(value));
-	}
-}
-
-void
-msn_sync_init(void)
-{
-	/* TODO: check prp, blp, bpr */
-
-	cbs_table = msn_table_new();
-
-	/* Syncing */
-	msn_table_add_cmd(cbs_table, NULL, "GTC", NULL);
-	msn_table_add_cmd(cbs_table, NULL, "BLP", blp_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "PRP", prp_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "LSG", lsg_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "LST", lst_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "BPR", bpr_cmd);
-}
-
-void
-msn_sync_end(void)
-{
-	msn_table_destroy(cbs_table);
-}
-
-MsnSync *
-msn_sync_new(MsnSession *session)
-{
-	MsnSync *sync;
-
-	sync = g_new0(MsnSync, 1);
-
-	sync->session = session;
-	sync->cbs_table = cbs_table;
-
-	return sync;
-}
-
-void
-msn_sync_destroy(MsnSync *sync)
-{
-	g_free(sync);
-}
--- a/libpurple/protocols/msnp9/sync.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/**
- * @file sync.h MSN list synchronization functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_SYNC_H_
-#define _MSN_SYNC_H_
-
-typedef struct _MsnSync MsnSync;
-
-#include "session.h"
-#include "table.h"
-#include "user.h"
-
-struct _MsnSync
-{
-	MsnSession *session;
-	MsnTable *cbs_table;
-	MsnTable *old_cbs_table;
-
-	int num_users;
-	int total_users;
-	int num_groups;
-	int total_groups;
-	MsnUser *last_user;
-};
-
-void msn_sync_init(void);
-void msn_sync_end(void);
-
-MsnSync * msn_sync_new(MsnSession *session);
-void msn_sync_destroy(MsnSync *sync);
-
-#endif /* _MSN_SYNC_H_ */
--- a/libpurple/protocols/msnp9/table.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-/**
- * @file table.c MSN helper structure
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "table.h"
-
-static void
-null_cmd_cb(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
-}
-
-static void
-null_error_cb(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
-{
-}
-
-MsnTable *
-msn_table_new()
-{
-	MsnTable *table;
-
-	table = g_new0(MsnTable, 1);
-
-	table->cmds = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_hash_table_destroy);
-	table->msgs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
-	table->errors = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
-
-	table->async = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
-	table->fallback = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
-
-	return table;
-}
-
-void
-msn_table_destroy(MsnTable *table)
-{
-	g_return_if_fail(table != NULL);
-
-	g_hash_table_destroy(table->cmds);
-	g_hash_table_destroy(table->msgs);
-	g_hash_table_destroy(table->errors);
-
-	g_hash_table_destroy(table->async);
-	g_hash_table_destroy(table->fallback);
-
-	g_free(table);
-}
-
-void
-msn_table_add_cmd(MsnTable *table,
-				  char *command, char *answer, MsnTransCb cb)
-{
-	GHashTable *cbs;
-
-	g_return_if_fail(table  != NULL);
-	g_return_if_fail(answer != NULL);
-
-	cbs = NULL;
-
-	if (command == NULL)
-	{
-		cbs = table->async;
-	}
-	else if (strcmp(command, "fallback") == 0)
-	{
-		cbs = table->fallback;
-	}
-	else
-	{
-		cbs = g_hash_table_lookup(table->cmds, command);
-
-		if (cbs == NULL)
-		{
-			cbs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
-			g_hash_table_insert(table->cmds, command, cbs);
-		}
-	}
-
-	if (cb == NULL)
-		cb = null_cmd_cb;
-
-	g_hash_table_insert(cbs, answer, cb);
-}
-
-void
-msn_table_add_error(MsnTable *table,
-					char *answer, MsnErrorCb cb)
-{
-	g_return_if_fail(table  != NULL);
-	g_return_if_fail(answer != NULL);
-
-	if (cb == NULL)
-		cb = null_error_cb;
-
-	g_hash_table_insert(table->errors, answer, cb);
-}
-
-void
-msn_table_add_msg_type(MsnTable *table,
-					   char *type, MsnMsgTypeCb cb)
-{
-	g_return_if_fail(table != NULL);
-	g_return_if_fail(type  != NULL);
-	g_return_if_fail(cb    != NULL);
-
-#if 0
-	if (cb == NULL)
-		cb = null_msg_cb;
-#endif
-
-	g_hash_table_insert(table->msgs, type, cb);
-}
--- a/libpurple/protocols/msnp9/table.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * @file table.h MSN helper structure
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_TABLE_H_
-#define _MSN_TABLE_H_
-
-typedef struct _MsnTable MsnTable;
-
-#include "cmdproc.h"
-#include "transaction.h"
-#include "msg.h"
-
-typedef void (*MsnMsgTypeCb)(MsnCmdProc *cmdproc, MsnMessage *msg);
-
-struct _MsnTable
-{
-	GHashTable *cmds;
-	GHashTable *msgs;
-	GHashTable *errors;
-
-	GHashTable *async;
-	GHashTable *fallback;
-};
-
-MsnTable *msn_table_new(void);
-void msn_table_destroy(MsnTable *table);
-
-void msn_table_add_cmd(MsnTable *table, char *command, char *answer,
-					   MsnTransCb cb);
-void msn_table_add_error(MsnTable *table, char *answer, MsnErrorCb cb);
-void msn_table_add_msg_type(MsnTable *table, char *type, MsnMsgTypeCb cb);
-
-#endif /* _MSN_TABLE_H_ */
--- a/libpurple/protocols/msnp9/transaction.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,221 +0,0 @@
-/**
- * @file transaction.c MSN transaction functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "transaction.h"
-
-MsnTransaction *
-msn_transaction_new(MsnCmdProc *cmdproc, const char *command,
-					const char *format, ...)
-{
-	MsnTransaction *trans;
-	va_list arg;
-
-	g_return_val_if_fail(command != NULL, NULL);
-
-	trans = g_new0(MsnTransaction, 1);
-
-	trans->cmdproc = cmdproc;
-	trans->command = g_strdup(command);
-
-	if (format != NULL)
-	{
-		va_start(arg, format);
-		trans->params = g_strdup_vprintf(format, arg);
-		va_end(arg);
-	}
-
-	/* trans->queue = g_queue_new(); */
-
-	return trans;
-}
-
-void
-msn_transaction_destroy(MsnTransaction *trans)
-{
-	g_return_if_fail(trans != NULL);
-
-	g_free(trans->command);
-	g_free(trans->params);
-	g_free(trans->payload);
-
-#if 0
-	if (trans->pendent_cmd != NULL)
-		msn_message_unref(trans->pendent_msg);
-#endif
-
-#if 0
-	MsnTransaction *elem;
-	if (trans->queue != NULL)
-	{
-		while ((elem = g_queue_pop_head(trans->queue)) != NULL)
-			msn_transaction_destroy(elem);
-
-		g_queue_free(trans->queue);
-	}
-#endif
-
-	if (trans->callbacks != NULL && trans->has_custom_callbacks)
-		g_hash_table_destroy(trans->callbacks);
-
-	if (trans->timer)
-		purple_timeout_remove(trans->timer);
-
-	g_free(trans);
-}
-
-char *
-msn_transaction_to_string(MsnTransaction *trans)
-{
-	char *str;
-
-	g_return_val_if_fail(trans != NULL, FALSE);
-
-	if (trans->params != NULL)
-		str = g_strdup_printf("%s %u %s\r\n", trans->command, trans->trId, trans->params);
-	else
-		str = g_strdup_printf("%s %u\r\n", trans->command, trans->trId);
-
-	return str;
-}
-
-void
-msn_transaction_queue_cmd(MsnTransaction *trans, MsnCommand *cmd)
-{
-	purple_debug_info("msn", "queueing command.\n");
-	trans->pendent_cmd = cmd;
-	msn_command_ref(cmd);
-}
-
-void
-msn_transaction_unqueue_cmd(MsnTransaction *trans, MsnCmdProc *cmdproc)
-{
-	MsnCommand *cmd;
-
-	if (!cmdproc->servconn->connected)
-		return;
-
-	purple_debug_info("msn", "unqueueing command.\n");
-	cmd = trans->pendent_cmd;
-
-	g_return_if_fail(cmd != NULL);
-
-	msn_cmdproc_process_cmd(cmdproc, cmd);
-	msn_command_unref(cmd);
-
-	trans->pendent_cmd = NULL;
-}
-
-#if 0
-void
-msn_transaction_queue(MsnTransaction *trans, MsnTransaction *elem)
-{
-	if (trans->queue == NULL)
-		trans->queue = g_queue_new();
-
-	g_queue_push_tail(trans->queue, elem);
-}
-
-void
-msn_transaction_unqueue(MsnTransaction *trans, MsnCmdProc *cmdproc)
-{
-	MsnTransaction *elem;
-
-	while ((elem = g_queue_pop_head(trans->queue)) != NULL)
-		msn_cmdproc_send_trans(cmdproc, elem);
-}
-#endif
-
-void
-msn_transaction_set_payload(MsnTransaction *trans,
-							const char *payload, int payload_len)
-{
-	g_return_if_fail(trans   != NULL);
-	g_return_if_fail(payload != NULL);
-
-	trans->payload = g_strdup(payload);
-	trans->payload_len = payload_len ? payload_len : strlen(trans->payload);
-}
-
-void
-msn_transaction_set_data(MsnTransaction *trans, void *data)
-{
-	g_return_if_fail(trans != NULL);
-
-	trans->data = data;
-}
-
-void
-msn_transaction_add_cb(MsnTransaction *trans, char *answer,
-					   MsnTransCb cb)
-{
-	g_return_if_fail(trans  != NULL);
-	g_return_if_fail(answer != NULL);
-
-	if (trans->callbacks == NULL)
-	{
-		trans->has_custom_callbacks = TRUE;
-		trans->callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
-												 NULL);
-	}
-	else if (trans->has_custom_callbacks != TRUE)
-		g_return_if_reached ();
-
-	g_hash_table_insert(trans->callbacks, answer, cb);
-}
-
-static gboolean
-transaction_timeout(gpointer data)
-{
-	MsnTransaction *trans;
-
-	trans = data;
-	g_return_val_if_fail(trans != NULL, FALSE);
-
-#if 0
-	purple_debug_info("msn", "timed out: %s %d %s\n", trans->command, trans->trId, trans->params);
-#endif
-
-	if (trans->timeout_cb != NULL)
-		trans->timeout_cb(trans->cmdproc, trans);
-
-	return FALSE;
-}
-
-void
-msn_transaction_set_timeout_cb(MsnTransaction *trans, MsnTimeoutCb cb)
-{
-	if (trans->timer)
-	{
-		purple_debug_error("msn", "This shouldn't be happening\n");
-		purple_timeout_remove(trans->timer);
-	}
-	trans->timeout_cb = cb;
-	trans->timer = purple_timeout_add_seconds(60, transaction_timeout, trans);
-}
-
-void
-msn_transaction_set_error_cb(MsnTransaction *trans, MsnErrorCb cb)
-{
-	trans->error_cb = cb;
-}
--- a/libpurple/protocols/msnp9/transaction.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/**
- * @file transaction.h MSN transaction functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_TRANSACTION_H
-#define _MSN_TRANSACTION_H
-
-typedef struct _MsnTransaction MsnTransaction;
-
-#include "command.h"
-#include "cmdproc.h"
-
-typedef void (*MsnTransCb)(MsnCmdProc *cmdproc, MsnCommand *cmd);
-typedef void (*MsnTimeoutCb)(MsnCmdProc *cmdproc, MsnTransaction *trans);
-typedef void (*MsnErrorCb)(MsnCmdProc *cmdproc, MsnTransaction *trans,
-						   int error);
-
-/**
- * A transaction. A sending command that will initiate the transaction.
- */
-struct _MsnTransaction
-{
-	MsnCmdProc *cmdproc;
-	unsigned int trId;
-
-	char *command;
-	char *params;
-
-	int timer;
-
-	void *data; /**< The data to be used on the different callbacks. */
-	GHashTable *callbacks;
-	gboolean has_custom_callbacks;
-	MsnErrorCb error_cb;
-	MsnTimeoutCb timeout_cb;
-
-	char *payload;
-	size_t payload_len;
-
-	GQueue *queue;
-	MsnCommand *pendent_cmd; /**< The command that is waiting for the result of
-							   this transaction. */
-};
-
-MsnTransaction *msn_transaction_new(MsnCmdProc *cmdproc,
-									const char *command,
-									const char *format, ...);
-void msn_transaction_destroy(MsnTransaction *trans);
-
-char *msn_transaction_to_string(MsnTransaction *trans);
-void msn_transaction_queue_cmd(MsnTransaction *trans, MsnCommand *cmd);
-void msn_transaction_unqueue_cmd(MsnTransaction *trans, MsnCmdProc *cmdproc);
-void msn_transaction_set_payload(MsnTransaction *trans,
-								 const char *payload, int payload_len);
-void msn_transaction_set_data(MsnTransaction *trans, void *data);
-void msn_transaction_add_cb(MsnTransaction *trans, char *answer,
-							MsnTransCb cb);
-void msn_transaction_set_error_cb(MsnTransaction *trans, MsnErrorCb cb);
-void msn_transaction_set_timeout_cb(MsnTransaction *trans, MsnTimeoutCb cb);
-
-#endif /* _MSN_TRANSACTION_H */
--- a/libpurple/protocols/msnp9/user.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,332 +0,0 @@
-/**
- * @file user.c User functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "user.h"
-#include "slp.h"
-
-MsnUser *
-msn_user_new(MsnUserList *userlist, const char *passport,
-			 const char *friendly_name)
-{
-	MsnUser *user;
-
-	user = g_new0(MsnUser, 1);
-
-	user->userlist = userlist;
-
-	msn_user_set_passport(user, passport);
-	if (friendly_name != NULL)
-		msn_user_set_friendly_name(user, friendly_name);
-
-	return user;
-}
-
-void
-msn_user_destroy(MsnUser *user)
-{
-	g_return_if_fail(user != NULL);
-
-	if (user->clientcaps != NULL)
-		g_hash_table_destroy(user->clientcaps);
-
-	if (user->group_ids != NULL)
-		g_list_free(user->group_ids);
-
-	if (user->msnobj != NULL)
-		msn_object_destroy(user->msnobj);
-
-	g_free(user->passport);
-	g_free(user->friendly_name);
-	g_free(user->phone.home);
-	g_free(user->phone.work);
-	g_free(user->phone.mobile);
-
-	g_free(user);
-}
-
-void
-msn_user_update(MsnUser *user)
-{
-	PurpleAccount *account;
-
-	account = user->userlist->session->account;
-
-	if (user->status != NULL) {
-		if (!strcmp(user->status, "offline") && user->mobile) {
-			purple_prpl_got_user_status(account, user->passport, "offline", NULL);
-			purple_prpl_got_user_status(account, user->passport, "mobile", NULL);
-		} else {
-			purple_prpl_got_user_status(account, user->passport, user->status, NULL);
-			purple_prpl_got_user_status_deactive(account, user->passport, "mobile");
-		}
-	}
-
-	if (user->idle)
-		purple_prpl_got_user_idle(account, user->passport, TRUE, -1);
-	else
-		purple_prpl_got_user_idle(account, user->passport, FALSE, 0);
-}
-
-void
-msn_user_set_state(MsnUser *user, const char *state)
-{
-	const char *status;
-
-	if (!g_ascii_strcasecmp(state, "BSY"))
-		status = "busy";
-	else if (!g_ascii_strcasecmp(state, "BRB"))
-		status = "brb";
-	else if (!g_ascii_strcasecmp(state, "AWY"))
-		status = "away";
-	else if (!g_ascii_strcasecmp(state, "PHN"))
-		status = "phone";
-	else if (!g_ascii_strcasecmp(state, "LUN"))
-		status = "lunch";
-	else
-		status = "available";
-
-	if (!g_ascii_strcasecmp(state, "IDL"))
-		user->idle = TRUE;
-	else
-		user->idle = FALSE;
-
-	user->status = status;
-}
-
-void
-msn_user_set_passport(MsnUser *user, const char *passport)
-{
-	g_return_if_fail(user != NULL);
-
-	g_free(user->passport);
-	user->passport = g_strdup(passport);
-}
-
-void
-msn_user_set_friendly_name(MsnUser *user, const char *name)
-{
-	MsnCmdProc *cmdproc;
-	MsnSession *session;
-	const char *encoded;
-
-	g_return_if_fail(user != NULL);
-
-	encoded = purple_url_encode(name);
-	session = user->userlist->session;
-
-	if (user->friendly_name && strcmp(user->friendly_name, name)
-		&& (strlen(encoded) < 387) && session->passport_info.verified &&
-		(user->list_op & MSN_LIST_FL_OP)) {
-		/* copy the new name to the server list, but only when new */
-		/* should we check this more thoroughly? */
-		cmdproc = session->notification->cmdproc;
-		msn_cmdproc_send(cmdproc, "REA", "%s %s",
-						 user->passport,
-						 encoded);
-	}
-
-	g_free(user->friendly_name);
-	user->friendly_name = g_strdup(name);
-}
-
-void
-msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img)
-{
-	MsnObject *msnobj = NULL;
-
-	g_return_if_fail(user != NULL);
-
-	msnobj = msn_object_new_from_image(img, "TFR2C2.tmp",
-			user->passport, MSN_OBJECT_USERTILE);
-
-	if(!msnobj)
-		purple_debug_error("msn", "Unable to open buddy icon from %s!\n", user->passport);
-
-	msn_user_set_object(user, msnobj);
-}
-
-void
-msn_user_add_group_id(MsnUser *user, int id)
-{
-	MsnUserList *userlist;
-	PurpleAccount *account;
-	PurpleBuddy *b;
-	PurpleGroup *g;
-	const char *passport;
-	const char *group_name;
-
-	g_return_if_fail(user != NULL);
-	g_return_if_fail(id >= 0);
-
-	user->group_ids = g_list_append(user->group_ids, GINT_TO_POINTER(id));
-
-	userlist = user->userlist;
-	account = userlist->session->account;
-	passport = msn_user_get_passport(user);
-
-	group_name = msn_userlist_find_group_name(userlist, id);
-
-	g = purple_find_group(group_name);
-
-	if ((id == 0) && (g == NULL))
-	{
-		g = purple_group_new(group_name);
-		purple_blist_add_group(g, NULL);
-	}
-
-	b = purple_find_buddy_in_group(account, passport, g);
-
-	if (b == NULL)
-	{
-		b = purple_buddy_new(account, passport, NULL);
-
-		purple_blist_add_buddy(b, NULL, g, NULL);
-	}
-
-	b->proto_data = user;
-}
-
-void
-msn_user_remove_group_id(MsnUser *user, int id)
-{
-	g_return_if_fail(user != NULL);
-	g_return_if_fail(id >= 0);
-
-	user->group_ids = g_list_remove(user->group_ids, GINT_TO_POINTER(id));
-}
-
-void
-msn_user_set_home_phone(MsnUser *user, const char *number)
-{
-	g_return_if_fail(user != NULL);
-
-	if (user->phone.home != NULL)
-		g_free(user->phone.home);
-
-	user->phone.home = (number == NULL ? NULL : g_strdup(number));
-}
-
-void
-msn_user_set_work_phone(MsnUser *user, const char *number)
-{
-	g_return_if_fail(user != NULL);
-
-	if (user->phone.work != NULL)
-		g_free(user->phone.work);
-
-	user->phone.work = (number == NULL ? NULL : g_strdup(number));
-}
-
-void
-msn_user_set_mobile_phone(MsnUser *user, const char *number)
-{
-	g_return_if_fail(user != NULL);
-
-	if (user->phone.mobile != NULL)
-		g_free(user->phone.mobile);
-
-	user->phone.mobile = (number == NULL ? NULL : g_strdup(number));
-}
-
-void
-msn_user_set_object(MsnUser *user, MsnObject *obj)
-{
-	g_return_if_fail(user != NULL);
-
-	if (user->msnobj != NULL)
-		msn_object_destroy(user->msnobj);
-
-	user->msnobj = obj;
-
-	if (user->list_op & MSN_LIST_FL_OP)
-		msn_queue_buddy_icon_request(user);
-}
-
-void
-msn_user_set_client_caps(MsnUser *user, GHashTable *info)
-{
-	g_return_if_fail(user != NULL);
-	g_return_if_fail(info != NULL);
-
-	if (user->clientcaps != NULL)
-		g_hash_table_destroy(user->clientcaps);
-
-	user->clientcaps = info;
-}
-
-const char *
-msn_user_get_passport(const MsnUser *user)
-{
-	g_return_val_if_fail(user != NULL, NULL);
-
-	return user->passport;
-}
-
-const char *
-msn_user_get_friendly_name(const MsnUser *user)
-{
-	g_return_val_if_fail(user != NULL, NULL);
-
-	return user->friendly_name;
-}
-
-const char *
-msn_user_get_home_phone(const MsnUser *user)
-{
-	g_return_val_if_fail(user != NULL, NULL);
-
-	return user->phone.home;
-}
-
-const char *
-msn_user_get_work_phone(const MsnUser *user)
-{
-	g_return_val_if_fail(user != NULL, NULL);
-
-	return user->phone.work;
-}
-
-const char *
-msn_user_get_mobile_phone(const MsnUser *user)
-{
-	g_return_val_if_fail(user != NULL, NULL);
-
-	return user->phone.mobile;
-}
-
-MsnObject *
-msn_user_get_object(const MsnUser *user)
-{
-	g_return_val_if_fail(user != NULL, NULL);
-
-	return user->msnobj;
-}
-
-GHashTable *
-msn_user_get_client_caps(const MsnUser *user)
-{
-	g_return_val_if_fail(user != NULL, NULL);
-
-	return user->clientcaps;
-}
--- a/libpurple/protocols/msnp9/user.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,266 +0,0 @@
-/**
- * @file user.h User functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_USER_H_
-#define _MSN_USER_H_
-
-typedef struct _MsnUser  MsnUser;
-
-#include "session.h"
-#include "object.h"
-
-#include "userlist.h"
-
-/**
- * A user.
- */
-struct _MsnUser
-{
-#if 0
-	MsnSession *session;    /**< The MSN session.               */
-#endif
-	MsnUserList *userlist;
-
-	char *passport;         /**< The passport account.          */
-	char *friendly_name;    /**< The friendly name.             */
-
-	const char *status;     /**< The state of the user.         */
-	gboolean idle;          /**< The idle state of the user.    */
-
-	struct
-	{
-		char *home;         /**< Home phone number.             */
-		char *work;         /**< Work phone number.             */
-		char *mobile;       /**< Mobile phone number.           */
-
-	} phone;
-
-	gboolean authorized;    /**< Authorized to add this user.   */
-	gboolean mobile;        /**< Signed up with MSN Mobile.     */
-
-	GList *group_ids;       /**< The group IDs.                 */
-
-	MsnObject *msnobj;      /**< The user's MSN Object.         */
-
-	GHashTable *clientcaps; /**< The client's capabilities.     */
-
-	int list_op;
-};
-
-/**************************************************************************/
-/** @name User API                                                        */
-/**************************************************************************/
-/*@{*/
-
-/**
- * Creates a new user structure.
- *
- * @param session      The MSN session.
- * @param passport     The initial passport.
- * @param stored_name  The initial stored name.
- *
- * @return A new user structure.
- */
-MsnUser *msn_user_new(MsnUserList *userlist, const char *passport,
-					  const char *friendly_name);
-
-/**
- * Destroys a user structure.
- *
- * @param user The user to destroy.
- */
-void msn_user_destroy(MsnUser *user);
-
-
-/**
- * Updates the user.
- *
- * Communicates with the core to update the ui, etc.
- *
- * @param user The user to update.
- */
-void msn_user_update(MsnUser *user);
-
-/**
- * Sets the new state of user.
- *
- * @param user The user.
- * @param state The state string.
- */
-void msn_user_set_state(MsnUser *user, const char *state);
-
-/**
- * Sets the passport account for a user.
- *
- * @param user     The user.
- * @param passport The passport account.
- */
-void msn_user_set_passport(MsnUser *user, const char *passport);
-
-/**
- * Sets the friendly name for a user.
- *
- * @param user The user.
- * @param name The friendly name.
- */
-void msn_user_set_friendly_name(MsnUser *user, const char *name);
-
-/**
- * Sets the buddy icon for a local user.
- *
- * @param user     The user.
- * @param img      The buddy icon image
- */
-void msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img);
-
-/**
- * Sets the group ID list for a user.
- *
- * @param user The user.
- * @param ids  The group ID list.
- */
-void msn_user_set_group_ids(MsnUser *user, GList *ids);
-
-/**
- * Adds the group ID for a user.
- *
- * @param user The user.
- * @param id   The group ID.
- */
-void msn_user_add_group_id(MsnUser *user, int id);
-
-/**
- * Removes the group ID from a user.
- *
- * @param user The user.
- * @param id   The group ID.
- */
-void msn_user_remove_group_id(MsnUser *user, int id);
-
-/**
- * Sets the home phone number for a user.
- *
- * @param user   The user.
- * @param number The home phone number.
- */
-void msn_user_set_home_phone(MsnUser *user, const char *number);
-
-/**
- * Sets the work phone number for a user.
- *
- * @param user   The user.
- * @param number The work phone number.
- */
-void msn_user_set_work_phone(MsnUser *user, const char *number);
-
-/**
- * Sets the mobile phone number for a user.
- *
- * @param user   The user.
- * @param number The mobile phone number.
- */
-void msn_user_set_mobile_phone(MsnUser *user, const char *number);
-
-/**
- * Sets the MSNObject for a user.
- *
- * @param user The user.
- * @param obj  The MSNObject.
- */
-void msn_user_set_object(MsnUser *user, MsnObject *obj);
-
-/**
- * Sets the client information for a user.
- *
- * @param user The user.
- * @param info The client information.
- */
-void msn_user_set_client_caps(MsnUser *user, GHashTable *info);
-
-
-/**
- * Returns the passport account for a user.
- *
- * @param user The user.
- *
- * @return The passport account.
- */
-const char *msn_user_get_passport(const MsnUser *user);
-
-/**
- * Returns the friendly name for a user.
- *
- * @param user The user.
- *
- * @return The friendly name.
- */
-const char *msn_user_get_friendly_name(const MsnUser *user);
-
-/**
- * Returns the home phone number for a user.
- *
- * @param user The user.
- *
- * @return The user's home phone number.
- */
-const char *msn_user_get_home_phone(const MsnUser *user);
-
-/**
- * Returns the work phone number for a user.
- *
- * @param user The user.
- *
- * @return The user's work phone number.
- */
-const char *msn_user_get_work_phone(const MsnUser *user);
-
-/**
- * Returns the mobile phone number for a user.
- *
- * @param user The user.
- *
- * @return The user's mobile phone number.
- */
-const char *msn_user_get_mobile_phone(const MsnUser *user);
-
-/**
- * Returns the MSNObject for a user.
- *
- * @param user The user.
- *
- * @return The MSNObject.
- */
-MsnObject *msn_user_get_object(const MsnUser *user);
-
-/**
- * Returns the client information for a user.
- *
- * @param user The user.
- *
- * @return The client information.
- */
-GHashTable *msn_user_get_client_caps(const MsnUser *user);
-
-/*@}*/
-
-#endif /* _MSN_USER_H_ */
--- a/libpurple/protocols/msnp9/userlist.c	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,709 +0,0 @@
-/**
- * @file userlist.c MSN user list support
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "userlist.h"
-
-const char *lists[] = { "FL", "AL", "BL", "RL" };
-
-typedef struct
-{
-	PurpleConnection *gc;
-	char *who;
-	char *friendly;
-
-} MsnPermitAdd;
-
-/**************************************************************************
- * Callbacks
- **************************************************************************/
-static void
-msn_accept_add_cb(gpointer data)
-{
-	MsnPermitAdd *pa = data;
-	MsnSession *session;
-	MsnUserList *userlist;
-
-	if (PURPLE_CONNECTION_IS_VALID(pa->gc)) {
-		session = pa->gc->proto_data;
-		userlist = session->userlist;
-
-		msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_AL, NULL);
-	}
-
-	g_free(pa->who);
-	g_free(pa->friendly);
-	g_free(pa);
-}
-
-static void
-msn_cancel_add_cb(gpointer data)
-{
-	MsnPermitAdd *pa = data;
-	MsnSession *session;
-	MsnUserList *userlist;
-
-	if (PURPLE_CONNECTION_IS_VALID(pa->gc)) {
-		session = pa->gc->proto_data;
-		userlist = session->userlist;
-
-		msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_BL, NULL);
-	}
-
-	g_free(pa->who);
-	g_free(pa->friendly);
-	g_free(pa);
-}
-
-static void
-got_new_entry(PurpleConnection *gc, const char *passport, const char *friendly)
-{
-	MsnPermitAdd *pa;
-
-	pa = g_new0(MsnPermitAdd, 1);
-	pa->who = g_strdup(passport);
-	pa->friendly = g_strdup(friendly);
-	pa->gc = gc;
-	
-	purple_account_request_authorization(purple_connection_get_account(gc), passport, NULL, friendly, NULL,
-					   purple_find_buddy(purple_connection_get_account(gc), passport) != NULL,
-					   msn_accept_add_cb, msn_cancel_add_cb, pa);
-}
-
-/**************************************************************************
- * Utility functions
- **************************************************************************/
-
-static gboolean
-user_is_in_group(MsnUser *user, int group_id)
-{
-	if (user == NULL)
-		return FALSE;
-
-	if (group_id < 0)
-		return FALSE;
-
-	if (g_list_find(user->group_ids, GINT_TO_POINTER(group_id)))
-		return TRUE;
-
-	return FALSE;
-}
-
-static gboolean
-user_is_there(MsnUser *user, int list_id, int group_id)
-{
-	int list_op;
-
-	if (user == NULL)
-		return FALSE;
-
-	list_op = 1 << list_id;
-
-	if (!(user->list_op & list_op))
-		return FALSE;
-
-	if (list_id == MSN_LIST_FL)
-	{
-		if (group_id >= 0)
-			return user_is_in_group(user, group_id);
-	}
-
-	return TRUE;
-}
-
-static const char*
-get_friendly_name(MsnUser *user)
-{
-	const char *friendly_name;
-
-	g_return_val_if_fail(user != NULL, NULL);
-
-	friendly_name = msn_user_get_friendly_name(user);
-
-	if (friendly_name != NULL)
-		friendly_name = purple_url_encode(friendly_name);
-	else
-		friendly_name = msn_user_get_passport(user);
-
-	/* this might be a bit of a hack, but it should prevent notification server
-	 * disconnections for people who have buddies with insane friendly names
-	 * who added you to their buddy list from being disconnected. Stu. */
-	/* Shx: What? Isn't the store_name obtained from the server, and hence it's
-	 * below the BUDDY_ALIAS_MAXLEN ? */
-	/* Stu: yeah, that's why it's a bit of a hack, as you pointed out, we're
-	 * probably decoding the incoming store_name wrong, or something. bleh. */
-
-	if (strlen(friendly_name) > BUDDY_ALIAS_MAXLEN)
-		friendly_name = msn_user_get_passport(user);
-
-	return friendly_name;
-}
-
-static void
-msn_request_add_group(MsnUserList *userlist, const char *who,
-					  const char *old_group_name, const char *new_group_name)
-{
-	MsnCmdProc *cmdproc;
-	MsnTransaction *trans;
-	MsnMoveBuddy *data;
-
-	cmdproc = userlist->session->notification->cmdproc;
-	data = g_new0(MsnMoveBuddy, 1);
-
-	data->who = g_strdup(who);
-
-	if (old_group_name)
-		data->old_group_name = g_strdup(old_group_name);
-
-	trans = msn_transaction_new(cmdproc, "ADG", "%s %d",
-								purple_url_encode(new_group_name),
-								0);
-
-	msn_transaction_set_data(trans, data);
-
-	msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-/**************************************************************************
- * Server functions
- **************************************************************************/
-
-MsnListId
-msn_get_list_id(const char *list)
-{
-	if (list[0] == 'F')
-		return MSN_LIST_FL;
-	else if (list[0] == 'A')
-		return MSN_LIST_AL;
-	else if (list[0] == 'B')
-		return MSN_LIST_BL;
-	else if (list[0] == 'R')
-		return MSN_LIST_RL;
-
-	return -1;
-}
-
-void
-msn_got_add_user(MsnSession *session, MsnUser *user,
-				 MsnListId list_id, int group_id)
-{
-	PurpleAccount *account;
-	const char *passport;
-	const char *friendly;
-
-	account = session->account;
-
-	passport = msn_user_get_passport(user);
-	friendly = msn_user_get_friendly_name(user);
-
-	if (list_id == MSN_LIST_FL)
-	{
-		PurpleConnection *gc;
-
-		gc = purple_account_get_connection(account);
-
-		serv_got_alias(gc, passport, friendly);
-
-		if (group_id >= 0)
-		{
-			msn_user_add_group_id(user, group_id);
-		}
-		else
-		{
-			/* session->sync->fl_users_count++; */
-		}
-	}
-	else if (list_id == MSN_LIST_AL)
-	{
-		purple_privacy_permit_add(account, passport, TRUE);
-	}
-	else if (list_id == MSN_LIST_BL)
-	{
-		purple_privacy_deny_add(account, passport, TRUE);
-	}
-	else if (list_id == MSN_LIST_RL)
-	{
-		PurpleConnection *gc;
-		PurpleConversation *convo;
-
-		gc = purple_account_get_connection(account);
-
-		purple_debug_info("msn",
-						"%s has added you to his or her buddy list.\n",
-						passport);
-
- 		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, passport, account);
- 		if (convo) {
- 			PurpleBuddy *buddy;
- 			char *msg;
- 
- 			buddy = purple_find_buddy(account, passport);
- 			msg = g_strdup_printf(
- 				_("%s has added you to his or her buddy list."),
- 				buddy ? purple_buddy_get_contact_alias(buddy) : passport);
- 			purple_conv_im_write(PURPLE_CONV_IM(convo), passport, msg,
- 				PURPLE_MESSAGE_SYSTEM, time(NULL));
- 			g_free(msg);
- 		}
- 
-		if (!(user->list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
-		{
-			/*
-			 * TODO: The friendly name was NULL for me when I
-			 *       looked at this.  Maybe we should use the store
-			 *       name instead? --KingAnt
-			 */
-			got_new_entry(gc, passport, friendly);
-		}
-	}
-
-	user->list_op |= (1 << list_id);
-	/* purple_user_add_list_id (user, list_id); */
-}
-
-void
-msn_got_rem_user(MsnSession *session, MsnUser *user,
-				 MsnListId list_id, int group_id)
-{
-	PurpleAccount *account;
-	const char *passport;
-
-	account = session->account;
-
-	passport = msn_user_get_passport(user);
-
-	if (list_id == MSN_LIST_FL)
-	{
-		/* TODO: When is the user totally removed? */
-		if (group_id >= 0)
-		{
-			msn_user_remove_group_id(user, group_id);
-			return;
-		}
-		else
-		{
-			/* session->sync->fl_users_count--; */
-		}
-	}
-	else if (list_id == MSN_LIST_AL)
-	{
-		purple_privacy_permit_remove(account, passport, TRUE);
-	}
-	else if (list_id == MSN_LIST_BL)
-	{
-		purple_privacy_deny_remove(account, passport, TRUE);
-	}
-	else if (list_id == MSN_LIST_RL)
-	{
-		PurpleConversation *convo;
-
-		purple_debug_info("msn",
-						"%s has removed you from his or her buddy list.\n",
-						passport);
-
-		convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, passport, account);
-		if (convo) {
-			PurpleBuddy *buddy;
-			char *msg;
-
-			buddy = purple_find_buddy(account, passport);
-			msg = g_strdup_printf(
-				_("%s has removed you from his or her buddy list."),
-				buddy ? purple_buddy_get_contact_alias(buddy) : passport);
-			purple_conv_im_write(PURPLE_CONV_IM(convo), passport, msg,
-				PURPLE_MESSAGE_SYSTEM, time(NULL));
-			g_free(msg);
-		}
-	}
-
-	user->list_op &= ~(1 << list_id);
-	/* purple_user_remove_list_id (user, list_id); */
-
-	if (user->list_op == 0)
-	{
-		purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n",
-						passport);
-
-	}
-}
-
-void
-msn_got_lst_user(MsnSession *session, MsnUser *user,
-				 int list_op, GSList *group_ids)
-{
-	PurpleConnection *gc;
-	PurpleAccount *account;
-	const char *passport;
-	const char *store;
-
-	account = session->account;
-	gc = purple_account_get_connection(account);
-
-	passport = msn_user_get_passport(user);
-	store = msn_user_get_friendly_name(user);
-
-	if (list_op & MSN_LIST_AL_OP)
-	{
-		/* These are users who are allowed to see our status. */
-		purple_privacy_deny_remove(account, passport, TRUE);
-		purple_privacy_permit_add(account, passport, TRUE);
-	}
-
-	if (list_op & MSN_LIST_BL_OP)
-	{
-		/* These are users who are not allowed to see our status. */
-		purple_privacy_permit_remove(account, passport, TRUE);
-		purple_privacy_deny_add(account, passport, TRUE);
-	}
-
-	if (list_op & MSN_LIST_FL_OP)
-	{
-		GSList *c;
-		for (c = group_ids; c != NULL; c = g_slist_next(c))
-		{
-			int group_id;
-			group_id = GPOINTER_TO_INT(c->data);
-			msn_user_add_group_id(user, group_id);
-		}
-
-		/* FIXME: It might be a real alias */
-		/* Umm, what? This might fix bug #1385130 */
-		serv_got_alias(gc, passport, store);
-	}
-
-	if (list_op & MSN_LIST_RL_OP)
-	{
-		/* These are users who have us on their buddy list. */
-		/*
-		 * TODO: What is store name set to when this happens?
-		 *       For one of my accounts "something@hotmail.com"
-		 *       the store name was "something."  Maybe we
-		 *       should use the friendly name, instead? --KingAnt
-		 */
-
-		if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
-		{
-			got_new_entry(gc, passport, store);
-		}
-	}
-
-	user->list_op = list_op;
-}
-
-/**************************************************************************
- * UserList functions
- **************************************************************************/
-
-MsnUserList*
-msn_userlist_new(MsnSession *session)
-{
-	MsnUserList *userlist;
-
-	userlist = g_new0(MsnUserList, 1);
-
-	userlist->session = session;
-	userlist->buddy_icon_requests = g_queue_new();
-	
-	/* buddy_icon_window is the number of allowed simultaneous buddy icon requests.
-	 * XXX With smarter rate limiting code, we could allow more at once... 5 was the limit set when
-	 * we weren't retrieiving any more than 5 per MSN session. */
-	userlist->buddy_icon_window = 1;
-
-	return userlist;
-}
-
-void
-msn_userlist_destroy(MsnUserList *userlist)
-{
-	GList *l;
-
-	for (l = userlist->users; l != NULL; l = l->next)
-	{
-		msn_user_destroy(l->data);
-	}
-
-	g_list_free(userlist->users);
-
-	for (l = userlist->groups; l != NULL; l = l->next)
-	{
-		msn_group_destroy(l->data);
-	}
-
-	g_list_free(userlist->groups);
-
-	g_queue_free(userlist->buddy_icon_requests);
-
-	if (userlist->buddy_icon_request_timer)
-		purple_timeout_remove(userlist->buddy_icon_request_timer);
-
-	g_free(userlist);
-}
-
-void
-msn_userlist_add_user(MsnUserList *userlist, MsnUser *user)
-{
-	userlist->users = g_list_prepend(userlist->users, user);
-}
-
-void
-msn_userlist_remove_user(MsnUserList *userlist, MsnUser *user)
-{
-	userlist->users = g_list_remove(userlist->users, user);
-}
-
-MsnUser *
-msn_userlist_find_user(MsnUserList *userlist, const char *passport)
-{
-	GList *l;
-
-	g_return_val_if_fail(passport != NULL, NULL);
-
-	for (l = userlist->users; l != NULL; l = l->next)
-	{
-		MsnUser *user = (MsnUser *)l->data;
-
-		g_return_val_if_fail(user->passport != NULL, NULL);
-
-		if (!strcmp(passport, user->passport))
-			return user;
-	}
-
-	return NULL;
-}
-
-void
-msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group)
-{
-	userlist->groups = g_list_append(userlist->groups, group);
-}
-
-void
-msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group)
-{
-	userlist->groups = g_list_remove(userlist->groups, group);
-}
-
-MsnGroup *
-msn_userlist_find_group_with_id(MsnUserList *userlist, int id)
-{
-	GList *l;
-
-	g_return_val_if_fail(userlist != NULL, NULL);
-	g_return_val_if_fail(id       >= 0,    NULL);
-
-	for (l = userlist->groups; l != NULL; l = l->next)
-	{
-		MsnGroup *group = l->data;
-
-		if (group->id == id)
-			return group;
-	}
-
-	return NULL;
-}
-
-MsnGroup *
-msn_userlist_find_group_with_name(MsnUserList *userlist, const char *name)
-{
-	GList *l;
-
-	g_return_val_if_fail(userlist != NULL, NULL);
-	g_return_val_if_fail(name     != NULL, NULL);
-
-	for (l = userlist->groups; l != NULL; l = l->next)
-	{
-		MsnGroup *group = l->data;
-
-		if ((group->name != NULL) && !g_ascii_strcasecmp(name, group->name))
-			return group;
-	}
-
-	return NULL;
-}
-
-int
-msn_userlist_find_group_id(MsnUserList *userlist, const char *group_name)
-{
-	MsnGroup *group;
-
-	group = msn_userlist_find_group_with_name(userlist, group_name);
-
-	if (group != NULL)
-		return msn_group_get_id(group);
-	else
-		return -1;
-}
-
-const char *
-msn_userlist_find_group_name(MsnUserList *userlist, int group_id)
-{
-	MsnGroup *group;
-
-	group = msn_userlist_find_group_with_id(userlist, group_id);
-
-	if (group != NULL)
-		return msn_group_get_name(group);
-	else
-		return NULL;
-}
-
-void
-msn_userlist_rename_group_id(MsnUserList *userlist, int group_id,
-							 const char *new_name)
-{
-	MsnGroup *group;
-
-	group = msn_userlist_find_group_with_id(userlist, group_id);
-
-	if (group != NULL)
-		msn_group_set_name(group, new_name);
-}
-
-void
-msn_userlist_remove_group_id(MsnUserList *userlist, int group_id)
-{
-	MsnGroup *group;
-
-	group = msn_userlist_find_group_with_id(userlist, group_id);
-
-	if (group != NULL)
-	{
-		msn_userlist_remove_group(userlist, group);
-		msn_group_destroy(group);
-	}
-}
-
-void
-msn_userlist_rem_buddy(MsnUserList *userlist,
-					   const char *who, int list_id, const char *group_name)
-{
-	MsnUser *user;
-	int group_id;
-	const char *list;
-
-	user = msn_userlist_find_user(userlist, who);
-	group_id = -1;
-
-	if (group_name != NULL)
-	{
-		group_id = msn_userlist_find_group_id(userlist, group_name);
-
-		if (group_id < 0)
-		{
-			/* Whoa, there is no such group. */
-			purple_debug_error("msn", "Group doesn't exist: %s\n", group_name);
-			return;
-		}
-	}
-
-	/* First we're going to check if not there. */
-	if (!(user_is_there(user, list_id, group_id)))
-	{
-		list = lists[list_id];
-		purple_debug_error("msn", "User '%s' is not there: %s\n",
-						 who, list);
-		return;
-	}
-
-	/* Then request the rem to the server. */
-	list = lists[list_id];
-
-	msn_notification_rem_buddy(userlist->session->notification, list, who, group_id);
-}
-
-void
-msn_userlist_add_buddy(MsnUserList *userlist,
-					   const char *who, int list_id,
-					   const char *group_name)
-{
-	MsnUser *user;
-	int group_id;
-	const char *list;
-	const char *friendly_name;
-
-	group_id = -1;
-
-	if (!purple_email_is_valid(who))
-	{
-		/* only notify the user about problems adding to the friends list
-		 * maybe we should do something else for other lists, but it probably
-		 * won't cause too many problems if we just ignore it */
-		if (list_id == MSN_LIST_FL)
-		{
-			char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
-			purple_notify_error(NULL, NULL, str,
-							  _("The username specified is invalid."));
-			g_free(str);
-		}
-
-		return;
-	}
-
-	if (group_name != NULL)
-	{
-		group_id = msn_userlist_find_group_id(userlist, group_name);
-
-		if (group_id < 0)
-		{
-			/* Whoa, we must add that group first. */
-			msn_request_add_group(userlist, who, NULL, group_name);
-			return;
-		}
-	}
-
-	user = msn_userlist_find_user(userlist, who);
-
-	/* First we're going to check if it's already there. */
-	if (user_is_there(user, list_id, group_id))
-	{
-		list = lists[list_id];
-		purple_debug_error("msn", "User '%s' is already there: %s\n", who, list);
-		return;
-	}
-
-	friendly_name = (user != NULL) ? get_friendly_name(user) : who;
-
-	/* Then request the add to the server. */
-	list = lists[list_id];
-
-	msn_notification_add_buddy(userlist->session->notification, list, who,
-							   friendly_name, group_id);
-}
-
-void
-msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
-						const char *old_group_name, const char *new_group_name)
-{
-	int new_group_id;
-
-	new_group_id = msn_userlist_find_group_id(userlist, new_group_name);
-
-	if (new_group_id < 0)
-	{
-		msn_request_add_group(userlist, who, old_group_name, new_group_name);
-		return;
-	}
-
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_FL, new_group_name);
-	msn_userlist_rem_buddy(userlist, who, MSN_LIST_FL, old_group_name);
-}
--- a/libpurple/protocols/msnp9/userlist.h	Fri Mar 05 04:19:05 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/**
- * @file userlist.h MSN user list support
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_USERLIST_H_
-#define _MSN_USERLIST_H_
-
-typedef struct _MsnUserList MsnUserList;
-
-#include "cmdproc.h"
-#include "user.h"
-#include "group.h"
-
-typedef enum
-{
-	MSN_LIST_FL,
-	MSN_LIST_AL,
-	MSN_LIST_BL,
-	MSN_LIST_RL
-
-} MsnListId;
-
-typedef struct
-{
-	char *who;
-	char *old_group_name;
-
-} MsnMoveBuddy;
-
-struct _MsnUserList
-{
-	MsnSession *session;
-
-	/* MsnUsers *users; */
-	/* MsnGroups *groups; */
-
-	GList *users;
-	GList *groups;
-
-	GQueue *buddy_icon_requests;
-	int buddy_icon_window;
-	guint buddy_icon_request_timer;
-
-	int fl_users_count;
-
-};
-
-MsnListId msn_get_list_id(const char *list);
-
-void msn_got_add_user(MsnSession *session, MsnUser *user,
-					  MsnListId list_id, int group_id);
-void msn_got_rem_user(MsnSession *session, MsnUser *user,
-					  MsnListId list_id, int group_id);
-void msn_got_lst_user(MsnSession *session, MsnUser *user,
-					  int list_op, GSList *group_ids);
-
-MsnUserList *msn_userlist_new(MsnSession *session);
-void msn_userlist_destroy(MsnUserList *userlist);
-void msn_userlist_add_user(MsnUserList *userlist, MsnUser *user);
-void msn_userlist_remove_user(MsnUserList *userlist, MsnUser *user);
-MsnUser *msn_userlist_find_user(MsnUserList *userlist,
-								const char *passport);
-void msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group);
-void msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group);
-MsnGroup *msn_userlist_find_group_with_id(MsnUserList *userlist, int id);
-MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist,
-											const char *name);
-int msn_userlist_find_group_id(MsnUserList *userlist,
-							   const char *group_name);
-const char *msn_userlist_find_group_name(MsnUserList *userlist,
-										 int group_id);
-void msn_userlist_rename_group_id(MsnUserList *userlist, int group_id,
-								  const char *new_name);
-void msn_userlist_remove_group_id(MsnUserList *userlist, int group_id);
-
-void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who,
-							int list_id, const char *group_name);
-void msn_userlist_add_buddy(MsnUserList *userlist, const char *who,
-							int list_id, const char *group_name);
-void msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
-							 const char *old_group_name,
-							 const char *new_group_name);
-
-#endif /* _MSN_USERLIST_H_ */
--- a/libpurple/protocols/mxit/mxit.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/mxit/mxit.h	Wed Mar 10 17:58:21 2010 +0000
@@ -37,7 +37,9 @@
 #endif
 #elif defined( _WIN32 )
 /* windows architecture */
+#ifndef HOST_NAME_MAX
 #define		HOST_NAME_MAX				512
+#endif
 #include	"libc_interface.h"
 #elif defined( __linux__ )
 /* linux architecture */
--- a/libpurple/protocols/null/nullprpl.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1117,9 +1117,10 @@
   NULL,                                /* send_attention */
   NULL,                                /* get_attention_types */
   sizeof(PurplePluginProtocolInfo),    /* struct_size */
-  NULL,
-  NULL,                                 /* initiate_media */
-  NULL                                  /* can_do_media */	
+  NULL,				       /* get_account_text_table */
+  NULL,                                /* initiate_media */
+  NULL,                                /* get_media_caps */
+  NULL				       /* get_moods */
 };
 
 static void nullprpl_init(PurplePlugin *plugin)
--- a/libpurple/protocols/oscar/oscar.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1573,6 +1573,7 @@
 
 	if (oscar_util_valid_name_icq((purple_account_get_username(account)))) {
 		od->icq = TRUE;
+		gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS;
 	} else {
 		gc->flags |= PURPLE_CONNECTION_HTML;
 		gc->flags |= PURPLE_CONNECTION_AUTO_RESP;
@@ -3797,7 +3798,7 @@
 	if (utf8 == NULL)
 		/* The conversion failed! */
 		utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]"));
-	serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time((time_t)NULL));
+	serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time(NULL));
 	g_free(utf8);
 
 	return 1;
--- a/libpurple/signals.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/signals.c	Wed Mar 10 17:58:21 2010 +0000
@@ -1009,6 +1009,21 @@
 }
 
 void
+purple_marshal_POINTER__POINTER(
+                                    PurpleCallback cb, va_list args, void *data,
+                                    void **return_val)
+{
+	gpointer ret_val;
+	void *arg1 = va_arg(args, void *);
+
+	ret_val = ((gpointer(*)(void *, void *))cb)(arg1, data);
+
+	if (return_val != NULL)
+		*return_val = ret_val;
+}
+
+
+void
 purple_marshal_POINTER__POINTER_INT(
                                     PurpleCallback cb, va_list args, void *data,
                                     void **return_val)
--- a/libpurple/signals.h	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/signals.h	Wed Mar 10 17:58:21 2010 +0000
@@ -355,6 +355,8 @@
 void purple_marshal_BOOLEAN__INT_POINTER(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 
+void purple_marshal_POINTER__POINTER(
+		PurpleCallback cb, va_list args, void *data, void **return_val);
 void purple_marshal_POINTER__POINTER_INT(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 void purple_marshal_POINTER__POINTER_INT64(
--- a/libpurple/upnp.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/upnp.c	Wed Mar 10 17:58:21 2010 +0000
@@ -542,7 +542,7 @@
 		len = recv(dd->fd, buf,
 			sizeof(buf) - 1, 0);
 
-		if(len > 0) {
+		if(len >= 0) {
 			buf[len] = '\0';
 			break;
 		} else if(errno != EINTR) {
--- a/libpurple/win32/global.mak	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/win32/global.mak	Wed Mar 10 17:58:21 2010 +0000
@@ -10,15 +10,14 @@
 
 # Locations of our various dependencies
 WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev
-ASPELL_TOP ?= $(WIN32_DEV_TOP)/aspell-dev-0-50-3-3
-GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.11-daa1
+GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.16
+ENCHANT_TOP ?= $(WIN32_DEV_TOP)/enchant_1.5.0-2_win32
 GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0-2.14
 GTK_BIN ?= $(GTK_TOP)/bin
 BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK
-LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.7.3
+LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.7.4
 MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa2
-NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4
-NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.11.4
+NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.12.5-nspr-4.8.2
 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10.0
 SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.8
 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5
@@ -103,7 +102,7 @@
 ifeq "$(origin CC)" "default"
   CC := gcc.exe
 endif
-GMSGFMT ?= $(GTK_BIN)/msgfmt
+GMSGFMT ?= $(WIN32_DEV_TOP)/gettext-0.17/bin/msgfmt
 MAKENSIS ?= makensis.exe
 MAKENSISOPT ?= /
 PERL ?= /cygdrive/c/perl/bin/perl
--- a/libpurple/win32/libc_interface.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/win32/libc_interface.c	Wed Mar 10 17:58:21 2010 +0000
@@ -62,7 +62,7 @@
 /* helpers */
 static int wpurple_is_socket( int fd ) {
 	int optval;
-	unsigned int optlen = sizeof(int);
+	int optlen = sizeof(int);
 
 	if( (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&optval, &optlen)) == SOCKET_ERROR ) {
 		int error = WSAGetLastError();
@@ -973,7 +973,7 @@
 
 		memset(zonename, 0, sizeof(zonename));
 		namesize = sizeof(zonename);
-		if ((r = RegQueryValueEx(key, "Std", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
+		if ((r = RegQueryValueEx(key, "Std", NULL, NULL, (LPBYTE)zonename, &namesize)) != ERROR_SUCCESS)
 		{
 			purple_debug_warning("wpurple", "could not query value for 'std' to identify Windows timezone: %i\n", (int) r);
 			RegCloseKey(key);
@@ -988,7 +988,7 @@
 		}
 		memset(zonename, 0, sizeof(zonename));
 		namesize = sizeof(zonename);
-		if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
+		if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, (LPBYTE)zonename, &namesize)) != ERROR_SUCCESS)
 		{
 			purple_debug_warning("wpurple", "could not query value for 'dlt' to identify Windows timezone: %i\n", (int) r);
 			RegCloseKey(key);
--- a/libpurple/win32/win32dep.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/libpurple/win32/win32dep.c	Wed Mar 10 17:58:21 2010 +0000
@@ -22,7 +22,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  *
  */
-#define _WIN32_IE 0x500
+#define _WIN32_IE 0x501
 #include "internal.h"
 #include <winuser.h>
 
--- a/pidgin/Makefile.am	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/Makefile.am	Wed Mar 10 17:58:21 2010 +0000
@@ -21,6 +21,7 @@
 		win32/winpidgin.c \
 		win32/wspell.c \
 		win32/wspell.h \
+		win32/nsis/generate_gtk_zip.sh \
 		win32/nsis/pixmaps/pidgin-header.bmp \
 		win32/nsis/pixmaps/pidgin-intro.bmp \
 		win32/nsis/pixmaps/pidgin-install.ico \
--- a/pidgin/Makefile.mingw	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/Makefile.mingw	Wed Mar 10 17:58:21 2010 +0000
@@ -9,8 +9,6 @@
 
 DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES))
 
-NEEDED_DLLS = $(GTKSPELL_TOP)/gtkspell/libgtkspell.dll
-
 ##
 ## VARIABLE DEFINITIONS
 ##
@@ -43,14 +41,12 @@
 			-I$(GTK_TOP)/include/atk-1.0 \
 			-I$(GTK_TOP)/include/cairo \
 			-I$(GTK_TOP)/lib/gtk-2.0/include \
-			-I$(GTKSPELL_TOP) \
-			-I$(ASPELL_TOP)/include
+			-I$(GTKSPELL_TOP)/include/gtkspell-2.0
 
 LIB_PATHS +=		-L$(GTK_TOP)/lib \
 			-L$(PURPLE_TOP) \
 			-L$(PIDGIN_TOP) \
-			-L$(PIDGIN_IDLETRACK_TOP) \
-			-L$(ASPELL_TOP)/lib
+			-L$(PIDGIN_IDLETRACK_TOP)
 
 ##
 ##  SOURCES, OBJECTS
@@ -151,7 +147,6 @@
 
 install_shallow: $(PIDGIN_INSTALL_DIR) $(EXE_TARGET).exe $(PIDGIN_TARGET).dll
 	cp $(EXE_TARGET).exe $(PIDGIN_TARGET).dll $(PIDGIN_INSTALL_DIR)
-	cp $(NEEDED_DLLS) $(PIDGIN_INSTALL_DIR)
 
 install: install_shallow all
 	$(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE) install
--- a/pidgin/gtkblist.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/gtkblist.c	Wed Mar 10 17:58:21 2010 +0000
@@ -2660,39 +2660,30 @@
 
 /* Altered from do_colorshift in gnome-panel */
 static void
-do_alphashift (GdkPixbuf *dest, GdkPixbuf *src, int shift)
+do_alphashift(GdkPixbuf *pixbuf, int shift)
 {
 	gint i, j;
-	gint width, height, has_alpha, srcrowstride, destrowstride;
-	guchar *target_pixels;
-	guchar *original_pixels;
-	guchar *pixsrc;
-	guchar *pixdest;
+	gint width, height, padding;
+	guchar *pixels;
 	int val;
-	guchar a;
-
-	has_alpha = gdk_pixbuf_get_has_alpha (src);
-	if (!has_alpha)
+
+	if (!gdk_pixbuf_get_has_alpha(pixbuf))
 	  return;
 
-	width = gdk_pixbuf_get_width (src);
-	height = gdk_pixbuf_get_height (src);
-	srcrowstride = gdk_pixbuf_get_rowstride (src);
-	destrowstride = gdk_pixbuf_get_rowstride (dest);
-	target_pixels = gdk_pixbuf_get_pixels (dest);
-	original_pixels = gdk_pixbuf_get_pixels (src);
+	width = gdk_pixbuf_get_width(pixbuf);
+	height = gdk_pixbuf_get_height(pixbuf);
+	padding = gdk_pixbuf_get_rowstride(pixbuf) - width * 4;
+	pixels = gdk_pixbuf_get_pixels(pixbuf);
 
 	for (i = 0; i < height; i++) {
-		pixdest = target_pixels + i*destrowstride;
-		pixsrc = original_pixels + i*srcrowstride;
 		for (j = 0; j < width; j++) {
-			*(pixdest++) = *(pixsrc++);
-			*(pixdest++) = *(pixsrc++);
-			*(pixdest++) = *(pixsrc++);
-			a = *(pixsrc++);
-			val = a - shift;
-			*(pixdest++) = CLAMP(val, 0, 255);
-		}
+			pixels++;
+			pixels++;
+			pixels++;
+			val = *pixels - shift;
+			*(pixels++) = CLAMP(val, 0, 255);
+		}
+		pixels += padding;
 	}
 }
 
@@ -6367,7 +6358,7 @@
 		g_object_ref(G_OBJECT(gtkblist->empty_avatar));
 		avatar = gtkblist->empty_avatar;
 	} else if ((!PURPLE_BUDDY_IS_ONLINE(buddy) || purple_presence_is_idle(presence))) {
-		do_alphashift(avatar, avatar, 77);
+		do_alphashift(avatar, 77);
 	}
 
 	emblem = pidgin_blist_get_emblem((PurpleBlistNode*) buddy);
@@ -7827,20 +7818,29 @@
 static void
 edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields)
 {
-	PurpleRequestField *f;
+	PurpleRequestField *mood_field, *text_field;
 	GList *l;
 
-	f = purple_request_fields_get_field(fields, "mood");
-	l = purple_request_field_list_get_selected(f);
+	mood_field = purple_request_fields_get_field(fields, "mood");
+	text_field = purple_request_fields_get_field(fields, "text");
+	l = purple_request_field_list_get_selected(mood_field);
 
 	if (l) {
-		const char *mood = purple_request_field_list_get_data(f, l->data);
+		const char *mood = purple_request_field_list_get_data(mood_field, l->data);
+		const char *text = purple_request_field_string_get_value(text_field);
 		PurpleAccount *account = purple_connection_get_account(gc);
 
-		if (mood != NULL) {
-			purple_account_set_status(account, "mood", TRUE,
-			                          PURPLE_MOOD_NAME, mood,
-			                          NULL);
+		if (mood != NULL && !purple_strequal(mood, "")) {
+			if (text) {
+				purple_account_set_status(account, "mood", TRUE,
+			                          	PURPLE_MOOD_NAME, mood,
+				    				  	PURPLE_MOOD_COMMENT, text,
+			                          	NULL);
+			} else {
+				purple_account_set_status(account, "mood", TRUE,
+			                          	PURPLE_MOOD_NAME, mood,
+			                          	NULL);
+			}
 		} else {
 			purple_account_set_status(account, "mood", FALSE, NULL);
 		}
@@ -7856,11 +7856,10 @@
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *g;
 	PurpleRequestField *f;
-	char* na_fn;
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurplePluginProtocolInfo *prpl_info;
 	PurpleMood *mood;
-
+	
 	g_return_if_fail(gc->prpl != NULL);
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
 
@@ -7870,14 +7869,10 @@
 	g = purple_request_field_group_new(NULL);
 	f = purple_request_field_list_new("mood", _("Please select your mood from the list"));
 
-	na_fn = g_build_filename("pixmaps", "pidgin", "emblems", "16", "not-authorized.png", NULL);
-
-	purple_request_field_list_add_icon(f, _("None"), na_fn, NULL);
+	purple_request_field_list_add(f, _("None"), "");
 	if (current_mood == NULL)
 		purple_request_field_list_add_selected(f, _("None"));
 
-	g_free(na_fn);
-
 	/* TODO: rlaager wants this sorted. */
 	for (mood = prpl_info->get_moods(account);
 	     mood->mood != NULL ; mood++) {
@@ -7898,6 +7893,15 @@
 
 	purple_request_fields_add_group(fields, g);
 
+	/* if the connection allows setting a mood message */
+	if (gc->flags & PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES) {
+		g = purple_request_field_group_new(NULL);
+		f = purple_request_field_string_new("text",
+		    _("Message (optional)"), NULL, FALSE);
+		purple_request_field_group_add_field(g, f);
+		purple_request_fields_add_group(fields, g);
+	}
+
 	purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"),
                               NULL, fields,
                               _("OK"), G_CALLBACK(edit_mood_cb),
@@ -8030,8 +8034,10 @@
 		if (prpl_info &&
 		    (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) ||
 			 PURPLE_PLUGIN_HAS_ACTIONS(plugin))) {
-			if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods)) {
+			if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) &&
+			    gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) {
 				GList *types;
+
 				for (types = purple_account_get_status_types(account);
 			     	types != NULL ; types = types->next) {
 					PurpleStatusType *type = types->data;
--- a/pidgin/gtkft.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/gtkft.c	Wed Mar 10 17:58:21 2010 +0000
@@ -575,7 +575,7 @@
 
 	/* Build the tree model */
 	/* Transfer type, Progress Bar, Filename, Size, Remaining */
-	model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_DOUBLE,
+	model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_INT,
 							   G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
 							   G_TYPE_POINTER);
 	dialog->model = model;
@@ -609,7 +609,7 @@
 	/* Progress bar column */
 	renderer = gtk_cell_renderer_progress_new();
 	column = gtk_tree_view_column_new_with_attributes(_("Progress"), renderer,
-				"percentage", COLUMN_PROGRESS, NULL);
+				"value", COLUMN_PROGRESS, NULL);
 	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
 	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
 
@@ -906,7 +906,7 @@
 	lfilename = utf8;
 	gtk_list_store_set(dialog->model, &data->iter,
 					   COLUMN_STATUS, pixbuf,
-					   COLUMN_PROGRESS, 0.0,
+					   COLUMN_PROGRESS, 0,
 					   COLUMN_FILENAME, (type == PURPLE_XFER_RECEIVE)
 					                     ? purple_xfer_get_filename(xfer)
 							     : lfilename,
@@ -1039,7 +1039,7 @@
 	remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer));
 
 	gtk_list_store_set(xfer_dialog->model, &data->iter,
-					   COLUMN_PROGRESS, purple_xfer_get_progress(xfer),
+					   COLUMN_PROGRESS, (gint)(purple_xfer_get_progress(xfer) * 100),
 					   COLUMN_SIZE, size_str,
 					   COLUMN_REMAINING, remaining_str,
 					   -1);
--- a/pidgin/gtkimhtml.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/gtkimhtml.c	Wed Mar 10 17:58:21 2010 +0000
@@ -191,7 +191,7 @@
 
 	purple_debug_info("imhtml clipboard", "from clipboard: %s\n", clipboard);
 
-	fd = g_fopen("e:\\purplecb.txt", "wb");
+	fd = g_fopen("c:\\purplecb.txt", "wb");
 	fprintf(fd, "%s", clipboard);
 	fclose(fd);
 #endif
@@ -1188,6 +1188,14 @@
 		memcpy(text, selection_data->data, selection_data->length);
 	}
 
+#ifdef _WIN32
+	if (gtk_selection_data_get_data_type(selection_data) == gdk_atom_intern("HTML Format", FALSE)) {
+		char *tmp = clipboard_win32_to_html(text);
+		g_free(text);
+		text = tmp;
+	}
+#endif
+
 	if (selection_data->length >= 2 &&
 		(*(guint16 *)text == 0xfeff || *(guint16 *)text == 0xfffe)) {
 		/* This is UTF-16 */
@@ -1247,13 +1255,16 @@
 #ifdef _WIN32
 	/* If we're on windows, let's see if we can get data from the HTML Format
 	   clipboard before we try to paste from the GTK buffer */
-	if (!clipboard_paste_html_win32(imhtml))
-#endif
-	{
+	if (!clipboard_paste_html_win32(imhtml)) {
+		GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
+		gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml);
+
+	}
+#else
 	GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
 	gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE),
 				       paste_received_cb, imhtml);
-	}
+#endif
 	g_signal_stop_emission_by_name(imhtml, "paste-clipboard");
 }
 
@@ -1679,8 +1690,10 @@
 	g_signal_connect_after(G_OBJECT(imhtml), "realize", G_CALLBACK(imhtml_realized_remove_primary), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "unrealize", G_CALLBACK(imhtml_destroy_add_primary), NULL);
 
+#ifndef _WIN32
 	g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set",
 		               G_CALLBACK(mark_set_so_update_selection_cb), imhtml);
+#endif
 
 	gtk_widget_add_events(GTK_WIDGET(imhtml),
 			GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK);
--- a/pidgin/pidginstock.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/pidginstock.c	Wed Mar 10 17:58:21 2010 +0000
@@ -270,37 +270,30 @@
 
 /* Altered from do_colorshift in gnome-panel */
 static void
-do_alphashift(GdkPixbuf *dest, GdkPixbuf *src)
+do_alphashift(GdkPixbuf *pixbuf)
 {
 	gint i, j;
-	gint width, height, has_alpha, srcrowstride, destrowstride;
-	guchar *target_pixels;
-	guchar *original_pixels;
-	guchar *pixsrc;
-	guchar *pixdest;
+	gint width, height, padding;
+	guchar *pixels;
 	guchar a;
 
-	has_alpha = gdk_pixbuf_get_has_alpha (src);
-	if (!has_alpha)
+	if (!gdk_pixbuf_get_has_alpha(pixbuf))
 		return;
 
-	width = gdk_pixbuf_get_width (src);
-	height = gdk_pixbuf_get_height (src);
-	srcrowstride = gdk_pixbuf_get_rowstride (src);
-	destrowstride = gdk_pixbuf_get_rowstride (dest);
-	target_pixels = gdk_pixbuf_get_pixels (dest);
-	original_pixels = gdk_pixbuf_get_pixels (src);
+	width = gdk_pixbuf_get_width(pixbuf);
+	height = gdk_pixbuf_get_height(pixbuf);
+	padding = gdk_pixbuf_get_rowstride(pixbuf) - width * 4;
+	pixels = gdk_pixbuf_get_pixels(pixbuf);
 
 	for (i = 0; i < height; i++) {
-		pixdest = target_pixels + i*destrowstride;
-		pixsrc = original_pixels + i*srcrowstride;
 		for (j = 0; j < width; j++) {
-			*(pixdest++) = *(pixsrc++);
-			*(pixdest++) = *(pixsrc++);
-			*(pixdest++) = *(pixsrc++);
-			a = *(pixsrc++);
-			*(pixdest++) = a / 2;
+			pixels++;
+			pixels++;
+			pixels++;
+			a = *(pixels);
+			*(pixels++) = a / 2;
 		}
+		pixels += padding;
 	}
 }
 
@@ -348,7 +341,7 @@
 	g_return_if_fail(filename != NULL);
 	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
 	if (translucent)
-		do_alphashift(pixbuf, pixbuf);
+		do_alphashift(pixbuf);
 
 	source = gtk_icon_source_new();
 	gtk_icon_source_set_pixbuf(source, pixbuf);
@@ -378,7 +371,7 @@
 		g_return_if_fail(filename != NULL);
 		pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
 		if (translucent)
-			do_alphashift(pixbuf, pixbuf);
+			do_alphashift(pixbuf);
 
 		source = gtk_icon_source_new();
 		gtk_icon_source_set_pixbuf(source, pixbuf);
--- a/pidgin/plugins/gtkbuddynote.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/plugins/gtkbuddynote.c	Wed Mar 10 17:58:21 2010 +0000
@@ -31,11 +31,13 @@
 		const gchar *note = purple_blist_node_get_string(node, "notes");
 
 		if ((note != NULL) && (*note != '\0')) {
-			char *tmp;
+			char *tmp, *esc;
 			purple_markup_html_to_xhtml(note, NULL, &tmp);
+			esc = g_markup_escape_text(tmp, -1);
+			g_free(tmp);
 			g_string_append_printf(text, _("\n<b>Buddy Note</b>: %s"),
-			                       tmp);
-			g_free(tmp);
+			                       esc);
+			g_free(esc);
 		}
 	}
 }
--- a/pidgin/plugins/xmppconsole.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/plugins/xmppconsole.c	Wed Mar 10 17:58:21 2010 +0000
@@ -621,7 +621,7 @@
 }
 
 static void
-signed_on_cb(PurpleConnection *gc)
+signing_on_cb(PurpleConnection *gc)
 {
 	if (!console)
 		return;
@@ -630,7 +630,9 @@
 	console->accounts = g_list_append(console->accounts, gc);
 	console->count++;
 
-	if (console->count > 1)
+	if (console->count == 1)
+		console->gc = gc;
+	else
 		gtk_widget_show_all(console->hbox);
 }
 
@@ -680,8 +682,8 @@
 			    PURPLE_CALLBACK(xmlnode_received_cb), NULL);
 	purple_signal_connect(jabber, "jabber-sending-text", xmpp_console_handle,
 			    PURPLE_CALLBACK(xmlnode_sent_cb), NULL);
-	purple_signal_connect(purple_connections_get_handle(), "signed-on",
-			    plugin, PURPLE_CALLBACK(signed_on_cb), NULL);
+	purple_signal_connect(purple_connections_get_handle(), "signing-on",
+			    plugin, PURPLE_CALLBACK(signing_on_cb), NULL);
 	purple_signal_connect(purple_connections_get_handle(), "signed-off",
 			    plugin, PURPLE_CALLBACK(signed_off_cb), NULL);
 
--- a/pidgin/win32/gtkdocklet-win32.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/gtkdocklet-win32.c	Wed Mar 10 17:58:21 2010 +0000
@@ -21,7 +21,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02111-1301, USA.
  */
-
+#define _WIN32_IE 0x0500
 #include <windows.h>
 #include <gdk/gdkwin32.h>
 #include <gdk/gdk.h>
@@ -51,7 +51,7 @@
 /* This is used to trigger click events on so they appear to GTK+ as if they are triggered by input */
 static GtkWidget *dummy_button = NULL;
 static GtkWidget *dummy_window = NULL;
-static NOTIFYICONDATA _nicon_data;
+static NOTIFYICONDATAW _nicon_data;
 
 static gboolean dummy_button_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
 	pidgin_docklet_clicked(event->button);
@@ -64,7 +64,7 @@
 	switch(msg) {
 	case WM_CREATE:
 		purple_debug_info("docklet", "WM_CREATE\n");
-		taskbarRestartMsg = RegisterWindowMessage("TaskbarCreated");
+		taskbarRestartMsg = RegisterWindowMessageW(L"TaskbarCreated");
 		break;
 
 	case WM_TIMER:
@@ -114,7 +114,7 @@
 		if (msg == taskbarRestartMsg) {
 			/* explorer crashed and left us hanging...
 			   This will put the systray icon back in it's place, when it restarts */
-			Shell_NotifyIcon(NIM_ADD, &_nicon_data);
+			Shell_NotifyIconW(NIM_ADD, &_nicon_data);
 		}
 		break;
 	}/* end switch */
@@ -124,10 +124,10 @@
 
 /* Create hidden window to process systray messages */
 static HWND systray_create_hiddenwin() {
-	WNDCLASSEX wcex;
-	LPCTSTR wname;
+	WNDCLASSEXW wcex;
+	wchar_t *wname;
 
-	wname = TEXT("WinpidginSystrayWinCls");
+	wname = L"WinpidginSystrayWinCls";
 
 	wcex.cbSize = sizeof(wcex);
 	wcex.style		= 0;
@@ -142,22 +142,25 @@
 	wcex.lpszClassName	= wname;
 	wcex.hIconSm		= NULL;
 
-	RegisterClassEx(&wcex);
+	RegisterClassExW(&wcex);
 
 	/* Create the window */
-	return (CreateWindow(wname, "", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, winpidgin_exe_hinstance(), 0));
+	return (CreateWindowW(wname, L"", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, winpidgin_exe_hinstance(), 0));
 }
 
 static void systray_init_icon(HWND hWnd) {
+	wchar_t *w;
 	ZeroMemory(&_nicon_data, sizeof(_nicon_data));
-	_nicon_data.cbSize = sizeof(NOTIFYICONDATA);
+	_nicon_data.cbSize = sizeof(NOTIFYICONDATAW);
 	_nicon_data.hWnd = hWnd;
 	_nicon_data.uID = 0;
 	_nicon_data.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
 	_nicon_data.uCallbackMessage = WM_TRAYMESSAGE;
 	_nicon_data.hIcon = NULL;
-	lstrcpy(_nicon_data.szTip, PIDGIN_NAME);
-	Shell_NotifyIcon(NIM_ADD, &_nicon_data);
+	w = g_utf8_to_utf16(PIDGIN_NAME, -1, NULL, NULL, NULL);
+	wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t));
+	g_free(w);
+	Shell_NotifyIconW(NIM_ADD, &_nicon_data);
 	pidgin_docklet_embedded();
 }
 
@@ -486,11 +489,11 @@
 	g_return_if_fail(hicon != NULL);
 
 	_nicon_data.hIcon = hicon;
-	Shell_NotifyIcon(NIM_MODIFY, &_nicon_data);
+	Shell_NotifyIconW(NIM_MODIFY, &_nicon_data);
 }
 
 static void systray_remove_nid(void) {
-	Shell_NotifyIcon(NIM_DELETE, &_nicon_data);
+	Shell_NotifyIconW(NIM_DELETE, &_nicon_data);
 }
 
 static void winpidgin_tray_update_icon(PurpleStatusPrimitive status,
@@ -547,19 +550,19 @@
 
 static void winpidgin_tray_blank_icon() {
 	_nicon_data.hIcon = NULL;
-	Shell_NotifyIcon(NIM_MODIFY, &_nicon_data);
+	Shell_NotifyIconW(NIM_MODIFY, &_nicon_data);
 }
 
 static void winpidgin_tray_set_tooltip(gchar *tooltip) {
-	if (tooltip) {
-		char *locenc = NULL;
-		locenc = g_locale_from_utf8(tooltip, -1, NULL, NULL, NULL);
-		lstrcpyn(_nicon_data.szTip, locenc, sizeof(_nicon_data.szTip) / sizeof(TCHAR));
-		g_free(locenc);
-	} else {
-		lstrcpy(_nicon_data.szTip, PIDGIN_NAME);
-	}
-	Shell_NotifyIcon(NIM_MODIFY, &_nicon_data);
+	const char *value = tooltip;
+	wchar_t *w;
+	if (value == NULL) {
+		value = PIDGIN_NAME;
+	}	
+	w = g_utf8_to_utf16(value, -1, NULL, NULL, NULL);
+	wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t));
+	g_free(w);
+	Shell_NotifyIconW(NIM_MODIFY, &_nicon_data);
 }
 
 static void winpidgin_tray_minimize(PidginBuddyList *gtkblist) {
--- a/pidgin/win32/gtkwin32dep.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/gtkwin32dep.c	Wed Mar 10 17:58:21 2010 +0000
@@ -375,11 +375,27 @@
 }
 
 void winpidgin_init(HINSTANCE hint) {
+	FARPROC proc;
 
 	purple_debug_info("winpidgin", "winpidgin_init start\n");
 
 	exe_hInstance = hint;
 
+	proc = wpurple_find_and_loadproc("exchndl.dll", "SetLogFile");
+	if (proc) {
+		gchar *debug_dir, *locale_debug_dir;
+		
+		debug_dir = g_build_filename(purple_user_dir(), "pidgin.RPT", NULL);
+		locale_debug_dir = g_locale_from_utf8(debug_dir, -1, NULL, NULL, NULL);
+
+		purple_debug_info("winpidgin", "Setting exchndl.dll LogFile to %s\n", debug_dir);
+
+		(proc)(locale_debug_dir);
+
+		g_free(debug_dir);
+		g_free(locale_debug_dir);
+	}
+
 	/* IdleTracker Initialization */
 	if(!winpidgin_set_idlehooks())
 		purple_debug_error("winpidgin", "Failed to initialize idle tracker\n");
--- a/pidgin/win32/nsis/available.lst	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/available.lst	Wed Mar 10 17:58:21 2010 +0000
@@ -4,72 +4,46 @@
 sq,AL,sq_AL,Albanian (Albania),sq_AL.zip
 bg,BG,bg_BG,Bulgarian (Bulgaria),bg_BG.zip
 ca,ES,ca_ES,Catalan (Spain),ca_ES.zip
+cop,EG,cop_EG,Coptic (Egypt),cop_EG.zip
 hr,HR,hr_HR,Croatian (Croatia),hr_HR.zip
 cs,CZ,cs_CZ,Czech (Czech Republic),cs_CZ.zip
 da,DK,da_DK,Danish (Denmark),da_DK.zip
 nl,NL,nl_NL,Dutch (Netherlands),nl_NL.zip
 nl,NL,nl_med,Dutch Medical (Netherlands),nl_med.zip
-nl,BE,nl_NL,Dutch (Belgium),nl_NL.zip
-nl,BE,nl_NL,Dutch Medical (Belgium),nl_NL.zip
 en,AU,en_AU,English (Australia),en_AU.zip
 en,CA,en_CA,English (Canada),en_CA.zip
 en,NZ,en_NZ,English (New Zealand),en_NZ.zip
 en,ZA,en_ZA,English (South Africa),en_ZA.zip
 en,GB,en_GB,English (United Kingdom),en_GB.zip
 en,US,en_US,English (United States),en_US.zip
-cop,EG,cop_EG,Coptic (Egypt),cop_EG.zip
 eo,ANY,eo_l3,Esperanto,eo.zip
 et,EE,et_EE,Estonian (Estonia),et_EE.zip
 fo,FO,fo_FO,Faroese (Faroe Islands),fo_FO.zip
-fr,BE,fr_FR,Français Réforme 1990 & Classique (Belgium),fr_FR_1-3-2.zip
-fr,CA,fr_FR,Français Réforme 1990 & Classique (Canada),fr_FR_1-3-2.zip
-fr,FR,fr_FR,Français Réforme 1990 & Classique (France),fr_FR_1-3-2.zip
-fr,LU,fr_FR,Français Réforme 1990 & Classique (Luxembourg),fr_FR_1-3-2.zip
-fr,MC,fr_FR,Français Réforme 1990 & Classique (Monaco),fr_FR_1-3-2.zip
-fr,CH,fr_FR,Français Réforme 1990 & Classique (Switzerland),fr_FR_1-3-2.zip
-fr,BE,fr_FR-classique,Français Classique (Belgium),fr_FR-classique_1-3-2.zip
-fr,CA,fr_FR-classique,Français Classique (Canada),fr_FR-classique_1-3-2.zip
-fr,FR,fr_FR-classique,Français Classique (France),fr_FR-classique_1-3-2.zip
-fr,LU,fr_FR-classique,Français Classique (Luxembourg),fr_FR-classique_1-3-2.zip
-fr,MC,fr_FR-classique,Français Classique (Monaco),fr_FR-classique_1-3-2.zip
-fr,CH,fr_FR-classique,Français Classique  (Switzerland),fr_FR-classique_1-3-2.zip
-fr,BE,fr_FR-1990,Français Réforme 1990 (Belgium),fr_FR-1990_1-3-2.zip
-fr,CA,fr_FR-1990,Français Réforme 1990 (Canada),fr_FR-1990_1-3-2.zip
-fr,FR,fr_FR-1990,Français Réforme 1990 (France),fr_FR-1990_1-3-2.zip
-fr,LU,fr_FR-1990,Français Réforme 1990 (Luxembourg),fr_FR-1990_1-3-2.zip
-fr,MC,fr_FR-1990,Français Réforme 1990 (Monaco),fr_FR-1990_1-3-2.zip
-fr,CH,fr_FR-1990,Français Réforme 1990 (Switzerland),fr_FR-1990_1-3-2.zip
+fr,FR,fr_FR-classique,Franais Classique,fr_FR-classique_1-3-2.zip
+fr,FR,fr_FR-1990,Franais Rforme 1990,fr_FR-1990_1-3-2.zip
+fr,FR,fr_FR,Franais Rforme 1990 & Classique,fr_FR_1-3-2.zip
 fy,NL,fy_NL,Frisian (Netherlands),fy_NL.zip
 gl,ES,gl_ES,Galician (Spain),gl_ES.zip
 gsc,FR,gsc_FR,Gascon (France),gsc_FR.zip
-de,AT,de_DE,German (Austria Base),de_DE.zip
 de,AT,de_AT,German (Austria Extension),de_AT.zip
 de,AT,de_AT_frami,German (Austria) neu 08/2006 (frami),de_AT_frami.zip
 de,DE,de_DE,German (Germany),de_DE.zip
 de,DE,de_DE_frami,German (Germany) neu 08/2006 (frami),de_DE_frami.zip
-de,LI,de_CH,German (Liechtenstein),de_CH.zip
-de,LI,de_CH_frami,German (Liechtenstein) neu 08/2006 (frami),de_CH_frami.zip
-de,LU,de_DE,German (Luxembourg),de_DE.zip
-de,LU,de_DE_frami,German (Luxembourg) neu 08/2006 (frami),de_DE_frami.zip
 de,CH,de_CH,German (Switzerland),de_CH.zip
 de,CT,de_CH_frami,German (Switzerland) neu 08/2006 (frami),de_CH_frami.zip
 el,GR,el_GR,Greek (Greece),el_GR.zip
 gu,IN,gu_IN,Gujarati (India),gu_IN.zip
-gd,GB,gd_GB,Scots Gaelic (Scotland),gd_GB.zip
 he,IL,he_IL,Hebrew (Israel),he_IL.zip
 hil,PH,hil_PH,Hiligaynon (Philippines),hil_PH.zip
 hu,HU,hu_HU,Hungarian (Hungary),hu_HU.zip
 hu,HU,hu_HU_comb,Hungarian (Hungary) collected compounds,hu_HU_comb.zip
 id,ID,id_ID,Indonesian (Indonesia),id_ID.zip
-ia,ANY,ia_ANY,Interlingua (ANY locale),ia_ANY.zip
 ga,IE,ga_IE,Irish (Ireland),ga_IE.zip
 it,IT,it_IT,Italian (Italy),it_IT.zip
-it,CH,it_IT,Italian (Switzerland),it_IT.zip
 sw,KE,sw_KE,Kiswahili (Africa),sw_KE.zip
+ku,TR,ku_TR,Kurdish (Turkey),ku_TR.zip
+it,IT,la,Latin,la.zip
 lv,LV,lv_LV,Latvian (Latvia),lv_LV.zip
-ku,TR,ku_TR,Kurdish (Turkey),ku_TR.zip
-ku,TR,ku_TR,Kurdish (Syria),ku_TR.zip
-it,IT,la,Latin (for x-register),la.zip
 lt,LT,lt_LT,Lithuanian (Lithuania),lt_LT.zip
 mk,MK,mk_MK,Macedonian (Macedonia),mk_MK.zip
 ms,MY,ms_MY,Malay (Malaysia),ms_MY.zip
@@ -89,6 +63,7 @@
 ru,RU,ru_RU,Russian (Russia),ru_RU.zip
 ru,RU,ru_RU_ye,Russian_ye (Russia),ru_RU_ye.zip
 ru,RU,ru_RU_yo,Russian_yo (Russia),ru_RU_yo.zip
+gd,GB,gd_GB,Scots Gaelic (Scotland),gd_GB.zip
 tn,ZA,tn_ZA,Setswana (Africa),tn_ZA.zip
 sk,SK,sk_SK,Slovak (Slovakia),sk_SK.zip
 sl,SI,sl_SI,Slovenian (Slovenia),sl_SI.zip
@@ -104,7 +79,6 @@
 es,EC,es_EC,Spanish (Ecuador),es_EC.zip
 es,SV,es_SV,Spanish (El Salvador),es_SV.zip
 es,GT,es_GT,Spanish (Guatemala),es_GT.zip
-es,HN,es_HN,Spanish (Honduras),es_HN.zip
 es,MX,es_MX,Spanish (Mexico),es_MX.zip
 es,NI,es_NI,Spanish (Nicaragua),es_NI.zip
 es,PA,es_PA,Spanish (Panama),es_PA.zip
@@ -118,8 +92,7 @@
 sv,SE,sv_SE,Swedish (Sweden),sv_SE.zip
 ts,ZA,ts_ZA,Tsonga (South Africa),ts_ZA.zip
 uk,UA,uk_UA,Ukrainian (Ukraine),uk_UA.zip
-ur,IN,ur_PK,Urdu (India),ur_PK.zip
-ur,PK,ur_PK,Urdu (Pakistan),ur_PK.zip
+ur,PK,ur_PK,Urdu,ur_PK.zip
 ve,ZA,ve_ZA,Venda (South Africa),ve_ZA.zip
 vi,VN,vi_VN,Vietnamese (Viet-Nam),vi_VN.zip
 cy,GB,cy_GB,Welsh (Wales),cy_GB.zip
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/win32/nsis/generate_gtk_zip.sh	Wed Mar 10 17:58:21 2010 +0000
@@ -0,0 +1,80 @@
+#!/bin/bash
+# Script to generate zip file for GTK+ runtime to be included in Pidgin installer
+
+PIDGIN_BASE=$1
+
+if [ ! -e $PIDGIN_BASE/ChangeLog.win32 ]; then
+	echo `basename $0` must must have the pidgin base dir specified as a parameter.
+	exit 1
+fi
+
+STAGE_DIR=$PIDGIN_BASE/pidgin/win32/nsis/gtk_runtime_stage
+#Subdirectory of $STAGE_DIR
+INSTALL_DIR=Gtk
+CONTENTS_FILE=$INSTALL_DIR/CONTENTS
+
+#This needs to be changed every time there is any sort of change.
+BUNDLE_VERSION=2.14.7.0
+
+ATK="http://ftp.gnome.org/pub/gnome/binaries/win32/atk/1.24/atk_1.24.0-1_win32.zip ATK 1.24.0-1"
+CAIRO="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/cairo_1.8.10-1_win32.zip Cairo 1.8.10-1"
+EXPAT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/expat_2.0.1-1_win32.zip Expat 2.0.1-1"
+FONTCONFIG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/fontconfig_2.8.0-2_win32.zip Fontconfig 2.8.0-2"
+FREETYPE="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/freetype_2.3.11-2_win32.zip Freetype 2.3.11-2"
+GETTEXT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-runtime-0.17-1.zip Gettext 0.17-1"
+GLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.20/glib_2.20.5-1_win32.zip Glib 2.20.5-1"
+GTK="http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.14/gtk+_2.14.7-1_win32.zip GTK+ 2.14.7-1"
+LIBJPEG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/jpeg-6b-4.zip libjpeg 6b-4"
+#Used by GTK+
+LIBPNG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.2.39-1_win32.zip libpng 1.2.39-1"
+#Used by Cairo
+LIBPNG2="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.4.0-1_win32.zip libpng 1.4.0-1"
+LIBTIFF="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libtiff_3.9.1-1_win32.zip libtiff 3.9.1-1"
+PANGO="http://ftp.gnome.org/pub/gnome/binaries/win32/pango/1.26/pango_1.26.2-1_win32.zip Pango 1.26.2-1"
+ZLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/zlib-1.2.3.zip zlib 1.2.3"
+
+ALL="ATK CAIRO EXPAT FONTCONFIG FREETYPE GETTEXT GLIB GTK LIBJPEG LIBPNG LIBPNG2 LIBTIFF PANGO ZLIB"
+
+mkdir -p $STAGE_DIR
+cd $STAGE_DIR
+
+rm -rf $INSTALL_DIR
+mkdir $INSTALL_DIR
+
+#new CONTENTS file
+echo Bundle Version $BUNDLE_VERSION > $CONTENTS_FILE
+
+function download_and_extract {
+	URL=${1%%\ *}
+	NAME=${1#*\ }
+	FILE=`basename $URL`
+	if [ ! -e $FILE ]; then
+		echo Downloading $NAME
+		wget $URL
+	fi
+	unzip -q $FILE -d $INSTALL_DIR
+	echo "$NAME" >> $CONTENTS_FILE
+}
+
+for VAL in $ALL
+do
+	VAR=${!VAL}
+	download_and_extract "$VAR"
+done
+
+#Default GTK+ Theme to MS-Windows
+echo gtk-theme-name = \"MS-Windows\" > $INSTALL_DIR/etc/gtk-2.0/gtkrc
+
+#Blow away translations that we don't have in Pidgin
+for LOCALE_DIR in $INSTALL_DIR/share/locale/*
+do
+	LOCALE=`basename $LOCALE_DIR`
+	if [ ! -e $PIDGIN_BASE/po/$LOCALE.po ]; then
+		echo Remove $LOCALE translation as it is missing from Pidgin
+		rm -r $LOCALE_DIR
+	fi
+done
+
+#Generate zip file to be included in installer
+zip -9 -r ../gtk-runtime-$BUNDLE_VERSION.zip Gtk
+
--- a/pidgin/win32/nsis/langmacros.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/langmacros.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -48,6 +48,7 @@
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DESKTOP_SHORTCUT_DESC		${CUR_LANG}
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_STARTMENU_SHORTCUT_DESC	${CUR_LANG}
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT DEBUG_SYMBOLS_SECTION_TITLE ${CUR_LANG}
+  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT TRANSLATIONS_SECTION_TITLE ${CUR_LANG}
 
   ; Installer Finish Page
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_FINISH_VISIT_WEB_SITE		${CUR_LANG}
@@ -65,34 +66,10 @@
   ; Spellcheck Section Prompts
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_TITLE	${CUR_LANG}
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ERROR		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DICT_ERROR		${CUR_LANG}
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT ASPELL_INSTALL_FAILED		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_BRETON		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CATALAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CZECH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_WELSH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DANISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GERMAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ENGLISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GREEK		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ESPERANTO		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SPANISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FAROESE		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FRENCH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ITALIAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DUTCH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_NORWEGIAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_POLISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_PORTUGUESE		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ROMANIAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_RUSSIAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SLOVAK		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SWEDISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_UKRAINIAN		${CUR_LANG}
 
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DEBUGSYMBOLS_ERROR ${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_GTK_DOWNLOAD_ERROR ${CUR_LANG}
+  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DEBUGSYMBOLS_ERROR		${CUR_LANG}
+  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_GTK_DOWNLOAD_ERROR		${CUR_LANG}
 
   !undef CUR_LANG
 !macroend
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Wed Mar 10 17:58:21 2010 +0000
@@ -28,6 +28,7 @@
 ShowInstDetails show
 ShowUninstDetails show
 SetDateSave on
+RequestExecutionLevel highest
 
 ; $name and $INSTDIR are set in .onInit function..
 
@@ -53,7 +54,6 @@
 ;Defines
 
 !define PIDGIN_NSIS_INCLUDE_PATH		"."
-!define PIDGIN_INSTALLER_DEPS			"..\..\..\..\win32-dev\pidgin-inst-deps-0.2"
 
 ; Remove these and the stuff that uses them at some point
 !define OLD_GAIM_REG_KEY			"SOFTWARE\gaim"
@@ -71,8 +71,8 @@
 !define PERL_REG_KEY				"SOFTWARE\Perl"
 !define PERL_DLL				"perl510.dll"
 
-!define ASPELL_REG_KEY				"SOFTWARE\Aspell"
 !define DOWNLOADER_URL				"http://pidgin.im/win32/download_redir.php"
+!define SPELL_DOWNLOAD_URL			"http://ftp.services.openoffice.org/pub/OpenOffice.org/contrib/dictionaries"
 
 !define MEMENTO_REGISTRY_ROOT			HKLM
 !define MEMENTO_REGISTRY_KEY			"${PIDGIN_UNINSTALL_KEY}"
@@ -302,7 +302,7 @@
           IfErrors uninstall_problem
             ; Ready to uninstall..
             ClearErrors
-            ExecWait '"$TEMP\$R6" /S /KEEPGTK=1 _?=$R1'
+            ExecWait '"$TEMP\$R6" /S /UPGRADE=1 _?=$R1'
             IfErrors exec_error
               Delete "$TEMP\$R6"
             Goto done
@@ -340,7 +340,7 @@
 !ifdef OFFLINE_INSTALLER
 
   SetOutPath $PLUGINSDIR
-  File /oname=gtk.zip "..\..\..\..\gtk_installer\gtk-runtime-${GTK_INSTALL_VERSION}.zip"
+  File /oname=gtk.zip ".\gtk-runtime-${GTK_INSTALL_VERSION}.zip"
 
 !else
 
@@ -352,12 +352,14 @@
   Pop $R0
   StrCmp $R0 "cancel" done
   StrCmp $R0 "success" +2
-    MessageBox MB_RETRYCANCEL "$(PIDGIN_GTK_DOWNLOAD_ERROR) : $R1" /SD IDCANCEL IDRETRY retry IDCANCEL done
+    MessageBox MB_RETRYCANCEL "$(PIDGIN_GTK_DOWNLOAD_ERROR) : $R2" /SD IDCANCEL IDRETRY retry IDCANCEL done
 
 !endif
 
+  ;Delete the old Gtk directory
+  RMDir /r "$INSTDIR\Gtk"
+
   SetOutPath "$INSTDIR"
-
   nsisunz::UnzipToLog $R1 "$INSTDIR"
   Pop $R0
   StrCmp $R0 "success" +2
@@ -415,7 +417,6 @@
     Delete "$INSTDIR\plugins\libjabber.dll"
 
     File /r /x locale ..\..\..\${PIDGIN_INSTALL_DIR}\*.*
-    File "${PIDGIN_INSTALLER_DEPS}\exchndl.dll"
 
     ; Check if Perl is installed, if so add it to the AppPaths
     ReadRegStr $R2 HKLM ${PERL_REG_KEY} ""
@@ -505,7 +506,7 @@
 !macro LANG_SECTION lang
   ${MementoUnselectedSection} "${lang}" SecLang_${lang}
     SetOutPath "$INSTDIR\locale\${lang}\LC_MESSAGES"
-    File /oname=pidgin.mo "..\..\..\${PIDGIN_INSTALL_DIR}\locale\${lang}\LC_MESSAGES\pidgin.mo"
+    File "..\..\..\${PIDGIN_INSTALL_DIR}\locale\${lang}\LC_MESSAGES\*.mo"
     SetOutPath "$INSTDIR"
   ${MementoSectionEnd}
 !macroend
@@ -518,109 +519,29 @@
 ;--------------------------------
 ;Spell Checking
 
-SectionGroup /e $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck
-  Section /o $(PIDGIN_SPELLCHECK_BRETON) SecSpellCheckBreton
-    Push ${SecSpellCheckBreton}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_CATALAN) SecSpellCheckCatalan
-    Push ${SecSpellCheckCatalan}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_CZECH) SecSpellCheckCzech
-    Push ${SecSpellCheckCzech}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_WELSH) SecSpellCheckWelsh
-    Push ${SecSpellCheckWelsh}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_DANISH) SecSpellCheckDanish
-    Push ${SecSpellCheckDanish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_GERMAN) SecSpellCheckGerman
-    Push ${SecSpellCheckGerman}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_GREEK) SecSpellCheckGreek
-    Push ${SecSpellCheckGreek}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_ENGLISH) SecSpellCheckEnglish
-    Push ${SecSpellCheckEnglish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_ESPERANTO) SecSpellCheckEsperanto
-    Push ${SecSpellCheckEsperanto}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_SPANISH) SecSpellCheckSpanish
-    Push ${SecSpellCheckSpanish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_FAROESE) SecSpellCheckFaroese
-    Push ${SecSpellCheckFaroese}
-    Call InstallAspellAndDict
+!macro SPELLCHECK_SECTION lang lang_name lang_file
+  Section /o "${lang_name}" SecSpell_${lang}
+    Push ${lang_file}
+    Push ${lang}
+    Call InstallDict
   SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_FRENCH) SecSpellCheckFrench
-    Push ${SecSpellCheckFrench}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_ITALIAN) SecSpellCheckItalian
-    Push ${SecSpellCheckItalian}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_DUTCH) SecSpellCheckDutch
-    Push ${SecSpellCheckDutch}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_NORWEGIAN) SecSpellCheckNorwegian
-    Push ${SecSpellCheckNorwegian}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_POLISH) SecSpellCheckPolish
-    Push ${SecSpellCheckPolish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_PORTUGUESE) SecSpellCheckPortuguese
-    Push ${SecSpellCheckPortuguese}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_ROMANIAN) SecSpellCheckRomanian
-    Push ${SecSpellCheckRomanian}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_RUSSIAN) SecSpellCheckRussian
-    Push ${SecSpellCheckRussian}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_SLOVAK) SecSpellCheckSlovak
-    Push ${SecSpellCheckSlovak}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_SWEDISH) SecSpellCheckSwedish
-    Push ${SecSpellCheckSwedish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_UKRAINIAN) SecSpellCheckUkrainian
-    Push ${SecSpellCheckUkrainian}
-    Call InstallAspellAndDict
-  SectionEnd
+!macroend
+SectionGroup $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck
+  !include "pidgin-spellcheck.nsh"
 SectionGroupEnd
 
 Section /o $(DEBUG_SYMBOLS_SECTION_TITLE) SecDebugSymbols
+  
   InitPluginsDir
-
-  ; We need to download and extract the debug symbols
-  StrCpy $R1 "$PLUGINSDIR\pidgin-${PIDGIN_VERSION}-dbgsym.zip"
+  StrCpy $R1 "$PLUGINSDIR\dbgsym.zip"
 !ifdef OFFLINE_INSTALLER
 
   SetOutPath $PLUGINSDIR
-  File /oname=pidgin-${PIDGIN_VERSION}-dbgsym.zip "..\..\..\..\gtk_installer\gtk-runtime-${GTK_INSTALL_VERSION}.zip"
+  File /oname=dbgsym.zip "..\..\..\pidgin-${PIDGIN_VERSION}-dbgsym.zip"
 
 !else
 
+  ; We need to download the debug symbols
   retry:
   StrCpy $R2 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=dbgsym"
   DetailPrint "Downloading Debug Symbols... ($R2)"
@@ -632,6 +553,7 @@
 
 !endif
 
+  SetOutPath "$INSTDIR"
   nsisunz::UnzipToLog $R1 "$INSTDIR"
   Pop $R0
   StrCmp $R0 "success" +2
@@ -769,27 +691,36 @@
     Delete "$INSTDIR\sounds\purple\send.wav"
     RMDir "$INSTDIR\sounds\purple"
     RMDir "$INSTDIR\sounds"
+    Delete "$INSTDIR\spellcheck\libenchant.dll"
+    Delete "$INSTDIR\spellcheck\libgtkspell-0.dll"
+    Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_aspell.dll"
+    Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_ispell.dll"
+    Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_myspell.dll"
+    RMDir "$INSTDIR\spellcheck\lib\enchant"
+    RMDir "$INSTDIR\spellcheck\lib"
+    RMDir "$INSTDIR\spellcheck"
     Delete "$INSTDIR\freebl3.dll"
     Delete "$INSTDIR\idletrack.dll"
-    Delete "$INSTDIR\libgtkspell.dll"
     Delete "$INSTDIR\libjabber.dll"
+    Delete "$INSTDIR\libnspr4.dll"
     Delete "$INSTDIR\libmeanwhile-1.dll"
     Delete "$INSTDIR\liboscar.dll"
+    Delete "$INSTDIR\libplc4.dll"
+    Delete "$INSTDIR\libplds4.dll"
     Delete "$INSTDIR\libpurple.dll"
     Delete "$INSTDIR\libsasl.dll"
     Delete "$INSTDIR\libsilc-1-1-2.dll"
     Delete "$INSTDIR\libsilcclient-1-1-2.dll"
-    Delete "$INSTDIR\libxml2.dll"
+    Delete "$INSTDIR\libxml2-2.dll"
     Delete "$INSTDIR\libymsg.dll"
-    Delete "$INSTDIR\nspr4.dll"
     Delete "$INSTDIR\nss3.dll"
+    Delete "$INSTDIR\nssutil3.dll"
     Delete "$INSTDIR\nssckbi.dll"
     Delete "$INSTDIR\pidgin.dll"
     Delete "$INSTDIR\pidgin.exe"
-    Delete "$INSTDIR\plc4.dll"
-    Delete "$INSTDIR\plds4.dll"
     Delete "$INSTDIR\smime3.dll"
     Delete "$INSTDIR\softokn3.dll"
+    Delete "$INSTDIR\sqlite3.dll"
     Delete "$INSTDIR\ssl3.dll"
     Delete "$INSTDIR\${PIDGIN_UNINST_EXE}"
     Delete "$INSTDIR\exchndl.dll"
@@ -801,10 +732,13 @@
     ; Remove the local GTK+ copy (if we're not just upgrading)
     ${GetParameters} $R0
     ClearErrors
-    ${GetOptions} "$R3" "/KEEPGTK=" $R1
+    ${GetOptions} "$R0" "/UPGRADE=" $R1
     IfErrors +2
-    StrCmp $R1 "1" +2
+    StrCmp $R1 "1" upgrade_done
     RMDir /r "$INSTDIR\Gtk"
+    ; Remove the downloaded spellcheck dictionaries (if we're not just upgrading)
+    RMDir /r "$INSTDIR\spellcheck"
+    upgrade_done:
 
     ;Try to remove Pidgin install dir (only if empty)
     RMDir "$INSTDIR"
@@ -843,50 +777,7 @@
 
   !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheck} \
         $(PIDGIN_SPELLCHECK_SECTION_DESCRIPTION)
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckBreton} \
-        "$(PIDGIN_SPELLCHECK_BRETON) (862kb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckCatalan} \
-        "$(PIDGIN_SPELLCHECK_CATALAN) (3.9Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckCzech} \
-        "$(PIDGIN_SPELLCHECK_CZECH) (17Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckWelsh} \
-        "$(PIDGIN_SPELLCHECK_WELSH) (4.2Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckDanish} \
-        "$(PIDGIN_SPELLCHECK_DANISH) (6.9Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckGerman} \
-        "$(PIDGIN_SPELLCHECK_GERMAN) (5.4Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckGreek} \
-        "$(PIDGIN_SPELLCHECK_GREEK) (7.1Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckEnglish} \
-        "$(PIDGIN_SPELLCHECK_ENGLISH) (2.3Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckEsperanto} \
-        "$(PIDGIN_SPELLCHECK_ESPERANTO) (5.7Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSpanish} \
-        "$(PIDGIN_SPELLCHECK_SPANISH) (7.0Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckFaroese} \
-        "$(PIDGIN_SPELLCHECK_FAROESE) (913kb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckFrench} \
-        "$(PIDGIN_SPELLCHECK_FRENCH) (9.3Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckItalian} \
-        "$(PIDGIN_SPELLCHECK_ITALIAN) (770kb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckDutch} \
-        "$(PIDGIN_SPELLCHECK_DUTCH) (3.7Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckNorwegian} \
-        "$(PIDGIN_SPELLCHECK_NORWEGIAN) (3.2Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckPolish} \
-        "$(PIDGIN_SPELLCHECK_POLISH) (9.3Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckPortuguese} \
-        "$(PIDGIN_SPELLCHECK_PORTUGUESE) (5.5Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckRomanian} \
-        "$(PIDGIN_SPELLCHECK_ROMANIAN) (906kb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckRussian} \
-        "$(PIDGIN_SPELLCHECK_RUSSIAN) (11Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSlovak} \
-        "$(PIDGIN_SPELLCHECK_SLOVAK) (8.0Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSwedish} \
-        "$(PIDGIN_SPELLCHECK_SWEDISH) (2.2Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckUkrainian} \
-        "$(PIDGIN_SPELLCHECK_UKRAINIAN) (12Mb)"
+
 !insertmacro MUI_FUNCTION_DESCRIPTION_END
 
 ;--------------------------------
@@ -1233,12 +1124,6 @@
 
   ${MementoSectionRestore}
 
-  !insertmacro SetSectionFlag ${SecSpellCheck} ${SF_RO}
-  !insertmacro UnselectSection ${SecSpellCheck}
-
-  ;Mark the dictionaries that are already installed as readonly
-  Call SelectAndDisableInstalledDictionaries
-
   ;Preselect the URI handlers as appropriate
   Call SelectURIHandlerSelections
 
@@ -1337,6 +1222,10 @@
     StrCpy $INSTDIR "$R2\Pidgin"
 
   instdir_done:
+
+  ;Mark the dictionaries that are already installed as readonly
+  Call SelectAndDisableInstalledDictionaries
+
 ;LogSet on
   Pop $R3
   Pop $R2
@@ -1368,6 +1257,10 @@
   Push $R0
   Push $R1
 
+!ifdef OFFLINE_INSTALLER
+    !insertmacro SelectSection ${SecDebugSymbols}
+!endif
+
   Call DoWeNeedGtk
   Pop $R0
   IntCmp $R0 1 done gtk_not_mandatory
@@ -1389,234 +1282,72 @@
 ; Convert the a Section index to the language code
 ; Push the section index onto the stack and pop off the language code after the call
 ; This will set the error code, if no match is found
-Function GetLangCodeForSection
-  ClearErrors
-  Push $R0
-  Exch
-  Pop $R0 ;This is the section index
-
-  IntCmp $R0 ${SecSpellCheckBreton} 0 +3 +3
-  StrCpy $R0 "br"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckCatalan} 0 +3 +3
-  StrCpy $R0 "ca"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckCzech} 0 +3 +3
-  StrCpy $R0 "cs"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckWelsh} 0 +3 +3
-  StrCpy $R0 "cy"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckDanish} 0 +3 +3
-  StrCpy $R0 "da"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckGerman} 0 +3 +3
-  StrCpy $R0 "de"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckGreek} 0 +3 +3
-  StrCpy $R0 "el"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckEnglish} 0 +3 +3
-  StrCpy $R0 "en"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckEsperanto} 0 +3 +3
-  StrCpy $R0 "eo"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckSpanish} 0 +3 +3
-  StrCpy $R0 "es"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckFaroese} 0 +3 +3
-  StrCpy $R0 "fo"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckFrench} 0 +3 +3
-  StrCpy $R0 "fr"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckItalian} 0 +3 +3
-  StrCpy $R0 "it"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckDutch} 0 +3 +3
-  StrCpy $R0 "nl"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckNorwegian} 0 +3 +3
-  StrCpy $R0 "no"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckPolish} 0 +3 +3
-  StrCpy $R0 "pl"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckPortuguese} 0 +3 +3
-  StrCpy $R0 "pt"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckRomanian} 0 +3 +3
-  StrCpy $R0 "ro"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckRussian} 0 +3 +3
-  StrCpy $R0 "ru"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckSlovak} 0 +3 +3
-  StrCpy $R0 "sk"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckSwedish} 0 +3 +3
-  StrCpy $R0 "sv"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckUkrainian} 0 +3 +3
-  StrCpy $R0 "uk"
-  Goto done
-
-  SetErrors
-
-  done:
-  Exch $R0
-FunctionEnd ;GetLangCodeForSection
 
 ; Select and Disable any Sections that have currently installed dictionaries
+!macro CHECK_SPELLCHECK_SECTION lang
+  ;Advance to the next (correct) section index
+  IntOp $R0 $R0 + 1
+  IfFileExists "$INSTDIR\spellcheck\share\enchant\myspell\${lang}.dic" 0 done_${lang}
+  SectionGetFlags $R0 $R1
+  IntOp $R1 $R1 | ${SF_RO} ; Mark Readonly
+  IntOp $R1 $R1 | ${SF_SELECTED} ; Select
+  SectionSetFlags $R0 $R1
+  done_${lang}:
+!macroend
 Function SelectAndDisableInstalledDictionaries
   Push $R0
   Push $R1
-  Push $R2
-
-  ; Start with the first language dictionary
-  IntOp $R0 ${SecSpellCheck} + 1
-
-  start:
-  ; If it is the end of the section group, stop
-  SectionGetFlags $R0 $R1
-  IntOp $R2 $R1 & ${SF_SECGRPEND}
-  IntCmp $R2 ${SF_SECGRPEND} done
-
-  Push $R0
-  Call GetLangCodeForSection
-  Pop $R2
-  IfErrors end_loop
-  ReadRegStr $R2 HKLM "${ASPELL_REG_KEY}-$R2" "" ; Check that the dictionary is installed
-  StrCmp $R2 "" end_loop ; If it isn't installed, skip to the next item
-  IntOp $R1 $R1 | ${SF_RO} ; Mark Readonly
-  IntOp $R1 $R1 | ${SF_SELECTED} ; Select
-  SectionSetFlags $R0 $R1
-
-  end_loop:
-  IntOp $R0 $R0 + 1 ;Advance to the next section
-  Goto start
 
-  done:
-  Pop $R2
-  Pop $R1
-  Pop $R0
-FunctionEnd
-
-Function InstallAspellAndDict
-  Push $R0
-  Exch
-  Call GetLangCodeForSection
-  Pop $R0 ;This is the language code
-  Push $R1
-
-  InitPluginsDir
-
-  IfErrors done ; We weren't able to convert the section to lang code
+  !insertmacro SetSectionFlag ${SecSpellCheck} ${SF_RO}
+  !insertmacro UnselectSection ${SecSpellCheck}
 
-  retry:
-    Call InstallAspell
-    Pop $R1
-    StrCmp $R1 "" +3
-    StrCmp $R1 "cancel" done
-    MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_ERROR) : $R1" /SD IDCANCEL IDRETRY retry IDCANCEL done
-
-  retry_dict:
-    Push $R0
-    Call InstallAspellDictionary
-    Pop $R1
-    StrCmp $R1 "" +3
-    StrCmp $R1 "cancel" done
-    MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_DICT_ERROR) : $R1" /SD IDCANCEL IDRETRY retry_dict
-
-  done:
+  IntOp $R0 ${SecSpellCheck} + 0
+  !include "pidgin-spellcheck-preselect.nsh"
 
   Pop $R1
   Pop $R0
 FunctionEnd
 
-Function InstallAspell
-  Push $R0
-  Push $R1
-  Push $R2
-
-  check:
-  ClearErrors
-  ReadRegDWORD $R0 HKLM ${ASPELL_REG_KEY} "AspellVersion"
-  IntCmp $R0 15 installed
-
-  ; If this is the check after installation, don't infinite loop on failure
-  StrCmp $R1 "$PLUGINSDIR\aspell_installer.exe" 0 +3
-    StrCpy $R0 $(ASPELL_INSTALL_FAILED)
-    Goto done
-
-  ; We need to download and install aspell
-  StrCpy $R1 "$PLUGINSDIR\aspell_installer.exe"
-  StrCpy $R2 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=aspell_core"
-  DetailPrint "Downloading Aspell... ($R2)"
-  NSISdl::download /TIMEOUT=10000 $R2 $R1
-  Pop $R0
-  StrCmp $R0 "success" +2
-    Goto done
-  ExecWait '"$R1"'
-  Delete $R1
-  Goto check ; Check that it is now installed correctly
-
-  installed: ;Aspell is currently installed, no error message
-    DetailPrint "Aspell is installed"
-    StrCpy $R0 ''
-
-  done:
-  Pop $R2
-  Pop $R1
-  Exch $R0
-FunctionEnd
-
-Function InstallAspellDictionary
+Function InstallDict
   Push $R0
   Exch
   Pop $R0 ;This is the language code
   Push $R1
+  Exch 2
+  Pop $R1 ;This is the language file
   Push $R2
   Push $R3
-  Push $R4
 
-  check:
   ClearErrors
-  ReadRegStr $R2 HKLM "${ASPELL_REG_KEY}-$R0" ""
-  StrCmp $R2 "" 0 installed
+  IfFileExists "$INSTDIR\spellcheck\share\enchant\myspell\$R0.dic" installed
+
+  InitPluginsDir
 
-  ; If this is the check after installation, don't infinite loop on failure
-  StrCmp $R1 "$PLUGINSDIR\aspell_dict-$R0.exe" 0 +3
-    StrCpy $R0 $(ASPELL_INSTALL_FAILED)
+  ; We need to download and install dictionary
+  StrCpy $R2 "$PLUGINSDIR\$R1"
+  StrCpy $R3 "${SPELL_DOWNLOAD_URL}/$R1"
+  DetailPrint "Downloading the $R0 Dictionary... ($R3)"
+  retry:
+  NSISdl::download /TIMEOUT=10000 "$R3" "$R2"
+  Pop $R3
+  StrCmp $R3 "cancel" done
+  StrCmp $R3 "success" +3
+    MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_ERROR) : $R3" /SD IDCANCEL IDRETRY retry IDCANCEL done
+    Goto done
+  SetOutPath "$INSTDIR\spellcheck\share\enchant\myspell"
+  nsisunz::UnzipToLog "$R2" "$OUTDIR"
+  SetOutPath "$INSTDIR"
+  Pop $R3
+  StrCmp $R3 "success" installed
+    DetailPrint "$R3" ;print error message to log
     Goto done
 
-  ; We need to download and install aspell
-  StrCpy $R1 "$PLUGINSDIR\aspell_dict-$R0.exe"
-  StrCpy $R3 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=lang_$R0"
-  DetailPrint "Downloading the Aspell $R0 Dictionary... ($R3)"
-  NSISdl::download /TIMEOUT=10000 $R3 $R1
-  Pop $R3
-  StrCmp $R3 "success" +3
-    StrCpy $R0 $R3
-    Goto done
-  ; Use a specific temporary $OUTDIR for each dictionary because the installer doesn't clean up after itself
-  StrCpy $R4 "$OUTDIR"
-  SetOutPath "$PLUGINSDIR\aspell_dict-$R0"
-  ExecWait '"$R1"'
-  SetOutPath "$R4"
-  RMDir /r "$PLUGINSDIR\aspell_dict-$R0"
-  Delete $R1
-  Goto check ; Check that it is now installed correctly
-
   installed: ;The dictionary is currently installed, no error message
-    DetailPrint "Aspell $R0 Dictionary is installed"
-    StrCpy $R0 ''
+    DetailPrint "$R0 Dictionary is installed"
 
   done:
-  Pop $R4
   Pop $R3
   Pop $R2
-  Pop $R1
-  Exch $R0
+  Pop $R0
+  Exch $R1
 FunctionEnd
--- a/pidgin/win32/nsis/translations/afrikaans.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/afrikaans.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -49,29 +49,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"Speltoets-ondersteuning"
 !define PIDGIN_SPELLCHECK_ERROR		"Fout met installering van speltoetser"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Fout met installering van speltoetswoordeboek"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Ondersteuning vir speltoeter.  (Internetverbinding benodigd vir installasie)"
-!define ASPELL_INSTALL_FAILED			"Installasie het misluk"
-!define PIDGIN_SPELLCHECK_BRETON		"Bretons"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalaans"
-!define PIDGIN_SPELLCHECK_CZECH		"Tsjeggies"
-!define PIDGIN_SPELLCHECK_WELSH		"Wallies"
-!define PIDGIN_SPELLCHECK_DANISH		"Deens"
-!define PIDGIN_SPELLCHECK_GERMAN		"Duits"
-!define PIDGIN_SPELLCHECK_GREEK		"Grieks"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Engels"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spaans"
-!define PIDGIN_SPELLCHECK_FAROESE		"Faroes"
-!define PIDGIN_SPELLCHECK_FRENCH		"Frans"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italiaans"
-!define PIDGIN_SPELLCHECK_DUTCH		"Nederlands"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Noorweegs"
-!define PIDGIN_SPELLCHECK_POLISH		"Pools"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugees"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Roemeens"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Russies"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slowaaks"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Sweeds"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Oekraens"
 
--- a/pidgin/win32/nsis/translations/arabic.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/arabic.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -47,29 +47,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"  "
 !define PIDGIN_SPELLCHECK_ERROR		"    "
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"     "
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"  .  (   )"
-!define ASPELL_INSTALL_FAILED			" "
-!define PIDGIN_SPELLCHECK_BRETON		"Breton"
-!define PIDGIN_SPELLCHECK_CATALAN		"Catalan"
-!define PIDGIN_SPELLCHECK_CZECH		"Czech"
-!define PIDGIN_SPELLCHECK_WELSH		"Welsh"
-!define PIDGIN_SPELLCHECK_DANISH		"Danish"
-!define PIDGIN_SPELLCHECK_GERMAN		"German"
-!define PIDGIN_SPELLCHECK_GREEK		"Greek"
-!define PIDGIN_SPELLCHECK_ENGLISH		"English"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spanish"
-!define PIDGIN_SPELLCHECK_FAROESE		"Faroese"
-!define PIDGIN_SPELLCHECK_FRENCH		"French"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italian"
-!define PIDGIN_SPELLCHECK_DUTCH		"Dutch"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norwegian"
-!define PIDGIN_SPELLCHECK_POLISH		"Polish"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portuguese"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Romanian"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Russian"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slovak"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Swedish"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainian"
 
--- a/pidgin/win32/nsis/translations/basque.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/basque.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -47,29 +47,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"Zuzentzaile Ortografikoa"
 !define PIDGIN_SPELLCHECK_ERROR		"Errorea Zuzentzaile Ortografikoa instalatzean"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Errorea Zuzentzaile Ortografikoarentzako hiztegia instalatzean"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Zuzentzaile Ortografikoa.  (Internet konexioa behar du instalatzeko)"
-!define ASPELL_INSTALL_FAILED			"Ezin izan da instalatu"
-!define PIDGIN_SPELLCHECK_BRETON		"Britaniera"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalana"
-!define PIDGIN_SPELLCHECK_CZECH		"Txekiera"
-!define PIDGIN_SPELLCHECK_WELSH		"Gaelikoa"
-!define PIDGIN_SPELLCHECK_DANISH		"Daniera"
-!define PIDGIN_SPELLCHECK_GERMAN		"Alemana"
-!define PIDGIN_SPELLCHECK_GREEK		"Grekoa"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Ingelesa"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperantoa"
-!define PIDGIN_SPELLCHECK_SPANISH		"Gaztelania"
-!define PIDGIN_SPELLCHECK_FAROESE		"Faroera"
-!define PIDGIN_SPELLCHECK_FRENCH		"Frantsesa"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italiera"
-!define PIDGIN_SPELLCHECK_DUTCH		"Nederlandera"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norvegiera"
-!define PIDGIN_SPELLCHECK_POLISH		"Poloniera"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugesa"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Errumaniera"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Errusiera"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Eslovakiera"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Suediera"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukraniera"
 
--- a/pidgin/win32/nsis/translations/catalan.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/catalan.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -46,29 +46,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Suport a la Verificaci de l'Ortografia "
 !define PIDGIN_SPELLCHECK_ERROR			"Error instal.lant verificaci de l'ortografia"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Error Instal.lant Diccionari  per a Verificaci de l'Ortografia"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Suport per a Verificaci de l'Ortografia.  (s necesaria connexi a internet per dur a terme la instal.laci)"
-!define ASPELL_INSTALL_FAILED			"La instal.laci ha fallat"
-!define PIDGIN_SPELLCHECK_BRETON			"Bret"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catal"
-!define PIDGIN_SPELLCHECK_CZECH			"Txec"
-!define PIDGIN_SPELLCHECK_WELSH			"Galls"
-!define PIDGIN_SPELLCHECK_DANISH			"Dans"
-!define PIDGIN_SPELLCHECK_GERMAN			"Alemany"
-!define PIDGIN_SPELLCHECK_GREEK			"Grec"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Angls"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Espanyol"
-!define PIDGIN_SPELLCHECK_FAROESE			"Feros"
-!define PIDGIN_SPELLCHECK_FRENCH			"Francs"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Itali"
-!define PIDGIN_SPELLCHECK_DUTCH			"Holands"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Noruec"
-!define PIDGIN_SPELLCHECK_POLISH			"Polons"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugus"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Romans"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rus"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Eslovac"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Suec"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ucrans"
 
--- a/pidgin/win32/nsis/translations/dutch.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/dutch.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -39,28 +39,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Spellingscontrole"
 !define PIDGIN_SPELLCHECK_ERROR			"Fout bij installatie van spellingscontrole"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Fout bij installatie van woordenboek voor spellingscontrole"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Ondersteuning voor spellingscontrole. (Internetverbinding nodig voor installatie)"
-!define ASPELL_INSTALL_FAILED			"Installatie mislukt"
-!define PIDGIN_SPELLCHECK_BRETON			"Bretons"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catalaans"
-!define PIDGIN_SPELLCHECK_CZECH			"Tsjechisch"
-!define PIDGIN_SPELLCHECK_WELSH			"Welsh"
-!define PIDGIN_SPELLCHECK_DANISH			"Deens"
-!define PIDGIN_SPELLCHECK_GERMAN			"Duits"
-!define PIDGIN_SPELLCHECK_GREEK			"Grieks"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Engels"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Spaans"
-!define PIDGIN_SPELLCHECK_FAROESE			"Faroese"
-!define PIDGIN_SPELLCHECK_FRENCH			"Frans"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Italiaans"
-!define PIDGIN_SPELLCHECK_DUTCH			"Nederlands"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Noors"
-!define PIDGIN_SPELLCHECK_POLISH			"Pools"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugees"
-!define PIDGIN_SPELLCHECK_ROMANIAN			"Roemeens"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Russisch"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slowaaks"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Zweeds"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Oekrans"
--- a/pidgin/win32/nsis/translations/english.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/english.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -25,13 +25,14 @@
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SHORTCUTS_SECTION_TITLE		"Shortcuts"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE	"Desktop"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE	"Start Menu"
+!insertmacro PIDGIN_MACRO_DEFAULT_STRING TRANSLATIONS_SECTION_TITLE		"Localizations"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SECTION_DESCRIPTION		"Core Pidgin files and dlls"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING GTK_SECTION_DESCRIPTION		"A multi-platform GUI toolkit, used by Pidgin"
 
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	"Shortcuts for starting Pidgin"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DESKTOP_SHORTCUT_DESC		"Create a shortcut to Pidgin on the Desktop"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_STARTMENU_SHORTCUT_DESC		"Create a Start Menu entry for Pidgin"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING DEBUG_SYMBOLS_SECTION_TITLE "Debug Symbols (for reporting crashes)"
+!insertmacro PIDGIN_MACRO_DEFAULT_STRING DEBUG_SYMBOLS_SECTION_TITLE		"Debug Symbols (for reporting crashes)"
 
 ; GTK+ Directory Page
 
@@ -53,32 +54,9 @@
 ; Spellcheck Section Prompts
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_TITLE	"Spellchecking Support"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ERROR		"Error Installing Spellchecking"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DICT_ERROR		"Error Installing Spellchecking Dictionary"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Support for Spellchecking.  (Internet connection required for installation)"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING ASPELL_INSTALL_FAILED			"Installation Failed"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_BRETON		"Breton"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CATALAN		"Catalan"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CZECH		"Czech"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_WELSH		"Welsh"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DANISH		"Danish"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GERMAN		"German"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GREEK		"Greek"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ENGLISH		"English"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SPANISH		"Spanish"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FAROESE		"Faroese"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FRENCH		"French"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ITALIAN		"Italian"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DUTCH		"Dutch"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_NORWEGIAN		"Norwegian"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_POLISH		"Polish"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_PORTUGUESE		"Portuguese"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ROMANIAN		"Romanian"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_RUSSIAN		"Russian"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SLOVAK		"Slovak"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SWEDISH		"Swedish"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainian"
+
+!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DEBUGSYMBOLS_ERROR		"Error Installing Debug Symbols"
 
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DEBUGSYMBOLS_ERROR "Error Installing Debug Symbols"
+!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_GTK_DOWNLOAD_ERROR		"Error Downloading the GTK+ Runtime"
 
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_GTK_DOWNLOAD_ERROR "Error Downloading the GTK+ Runtime"
--- a/pidgin/win32/nsis/translations/finnish.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/finnish.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -48,29 +48,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Oikolukutuki"
 !define PIDGIN_SPELLCHECK_ERROR			"Virhe asennettaessa oikolukua"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Virhe asennettaessa oikoluvun sanakirjaa"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Tuki oikoluvulle.  (Asennukseen tarvitaan Internet-yhteys)"
-!define ASPELL_INSTALL_FAILED			"Asennus eponnistui"
-!define PIDGIN_SPELLCHECK_BRETON		"bretoni"
-!define PIDGIN_SPELLCHECK_CATALAN		"katalaani"
-!define PIDGIN_SPELLCHECK_CZECH		"tshekki"
-!define PIDGIN_SPELLCHECK_WELSH		"kymri"
-!define PIDGIN_SPELLCHECK_DANISH		"tanska"
-!define PIDGIN_SPELLCHECK_GERMAN		"saksa"
-!define PIDGIN_SPELLCHECK_GREEK		"kreikka"
-!define PIDGIN_SPELLCHECK_ENGLISH		"englanti"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"espanja"
-!define PIDGIN_SPELLCHECK_FAROESE		"fri"
-!define PIDGIN_SPELLCHECK_FRENCH		"ranska"
-!define PIDGIN_SPELLCHECK_ITALIAN		"italia"
-!define PIDGIN_SPELLCHECK_DUTCH		"hollanti"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"norja"
-!define PIDGIN_SPELLCHECK_POLISH		"puola"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"portugali"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"romania"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"venj"
-!define PIDGIN_SPELLCHECK_SLOVAK		"slovakia"
-!define PIDGIN_SPELLCHECK_SWEDISH		"ruotsi"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ukraina"
 
--- a/pidgin/win32/nsis/translations/french.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/french.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -52,28 +52,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Correction orthographique"
 !define PIDGIN_SPELLCHECK_ERROR			"Erreur  l'installation du correcteur orthographique"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Erreur  l'installation du dictionnaire pour le correcteur orthographique"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Correction orthogaphique. (Une connexion internet est ncessaire pour son installation)"
-!define ASPELL_INSTALL_FAILED			"chec de l'installation"
-!define PIDGIN_SPELLCHECK_BRETON			"Breton"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catalan"
-!define PIDGIN_SPELLCHECK_CZECH			"Tchque"
-!define PIDGIN_SPELLCHECK_WELSH			"Gallois"
-!define PIDGIN_SPELLCHECK_DANISH			"Danois"
-!define PIDGIN_SPELLCHECK_GERMAN			"Allemand"
-!define PIDGIN_SPELLCHECK_GREEK			"Grec"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Anglais"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Espranto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Espagnol"
-!define PIDGIN_SPELLCHECK_FAROESE			"Fringien"
-!define PIDGIN_SPELLCHECK_FRENCH			"Franais"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Italien"
-!define PIDGIN_SPELLCHECK_DUTCH			"Hollandais"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norvgien"
-!define PIDGIN_SPELLCHECK_POLISH			"Polonais"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugais"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Roumain"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Russe"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slovaque"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Sudois"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainien"
--- a/pidgin/win32/nsis/translations/german.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/german.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -50,28 +50,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"Untersttzung fr Rechtschreibkontrolle"
 !define PIDGIN_SPELLCHECK_ERROR		"Fehler bei der Installation der Rechtschreibkontrolle"
-!define PIDGIN_SPELLCHECK_DICT_ERROR	"Fehler bei der Installation des Wrterbuches fr die Rechtschreibkontrolle"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Untersttzung fr Rechtschreibkontrolle.  (Fr die Installation ist eine Internet-Verbindung ntig)"
-!define ASPELL_INSTALL_FAILED			"Installation gescheitert"
-!define PIDGIN_SPELLCHECK_BRETON		"Bretonisch"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalanisch"
-!define PIDGIN_SPELLCHECK_CZECH		"Tschechisch"
-!define PIDGIN_SPELLCHECK_WELSH		"Walisisch"
-!define PIDGIN_SPELLCHECK_DANISH		"Dnisch"
-!define PIDGIN_SPELLCHECK_GERMAN		"Deutsch"
-!define PIDGIN_SPELLCHECK_GREEK		"Griechisch"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Englisch"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spanisch"
-!define PIDGIN_SPELLCHECK_FAROESE		"Farersprache"
-!define PIDGIN_SPELLCHECK_FRENCH		"Franzsisch"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italienisch"
-!define PIDGIN_SPELLCHECK_DUTCH		"Hollndisch"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norwegisch"
-!define PIDGIN_SPELLCHECK_POLISH		"Polnisch"
-!define PIDGIN_SPELLCHECK_PORTUGUESE	"Portugiesisch"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumnisch"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Russisch"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slowakisch"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Schwedisch"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainisch"
--- a/pidgin/win32/nsis/translations/hebrew.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/hebrew.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -50,29 +50,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"  "
 !define PIDGIN_SPELLCHECK_ERROR		"   "
-!define PIDGIN_SPELLCHECK_DICT_ERROR	"    "
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"    (   )"
-!define ASPELL_INSTALL_FAILED			" "
-!define PIDGIN_SPELLCHECK_BRETON		""
-!define PIDGIN_SPELLCHECK_CATALAN		""
-!define PIDGIN_SPELLCHECK_CZECH		"'"
-!define PIDGIN_SPELLCHECK_WELSH		""
-!define PIDGIN_SPELLCHECK_DANISH		""
-!define PIDGIN_SPELLCHECK_GERMAN		""
-!define PIDGIN_SPELLCHECK_GREEK		""
-!define PIDGIN_SPELLCHECK_ENGLISH		""
-!define PIDGIN_SPELLCHECK_ESPERANTO		""
-!define PIDGIN_SPELLCHECK_SPANISH		""
-!define PIDGIN_SPELLCHECK_FAROESE		""
-!define PIDGIN_SPELLCHECK_FRENCH		""
-!define PIDGIN_SPELLCHECK_ITALIAN		""
-!define PIDGIN_SPELLCHECK_DUTCH		""
-!define PIDGIN_SPELLCHECK_NORWEGIAN		""
-!define PIDGIN_SPELLCHECK_POLISH		""
-!define PIDGIN_SPELLCHECK_PORTUGUESE	""
-!define PIDGIN_SPELLCHECK_ROMANIAN		""
-!define PIDGIN_SPELLCHECK_RUSSIAN		""
-!define PIDGIN_SPELLCHECK_SLOVAK		""
-!define PIDGIN_SPELLCHECK_SWEDISH		""
-!define PIDGIN_SPELLCHECK_UKRAINIAN		""
 
--- a/pidgin/win32/nsis/translations/hungarian.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/hungarian.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -49,29 +49,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Helyesrs-ellenrzs tmogatsa"
 !define PIDGIN_SPELLCHECK_ERROR			"Hiba a helyesrs-ellenrzs teleptse kzben"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Hiba a helyesrs-ellenrzsi sztr teleptse kzben"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Helyesrs-ellenrzs tmogatsa. (Internetkapcsolat szksges a teleptshez)"
-!define ASPELL_INSTALL_FAILED			"A telepts sikertelen"
-!define PIDGIN_SPELLCHECK_BRETON			"Breton"
-!define PIDGIN_SPELLCHECK_CATALAN			"Kataln"
-!define PIDGIN_SPELLCHECK_CZECH			"Cseh"
-!define PIDGIN_SPELLCHECK_WELSH			"Walesi"
-!define PIDGIN_SPELLCHECK_DANISH			"Dn"
-!define PIDGIN_SPELLCHECK_GERMAN			"Nmet"
-!define PIDGIN_SPELLCHECK_GREEK			"Grg"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Angol"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Eszperant"
-!define PIDGIN_SPELLCHECK_SPANISH			"Spanyol"
-!define PIDGIN_SPELLCHECK_FAROESE			"Farai"
-!define PIDGIN_SPELLCHECK_FRENCH			"Francia"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Olasz"
-!define PIDGIN_SPELLCHECK_DUTCH			"Holland"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norvg"
-!define PIDGIN_SPELLCHECK_POLISH			"Lengyel"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugl"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Romn"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Orosz"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Szlovk"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Svd"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrn"
 
--- a/pidgin/win32/nsis/translations/italian.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/italian.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -49,29 +49,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"Supporto per il correttore ortografico"
 !define PIDGIN_SPELLCHECK_ERROR		"Errore nell'installazione del correttore ortografico"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Errore nell'installazione del dizionario per il correttore ortografico"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Supporto per il correttore ortografico.  ( richiesta una connessione a internet per l'installazione)"
-!define ASPELL_INSTALL_FAILED			"Installazione fallita"
-!define PIDGIN_SPELLCHECK_BRETON		"Bretone"
-!define PIDGIN_SPELLCHECK_CATALAN		"Catalano"
-!define PIDGIN_SPELLCHECK_CZECH		"Ceco"
-!define PIDGIN_SPELLCHECK_WELSH		"Gallese"
-!define PIDGIN_SPELLCHECK_DANISH		"Danese"
-!define PIDGIN_SPELLCHECK_GERMAN		"Tedesco"
-!define PIDGIN_SPELLCHECK_GREEK		"Greco"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Inglese"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spagnolo"
-!define PIDGIN_SPELLCHECK_FAROESE		"Faroese"
-!define PIDGIN_SPELLCHECK_FRENCH		"Francese"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italiano"
-!define PIDGIN_SPELLCHECK_DUTCH		"Olandese"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norvegese"
-!define PIDGIN_SPELLCHECK_POLISH		"Polacco"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portoghese"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumeno"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Russo"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slovacco"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Svedese"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ucraino"
 
--- a/pidgin/win32/nsis/translations/japanese.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/japanese.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -45,29 +45,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Xy`FbÑT|[g"
 !define PIDGIN_SPELLCHECK_ERROR			"Xy`FbÑCXg[Ɏs܂"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Xy`FbÑCXg[Ɏs܂B"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Xy`FbÑT|[g  (C^[lbgڑCXg[ɕKvł)"
-!define ASPELL_INSTALL_FAILED			"CXg[Ɏs܂"
-!define PIDGIN_SPELLCHECK_BRETON			"u^[j"
-!define PIDGIN_SPELLCHECK_CATALAN			"J^[j"
-!define PIDGIN_SPELLCHECK_CZECH			"`FR"
-!define PIDGIN_SPELLCHECK_WELSH			"EF[Y"
-!define PIDGIN_SPELLCHECK_DANISH			"f}[N"
-!define PIDGIN_SPELLCHECK_GERMAN			"hCc"
-!define PIDGIN_SPELLCHECK_GREEK			"MV"
-!define PIDGIN_SPELLCHECK_ENGLISH			"p"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"GXyg"
-!define PIDGIN_SPELLCHECK_SPANISH			"XyC"
-!define PIDGIN_SPELLCHECK_FAROESE			"tF["
-!define PIDGIN_SPELLCHECK_FRENCH			"tX"
-!define PIDGIN_SPELLCHECK_ITALIAN			"C^A"
-!define PIDGIN_SPELLCHECK_DUTCH			"I_"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"mEF["
-!define PIDGIN_SPELLCHECK_POLISH			"|[h"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"|gK"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"[}jA"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"VA"
-!define PIDGIN_SPELLCHECK_SLOVAK			"X@LA"
-!define PIDGIN_SPELLCHECK_SWEDISH			"XEF[f"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ENCi"
 
--- a/pidgin/win32/nsis/translations/kurdish.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/kurdish.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -44,29 +44,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Desteka kontrola rastnivs"
 !define PIDGIN_SPELLCHECK_ERROR			"Di sazkirina kontrola rastnivs de ewt derket."
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Di sazkirina ferhenga rastnivs de ewt derket."
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Desteka kontrola rastnivs.  (Ji bo sazkirin nternet pwst e)"
-!define ASPELL_INSTALL_FAILED			"Sazkirin Serneket"
-!define PIDGIN_SPELLCHECK_BRETON			"Breton"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catalan"
-!define PIDGIN_SPELLCHECK_CZECH			"ek"
-!define PIDGIN_SPELLCHECK_WELSH			"Welsh"
-!define PIDGIN_SPELLCHECK_DANISH			"Danik"
-!define PIDGIN_SPELLCHECK_GERMAN			"Alman"
-!define PIDGIN_SPELLCHECK_GREEK			"Yewnan"
-!define PIDGIN_SPELLCHECK_ENGLISH			"ngilz"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Span"
-!define PIDGIN_SPELLCHECK_FAROESE			"Faroese"
-!define PIDGIN_SPELLCHECK_FRENCH			"Frans"
-!define PIDGIN_SPELLCHECK_ITALIAN			"tal"
-!define PIDGIN_SPELLCHECK_DUTCH			"Dutch"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norwec"
-!define PIDGIN_SPELLCHECK_POLISH			"Pol"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portekiz"
-!define PIDGIN_SPELLCHECK_ROMANIAN			"Roman"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rus"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slovak"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Swd"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrayn"
 
--- a/pidgin/win32/nsis/translations/lithuanian.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/lithuanian.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -47,29 +47,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"Raybos tikrinimo palaikymas"
 !define PIDGIN_SPELLCHECK_ERROR		"Raybos tikrinimo palaikymo diegimo klaida"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Raybos tikrinimo odyno diegimo klaida"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Raybos tikrinimo palaikymas.  (Diegimui btina interneto jungtis)"
-!define ASPELL_INSTALL_FAILED			"Diegimas nepavyko"
-!define PIDGIN_SPELLCHECK_BRETON		"Breton kalba"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalon kalba"
-!define PIDGIN_SPELLCHECK_CZECH		"ek kalba"
-!define PIDGIN_SPELLCHECK_WELSH		"Val kalba"
-!define PIDGIN_SPELLCHECK_DANISH		"Dan kalba"
-!define PIDGIN_SPELLCHECK_GERMAN		"Vokiei kalba"
-!define PIDGIN_SPELLCHECK_GREEK		"Graik kalba"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Angl kalba"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto kalba"
-!define PIDGIN_SPELLCHECK_SPANISH		"Ispan kalba"
-!define PIDGIN_SPELLCHECK_FAROESE		"Farer kalba"
-!define PIDGIN_SPELLCHECK_FRENCH		"Prancz kalba"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Ital kalba"
-!define PIDGIN_SPELLCHECK_DUTCH		"Oland kalba"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norveg kalba"
-!define PIDGIN_SPELLCHECK_POLISH		"Lenk kalba"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugal kalba"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumun kalba"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Rus kalba"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slovak kalba"
-!define PIDGIN_SPELLCHECK_SWEDISH		"ved kalba"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainiei kalba"
 
--- a/pidgin/win32/nsis/translations/norwegian.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/norwegian.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -45,28 +45,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Sttte for stavekontroll"
 !define PIDGIN_SPELLCHECK_ERROR			"Det oppstod en feil ved installering av stavekontroll"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Det oppstod en feil ved installering av ordboken for stavekontroll"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Sttte for stavekontroll. (Internettoppkobling pkrevd for installasjon)"
-!define ASPELL_INSTALL_FAILED			"Installasjonen mislyktes."
-!define PIDGIN_SPELLCHECK_BRETON			"Bretagnsk"
-!define PIDGIN_SPELLCHECK_CATALAN			"Katalansk"
-!define PIDGIN_SPELLCHECK_CZECH			"Tsjekkisk"
-!define PIDGIN_SPELLCHECK_WELSH			"Walisisk"
-!define PIDGIN_SPELLCHECK_DANISH			"Dansk"
-!define PIDGIN_SPELLCHECK_GERMAN			"Tysk"
-!define PIDGIN_SPELLCHECK_GREEK			"Gresk"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Engelsk"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Spansk"
-!define PIDGIN_SPELLCHECK_FAROESE			"Frysk"
-!define PIDGIN_SPELLCHECK_FRENCH			"Fransk"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Italiensk"
-!define PIDGIN_SPELLCHECK_DUTCH			"Nederlandsk"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norsk"
-!define PIDGIN_SPELLCHECK_POLISH			"Polsk"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugisisk"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumensk"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Russisk"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slovakisk"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Svensk"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainsk"
--- a/pidgin/win32/nsis/translations/norwegian_nynorsk.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/norwegian_nynorsk.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -9,7 +9,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Installasjonsprogrammet kjrer allereie."
 !define PIDGIN_IS_RUNNING			"Pidgin kjrer no. Lukk programmet og prv igjen."
-!define GTK_INSTALLER_NEEDED			"GTK+-kjremiljet manglar eller treng  bli oppdatert.$\rInstaller v${GTK_MIN_VERSION} eller nyare av GTK+-kjremiljet"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Neste >"
@@ -28,20 +27,12 @@
 !define PIDGIN_DESKTOP_SHORTCUT_DESC		"Lag ein snarveg til Pidgin p skrivebordet"
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Lag ein snarveg til Pidgin p startmenyen"
 
-; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Fann ei gammal utgve av GTK+-kjremiljet. Vil du oppdatera ho?$\rMerk: $(^Name) vil kanskje ikkje fungera om du ikkje oppdaterer."
-!define GTK_WINDOWS_INCOMPATIBLE		"Windows 95/98/Me er ikkje kompatibelt med GTK+ 2.8.0 eller nyare. GTK+ ${GTK_INSTALL_VERSION} kjem ikkje til  bli installert.$\rInstallasjonen vil bli abvroten om ikkje GTK+ ${GTK_MIN_VERSION} eller nyare allereie er installert."
-
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Besk Pidgin si nettside"
 
 ; Pidgin Section Prompts and Texts
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Klarte ikkje  avinstallera Pidgin-utgva som er i bruk. Den nye utgva kjem til  bli installert utan  ta vekk den gjeldande."
 
-; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Klarte ikkje  installera GTK+-kjremiljet."
-!define GTK_BAD_INSTALL_PATH			"Klarer ikkje  laga eller f tilgang til bana du skreiv."
-
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"URI-referanse"
 
--- a/pidgin/win32/nsis/translations/persian.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/persian.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -51,29 +51,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	" ؝ "
 !define PIDGIN_SPELLCHECK_ERROR		"   ؝ "
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"   ʝ ؝ "
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	" ؝ . (     )"
-!define ASPELL_INSTALL_FAILED			" Ԙ "
-!define PIDGIN_SPELLCHECK_BRETON		""
-!define PIDGIN_SPELLCHECK_CATALAN		""
-!define PIDGIN_SPELLCHECK_CZECH		""
-!define PIDGIN_SPELLCHECK_WELSH		""
-!define PIDGIN_SPELLCHECK_DANISH		"ј"
-!define PIDGIN_SPELLCHECK_GERMAN		""
-!define PIDGIN_SPELLCHECK_GREEK		""
-!define PIDGIN_SPELLCHECK_ENGLISH		""
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Ӂ"
-!define PIDGIN_SPELLCHECK_SPANISH		"Ӂ"
-!define PIDGIN_SPELLCHECK_FAROESE		""
-!define PIDGIN_SPELLCHECK_FRENCH		""
-!define PIDGIN_SPELLCHECK_ITALIAN		""
-!define PIDGIN_SPELLCHECK_DUTCH		""
-!define PIDGIN_SPELLCHECK_NORWEGIAN		""
-!define PIDGIN_SPELLCHECK_POLISH		""
-!define PIDGIN_SPELLCHECK_PORTUGUESE		""
-!define PIDGIN_SPELLCHECK_ROMANIAN		""
-!define PIDGIN_SPELLCHECK_RUSSIAN		""
-!define PIDGIN_SPELLCHECK_SLOVAK		"ǘ"
-!define PIDGIN_SPELLCHECK_SWEDISH		""
-!define PIDGIN_SPELLCHECK_UKRAINIAN		""
 
--- a/pidgin/win32/nsis/translations/polish.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/polish.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -25,17 +25,9 @@
 !define PIDGIN_DESKTOP_SHORTCUT_DESC		"Utworzenie skrtu do programu Pidgin na pulpicie"
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Utworzenie wpisu w menu Start dla programu Pidgin"
 
-; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Odnaleziono star wersj biblioteki GTK+. Zaktualizowa j?$\rUwaga: program $(^Name) moe bez tego nie dziaa."
-!define GTK_WINDOWS_INCOMPATIBLE		"Systemy Windows 95/98/Me s niezgodne z bibliotek GTK+ 2.8.0 lub nowsz. Biblioteka GTK+ ${GTK_INSTALL_VERSION} nie zostanie zainstalowana.$\rJeli brak zainstalowanej biblioteki GTK+ ${GTK_MIN_VERSION} lub nowszej, instalacja zostanie przerwana."
-
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Odwied stron WWW programu Pidgin"
 
-; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Bd podczas instalowania biblioteki GTK+."
-!define GTK_BAD_INSTALL_PATH			"Nie mona uzyska dostpu do podanej cieki lub jej utworzy."
-
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Instalator nie moe odnale wpisw w rejestrze dla programu Pidgin.$\rMoliwe, e inny uytkownik zainstalowa ten program."
-!define un.PIDGIN_UNINSTALL_ERROR_2		"Brak uprawnie do odinstalowania tego programu."
\ No newline at end of file
+!define un.PIDGIN_UNINSTALL_ERROR_2		"Brak uprawnie do odinstalowania tego programu."
--- a/pidgin/win32/nsis/translations/portuguese-br.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/portuguese-br.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -43,28 +43,4 @@
 !define URI_HANDLERS_SECTION_TITLE             "Handlers para endereos"
 !define PIDGIN_SPELLCHECK_SECTION_TITLE        "Suporte a verificao ortogrfica"
 !define PIDGIN_SPELLCHECK_ERROR                "Erro ao instalar a verificao ortogrfica"
-!define PIDGIN_SPELLCHECK_DICT_ERROR           "Erro ao instalar o dicionrio da verificao ortogrfica"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION  "Suporte a verificao ortogrfica (A instalao necessita de conexo a internet)"
-!define ASPELL_INSTALL_FAILED                  "Falha na instalao"
-!define PIDGIN_SPELLCHECK_BRETON               "Breto"
-!define PIDGIN_SPELLCHECK_CATALAN              "Catalo"
-!define PIDGIN_SPELLCHECK_CZECH                "Tcheco"
-!define PIDGIN_SPELLCHECK_WELSH                "Gals" 
-!define PIDGIN_SPELLCHECK_DANISH               "Dinamarqus"
-!define PIDGIN_SPELLCHECK_GERMAN               "Alemo" 
-!define PIDGIN_SPELLCHECK_GREEK                "Grego"
-!define PIDGIN_SPELLCHECK_ENGLISH              "Ingls"
-!define PIDGIN_SPELLCHECK_ESPERANTO            "Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH              "Espanhol"
-!define PIDGIN_SPELLCHECK_FAROESE              "Feros"
-!define PIDGIN_SPELLCHECK_FRENCH               "Francs"
-!define PIDGIN_SPELLCHECK_ITALIAN              "Italiano"
-!define PIDGIN_SPELLCHECK_DUTCH                "Holands"
-!define PIDGIN_SPELLCHECK_NORWEGIAN            "Noruegus" 
-!define PIDGIN_SPELLCHECK_POLISH               "Polons"
-!define PIDGIN_SPELLCHECK_PORTUGUESE           "Portugus"
-!define PIDGIN_SPELLCHECK_ROMANIAN             "Romeno"
-!define PIDGIN_SPELLCHECK_RUSSIAN              "Russo" 
-!define PIDGIN_SPELLCHECK_SLOVAK               "Eslovaco"
-!define PIDGIN_SPELLCHECK_SWEDISH              "Sueco"
-!define PIDGIN_SPELLCHECK_UKRAINIAN            "Ucraniano"
--- a/pidgin/win32/nsis/translations/simp-chinese.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/simp-chinese.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -43,28 +43,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"ƴд֧"
 !define PIDGIN_SPELLCHECK_ERROR			"װƴд"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"װƴдֵ"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"ƴд֧֡(װҪӵ Internet)"
-!define ASPELL_INSTALL_FAILED			"װʧ"
-!define PIDGIN_SPELLCHECK_BRETON			""
-!define PIDGIN_SPELLCHECK_CATALAN			"̩"
-!define PIDGIN_SPELLCHECK_CZECH			"ݿ"
-!define PIDGIN_SPELLCHECK_WELSH			"ʿ"
-!define PIDGIN_SPELLCHECK_DANISH			""
-!define PIDGIN_SPELLCHECK_GERMAN			""
-!define PIDGIN_SPELLCHECK_GREEK			"ϣ"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Ӣ"
-!define PIDGIN_SPELLCHECK_ESPERANTO		""
-!define PIDGIN_SPELLCHECK_SPANISH			""
-!define PIDGIN_SPELLCHECK_FAROESE			""
-!define PIDGIN_SPELLCHECK_FRENCH			""
-!define PIDGIN_SPELLCHECK_ITALIAN			""
-!define PIDGIN_SPELLCHECK_DUTCH			""
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Ų"
-!define PIDGIN_SPELLCHECK_POLISH			""
-!define PIDGIN_SPELLCHECK_PORTUGUESE		""
-!define PIDGIN_SPELLCHECK_ROMANIAN			""
-!define PIDGIN_SPELLCHECK_RUSSIAN			""
-!define PIDGIN_SPELLCHECK_SLOVAK			"˹工"
-!define PIDGIN_SPELLCHECK_SWEDISH			""
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ڿ"
--- a/pidgin/win32/nsis/translations/slovak.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/slovak.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -43,29 +43,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Podpora kontroly pravopisu"
 !define PIDGIN_SPELLCHECK_ERROR			"Chyba pri intalcii kontroly pravopisu"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Chyba pri intalcii slovnka kontroly pravopisu"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Podpora kontroly pravopisu (Nutn pripojenie k Internetu)"
-!define ASPELL_INSTALL_FAILED			"Intalcia zlyhala"
-!define PIDGIN_SPELLCHECK_BRETON			"Bretnsky"
-!define PIDGIN_SPELLCHECK_CATALAN			"Katalnsky"
-!define PIDGIN_SPELLCHECK_CZECH			"esk"
-!define PIDGIN_SPELLCHECK_WELSH			"Welshsk"
-!define PIDGIN_SPELLCHECK_DANISH			"Dnsky"
-!define PIDGIN_SPELLCHECK_GERMAN			"Nemeck"
-!define PIDGIN_SPELLCHECK_GREEK			"Grcky"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Anglick"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperantsk"
-!define PIDGIN_SPELLCHECK_SPANISH			"panielsk"
-!define PIDGIN_SPELLCHECK_FAROESE			"Faroesk"
-!define PIDGIN_SPELLCHECK_FRENCH			"Franczsky"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Taliansk"
-!define PIDGIN_SPELLCHECK_DUTCH			"Holandsk"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Nrsky"
-!define PIDGIN_SPELLCHECK_POLISH			"Posk"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugalsk"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumunsk"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rusk"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slovensk"
-!define PIDGIN_SPELLCHECK_SWEDISH			"vdsky"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrajinsk"
 
--- a/pidgin/win32/nsis/translations/slovenian.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/slovenian.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -49,29 +49,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Podpora preverjanja rkovanja"
 !define PIDGIN_SPELLCHECK_ERROR			"Napaka pri nameanju preverjanja rkovanja"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Napaka pri nameanju slovarja za preverjanje rkovanja"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Podpora preverjanja rkovanja.  (Za namestitev je potrebna spletna povezava)"
-!define ASPELL_INSTALL_FAILED			"Namestitev ni uspela."
-!define PIDGIN_SPELLCHECK_BRETON		"bretonski"
-!define PIDGIN_SPELLCHECK_CATALAN		"katalonski"
-!define PIDGIN_SPELLCHECK_CZECH			"eki"
-!define PIDGIN_SPELLCHECK_WELSH			"velki"
-!define PIDGIN_SPELLCHECK_DANISH		"danski"
-!define PIDGIN_SPELLCHECK_GERMAN		"nemki"
-!define PIDGIN_SPELLCHECK_GREEK			"grki"
-!define PIDGIN_SPELLCHECK_ENGLISH		"angleki"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"esperantski"
-!define PIDGIN_SPELLCHECK_SPANISH		"panski"
-!define PIDGIN_SPELLCHECK_FAROESE		"farojski"
-!define PIDGIN_SPELLCHECK_FRENCH		"francoski"
-!define PIDGIN_SPELLCHECK_ITALIAN		"italijanski"
-!define PIDGIN_SPELLCHECK_DUTCH			"nizozemski"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"norveki"
-!define PIDGIN_SPELLCHECK_POLISH		"poljski"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"portugalski"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"romunski"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"ruski"
-!define PIDGIN_SPELLCHECK_SLOVAK		"slovaki"
-!define PIDGIN_SPELLCHECK_SLOVENIAN		"slovenski"
-!define PIDGIN_SPELLCHECK_SWEDISH		"vedski"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ukrajinski"
--- a/pidgin/win32/nsis/translations/swedish.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/swedish.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -49,28 +49,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Std fr rttstavning"
 !define PIDGIN_SPELLCHECK_ERROR			"Fel vid installation fr rttstavning"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Fel vid installation av rttstavningsordlista"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Std fr Rttstavning.  (Internetanslutning krvs fr installation)"
-!define ASPELL_INSTALL_FAILED			"Installationen misslyckades"
-!define PIDGIN_SPELLCHECK_BRETON		"Bretonska"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalanska"
-!define PIDGIN_SPELLCHECK_CZECH			"Tjeckiska"
-!define PIDGIN_SPELLCHECK_WELSH			"Kymriska"
-!define PIDGIN_SPELLCHECK_DANISH		"Danska"
-!define PIDGIN_SPELLCHECK_GERMAN		"Tyska"
-!define PIDGIN_SPELLCHECK_GREEK			"Grekiska"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Engelska"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spanska"
-!define PIDGIN_SPELLCHECK_FAROESE		"Friska"
-!define PIDGIN_SPELLCHECK_FRENCH		"Franska"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italienska"
-!define PIDGIN_SPELLCHECK_DUTCH			"Nederlndska"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norska"
-!define PIDGIN_SPELLCHECK_POLISH		"Polska"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugisiska"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumnska"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Ryska"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slovakiska"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Svenska"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainska"
--- a/pidgin/win32/nsis/translations/trad-chinese.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/trad-chinese.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -51,29 +51,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"rˬd\"
 !define PIDGIN_SPELLCHECK_ERROR			"w˫rˬd~oͿ~"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"w˫rˬdΪ~oͿ~"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"rˬd䴩]w˶ںsu^C"
-!define ASPELL_INSTALL_FAILED			"w˥"
-!define PIDGIN_SPELLCHECK_BRETON		"h"
-!define PIDGIN_SPELLCHECK_CATALAN		"["
-!define PIDGIN_SPELLCHECK_CZECH			"J"
-!define PIDGIN_SPELLCHECK_WELSH			"º"
-!define PIDGIN_SPELLCHECK_DANISH		""
-!define PIDGIN_SPELLCHECK_GERMAN		"w"
-!define PIDGIN_SPELLCHECK_GREEK			"þ"
-!define PIDGIN_SPELLCHECK_ENGLISH		"^"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"@ɻy"
-!define PIDGIN_SPELLCHECK_SPANISH		"Z"
-!define PIDGIN_SPELLCHECK_FAROESE		"kùsq"
-!define PIDGIN_SPELLCHECK_FRENCH		"k"
-!define PIDGIN_SPELLCHECK_ITALIAN		"NjQ"
-!define PIDGIN_SPELLCHECK_DUTCH			""
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"¤"
-!define PIDGIN_SPELLCHECK_POLISH		"i"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		""
-!define PIDGIN_SPELLCHECK_ROMANIAN		"ùȤ"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"X"
-!define PIDGIN_SPELLCHECK_SLOVAK		"J"
-!define PIDGIN_SPELLCHECK_SWEDISH		""
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"QJ"
 
--- a/pidgin/win32/nsis/translations/valencian.nsh	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/nsis/translations/valencian.nsh	Wed Mar 10 17:58:21 2010 +0000
@@ -46,29 +46,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Soport de Correccio Ortografica"
 !define PIDGIN_SPELLCHECK_ERROR			"Erro Instalant Correccio Ortografica"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Erro Instalant Diccionari de Correccio Ortografica"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Soport per a Correccio Ortografica.  (es requerix conexio a Internet per a fer l'instalacio)"
-!define ASPELL_INSTALL_FAILED			"L'Instalacio fall"
-!define PIDGIN_SPELLCHECK_BRETON			"Breto"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catal"
-!define PIDGIN_SPELLCHECK_CZECH			"Chec"
-!define PIDGIN_SPELLCHECK_WELSH			"Gals"
-!define PIDGIN_SPELLCHECK_DANISH			"Danes"
-!define PIDGIN_SPELLCHECK_GERMAN			"Alem"
-!define PIDGIN_SPELLCHECK_GREEK			"Grec"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Angles"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Espanyol"
-!define PIDGIN_SPELLCHECK_FAROESE			"Feroes"
-!define PIDGIN_SPELLCHECK_FRENCH			"Frances"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Itali"
-!define PIDGIN_SPELLCHECK_DUTCH			"Holandes"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Noruec"
-!define PIDGIN_SPELLCHECK_POLISH			"Polac"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugues"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Romanes"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rus"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Eslovac"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Suec"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ucrani"
 
--- a/pidgin/win32/winpidgin.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/winpidgin.c	Wed Mar 10 17:58:21 2010 +0000
@@ -97,7 +97,7 @@
 	return ret;
 }
 
-static void common_dll_prep(const TCHAR *path) {
+static BOOL common_dll_prep(const TCHAR *path) {
 	HMODULE hmod;
 	HKEY hkey;
 	struct _stat stat_buf;
@@ -110,7 +110,7 @@
 	if (_tstat(test_path, &stat_buf) != 0) {
 		printf("Unable to determine GTK+ path. \n"
 			"Assuming GTK+ is in the PATH.\n");
-		return;
+		return FALSE;
 	}
 
 
@@ -184,6 +184,20 @@
 				printf("SafeDllSearchMode is set to 0\n");
 		}/*end else*/
 	}
+
+	return TRUE;
+}
+
+static BOOL dll_prep(const TCHAR *pidgin_dir) {
+	TCHAR path[MAX_PATH + 1];
+	path[0] = _T('\0');
+
+	if (*pidgin_dir) {
+		_sntprintf(path, sizeof(path) / sizeof(TCHAR), _T("%s\\Gtk\\bin"), pidgin_dir);
+		path[sizeof(path) / sizeof(TCHAR)] = _T('\0');
+	}
+
+	return common_dll_prep(path);
 }
 
 static void portable_mode_dll_prep(const TCHAR *pidgin_dir) {
@@ -224,22 +238,11 @@
 	_tprintf(_T("%s\n"), path2);
 	_tputenv(path2);
 
-	/* set the GTK+ path to be \\path\to\GTK\bin */
-	_tcscat(path, _T("\\GTK\\bin"));
-
-	common_dll_prep(path);
-}
-
-static void dll_prep(const TCHAR *pidgin_dir) {
-	TCHAR gtk_path[MAX_PATH + 1];
-	gtk_path[0] = _T('\0');
-
-	if (*pidgin_dir) {
-		_sntprintf(gtk_path, sizeof(gtk_path) / sizeof(TCHAR), _T("%s\\Gtk\\bin"), pidgin_dir);
-		gtk_path[sizeof(gtk_path) / sizeof(TCHAR)] = _T('\0');
+	if (!dll_prep(pidgin_dir)) {
+		/* set the GTK+ path to be \\path\to\GTK\bin */
+		_tcscat(path, _T("\\GTK\\bin"));
+		common_dll_prep(path);
 	}
-
-	common_dll_prep(gtk_path);
 }
 
 static TCHAR* winpidgin_lcid_to_posix(LCID lcid) {
--- a/pidgin/win32/wspell.c	Fri Mar 05 04:19:05 2010 +0000
+++ b/pidgin/win32/wspell.c	Wed Mar 10 17:58:21 2010 +0000
@@ -3,7 +3,7 @@
  *
  * File: wspell.c
  * Date: March, 2003
- * Description: Windows Purple gtkspell interface.
+ * Description: Windows Pidgin gtkspell interface.
  *
  * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com>
  *
@@ -32,8 +32,22 @@
 #include "win32dep.h"
 #include "wspell.h"
 
+/* Intermediate function so that we can eat Enchant error popups when it doesn't find a DLL
+ * This is fixed upstream, but not released */
+GtkSpell*         (*wpidginspell_new_attach_proxy)              (GtkTextView *,
+							     const gchar *,
+							     GError **) = NULL;
+
 /* GTKSPELL DUMMY FUNCS */
-static GtkSpell* wgtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) {return NULL;}
+static GtkSpell* wgtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) {
+	GtkSpell *ret = NULL;
+	if (wpidginspell_new_attach_proxy) {
+		UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
+		ret = wpidginspell_new_attach_proxy(view, lang, error);
+		SetErrorMode(old_error_mode);
+	}
+	return ret;
+}
 static GtkSpell* wgtkspell_get_from_text_view(GtkTextView *view) {return NULL;}
 static void wgtkspell_detach(GtkSpell *spell) {}
 static gboolean wgtkspell_set_language(GtkSpell *spell, const gchar *lang, GError **error) {return FALSE;}
@@ -54,28 +68,44 @@
 
 void              (*wpidginspell_recheck_all)             (GtkSpell*) = wgtkspell_recheck_all;
 
+#define GTKSPELL_DLL "libgtkspell-0.dll"
+
 static void load_gtkspell() {
-	wpidginspell_new_attach = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_new_attach" );
-	wpidginspell_get_from_text_view = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_get_from_text_view");
-	wpidginspell_detach = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_detach");
-	wpidginspell_set_language = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_set_language");
-	wpidginspell_recheck_all = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_recheck_all");
+	UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
+	gchar *tmp, *tmp2;
+	const char *path = g_getenv("PATH");
+	tmp = g_build_filename(wpurple_install_dir(), "spellcheck", NULL);
+	tmp2 = g_strdup_printf("%s%s%s", (path ? path : ""),
+		(path ? G_SEARCHPATH_SEPARATOR_S : ""),
+		tmp);
+	g_free(tmp);
+	g_setenv("PATH", tmp2, TRUE);
+
+	/* Suppress error popups */
+	wpidginspell_new_attach_proxy = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_new_attach" );
+	if (wpidginspell_new_attach_proxy) {
+		wpidginspell_get_from_text_view = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_get_from_text_view");
+		wpidginspell_detach = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_detach");
+		wpidginspell_set_language = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_set_language");
+		wpidginspell_recheck_all = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_recheck_all");
+	} else {
+		purple_debug_warning("wspell", "Couldn't load gtkspell (%s) \n", GTKSPELL_DLL);
+		/*wpidginspell_new_attach = wgtkspell_new_attach;*/
+	}
+	SetErrorMode(old_error_mode);
 }
 
-static char* lookup_aspell_path() {
+static void lookup_aspell_path() {
 	const char *tmp;
+	gchar *aspell_path;
 
 	if ((tmp = g_getenv("PIDGIN_ASPELL_DIR")))
-		return g_strdup(tmp);
-
-	return wpurple_read_reg_string(HKEY_LOCAL_MACHINE, "Software\\Aspell", "Path");
-}
-
-void winpidgin_spell_init() {
-	char *aspell_path = lookup_aspell_path();
+		aspell_path = g_strdup(tmp);
+	else
+		aspell_path = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, "Software\\Aspell", "Path");
 
 	if (aspell_path != NULL) {
-		char *tmp = g_strconcat(aspell_path, "\\aspell-15.dll", NULL);
+		char *tmp = g_build_filename(aspell_path, "aspell-15.dll", NULL);
 		if (g_file_test(tmp, G_FILE_TEST_EXISTS)) {
 			const char *path = g_getenv("PATH");
 			purple_debug_info("wspell", "Found Aspell in %s\n", aspell_path);
@@ -88,14 +118,18 @@
 
 			g_setenv("PATH", tmp, TRUE);
 
-			load_gtkspell();
-		} else {
+		} else
 			purple_debug_warning("wspell", "Couldn't find aspell-15.dll\n");
-		}
 
 		g_free(tmp);
 		g_free(aspell_path);
-	} else {
-		purple_debug_warning("wspell", "Couldn't find path for Aspell\n");
-	}
+	} else
+		purple_debug_warning("wspell", "Aspell installation not found (this isn't necessarily a problem)\n");
 }
+
+void winpidgin_spell_init() {
+	/* We keep doing the aspell path thing so that previously installed dictionaries still work */
+	lookup_aspell_path();
+
+	load_gtkspell();
+}
--- a/po/POTFILES.in	Fri Mar 05 04:19:05 2010 +0000
+++ b/po/POTFILES.in	Wed Mar 10 17:58:21 2010 +0000
@@ -119,16 +119,6 @@
 libpurple/protocols/msn/state.c
 libpurple/protocols/msn/switchboard.c
 libpurple/protocols/msn/userlist.c
-libpurple/protocols/msnp9/dialog.c
-libpurple/protocols/msnp9/error.c
-libpurple/protocols/msnp9/msn.c
-libpurple/protocols/msnp9/nexus.c
-libpurple/protocols/msnp9/notification.c
-libpurple/protocols/msnp9/servconn.c
-libpurple/protocols/msnp9/session.c
-libpurple/protocols/msnp9/state.c
-libpurple/protocols/msnp9/switchboard.c
-libpurple/protocols/msnp9/userlist.c
 libpurple/protocols/mxit/actions.c
 libpurple/protocols/mxit/filexfer.c
 libpurple/protocols/mxit/http.c