changeset 29995:2292d8896b0b

merged with im.pidgin.pidgin
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Tue, 16 Mar 2010 12:07:06 +0900
parents 10c2702ecfff (current diff) 6ca9aa923250 (diff)
children f988f25259c7
files configure.ac libpurple/protocols/gg/lib/http.c libpurple/protocols/gg/lib/pubdir50.c libpurple/protocols/irc/irc.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/message.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 libpurple/protocols/oscar/oscar.c libpurple/protocols/yahoo/libymsg.c pidgin/gtkblist.c pidgin/gtkimhtml.c
diffstat 157 files changed, 9857 insertions(+), 23770 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Mar 04 15:19:39 2010 +0900
+++ b/ChangeLog	Tue Mar 16 12:07:06 2010 +0900
@@ -20,10 +20,39 @@
 	* 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)
+	* Added a menu set mood globally for all mood-supporting accounts
+	  (currently XMPP and ICQ).
 
 	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.  Since most of the icons
+	  available reflect moods, this is labeled "Set Mood" on the Accounts->ICQ
+	  Account menu. (Andrew Ivanov, Tomテ。ナ。 Kebert, Yuriy Yevgrafov, and trac
+	  users bob007, salieff, and nops)
+	* Allow setting and displaying icons between 1x1 and 100x100 pixels.
+	  Previously only icons between 48x48 and 52x64 were allowed.
+
+	MSN:
+	* Support for version 9 of the MSN protocol has been removed.  This
+	  version is no longer supported on the servers.
+
+	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)
+
+	Yahoo:
+	* Attempt to better handle transparent proxies interfering with HTTP-based
+	  login.
+	* Fix handling of P2P packets, thus fixing the loss of some messages.
+
 version 2.6.6 (02/18/2010):
 	libpurple:
 	* Fix 'make check' on OS X. (David Fang)
--- a/ChangeLog.API	Thu Mar 04 15:19:39 2010 +0900
+++ b/ChangeLog.API	Tue Mar 16 12:07:06 2010 +0900
@@ -23,6 +23,7 @@
 		* ui-caps-changed media manager signal
 		* sent-attention conversation signal
 		* got-attention conversation signal
+		* PurpleMood struct in status.h
 
 	Pidgin:
 		Added:
--- a/ChangeLog.win32	Thu Mar 04 15:19:39 2010 +0900
+++ b/ChangeLog.win32	Tue Mar 16 12:07:06 2010 +0900
@@ -1,10 +1,14 @@
 version 2.7.0 (??/??/????):
+	* Updated GTK+ to 2.16.6
+	* Private GTK+ Runtime now used (GTK+ Installer no longer supported)
 	* Minimum required GTK+ version increased to 2.14.0
-	* Private GTK+ Runtime now used (GTK+ Installer no longer supported)
 	* 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	Thu Mar 04 15:19:39 2010 +0900
+++ b/Makefile.mingw	Tue Mar 16 12:07:06 2010 +0900
@@ -31,7 +31,7 @@
     exit; \
 }' VERSION)
 
-GTK_INSTALL_VERSION = 2.14.7.0
+GTK_INSTALL_VERSION = 2.16.6.0
 
 STRIPPED_RELEASE_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-win32bin
 DEBUG_SYMBOLS_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-dbgsym
@@ -41,11 +41,13 @@
 # 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 \
@@ -88,29 +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)
 
 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
-	rm -f pidgin/win32/nsis/pidgin-translations.nsh
+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_installer_includes
+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_installer_includes 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	Thu Mar 04 15:19:39 2010 +0900
+++ b/configure.ac	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/connection.h	Tue Mar 16 12:07:06 2010 +0900
@@ -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/ft.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/ft.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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;
@@ -1088,6 +1088,7 @@
 		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,47 +1100,41 @@
 			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 {
-			gboolean read = TRUE;
-			if (priv->buffer) {
-				if (priv->buffer->len < s) {
-					s -= priv->buffer->len;
-					read = TRUE;
-				} else {
-					read = FALSE;
-				}
-			}
-			if (read) {
+				result = tmp;
+			} else {
 				buffer = g_malloc(s);
 				result = fread(buffer, 1, s, xfer->dest_fp);
 				if (result != s) {
@@ -1150,14 +1145,14 @@
 				}
 			}
 		}
-	
+
 		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) {
@@ -1172,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/plugins/perl/common/Makefile.mingw	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/plugins/perl/common/Makefile.mingw	Tue Mar 16 12:07:06 2010 +0900
@@ -13,7 +13,6 @@
 
 TARGET = Purple
 AUTOSPLIT = lib/auto/Purple/autosplit.ix
-EXTUTILS ?= C:/perl/lib/ExtUtils
 PERL_PLUGIN_TOP := ..
 
 ##
--- a/libpurple/plugins/perl/perl-common.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/plugins/perl/perl-common.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/plugins/perl/perl-common.h	Tue Mar 16 12:07:06 2010 +0900
@@ -66,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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/plugins/perl/perl-handlers.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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/protocols/Makefile.am	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/Makefile.am	Tue Mar 16 12:07:06 2010 +0900
@@ -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/gg/Makefile.am	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/Makefile.am	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/Makefile.mingw	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/buddylist.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/gg.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/common.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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. Woシny <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ト囘zy 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ナZ 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ト囘ト przekazywane informacje odpluskwiania.
+ *
+ * Funkcja \c gg_debug() i pochodne mogト byト przechwytywane przez aplikacjト
+ * korzystajトcト z biblioteki, by wyナ孩ietliト je na ナシトdanie uナシytkownika lub
+ * zapisaト do pテウナコniejszej analizy. Jeナ嬪i nie okreナ嬪ono pliku, wybrane
+ * informacje bト囘ト wysyナBne do standardowego wyjナ嫩ia bナて囘u (\c stderr).
+ *
+ * \ingroup debug
+ */
 FILE *gg_debug_file = NULL;
 
 #ifndef GG_DEBUG_DISABLE
 
-/*
- * gg_debug() // funkcja wewn黎rzna
+/**
+ * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji.
+ *
+ * Jeナ嬪i aplikacja ustawiナB odpowiedniト funkcjト obsナVgi w
+ * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoナZwana.
+ * W przeciwnym wypadku wynik jest wysyナBny do standardowego wyjナ嫩ia bナて囘u.
  *
- * wyカwietla komunikat o danym poziomie, o ile uソytkownik sobie tego ソyczy.
+ * \param sess Struktura sesji (moナシe byト \c NULL)
+ * \param level Poziom informacji
+ * \param format Format wiadomoナ嫩i (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 wiadomoカci
- *  - format... - treカ wiadomoカci (kompatybilna z printf())
+ * \param level Poziom wiadomoナ嫩i
+ * \param format Format wiadomoナ嫩i (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ナ嫩i
+ * \param format Format wiadomoナ嫩i (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ト冪na funkcja
+ * systemowa jest zgodna ze standardem C99 czy wczeナ嬾iejszymi.
  *
- * robi dokウadnie to samo, co vsprintf(), tyle ソe alokuje sobie wczeカniej
- * miejsce na dane. powinno dziaウa na tych maszynach, ktre majア funkcj
- * vsnprintf() zgodnア z C99, jak i na wczeカniejszych.
+ * \param format Format wiadomoナ嫩i (zgodny z \c printf)
+ * \param ap Lista argumentテウw (zgodna z \c printf)
  *
- *  - format - opis wyカwietlanego tekstu jak dla printf()
- *  - ap - lista argumentw dla printf()
+ * \return Zaalokowany bufor lub NULL, jeナ嬪i zabrakナP pamiト冂i.
  *
- * zaalokowany bufor, ktry naleソy pシniej zwolni, lub NULL
- * jeカli nie udaウo 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, wi鹹
-		 * musimy poda coカ istniejアcego jako cel printf()owania. */
+
+		/* libce Solarisa przy buforze NULL zawsze zwracajト -1, wiト冂
+		 * 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ト冪na funkcja
+ * systemowa jest zgodna ze standardem C99 czy wczeナ嬾iejszymi.
  *
- * robi dokウadnie to samo, co sprintf(), tyle ソe alokuje sobie wczeカniej
- * miejsce na dane. powinno dziaウa na tych maszynach, ktre majア funkcj
- * vsnprintf() zgodnア z C99, jak i na wczeカniejszych.
+ * \param format Format wiadomoナ嫩i (zgodny z \c printf)
  *
- *  - format... - treカ taka sama jak w funkcji printf()
+ * \return Zaalokowany bufor lub NULL, jeナ嬪i zabrakナP pamiト冂i.
  *
- * zaalokowany bufor, ktry naleソy pシniej zwolni, lub NULL
- * jeカli nie udaウo 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, dzielアc
- * na kolejne stringi. zdarza si, nie ma potrzeby pisania funkcji dublujアcej
- * bufor ソeby tylko mie nieruszone dane wejカciowe, skoro i tak nie b鹽ア nam
- * poシniej potrzebne. obcina `\r\n'.
- * 
- *  - ptr - wskaシnik do zmiennej, ktra przechowuje aktualnア pozycj
- *    w przemiatanym buforze
- * 
- * wskaシnik do kolejnej linii tekstu lub NULL, jeカli to juソ koniec bufora.
+/**
+ * \internal Pobiera liniト tekstu z bufora.
+ *
+ * Funkcja niszczy bufor ナコrテウdナPwy 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ナPナシenie
+ *            w analizowanym buforze
+ *
+ * \return Wskaナコnik do kolejnej linii tekstu lub NULL, jeナ嬪i 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カ gウupich zaleソnoカci
- * na dziwnych systemach.
- *
- *  - addr - adres serwera (struct in_addr *)
- *  - port - port serwera
- *  - async - asynchroniczne poウアczenie
+ * Funkcja czyta tekst znak po znaku, wiト冂 nie jest efektywna, ale dziト冖i
+ * brakowi buforowania, nie koliduje z innymi funkcjami odczytu.
  *
- * deskryptor gniazda lub -1 w przypadku bウ鹽u (kod bウ鹽u 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ナVgoナ崙 bufora
  *
- * czyta jednア lini tekstu z gniazda.
- *
- *  - sock - deskryptor gniazda
- *  - buf - wskaシnik do bufora
- *  - length - dウugoカ bufora
- *
- * jeカli trafi na bウアd odczytu lub podano nieprawidウowe parametry, zwraca NULL.
- * inaczej zwraca buf.
+ * \return Zwraca \c buf jeナ嬪i siト powiodナP, lub \c NULL w przypadku bナて囘u.
  */
 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ナて囘u
  *
- * 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 przyci鹹ia
+ * Funkcja dziaナB bezpoナ孑ednio 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 wewn黎rzna
+/**
+ * \internal Koduje ciトg znakテウw do postacji adresu HTTP.
  *
- * zamienia podany tekst na ciアg znakw do formularza http. przydaje si
- * przy rソnych usウugach katalogu publicznego.
+ * Zamienia znaki niedrukowalne, spoza ASCII i majトce specjalne znaczenie
+ * dla protokoナV HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkowト
+ * wartoナ嫩iト znaku.
+ * 
+ * \param str Ciトg znakテウw do zakodowania
  *
- *  - str - ciアg znakw do zakodowania
+ * \return Zaalokowany bufor lub \c NULL w przypadku bナて囘u.
  *
- * zaalokowany bufor, ktry naleソy pシniej zwolni albo NULL
- * w przypadku bウ鹽u.
+ * \ingroup helper
  */
 char *gg_urlencode(const char *str)
 {
@@ -400,16 +479,19 @@
 	return buf;
 }
 
-/*
- * gg_http_hash() // funkcja wewn黎rzna
+/**
+ * \internal Wyznacza skrテウt dla usナVg HTTP.
  *
- * funkcja liczアca hash dla adresu e-mail, hasウa i paru innych.
+ * Funkcja jest wykorzystywana do wyznaczania skrテウtu adresu e-mail, hasナB
+ * i innych wartoナ嫩i przekazywanych jako parametry usナVg HTTP.
  *
- *  - format... - format kolejnych parametrw ('s' jeカli dany parametr jest
- *                ciアgiem znakw lub 'u' jeカli numerem GG)
+ * W parametrze \c format naleナシy umieナ嫩iト znaki okreナ嬪ajトce postaト kolejnych
+ * parametrテウw: \c 's' jeナ嬪i parametr jest ciトgiem znakテウw, \c 'u' jeナ嬪i jest
+ * liczbト.
  *
- * hash wykorzystywany przy rejestracji i wszelkich manipulacjach wウasnego
- * 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() troszczアcy si o wspウbieソnoカ, gdy mamy do
- * dyspozycji funkcj gethostbyname_r().
- *
- *  - hostname - nazwa serwera
- *
- * zwraca wskaシnik na struktur in_addr, ktrア naleソy 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ナP ostatnio tworzone dla wトtku.
  *
- * zwraca deskryptor gniazda, ktre byウo ostatnio tworzone dla wアtku
- * o podanym identyfikatorze.
+ * Jeナ嬪i na win32 przy poナてczeniach synchronicznych zapamiト冲amy w jakim
+ * wトtku uruchomiliナ嬶y 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)
  *
- * jeカli na win32 przy poウアczeniach synchronicznych zapami黎amy w jakim
- * wアtku uruchomiliカmy funkcj, ktra si z czymkolwiek ウアczy, to z osobnego
- * wアtku moソemy anulowa poウアczenie poprzez gg_win32_thread_socket(watek, -1);
- * 
- * - thread_id - id wアtku. jeカli jest rwne 0, brany jest aktualny wアtek,
- *               jeカli rwne -1, usuwa wpis o podanym sockecie.
- * - socket - deskryptor gniazda. jeカli rwne 0, zwraca deskryptor gniazda
- *            dla podanego wアtku, jeカli rwne -1, usuwa wpis, jeカli coカ
- *            innego, ustawia dla podanego wアtku dany numer deskryptora.
+ * \param thread_id Identyfikator wトtku (jeナ嬪i jest rテウwne 0, brany jest
+ *                  aktualny wトtek, jeナ嬪i rテウwne -1, usuwa wpis dotyczトcy
+ *                  danego gniazda sockecie)
+ * \param socket Deskryptor gniazda (jeナ嬪i rテウwne 0, zwraca deskryptor gniazda
+ *               dla podanego wトtku, jeナ嬪i rテウwne -1, usuwa wpis, jeナ嬪i coナ
+ *               innego, ustawia dla podanego wトtku dany numer deskryptora)
  *
- * jeカli socket jest rwne 0, zwraca deskryptor gniazda dla podanego wアtku.
+ * \return Jeナ嬪i 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 ciアg znakw w base64.
+ * \param buf Bufor z danami do zakodowania
  *
- *  - buf - ciアg 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 ciアg znakw z base64.
+ * \param buf Bufor ナコrテウdナPwy z danymi do zdekodowania
  *
- *  - buf - ciアg 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 wewn黎rzna
+/**
+ * \internal Tworzy nagナづウwek autoryzacji serwera poナ孑edniczトcego.
  *
- * tworzy nagウwek autoryzacji dla proxy.
- * 
- * zaalokowany tekst lub NULL, jeカli proxy nie jest wウアczone 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ナ嬪i serwer poナ孑edniczト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ナOienia tablicy pomocniczej do wyznaczania sumy
+ * kontrolnej.
+ */
 static int gg_crc32_initialized = 0;
 
-/*
- * gg_crc32_make_table()  // funkcja wewn黎rzna
+/**
+ * \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 jeカli pierwszy
- *  - buf - bufor danych
- *  - size - iloカ danych
+ * \param crc Suma kontrola poprzedniego bloku danych lub 0 jeナ嬪i liczona
+ *            jest suma kontrolna pierwszego bloku
+ * \param buf Bufor danych
+ * \param len DナVgoナ崙 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ト囘zy 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ナPwy 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ナPwy.
+ * \param n DナVgoナ崙 tekstu ナコrテウdナPwego.
+ * \param ch Wskaナコnik na wynik dekodowania.
+ *
+ * \return DナVgoナ崙 zdekodowanej sekwencji w bajtach lub wartoナ崙 mniejsza
+ * od zera w przypadku bナて囘u.
+ */
+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ナPwy 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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/compat.h	Tue Mar 16 12:07:06 2010 +0900
@@ -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. Woシny <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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/dcc.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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ナгki <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ナVga poナてczeナ bezpoナ孑ednich 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 wewn黎rzna
+
+/**
+ * \internal Przekazuje zawartoナ崙 pakietu do odpluskwiania.
  *
- * wyカwietla 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ナB ナシトdanie zwrotnego poナてczenia bezpoナ孑edniego.
+ *
+ * Funkcjト wykorzystuje siト, jeナ嬪i nie ma moナシliwoナ嫩i poナてczenia siト z odbiorcト
+ * pliku lub rozmowy gナPsowej. Po otrzymaniu ナシトdania druga strona sprテウbuje
+ * nawiトzaト zwrotne poナてczenie bezpoナ孑ednie z nadawcト.
  * gg_dcc_request()
  *
- * wysyウa informacj o tym, ソe dany klient powinien si z nami poウアczy.
- * wykorzystywane, kiedy druga strona, ktrej chcemy coカ wysウa jest za
- * maskaradア.
+ * \param sess Struktura sesji
+ * \param uin Numer odbiorcy
  *
- *  - sess - struktura opisujアca 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 wewn黎rzna
+/**
+ * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32.
  *
- * zamienia czas w postaci unixowej na windowsowy.
+ * \note Funkcja dziaナB jedynie gdy kompilator obsナVguje 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ナOia pola struktury \c gg_dcc niezbト囘ne do wysナBnia pliku.
  *
- * wypeウnia pola struct gg_dcc niezb鹽ne do wysウania pliku.
+ * \note Wiト冖szト funkcjonalnoナ崙 zapewnia funkcja \c gg_dcc_fill_file_info2().
  *
- *  - d - struktura opisujアca poウアczenie DCC
- *  - filename - nazwa pliku
+ * \param d Struktura poナてczenia
+ * \param filename Nazwa pliku
  *
- * 0, -1.
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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()
- *
- * wypeウnia pola struct gg_dcc niezb鹽ne do wysウania pliku.
+/**
+ * WypeナOia pola struktury \c gg_dcc niezbト囘ne do wysナBnia pliku.
  *
- *  - d - struktura opisujアca poウアczenie 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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 wewn黎rzna
- * 
- * inicjuje proces wymiany pliku z danym klientem.
+/**
+ * \internal Rozpoczyna poナてczenie bezpoナ孑ednie z danym klientem.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wウasny 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ナBsny 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 jeカli wystアpiウ bウアd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u
  */
 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 wysウaウ do
- * nas ソアdanie poウアczenia.
+/**
+ * Rozpoczyna odbieranie pliku przez zwrotne poナてczenie bezpoナ孑ednie.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wウasny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP nadawcy
+ * \param port Port nadawcy
+ * \param my_uin WナBsny numer
+ * \param peer_uin Numer nadawcy
  *
- * zaalokowana struct gg_dcc lub NULL jeカli wystアpiウ bウアd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u
+ *
+ * \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 wysyウania pliku do danego klienta.
+/**
+ * Rozpoczyna wysyナBnie pliku.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wウasny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin WナBsny numer
+ * \param peer_uin Numer odbiorcy
  *
- * zaalokowana struct gg_dcc lub NULL jeカli wystアpiウ bウアd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u
+ *
+ * \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 nawiアza poウアczenie gウosowe.
+/**
+ * Rozpoczyna poナてczenie gナPsowe.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wウasny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin WナBsny numer
+ * \param peer_uin Numer odbiorcy
  *
- * zaalokowana struct gg_dcc lub NULL jeカli wystアpiウ bウアd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u
+ *
+ * \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ナ孑edniego.
+ *
+ * Funkcjト naleナシy wywoナBト po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK.
  *
- * po zdarzeniu GG_EVENT_DCC_CALLBACK naleソy ustawi typ poウアczenia 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 opisujアca poウアczenie
- *  - type - typ poウアczenia (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 wewn黎rzna
+
+/**
+ * \internal Funkcja zwrotna poナてczenia bezpoナ孑edniego.
  *
- * wywoウywana 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ナVje ona \c gg_watch_fd() i zachowuje wynik w polu \c event.
  *
- *  - d - structura opisujアca poウアczenie
+ * \note Funkcjonalnoナ崙 funkcjo zwrotnej nie jest juナシ wspierana.
  *
- * 0, -1.
+ * \param d Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  */
 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ナVchujトce dla poナてczeナ bezpoナ孑ednich.
  *
- * tworzy gniazdo dla bezpoカredniej komunikacji mi鹽zy klientami.
+ * Funkcja przywiトzuje gniazdo do pierwszego wolnego portu TCP.
  *
- *  - uin - wウasny numer
- *  - port - preferowany port, jeカli rwny 0 lub -1, prbuje domyカlnego
+ * \param uin WナBsny numer
+ * \param port Preferowany port (jeナ嬪i rテウwny 0 lub -1, prテウbuje siト domyナ嬪nego)
  *
- * zaalokowana struct gg_dcc, ktrア poシniej naleソy zwolni funkcjア
- * gg_dcc_free(), albo NULL jeカli wystアpiウ bウアd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u
+ *
+ * \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()
- *
- * wysyウa ramk danych dla rozmowy gウosowej.
+/**
+ * WysyナB ramkト danych poナてczenia gナPsowego.
  *
- *  - d - struktura opisujアca poウアczenie dcc
- *  - buf - bufor z danymi
- *  - length - rozmiar ramki
+ * \param d Struktura poナてczenia
+ * \param buf Bufor z danymi
+ * \param length DナVgoナ崙 bufora z danymi
  *
- * 0, -1.
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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ナ孑edniego z obsナVgト bナて囘テウ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ナB dane do poナてczenia bezpoナ孑edniego z obsナVgト bナて囘テウ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ナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia.
  *
- * funkcja, ktrア naleソy wywoウa, gdy coカ si zmieni na gg_dcc->fd.
+ * Funkcja zwraca strukturト zdarzenia \c gg_event. Jeナ嬪i rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyナP 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, jeカli zabrakウo pami鹹i na niア.
+ * \return Struktura zdarzenia lub \c NULL jeナ嬪i 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, wi鹹 odwracamy z powrotem */
+				/* zostajト teraz u nas, wiト冂 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);
 
-				/* bウアd */
+				/* 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;
 				}
-				
-				/* jeカli wczytaliカmy wi鹹ej, utnijmy. */
+
+				/* jeナ嬪i wczytaliナ嬶y wiト冂ej, 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);
-				
-				/* bウアd */
+
+				/* 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ナ孑ednie.
  *
- * zwalnia pami po strukturze poウアczenia 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	Tue Mar 16 12:07:06 2010 +0900
@@ -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ナгki <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ナVga poナてczeナ bezpoナ孑ednich 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ナ孑ednie do sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナ孑ednie z sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナ嬪i 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ナ孑ednie
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナVchujトce dla poナてczenia bezpoナ孑edniego
+ *
+ * \param dcc Struktura poナてczenia
+ * \param port Preferowany port (jeナ嬪i rテウwny 0 lub -1, prテウbuje siト domyナ嬪nego)
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナVchujトce i wysyナB jego parametry
+ *
+ * \param dcc Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナB 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナBnie 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ナて囘u
+ *
+ * \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ナBnie 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ナ嬪i ma byト wyznaczony)
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku bナて囘u
+ *
+ * \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ナBnie pliku o danym deskryptorze.
+ *
+ * \note WysyナBnie pliku nie bト囘zie dziaナBト poprawnie, jeナ嬪i deskryptor
+ * ナコrテウdナPwy 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ナて囘u
+ *
+ * \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ナBnia pliku
+ *
+ * \note Biblioteka nie zmienia poナPナシenia w odbieranych plikach. Jeナ嬪i offset
+ * poczトtkowy jest rテウナシny od zera, naleナシy ustawiト go funkcjト \c lseek() lub
+ * podobnト.
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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ナBnia pliku.
+ *
+ * \param dcc Struktura poナてczenia
+ * \param reason Powテウd odrzucenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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ナVguje pakiet identyfikatora poナてczenia bezpoナ孑edniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treナ崙 pakietu
+ * \param len DナVgoナ崙 pakietu
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナVguje pakiet akceptacji poナてczenia bezpoナ孑edniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treナ崙 pakietu
+ * \param len DナVgoナ崙 pakietu
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナBト 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ナ嬶y wywoナBト juナシ zdarzenie GG_DCC7_ACCEPT?
+	
+	dcc->offset = gg_fix32(p->offset);
+	dcc->state = GG_STATE_WAITING_FOR_INFO;
+
+	return 0;
+}
+
+/**
+ * \internal ObsナVguje pakiet informacji o poナてczeniu bezpoナ孑ednim.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treナ崙 pakietu
+ * \param len DナVgoナ崙 pakietu
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナ嬪i 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ナVguje pakiet odrzucenia poナてczenia bezpoナ孑edniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treナ崙 pakietu
+ * \param len DナVgoナ崙 pakietu
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナVguje pakiet nowego poナてczenia bezpoナ孑edniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treナ崙 pakietu
+ * \param len DナVgoナ崙 pakietu
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ト冲rzne w zaleナシnoナ嫩i od rodzaju
+ * poナてczenia.
+ * 
+ * \param dcc Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u.
+ */
+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ナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia.
+ *
+ * Funkcja zwraca strukturト zdarzenia \c gg_event. Jeナ嬪i rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyナP 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ナ嬪i 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ナ孑ednie.
+ *
+ * \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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/events.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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. Woシny <speedy@ziew.org>
- *                          Arkadiusz Miカkiewicz <arekm@pld-linux.org>
+ *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woナコny <speedy@ziew.org>
+ *                          Arkadiusz Miナ嫐iewicz <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ナVga 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ナZwaト za kaナシdym razem gdy funkcja biblioteki zwrテウci
+ * strukturト \c gg_event.
  *
- *  - e - wskaシnik 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ナBnia.
  *
- *  - 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ナ嬪i siト powiodナP, -1 jeナ嬪i 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 wewn黎rzna
+/**
+ * \internal Analizuje przychodzトcy pakiet z obrazkiem.
  *
- * parsuje przychodzアcy pakiet z obrazkiem.
- *
- *  - e - opis zdarzenia
- *  - 
+ * \param e Struktura zdarzenia
+ * \param p Bufor z danymi
+ * \param len DナVgoナ崙 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;
 
-	/* jeカli skoczono odbiera obrazek, wygeneruj zdarzenie */
+	/* jeナ嬪i 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 wewn黎rzna
- *
- * obsウuguje pakiet z przychodzアcア wiadomoカciア, rozbijajアc go na dodatkowe
- * struktury (konferencje, kolorki) w razie potrzeby.
+/**
+ * \internal Analizuje informacje rozszerzone wiadomoナ嫩i.
+ * 
+ * \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 - nagウwek pakietu
- *  - e - opis zdarzenia
- *
- * 0, -1.
+ * \return 0 jeナ嬪i siト powiodナP, -1 jeナ嬪i wiadomoナ崙 obsナVナシono i wynik ma
+ * zostaト przekazany aplikacji, -2 jeナ嬪i wystトpiナ bナてd ogテウlny, -3 jeナ嬪i
+ * 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ナ嫩iト.
+ *
+ * Rozbija pakiet na poszczegテウlne skナBdniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wskaナコnik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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 wewn黎rzna
+/**
+ * \internal Zamienia tekst w formacie HTML na czysty tekst.
+ *
+ * \param dst Bufor wynikowy (moナシe byト \c NULL)
+ * \param html Tekst ナコrテウdナPwy
+ *
+ * \note Dokleja \c \\0 na koナcu bufora wynikowego.
  *
- * patrzy na gniazdo, odbiera pakiet i wypeウnia struktur zdarzenia.
+ * \return DナVgoナ崙 tekstu wynikowego bez \c \\0 (nawet jeナ嬪i \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ナ嫩iト protokoナV Gadu-Gadu 8.0.
+ *
+ * Rozbija pakiet na poszczegテウlne skナBdniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wskaナコnik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
  *
- *  - sess - struktura opisujアca sesj
- *  - e - opis zdarzenia
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ト冂 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ナOia strukturト zdarzenia.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 jeナ嬪i 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;
 
-			/* jeカli odpowiedシ na eksport, wywoウaj zdarzenie tylko
+			/* jeナ嬪i odpowiedナコ na eksport, wywoナBj 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ア naleソy wywoウa, gdy coカ si stanie z obserwowanym
- * deskryptorem. zwraca klientowi informacj o tym, co si dzieje.
+/** \endcond */
+
+/**
+ * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze sesji.
  *
- *  - sess - opis sesji
+ * Funkcja zwraca strukturト zdarzenia \c gg_event. Jeナ嬪i rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyナP siト jeszcze nic wartego odnotowania.
+ * Strukturト zdarzenia naleナシy zwolniト funkcja \c gg_event_free().
  *
- * wskaシnik do struktury gg_event, ktrア trzeba zwolni pシniej
- * za pomocア gg_event_free(). jesli rodzaj zdarzenia jest rwny
- * GG_EVENT_NONE, naleソy je zignorowa. jeカli zwrciウo NULL,
- * staウo si coカ niedobrego -- albo zabrakウo pami鹹i albo zerwaウo
- * poウアczenie.
+ * \param sess Struktura sesji
+ *
+ * \return Struktura zdarzenia lub \c NULL jeナ嬪i 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;
 			}
 
-			/* jeカli jesteカmy w resolverze i mamy ustawiony port
-			 * proxy, znaczy, ソe resolvowaliカmy proxy. zatem
+			/* jeナ嬪i jesteナ嬶y w resolverze i mamy ustawiony port
+			 * proxy, znaczy, ナシe resolvowaliナ嬶y proxy. zatem
 			 * wpiszmy jego adres. */
 			if (sess->proxy_port)
 				sess->proxy_addr = addr.s_addr;
 
 			/* zapiszmy sobie adres huba i adres serwera (do
-			 * bezpoカredniego poウアczenia, jeカli hub leソy)
+			 * bezpoナ孑edniego poナてczenia, jeナ嬪i 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, zaleソnie
-			 * od tego, co resolvowaliカmy. */
+			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ナ嬶y. */
 			if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) {
-				/* jeカli w trybie asynchronicznym gg_connect()
-				 * zwrci bウアd, nie ma sensu prbowa dalej. */
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+				/* jeナ嬪i 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;
 			}
 
-			/* jeカli podano serwer i ウアczmy si przez proxy,
-			 * jest to bezpoカrednie poウアczenie, inaczej jest
+			/* jeナ嬪i podano serwer i ナてczmy siト przez proxy,
+			 * jest to bezpoナ孑ednie 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");
-
-			/* jeカli asynchroniczne, sprawdzamy, czy nie wystアpiウ
-			 * przypadkiem jakiカ bウアd. */
+			const char *host;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
+
+			/* jeナ嬪i 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 udaウo si poウアczy 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 bウ鹽ach 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, wi鹹 zawsze zmieカci si
-			 * do bufora gniazda. jeカli write() zwrci mniej,
-			 * staウo si coカ zウego. */
+			gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
+
+			/* zapytanie jest krテウtkie, wiト冂 zawsze zmieナ嫩i siト
+			 * do bufora gniazda. jeナ嬪i write() zwrテウci mniej,
+			 * staナP siト coナ zナFgo. */
 			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 porzアdku. */
+			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);
-
-				/* jeカli otrzymaliカmy jakieカ dziwne informacje,
-				 * prbujemy si ウアczy z pomini鹹iem huba. */
-				if (sess->proxy_addr && sess->proxy_port) {
-					if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
-						/* trudno. nie wyszウo. */
-						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 nagウwka. */
+
+			/* 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);
-			
-			/* jeカli pierwsza liczba w linii nie jest rwna zeru,
-			 * oznacza to, ソe mamy wiadomoカ systemowア. */
+
+			/* jeナ嬪i 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) {
-				/* jeカli mamy proxy, ウアczymy si z nim. */
+				/* jeナ嬪i mamy proxy, ナてczymy siト z nim. */
 				if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
-					/* nie wyszウo? trudno. */
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
+					/* nie wyszナP? 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 wウaカciwym serwerem. */
+			/* ナてczymy siト z wナBナ嫩iwym 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 wyszウo? prbujemy portu 443. */
+				/* nie wyszナP? prテウbujemy portu 443. */
 				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
-					/* ostatnia deska ratunku zawiodウa?
+					/* ostatnia deska ratunku zawiodナB?
 					 * 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");
-
-			/* jeカli wystアpiウ bウアd podczas ウアczenia si... */
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
+
+			sess->soft_timeout = 0;
+
+			/* jeナ嬪i wystトpiナ bナてd podczas ナてczenia siト... */
 			if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-				/* jeカli nie udaウo si poウアczenie z proxy,
-				 * nie mamy czego prbowa wi鹹ej. */
+				/* jeナ嬪i nie udaナP siト poナてczenie z proxy,
+				 * nie mamy czego prテウbowaト wiト冂ej. */
 				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
-				/* jeカli logujemy si po TLS, nie prbujemy
-				 * si ウアczy juソ z niczym innym w przypadku
-				 * bウ鹽u. nie doカ, ソe nie ma sensu, to i
-				 * trzeba by si bawi w tworzenie na nowo
+#ifdef GG_CONFIG_HAVE_OPENSSL
+				/* jeナ嬪i logujemy siト po TLS, nie prテウbujemy
+				 * siト ナてczyト juナシ z niczym innym w przypadku
+				 * bナて囘u. 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;
 
-			/* jeカli mamy proxy, wyカlijmy zapytanie. */
+			/* jeナ嬪i mamy proxy, wyナ嬪ijmy 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);
-				
-				/* wysyウamy zapytanie. jest ono na tyle krtkie,
-				 * ソe musi si zmieカci w buforze gniazda. jeカli
-				 * write() zawiedzie, staウo si coカ zウego. */
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n//   %s", buf);
+
+				/* wysyナBmy zapytanie. jest ono na tyle krテウtkie,
+				 * ナシe musi siト zmieナ嫩iト w buforze gniazda. jeナ嬪i
+				 * write() zawiedzie, staナP siト coナ zナFgo. */
 				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 gウupi 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ナVpi 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. gウupio, ale dziaウa. */
+				 * fazie. gナVpio, ale dziaナB. */
 				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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/http.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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ナVga 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 poウアczenie 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ト囘ト przeprowadzane po wykryciu zmian (\c watch) na obserwowanym
+ * deskryptorze (\c fd) i wywoナBniu funkcji \c gg_http_watch_fd().
  *
- *  - hostname - adres serwera
- *  - port - port serwera
- *  - async - asynchroniczne poウアczenie
- *  - method - metoda http (GET, POST, cokolwiek)
- *  - path - カcieソka do zasobu (musi by poprzedzona ,,/'')
- *  - header - nagウwek 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ア poシniej naleソy
- * zwolni funkcjア gg_http_free(), albo NULL jeカli wystアpiウ bウアd.
+ * \param hostname Adres serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego poナてczenia
+ * \param method Metoda HTTP
+ * \param path ナ喞ieナシ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ナ嬪i 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 obsウudze HTTP funkcj tア naleソy wywoウa, jeカli
- * zmieniウo si coカ na obserwowanym deskryptorze.
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia.
  *
- *  - h - struktura opisujアca poウアczenie
+ * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne
+ * \c GG_STATE_PARSING. W tym miejscu dziaナBnie przejmuje zwykle funkcja
+ * korzystajトca z \c gg_http_watch_fd(). W przypadku bナて囘u poナてczenia,
+ * pole \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u znajdzie siト
+ * w polu \c error.
  *
- * jeカli wszystko poszウo dobrze to 0, inaczej -1. poウアczenie b鹽zie
- * zakoczone, jeカli h->state == GG_STATE_PARSING. jeカli wystアpi jakiカ
- * bウアd, to b鹽zie tam GG_STATE_ERROR i odpowiedni kod bウ鹽u w h->error.
+ * \param h Struktura poナてczenia
+ *
+ * \return \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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.
  *
- * jeカli poウアczenie jest w trakcie, przerywa je. nie zwalnia h->data.
- * 
- *  - h - struktura opisujアca poウアczenie
+ * 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 wewn黎rzna
+/**
+ * \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ア poウアczenie i zwalnia pami po nim.
+ * Jeナ嬪i poナてczenie nie zostaナP jeszcze zakoナczone, jest przerywane.
  *
- *  - h - struktura, ktrア naleソy 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	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/libgadu.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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. Woシny <speedy@ziew.org>
- *                          Arkadiusz Miカkiewicz <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ナ嫐iewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliナгki <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ナBdajトcト siト ze staナZch \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ナ嬪i zarテウwno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, sト rテウwne
+ * \c NULL, informacje sト wysyナBne do standardowego wyjナ嫩ia bナて囘u (\c stderr).
+ *
+ * \param level Poziom rejestracji
+ * \param format Format wiadomoナ嫩i (zgodny z \c printf)
+ * \param ap Lista argumentテウw (zgodna z \c printf)
+ *
+ * \note Funkcja jest przesナBniana 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ナ嬪i zarテウwno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, sト rテウwne
+ * \c NULL, informacje sト wysyナBne do standardowego wyjナ嫩ia bナて囘u.
+ *
+ * \param sess Sesja ktテウrej dotyczy informacja lub \c NULL
+ * \param level Poziom rejestracji
+ * \param format Format wiadomoナ嫩i (zgodny z \c printf)
+ * \param ap Lista argumentテウw (zgodna z \c printf)
+ *
+ * \note Funkcja przesナBnia 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ナVchujトcego dla poナてczeナ bezpoナ孑ednich.
+ * 
+ * \ingroup ip
+ */
 int gg_dcc_port = 0;
+
+/**
+ * Adres IP gniazda nasナVchujトcego dla poナてczeナ bezpoナ孑ednich.
+ *
+ * \ingroup ip
+ */
 unsigned long gg_dcc_ip = 0;
 
+/**
+ * Adres lokalnego interfejsu IP, z ktテウrego wywoナZwane sト wszystkie poナてczenia.
+ *
+ * \ingroup ip
+ */
 unsigned long gg_local_ip = 0;
-/*
- * zmienne opisujアce parametry proxy http.
+
+/**
+ * Flaga wナてczenia poナてczeナ przez serwer poナ孑edniczトcy.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_enabled = 0;
+
+/**
+ * Adres serwera poナ孑edniczトcego.
+ *
+ * \ingroup proxy
  */
 char *gg_proxy_host = NULL;
+
+/**
+ * Port serwera poナ孑edniczトcego.
+ *
+ * \ingroup proxy
+ */
 int gg_proxy_port = 0;
-int gg_proxy_enabled = 0;
+
+/**
+ * Flaga uナシywania serwera poナ孑edniczトcego jedynie dla usナVg HTTP.
+ *
+ * \ingroup proxy
+ */
 int gg_proxy_http_only = 0;
+
+/**
+ * Nazwa uナシytkownika do autoryzacji serwera poナ孑edniczトcego.
+ *
+ * \ingroup proxy
+ */
 char *gg_proxy_username = NULL;
+
+/**
+ * HasナP uナシytkownika do autoryzacji serwera poナ孑edniczト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ナPwie.
+ *
+ * Ze wzglト囘u na little-endianowoナ崙 protokoナV Gadu-Gadu, na maszynach
+ * big-endianowych odwraca kolejnoナ崙 bajtテウw w sナPwie.
  *
- * zamienia kolejnoカ bajtw w liczbie 32-bitowej tak, by odpowiadaウa
- * kolejnoカci bajtw w protokole GG. ze wzgl鹽u na LE-owoカ serwera,
- * zamienia tylko na maszynach BE-wych.
+ * \param x Liczba do zamiany
  *
- *  - x - liczba do zamiany
+ * \return Liczba z odpowiedniト kolejnoナ嫩iト bajtテウw
  *
- * liczba z odpowiedniア kolejnoカciア 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ナPwie.
+ *
+ * Ze wzglト囘u na little-endianowoナ崙 protokoナV Gadu-Gadu, na maszynach
+ * big-endianowych zamienia kolejnoナ崙 bajtテウw w sナPwie.
  *
- * zamienia kolejnoカ bajtw w liczbie 16-bitowej tak, by odpowiadaウa
- * kolejnoカci bajtw w protokole GG. ze wzgl鹽u na LE-owoカ serwera,
- * zamienia tylko na maszynach BE-wych.
+ * \param x Liczba do zamiany
  *
- *  - x - liczba do zamiany
+ * \return Liczba z odpowiedniト kolejnoナ嫩iト bajtテウw
  *
- * liczba z odpowiedniア kolejnoカciア 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 wewn黎rzna
- * 
- * liczy hash z hasウa i danego seeda.
- * 
- *  - password - hasウo do hashowania
- *  - seed - wartoカ podana przez serwer
+/**
+ * \internal Liczy skrテウt z hasナB i ziarna.
  *
- * hash.
+ * \param password HasナP
+ * \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 wewn黎rzna
- *
- * tworzy potok, forkuje si i w drugim procesie zaczyna resolvowa 
- * podanego hosta. zapisuje w sesji deskryptor potoku. jeカli coカ tam
- * b鹽zie gotowego, znaczy, ソe moソna wczyta struct in_addr. jeカli
- * nie znajdzie, zwraca INADDR_NONE.
- *
- *  - fd - wskaシnik 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 wewn黎rzna
- *
- * tworzy potok, nowy wアtek i w nim zaczyna resolvowa podanego hosta.
- * zapisuje w sesji deskryptor potoku. jeカli coカ tam b鹽zie gotowego,
- * znaczy, ソe moソna wczyta struct in_addr. jeカli nie znajdzie, zwraca
- * INADDR_NONE.
- *
- *  - fd - wskaシnik do zmiennej przechowujアcej desktyptor resolvera
- *  - resolver - wskaシnik do wskaシnika resolvera
- *  - hostname - nazwa hosta do zresolvowania
+ * Funkcja odbiera dane od serwera zajmujトc siト TLS w razie koniecznoナ嫩i.
  *
- * 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ナVgoナ崙 bufora
  *
- * czyta z gniazda okreカlonア iloカ bajtw. bierze pod uwag, czy mamy
- * poウアczenie zwykウe czy TLS.
- *
- *  - sess - sesja,
- *  - buf - bufor,
- *  - length - iloカ bajtw,
- *
- * takie same wartoカci 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ナB do serwera dane binarne.
  *
- * zapisuje do gniazda okreカlonア iloカ bajtw. bierze pod uwag, czy mamy
- * poウアczenie zwykウe czy TLS.
+ * Funkcja wysyナB dane do serwera zajmujトc siト TLS w razie koniecznoナ嫩i.
  *
- *  - sess - sesja,
- *  - buf - bufor,
- *  - length - iloカ bajtw,
+ * \param sess Struktura sesji
+ * \param buf Bufor z danymi
+ * \param length DナVgoナ崙 bufora
  *
- * takie same wartoカci 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 wewn黎rzna
+/**
+ * \internal Odbiera pakiet od serwera.
  *
- * odbiera jeden pakiet i zwraca wskaシnik do niego. pami po nim
- * naleソy zwolni za pomocア free().
+ * Funkcja odczytuje nagナづウwek pakietu, a nastト冪nie jego zawartoナ崙 i zwraca
+ * w zaalokowanym buforze.
  *
- *  - sess - opis sesji
+ * Przy poナてczeniach asynchronicznych, funkcja moナシe nie byト w stanie
+ * skompletowaト caナFgo pakietu -- w takim przypadku zwrテウci -1, a kodem bナて囘u
+ * bト囘zie \c EAGAIN.
  *
- * w przypadku bウ鹽u NULL, kod bウ鹽u w errno. naleソy zwrci uwag, ソe gdy
- * poウアczenie jest nieblokujアce, a kod bウ鹽u wynosi EAGAIN, nie udaウo si
- * odczyta caウego pakietu i nie naleソy tego traktowa jako bウアd.
+ * \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 wewn黎rzna
+/**
+ * \internal WysyナB pakiet do serwera.
  *
- * konstruuje pakiet i wysyウa go do serwera.
+ * Funkcja konstruuje pakiet do wysナBnia z dowolnej liczby fragmentテウw. Jeナ嬪i
+ * rozmiar pakietu jest za duナシy, by mテウc go wysナBト za jednym razem, pozostaナB
+ * czト卩崙 zostanie zakolejkowana i wysナBna, gdy bト囘zie to moナシliwe.
  *
- *  - sock - deskryptor gniazda
- *  - type - typ pakietu
- *  - payload_1 - pierwsza czカ pakietu
- *  - payload_length_1 - dウugoカ pierwszej czカci
- *  - payload_2 - druga czカ pakietu
- *  - payload_length_2 - dウugoカ drugiej czカci
- *  - ... - kolejne czカci pakietu i ich dウugoカci
- *  - NULL - kocowym parametr (konieczny!)
+ * \param sess Struktura sesji
+ * \param type Rodzaj pakietu
+ * \param ... Lista kolejnych czト卩嫩i pakietu (wskaナコnik na bufor i dナVgoナ崙
+ *            typu \c int) zakoナczona \c NULL
  *
- * jeカli si powiodウo, zwraca 0, w przypadku bウ鹽u -1. jeカli errno == ENOMEM,
- * zabrakウo pami鹹i. inaczej byウ bウアd przy wysyウaniu pakietu. dla errno == 0
- * nie wysウano caウego pakietu.
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  */
 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 wewn黎rzna
+/**
+ * \internal Funkcja zwrotna sesji.
+ *
+ * Pole \c callback struktury \c gg_session zawiera wskaナコnik do tej funkcji.
+ * WywoナVje ona \c gg_watch_fd i zachowuje wynik w polu \c event.
+ *
+ * \note Korzystanie z tej funkcjonalnoナ嫩i nie jest juナシ zalecane.
  *
- * wywoウywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje
- * do gg_session->event jego wynik.
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  */
-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 obsウuguje si przez
- * gg_watch_fd().
+ * Przy poナてczeniu synchronicznym funkcja zakoナczy dziaナBnie po nawiトzaniu
+ * poナてczenia lub gdy wystトpi bナてd. Po udanym poナてczeniu naleナシy wywoナZwaト
+ * funkcjト \c gg_watch_fd(), ktテウra odbiera informacje od serwera i zwraca
+ * informacje o zdarzeniach.
  *
- * UWAGA! program musi obsウuソy SIGCHLD, jeカli ウア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ト冲li zdarzeナ (Glib, Qt itp.). Pole \c check
+ * jest maskト bitowト mテウwiトcト, czy biblioteka chce byト informowana o moナシliwoナ嫩i
+ * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE).
+ * Po zaobserwowaniu zmian na deskryptorze naleナシy wywoナBト 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ナVナシyト sygnaナ SIGCHLD.
  *
- *  - p - struktura opisujアca poczアtkowy stan. wymagane pola: uin, 
- *    password
+ * \note Po nawiトzaniu poナてczenia z serwerem naleナシy wysナBト listト kontaktテウw
+ * za pomocト funkcji \c gg_notify() lub \c gg_notify_ex().
  *
- * w przypadku bウ鹽u NULL, jeカli idzie dobrze (async) albo poszウo
- * dobrze (sync), zwrci wskaシnik 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ナて囘u.
+ *
+ * \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ト冲aト, ナシeby nie ciトト w ナ孑odku 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ナP? 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ナB?
+					 * 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ナB do serwera pakiet utrzymania poナてczenia.
+ *
+ * Klient powinien regularnie co minutト wysyナBト pakiet utrzymania poナてczenia,
+ * inaczej serwer uzna, ナシe klient straciナ ナてcznoナ崙 z sieciト i zerwie
+ * poナてczenie.
+ *
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  *
- * prbuje zamknア poウアczenia 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ト冂 po jej wywoナBniu naleナシy uナシyト
+ * \c gg_free_session(). Jeナ嬪i chce siト ustawiト opis niedostト冪noナ嫩i, naleナシy
+ * wczeナ嬾iej wywoナBト funkcjト \c gg_change_status_descr() lub
+ * \c gg_change_status_descr_time().
+ *
+ * \note Jeナ嬪i 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 mogウo 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ナBト
+ * po zamkniト冂iu 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ナP 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ナBjトca pakiet zmiany statusu uナシytkownika.
  *
- * zmienia status uソytkownika. 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 uソytkownika
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  *
- * 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ナVgi poナてczeナ gナPsowych 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ト冲aト o tym, ナシeby nie ucinaト w ナ孑odku 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 uソytkownika na opisowy.
+ * \param sess Struktura sesji
+ * \param status Nowy status uナシytkownika
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  *
- *  - sess - opis sesji
- *  - status - nowy status uソytkownika
- *  - 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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 uソytkownika na opisowy z godzinア powrotu.
+/**
+ * Zmienia status uナシytkownika na status opisowy z podanym czasem powrotu.
  *
- *  - sess - opis sesji
- *  - status - nowy status uソytkownika
- *  - 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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ナB 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ナ嫩i
+ * \param recipient Numer adresata
+ * \param message Treナ崙 wiadomoナ嫩i
+ *
+ * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u.
+ *
+ * \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ナB wiadomoナ崙 formatowanト.
+ *
+ * Zwraca losowy numer sekwencyjny, ktテウry moナシna zignorowaト albo wykorzystaト
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomoナ嫩i
+ * \param recipient Numer adresata
+ * \param message Treナ崙 wiadomoナ嫩i
+ * \param format Informacje o formatowaniu
+ * \param formatlen DナVgoナ崙 informacji o formatowaniu
+ *
+ * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u.
+ *
+ * \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ナB 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ナ嫩i
+ * \param recipients_count Liczba adresatテウw
+ * \param recipients Wskaナコnik do tablicy z numerami adresatテウw
+ * \param message Treナ崙 wiadomoナ嫩i
+ *
+ * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u.
+ *
+ * \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ナPナシenie w buforze roboczym
+ * \param src Dodawany tekst
+ * \param len DナVgoナ崙 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ナPwy
+ * \param format Atrybuty tekstu ナコrテウdナPwego
+ * \param format_len DナVgoナ崙 bloku atrybutテウw tekstu ナコrテウdナPwego
+ *
+ * \note Dokleja \c \\0 na koナcu bufora wynikowego.
+ *
+ * \return DナVgoナ崙 tekstu wynikowego bez \c \\0 (nawet jeナ嬪i \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ナB 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ナ嫩i
+ * \param recipients_count Liczba adresatテウw
+ * \param recipients Wskaナコnik do tablicy z numerami adresatテウw
+ * \param message Treナ崙 wiadomoナ嫩i
+ * \param format Informacje o formatowaniu
+ * \param formatlen DナVgoナ崙 informacji o formatowaniu
+ *
+ * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u.
+ * 
+ * \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ナV. Jeナ嬪i wysyナBmy kilka
+		// wiadomoナ嫩i w ciトgu jednej sekundy, zwiト冖szamy poprzedniト
+		// wartoナ崙, ナシeby kaナシda wiadomoナ崙 miaナB 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ナB wiadomoナ崙 binarnト przeznaczonト dla klienta.
+ *
+ * Wiadomoナ嫩i miト囘zy klientami przesyナB siト np. w celu wywoナBnia zwrotnego
+ * poナてczenia bezpoナ孑edniego. Funkcja zwraca losowy numer sekwencyjny,
+ * ktテウry moナシna zignorowaト albo wykorzystaト do potwierdzenia.
  *
- * wylogowuje uソytkownika i zamyka poウアczenie, ale nie zwalnia pami鹹i.
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomoナ嫩i
+ * \param recipient Numer adresata
+ * \param message Treナ崙 wiadomoナ嫩i
+ * \param message_len DナVgoナ崙 wiadomoナ嫩i
  *
- *  - sess - opis sesji
+ * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u.
+ *
+ * \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ナB ナシトdanie obrazka o podanych parametrach.
  *
- * wysyウa ソアdanie wysウania obrazka o podanych parametrach.
+ * Wiadomoナ嫩i obrazkowe nie zawierajト samych obrazkテウw, a tylko ich rozmiary
+ * i sumy kontrolne. Odbiorca najpierw szuka obrazkテウw w swojej pamiト冂i
+ * podrト冂znej i dopiero gdy ich nie znajdzie, wysyナB ナシト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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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()
- *
- * wysyウa ソアdany obrazek.
+/**
+ * WysyナB ナシト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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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 カcieソki, zostaw tylko nazw pliku */
+	/* wytnij ナ嫩ieナシ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 kawaウku jest nazwa pliku */
+		/* w pierwszym kawaナLu 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ナB do serwera listト kontaktテウw.
  *
- * wysyウa wiadomoカ do innego uソytkownika. zwraca losowy numer
- * sekwencyjny, ktry moソna zignorowa albo wykorzysta do potwierdzenia.
+ * Funkcja informuje serwer o liナ嫩ie kontaktテウw, ktテウrych statusy bト囘ト
+ * 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ナBト po poナてczeniu, nawet jeナ嬪i
+ * jest pusta.
  *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoカci
- *  - recipient - numer adresata
- *  - message - treカ wiadomoカci
- *  - message_len - dウugoカ
+ * \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 wiadomoカci lub -1 w przypadku bウ鹽u.
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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()
- *
- * wysyウa wiadomoカ do innego uソytkownika. zwraca losowy numer
- * sekwencyjny, ktry moソna zignorowa albo wykorzysta do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoカci
- *  - recipient - numer adresata
- *  - message - treカ wiadomoカci
- *
- * numer sekwencyjny wiadomoカci lub -1 w przypadku bウ鹽u.
- */
-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()
- *
- * wysyウa kolorowア wiadomoカ do innego uソytkownika. zwraca losowy numer
- * sekwencyjny, ktry moソna zignorowa albo wykorzysta do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoカci
- *  - recipient - numer adresata
- *  - message - treカ wiadomoカci
- *  - format - informacje o formatowaniu
- *  - formatlen - dウugoカ informacji o formatowaniu
- *
- * numer sekwencyjny wiadomoカci lub -1 w przypadku bウ鹽u.
- */
-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()
- *
- * wysyウa wiadomoカ do kilku uソytkownikow (konferencja). zwraca losowy numer
- * sekwencyjny, ktry moソna zignorowa albo wykorzysta do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoカci
- *  - recipients_count - iloカ adresatw
- *  - recipients - numerki adresatw
- *  - message - treカ wiadomoカci
- *
- * numer sekwencyjny wiadomoカci lub -1 w przypadku bウ鹽u.
- */
-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()
- *
- * wysyウa kolorowア wiadomoカ do kilku uソytkownikow (konferencja). zwraca
- * losowy numer sekwencyjny, ktry moソna zignorowa albo wykorzysta do
- * potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoカci
- *  - recipients_count - iloカ adresatw
- *  - recipients - numerki adresatw
- *  - message - treカ wiadomoカci
- *  - format - informacje o formatowaniu
- *  - formatlen - dウugoカ informacji o formatowaniu
- *
- * numer sekwencyjny wiadomoカci lub -1 w przypadku bウ鹽u.
- */
-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()
- *
- * wysyウa 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()
- *
- * wysyウa serwerowi list kontaktw (wraz z odpowiadajアcymi im typami userw),
- * dzi麑i czemu wie, czyj stan nas interesuje.
- *
- *  - sess - opis sesji
- *  - userlist - wskaシnik do tablicy numerw
- *  - types - wskaシnik do tablicy typw uソytkownikw
- *  - 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ナB do serwera listト kontaktテウw.
  *
- * wysyウa serwerowi list kontaktw, dzi麑i 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 - wskaシnik 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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 poウアczenia.
- * dodawanemu uソytkownikowi okreカlamy 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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 poウアczenia.
- *
- *  - 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 poウアczenia.
- * usuwanemu uソytkownikowi okreカlamy 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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 poウアczenia.
+ * 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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ナB do serwera zapytanie dotyczトce listy kontaktテウw.
  *
- * wysyウa ソアdanie/zapytanie listy kontaktw na serwerze.
+ * Funkcja sナVナシ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ナZwu na poナてczenie. Format
+ * listy kontaktテウw jest ignorowany przez serwer, ale ze wzglト囘u 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 (moソe by NULL)
+ * Program nie musi siト przejmowaト fragmentacjト listy kontaktテウw wynikajトcト
+ * z protokoナV -- wysyナB 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/libgadu.h	Tue Mar 16 12:07:06 2010 +0900
@@ -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. Woシny <speedy@ziew.org>
- *                          Arkadiusz Miカkiewicz <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ナ嫐iewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliナгki <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 reprezentujアcy 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 opisujアca rソne sesje. przydatna w klientach.
+/**
+ * Identyfikator poナてczenia bezpoナ孑edniego 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;			/* podglアdany deskryptor */ \
-	int check;		/* sprawdzamy zapis czy odczyt */ \
-	int state;		/* aktualny stan maszynki */ \
-	int error;		/* kod bウ鹽u 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ナて囘u 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ナZ 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ナ嬪ny 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ナPwy 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ナPwe kodowanie */
+} gg_encoding_t;
+
+/**
+ * Sesja Gadu-Gadu.
  *
- * struktura opisujアca 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 poウアczenie jest asynchroniczne */
-	int pid;        	/* pid procesu resolvera */
-	int port;       	/* port, z ktrym si ウアczymy */
-	int seq;        	/* numer sekwencyjny ostatniej wiadomoカci */
-	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ナ嫩i */
+	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ナBniu \c callback */
 
-	uint32_t proxy_addr;	/* adres proxy, keszowany */
-	uint16_t proxy_port;	/* port proxy */
+	uint32_t proxy_addr;	/**< Adres serwera poナ孑edniczトcego */
+	uint16_t proxy_port;	/**< Port serwera poナ孑edniczトcego */
 
-	uint32_t hub_addr;	/* adres huba po resolvni鹹iu */
-	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 sウucha */
+	uint32_t client_addr;	/**< Adres gniazda dla poナてczeナ bezpoナ孑ednich do wersji Gadu-Gadu 6.x */
+	uint16_t client_port;	/**< Port gniazda dla poナてczeナ bezpoナ孑ednich 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ナ孑ednich do wersji Gadu-Gadu 6.x */
+	uint16_t external_port;	/**< Publiczny port dla poナてczeナ bezpoナ孑ednich do wersji Gadu-Gadu 6.x */
 
-	uin_t uin;		/* numerek klienta */
-	char *password;		/* i jego hasウo. zwalniane automagicznie */
+	uin_t uin;		/**< WナBsny numer Gadu-Gadu */
+	char *password;		/**< HasナP (zwalniane po uナシyciu) */
 
-	int initial_status;	/* poczアtkowy 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ナZch do wczytania bajtテウw pakietu */
 
-	int protocol_version;	/* wersja uソywanego protokoウu */
-	char *client_version;	/* wersja uソywanego klienta */
-	int last_sysmsg;	/* ostatnia wiadomoカ systemowa */
+	int protocol_version;	/**< Wersja protokoナV (bez flag) */
+	char *client_version;	/**< Wersja klienta */
+	int last_sysmsg;	/**< Numer ostatniej wiadomoナ嫩i systemowej */
 
-	char *initial_descr;	/* poczアtkowy opis stanu klienta */
+	char *initial_descr;	/**< Poczトtkowy opis statusu */
 
-	void *resolver;		/* wskaシnik na informacje resolvera */
+	void *resolver;		/**< Dane prywatne procesu lub wトtku rozwiトzujトcego nazwト serwera */
 
-	char *header_buf;	/* bufor na poczアtek nagウwka */
-	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ナVgiwanych obrazkテウw w KiB */
+
+	char *userlist_reply;	/**< Bufor z odbieranト listト kontaktテウw */
+
+	int userlist_blocks;	/**< Liczba czト卩嫩i 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ナB (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
+
+	char *send_buf;		/**< Bufor z danymi do wysナBnia */
+	int send_left;		/**< Liczba bajtテウw do wysナBnia */
 
-	int userlist_blocks;	/* na ile kawaウkw podzielono list kontaktw */
+	struct gg_dcc7 *dcc7_list;	/**< Lista poナてczeナ bezpoナ孑ednich skojarzonych z sesjト */
+	
+	int soft_timeout;	/**< Flaga mテウwiトca, ナシe po przekroczeniu \c timeout naleナシy wywoナBト \c gg_watch_fd() */
+
+	int protocol_flags;	/**< Flagi protokoナV */
 
-	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ナV */
 };
 
-/*
- * struct gg_http
+/**
+ * Poナてczenie HTTP.
  *
- * oglna struktura opisujアca 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 poウアczenie 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ナVgi HTTP */
 
-	char *query;            /* bufor zapytania http */
-	char *header;           /* bufor nagウwka */
-	int header_size;        /* rozmiar wczytanego nagウwka */
-	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 uソytkownika, 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;		/* wskaシnik na informacje resolvera */
-
-	unsigned int body_done;	/* ile juソ treカci 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 niezb鹽nej przy
- * wysyウaniu plikw.
+ * Wykorzystywana przy poナてczeniach bezpoナ孑ednich 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ナ孑ednie do wersji Gadu-Gadu 6.x.
  *
- * struktura opisujアca nasウuchujアce gniazdo poウアcze mi鹽zy 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ナBniu \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 kawaウka */
-	unsigned int chunk_offset;/* offset w aktualnym kawaウku */
+	int active;		/**< Flaga poナてczenia aktywnego (nieuナシywana) */
+	int port;		/**< Port gniazda nasナVchujトcego */
+	uin_t uin;		/**< WナBsny numer Gadu-Gadu */
+	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony poナてczenia */
+	int file_fd;		/**< deskryptor pliku */
+	unsigned int offset;	/**< PoナPナシenie w pliku */
+	unsigned int chunk_size;
+				/**< Rozmiar kawaナLa pliku */
+	unsigned int chunk_offset;
+				/**< PoナPナシenie w aktualnym kawaナLu pliku */
 	struct gg_file_info file_info;
-				/* informacje o pliku */
-	int established;	/* poウアczenie ustanowione */
-	uint8_t *voice_buf;	/* bufor na pakiet poウアczenia gウosowego */
-	int incoming;		/* poウアczenie przychodzアce */
-	char *chunk_buf;	/* bufor na kawaウek 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ナPsowego */
+	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ナ孑enich */
+#define GG_DCC7_FILENAME_LEN	255	/**< Maksymalny rozmiar nazwy pliku w poナてczeniach bezpoナ孑ednich */
+#define GG_DCC7_INFO_LEN	64	/**< Maksymalny rozmiar informacji o poナてczeniach bezpoナ孑ednich */
+
+/**
+ * Poナてczenie bezpoナ孑ednie 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ナBsny numer Gadu-Gadu */
+	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony poナてczenia */
+
+	int file_fd;		/**< Deskryptor przesyナBnego pliku */
+	unsigned int offset;	/**< Aktualne poナPナシenie w przesyナBnym pliku */
+	unsigned int size;	/**< Rozmiar przesyナBnego pliku */
+	unsigned char filename[GG_DCC7_FILENAME_LEN + 1];
+				/**< Nazwa przesyナBnego pliku */
+	unsigned char hash[GG_DCC7_HASH_LEN];
+				/**< Skrテウt SHA1 przesyナBnego pliku */
+
+	int dcc_type;		/**< Rodzaj poナてczenia bezpoナ孑edniego */
+	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ト冪ne poナてczenie w liナ嫩ie */
+
+	int soft_timeout;	/**< Flaga mテウwiトca, ナシe po przekroczeniu \c timeout naleナシy wywoナBト \c gg_dcc7_watch_fd() */
+	int seek;		/**< Flaga mテウwiトca, ナシe moナシna zmieniaト poナPナシenie w wysyナBnym pliku */
+};
+
+/**
+ * Rodzaj sesji.
  */
 enum gg_session_t {
-	GG_SESSION_GG = 1,	/* poウアczenie z serwerem gg */
-	GG_SESSION_HTTP,	/* oglna sesja http */
-	GG_SESSION_SEARCH,	/* szukanie */
-	GG_SESSION_REGISTER,	/* rejestrowanie */
-	GG_SESSION_REMIND,	/* przypominanie hasウa */
-	GG_SESSION_PASSWD,	/* zmiana hasウa */
-	GG_SESSION_CHANGE,	/* zmiana informacji o sobie */
-	GG_SESSION_DCC,		/* oglne poウアczenie DCC */
-	GG_SESSION_DCC_SOCKET,	/* nasウuchujアcy socket */
-	GG_SESSION_DCC_SEND,	/* wysyウanie pliku */
-	GG_SESSION_DCC_GET,	/* odbieranie pliku */
-	GG_SESSION_DCC_VOICE,	/* rozmowa gウosowa */
-	GG_SESSION_USERLIST_GET,	/* pobieranie userlisty */
-	GG_SESSION_USERLIST_PUT,	/* wysyウanie 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ナB */
+	GG_SESSION_PASSWD,	/**< Zmiana hasナB */
+	GG_SESSION_CHANGE,	/**< Zmiana informacji w katalogu publicznym (nieaktualne) */
+	GG_SESSION_DCC,		/**< Poナてczenie bezpoナ孑ednie (do wersji 6.x) */
+	GG_SESSION_DCC_SOCKET,	/**< Gniazdo nasナVchujトce (do wersji 6.x) */
+	GG_SESSION_DCC_SEND,	/**< WysyナBnie pliku (do wersji 6.x) */
+	GG_SESSION_DCC_GET,	/**< Odbieranie pliku (do wersji 6.x) */
+	GG_SESSION_DCC_VOICE,	/**< Rozmowa gナPsowa (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ナVchujトce (od wersji 7.x) */
+	GG_SESSION_DCC7_SEND,	/**< WysyナBnie pliku (od wersji 7.x) */
+	GG_SESSION_DCC7_GET,	/**< Odbieranie pliku (od wersji 7.x) */
+	GG_SESSION_DCC7_VOICE,	/**< Rozmowa gナPsowa (od wersji 7.x) */
 
-	GG_SESSION_USER0 = 256,	/* zdefiniowana dla uソytkownika */
-	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 wystアpi. */
-	GG_STATE_RESOLVING,             /* wywoウaウ gethostbyname() */
-	GG_STATE_CONNECTING,            /* wywoウaウ connect() */
-	GG_STATE_READING_DATA,		/* czeka na dane http */
-	GG_STATE_ERROR,			/* wystアpiウ bウアd. 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ナて囘u w polu \c error */
 
-		/* gg_session */
-	GG_STATE_CONNECTING_HUB,	/* wywoウaウ connect() na huba */
-	GG_STATE_CONNECTING_GG,         /* wywoウaウ connect() na serwer */
-	GG_STATE_READING_KEY,           /* czeka na klucz */
-	GG_STATE_READING_REPLY,         /* czeka na odpowiedシ */
-	GG_STATE_CONNECTED,             /* poウアczyウ 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,		/* wysyウa zapytanie http */
-	GG_STATE_READING_HEADER,	/* czeka na nagウwek http */
-	GG_STATE_PARSING,               /* przetwarza dane */
-	GG_STATE_DONE,                  /* skoczyウ */
+	/* gg_http */
+	GG_STATE_SENDING_QUERY,		/**< WysナBno 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 poウアczenia */
+	/* 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,		/* wysyウa potwierdzenie dcc */
+	GG_STATE_READING_UIN_2,		/* czeka na swテウj uin */
+	GG_STATE_SENDING_ACK,		/* wysyナB potwierdzenie dcc */
 	GG_STATE_READING_ACK,		/* czeka na potwierdzenie dcc */
-	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_REQUEST,	/* czeka na komendト */
+	GG_STATE_SENDING_REQUEST,	/* wysyナB komendト */
+	GG_STATE_SENDING_FILE_INFO,	/* wysyナB 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,	/* wysyウa potwierdzenie pliku */
+	GG_STATE_SENDING_FILE_ACK,	/* wysyナB potwierdzenie pliku */
 	GG_STATE_READING_FILE_ACK,	/* czeka na potwierdzenie pliku */
-	GG_STATE_SENDING_FILE_HEADER,	/* wysyウa nagウwek pliku */
-	GG_STATE_READING_FILE_HEADER,	/* czeka na nagウwek */
+	GG_STATE_SENDING_FILE_HEADER,	/* wysyナB nagナづウwek pliku */
+	GG_STATE_READING_FILE_HEADER,	/* czeka na nagナづウwek */
 	GG_STATE_GETTING_FILE,		/* odbiera plik */
-	GG_STATE_SENDING_FILE,		/* wysyウa plik */
+	GG_STATE_SENDING_FILE,		/* wysyナB 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,	/* wysyウa potwierdzenie voip */
-	GG_STATE_SENDING_VOICE_REQUEST,	/* wysyウa ソアdanie voip */
-	GG_STATE_READING_TYPE,		/* czeka na typ poウアczenia */
+	GG_STATE_SENDING_VOICE_ACK,	/* wysyナB potwierdzenie voip */
+	GG_STATE_SENDING_VOICE_REQUEST,	/* wysyナB ナシトdanie voip */
+	GG_STATE_READING_TYPE,		/* czeka na typ poナてczenia */
 
 	/* nowe. bez sensu jest to API. */
-	GG_STATE_TLS_NEGOTIATION	/* negocjuje poウアczenie TLS */
+	GG_STATE_TLS_NEGOTIATION,	/**< Negocjacja poナてczenia szyfrowanego */
+
+	GG_STATE_REQUESTING_ID,		/**< Oczekiwanie na nadanie identyfikatora poナてczenia bezpoナ孑edniego */
+	GG_STATE_WAITING_FOR_ACCEPT,	/**< Oczekiwanie na potwierdzenie lub odrzucenie poナてczenia bezpoナ孑edniego */
+	GG_STATE_WAITING_FOR_INFO,	/**< Oczekiwanie na informacje o poナてczeniu bezpoナ孑ednim */
+
+	GG_STATE_READING_ID,		/**< Odebranie identyfikatora poナてczenia bezpoナ孑edniego */
+	GG_STATE_SENDING_ID		/**< WysナBno identyfikatora poナてczenia bezpoナ孑edniego */
 };
 
-/*
- * 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
- * poウアczenia.
+ * \ingroup events
  */
 enum gg_check_t {
-	GG_CHECK_NONE = 0,		/* nic. nie powinno wystアpi */
-	GG_CHECK_WRITE = 1,		/* sprawdzamy moソliwoカ zapisu */
-	GG_CHECK_READ = 2		/* sprawdzamy moソliwoカ 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ナZ przeniesione
+ * do struktury, by uniknトト zmian API po rozszerzeniu protokoナV i dodaniu
+ * kolejnych opcji poナてczenia. Czト卩崙 parametrテウw, ktテウre nie sト juナシ aktualne
+ * lub nie majト znaczenia, zostaナB usuniト冲a z dokumentacji.
  *
- * parametry gg_login(). przeniesiono do struktury, ソeby uniknア problemw
- * z ciアgウymi zmianami API, gdy dodano coカ nowego do protokoウu.
+ * \ingroup login
  */
 struct gg_login_params {
-	uin_t uin;			/* numerek */
-	char *password;			/* hasウo */
-	int async;			/* asynchroniczne sockety? */
-	int status;			/* poczアtkowy 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 protokoウu */
-	char *client_version;		/* wersja klienta */
-	int has_audio;			/* czy ma dシwi麑? */
-	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ナP */
+	int async;			/**< Flaga asynchronicznego poナてczenia (domyナ嬪nie nie) */
+	int status;			/**< Poczトtkowy status uナシytkownika (domyナ嬪nie \c GG_STATUS_AVAIL) */
+	char *status_descr;		/**< Poczトtkowy opis uナシytkownika (domyナ嬪nie brak) */
+	uint32_t server_addr;		/**< Adres serwera Gadu-Gadu (domyナ嬪nie pobierany automatycznie) */
+	uint16_t server_port;		/**< Port serwera Gadu-Gadu (domyナ嬪nie pobierany automatycznie) */
+#ifndef DOXYGEN
+	uint32_t client_addr;		/**< Adres poナてczeナ bezpoナ孑ednich (nieaktualne) */
+	uint16_t client_port;		/**< Port poナてczeナ bezpoナ孑ednich (nieaktualne) */
+#endif
+	int protocol_version;		/**< Wersja protokoナV wysyナBna do serwera (domyナ嬪nie najnowsza obsナVgiwana) */
+	char *client_version;		/**< Wersja klienta wysyナBna do serwera (domyナ嬪nie najnowsza znana) */
+	int has_audio;			/**< Flaga obsナVgi poナてczeナ gナPsowych */
+	int last_sysmsg;		/**< Numer ostatnio odebranej wiadomoナ嫩i systemowej */
+	uint32_t external_addr;		/**< Adres publiczny dla poナてczeナ bezpoナ孑ednich (6.x) */
+	uint16_t external_port;		/**< Port publiczny dla poナてczeナ bezpoナ孑ednich (6.x) */
+#ifndef DOXYGEN
+	int tls;			/**< Flaga poナてczenia szyfrowanego (nieaktualna) */
+#endif
+	int image_size;			/**< Maksymalny rozmiar obsナVgiwanych obrazkテウw w kilobajtach */
+#ifndef DOXYGEN
+	int era_omnix;			/**< Flaga udawania klienta Era Omnix (nieaktualna) */
+#endif
+	int hash_type;			/**< Rodzaj skrテウtu hasナB (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domyナ嬪nie SHA1) */
+	gg_encoding_t encoding;		/**< Rodzaj kodowania uナシywanego w sesji (domyナ嬪nie CP1250) */
+	gg_resolver_t resolver;		/**< Sposテウb rozwiトzywania nazw (patrz \ref build-resolver) */
+	int protocol_features;		/**< Opcje protokoナV (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;	/* nast麪ny na liカcie */
-};
+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 wydarzyウo */
-	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 wysウania wiadomoカci */
-	GG_EVENT_PONG,			/* pakiet pong */
-	GG_EVENT_CONN_FAILED,		/* poウアczenie si nie udaウo */
-	GG_EVENT_CONN_SUCCESS,		/* poウアczenie si powiodウo */
-	GG_EVENT_DISCONNECT,		/* serwer zrywa poウアczenie */
+	GG_EVENT_NONE = 0,		/**< Nie wydarzyナP siト nic wartego uwagi */
+	GG_EVENT_MSG,			/**< \brief Otrzymano wiadomoナ崙. Przekazuje rテウwnieナシ wiadomoナ嫩i systemowe od numeru 0. */
+	GG_EVENT_NOTIFY,		/**< \brief Informacja o statusach osテウb z listy kontaktテウw (przed 6.0). Zdarzenie naleナシy obsナVgiwaト, jeナ嬪i planuje siト uナシywaト protokoナV w wersji starszej niナシ domyナ嬪na. */
+	GG_EVENT_NOTIFY_DESCR,		/**< \brief Informacja o statusie opisowym osoby z listy kontaktテウw (przed 6.0). Zdarzenie naleナシy obsナVgiwaト, jeナ嬪i planuje siト uナシywaト protokoナV w wersji starszej niナシ domyナ嬪na. */
+	GG_EVENT_STATUS,		/**< \brief Zmiana statusu osoby z listy kontaktテウw (przed 6.0). Zdarzenie naleナシy obsナVgiwaト, jeナ嬪i planuje siト uナシywaト protokoナV w wersji starszej niナシ domyナ嬪na. */
+	GG_EVENT_ACK,			/**< Potwierdzenie dorト冂zenia wiadomoナ嫩i */
+	GG_EVENT_PONG,			/**< \brief Utrzymanie poナてczenia. Obecnie serwer nie wysyナB juナシ do klienta ramek utrzymania poナてczenia, polega wyナてcznie na wysyナBniu ramek przez klienta. */
+	GG_EVENT_CONN_FAILED,		/**< \brief Nie udaナP siト poナてczyト */
+	GG_EVENT_CONN_SUCCESS,		/**< \brief Poナてczono z serwerem. Pierwszト rzeczト, jakト naleナシy zrobiト jest wysナBnie 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ナ孑ednie (6.x) */
+	GG_EVENT_DCC_ERROR,		/**< Bナてd poナてczenia bezpoナ孑edniego (6.x) */
+	GG_EVENT_DCC_DONE,		/**< Zakoナczono poナてczenie bezpoナ孑ednie (6.x) */
+	GG_EVENT_DCC_CLIENT_ACCEPT,	/**< Moment akceptacji klienta w poナてczeniu bezpoナ孑ednim (6.x) */
+	GG_EVENT_DCC_CALLBACK,		/**< Zwrotne poナてczenie bezpoナ孑ednie (6.x) */
+	GG_EVENT_DCC_NEED_FILE_INFO,	/**< Naleナシy wypeナOiト \c file_info dla poナてczenia bezpoナ孑edniego (6.x) */
+	GG_EVENT_DCC_NEED_FILE_ACK,	/**< Czeka na potwierdzenie pliku w poナてczeniu bezpoナ孑ednim (6.x) */
+	GG_EVENT_DCC_NEED_VOICE_ACK,	/**< Czeka na potwierdzenie rozmowy w poナてczeniu bezpoナ孑ednim (6.x) */
+	GG_EVENT_DCC_VOICE_DATA, 	/**< Dane bezpoナ孑edniego poナてczenia gナPsowego (6.x) */
 
-	GG_EVENT_DCC_NEW,		/* nowe poウアczenie mi鹽zy klientami */
-	GG_EVENT_DCC_ERROR,		/* bウアd poウアczenia mi鹽zy klientami */
-	GG_EVENT_DCC_DONE,		/* zakoczono poウアczenie */
-	GG_EVENT_DCC_CLIENT_ACCEPT,	/* moment akceptacji klienta */
-	GG_EVENT_DCC_CALLBACK,		/* klient si poウアczyウ na ソアdanie */
-	GG_EVENT_DCC_NEED_FILE_INFO,	/* naleソy wypeウni 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 gウosowej */
+	GG_EVENT_PUBDIR50_SEARCH_REPLY,	/**< Odpowiedナコ katalogu publicznego */
+	GG_EVENT_PUBDIR50_READ,		/**< Odczytano wナBsne dane z katalogu publicznego */
+	GG_EVENT_PUBDIR50_WRITE,	/**< Zmieniono wナBsne 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ナBnia obrazka z wiadommoナ嫩i */
+	GG_EVENT_IMAGE_REPLY,		/**< PrzysナBno obrazek z wiadomoナ嫩i */
+	GG_EVENT_DCC_ACK,		/**< Potwierdzenie transmisji w poナてczeniu bezpoナ孑ednim (6.x) */
 
-	GG_EVENT_PUBDIR50_SEARCH_REPLY,	/* odpowiedz wyszukiwania */
-	GG_EVENT_PUBDIR50_READ,		/* odczytano wウasne dane z katalogu */
-	GG_EVENT_PUBDIR50_WRITE,	/* wpisano wウasne dane do katalogu */
+	GG_EVENT_DCC7_NEW,		/**< Nowe poナてczenie bezpoナ孑ednie (7.x) */
+	GG_EVENT_DCC7_ACCEPT,		/**< Zaakceptowano poナてczenie bezpoナ孑ednie (7.x), nowy deskryptor */
+	GG_EVENT_DCC7_REJECT,		/**< Odrzucono poナてczenie bezpoナ孑ednie (7.x) */
+	GG_EVENT_DCC7_CONNECTED,	/**< Zestawiono poナてczenie bezpoナ孑ednie (7.x), nowy deskryptor */
+	GG_EVENT_DCC7_ERROR,		/**< Bナてd poナてczenia bezpoナ孑edniego (7.x) */
+	GG_EVENT_DCC7_DONE,		/**< Zakoナczono poナてczenie bezpoナ孑ednie (7.x) */
+	GG_EVENT_DCC7_PENDING,		/**< Trwa prテウba poナてczenia bezpoナ孑edniego (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,		/* proカba o wysウanie obrazka GG 6.0 */
-	GG_EVENT_IMAGE_REPLY,		/* podesウany 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ト冪ny z opisem dotarナB do serwera i moナシna zakoナczyト poナてczenie TCP. */
 };
 
 #define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
 
-/*
- * enum gg_failure_t
- *
- * okreカla powd nieudanego poウアczenia.
+/**
+ * Powテウd nieudanego poナてczenia.
  */
 enum gg_failure_t {
-	GG_FAILURE_RESOLVING = 1,	/* nie znaleziono serwera */
-	GG_FAILURE_CONNECTING,		/* nie moソna si poウアczy */
-	GG_FAILURE_INVALID,		/* serwer zwrciウ 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, 		/* XXX nieuソywane */
-	GG_FAILURE_TLS,			/* bウアd negocjacji TLS */
-	GG_FAILURE_NEED_EMAIL 		/* serwer rozウアczyウ nas z proカbア o zmian emaila */
+	GG_FAILURE_RESOLVING = 1,	/**< Nie znaleziono serwera */
+	GG_FAILURE_CONNECTING,		/**< Bナてd poナてczenia */
+	GG_FAILURE_INVALID,		/**< Serwer zwrテウciナ nieprawidナPwe dane */
+	GG_FAILURE_READING,		/**< Zerwano poナてczenie podczas odczytu */
+	GG_FAILURE_WRITING,		/**< Zerwano poナてczenie podczas zapisu */
+	GG_FAILURE_PASSWORD,		/**< NieprawidナPwe hasナP */
+	GG_FAILURE_404, 		/**< Nieuナシywane */
+	GG_FAILURE_TLS,			/**< Bナてd negocjacji szyfrowanego poナてczenia */
+	GG_FAILURE_NEED_EMAIL, 		/**< Serwer rozナてczyナ nas z proナ嫦ト o zmianト adresu e-mail */
+	GG_FAILURE_INTRUDER,		/**< Zbyt wiele prテウb poナてczenia z nieprawidナPwym hasナFm */
+	GG_FAILURE_UNAVAILABLE		/**< Serwery sト wyナてczone */
 };
 
-/*
- * enum gg_error_t
+/**
+ * Kod bナて囘u danej operacji.
  *
- * okreカla rodzaj bウ鹽u wywoウanego przez danア operacj. nie zawiera
- * przesadnie szczegウowych informacji o powodzie bウ鹽u, by nie komplikowa
- * obsウugi bウ鹽w. jeカli wymagana jest wi麑sza dokウadnoカ, naleソy sprawdzi
- * zawartoカ zmiennej errno.
+ * Nie zawiera przesadnie szczegテウナPwych informacji o powodach bナて囘テウw, by nie
+ * komplikowaト ich obsナVgi. Jeナ嬪i wymagana jest wiト冖sza dokナBdnoナ崙, naleナシy
+ * sprawdziト zawartoナ崙 zmiennej systemowej \c errno.
  */
 enum gg_error_t {
-	GG_ERROR_RESOLVING = 1,		/* bウアd znajdowania hosta */
-	GG_ERROR_CONNECTING,		/* bウアd ウaczenia si */
-	GG_ERROR_READING,		/* bウアd odczytu */
-	GG_ERROR_WRITING,		/* bウアd wysyウania */
+	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ナBnia */
 
-	GG_ERROR_DCC_HANDSHAKE,		/* bウアd negocjacji */
-	GG_ERROR_DCC_FILE,		/* bウアd odczytu/zapisu pliku */
-	GG_ERROR_DCC_EOF,		/* plik si skoczyウ? */
-	GG_ERROR_DCC_NET,		/* bウアd wysyウania/odbierania */
-	GG_ERROR_DCC_REFUSED 		/* poウアczenie 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ナBnia/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ナBnia/odbierania */
+	GG_ERROR_DCC7_REFUSED 		/**< Poナてczenie odrzucone */
 };
 
-/*
- * struktury dotyczアce wyszukiwania w GG 5.0. NIE NALEッY SIハ DO NICH
- * ODWO」YWAニ BEZPOヲREDNIO! do dost麪u do nich sウuソア 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ト冪nego 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 opisujアcy zapytanie lub wynik zapytania katalogu publicznego
- * z protokoウu GG 5.0. nie naleソy si odwoウywa bezpoカrednio do jego
- * pl -- sウuソア do tego funkcje gg_pubdir50_*()
+ * Do pテウl nie naleナシy siト odwoナZwaト bezpoナ孑ednio -- wszystkie niezbト囘ne
+ * informacje sト dostト冪ne 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ナ嫩i */
+	time_t time;		/**< Czas nadania */
+	unsigned char *message;	/**< Treナ崙 wiadomoナ嫩i */
+
+	int recipients_count;	/**< Liczba odbiorcテウw konferencji */
+	uin_t *recipients;	/**< Odbiorcy konferencji */
+
+	int formats_length;	/**< DナVgoナ崙 informacji o formatowaniu tekstu */
+	void *formats;		/**< Informacje o formatowaniu tekstu */
+	uint32_t seq;		/**< Numer sekwencyjny wiadomoナ嫩i */
+
+	char *xhtml_message;	/**< Treナ崙 wiadomoナ嫩i w formacie XHTML (moナシe byト rテウwne \c NULL, jeナ嬪i wiadomoナ崙 nie zawiera treナ嫩i XHTML) */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR.
+ */
+struct gg_event_notify_descr {
+	struct gg_notify_reply *notify;	/**< Informacje o liナ嫩ie 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ナ孑ednich */
+	uint16_t remote_port;	/**< Port dla poナてczeナ bezpoナ孑ednich */
+	int version;		/**< Wersja protokoナV */
+	int image_size;		/**< Maksymalny rozmiar obsナVgiwanych 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ナ孑ednich */
+	uint16_t remote_port;	/**< Port dla poナてczeナ bezpoナ孑ednich */
+	int version;		/**< Wersja protokoナV */
+	int image_size;		/**< Maksymalny rozmiar obsナVgiwanych 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ト冂zenia */
+	int seq;		/**< Numer sekwencyjny wiadomoナ嫩i */
+};
+
+/**
+ * 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ト冖owe */
+	int length;		/**< Rozmiar danych dナコwiト冖owych */
+};
+
+/**
+ * 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 opisujアca 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ナ嫩i (\c GG_EVENT_ACK) */
+	struct gg_event_image_request image_request;	/**< ナサトdanie wysナBnia 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ナ孑ednie (\c GG_EVENT_DCC_NEW) */
+	enum gg_error_t dcc_error;	/**< Bナてd poナてczenia bezpoナ孑edniego (\c GG_EVENT_DCC_ERROR) */
+	struct gg_event_dcc_voice_data dcc_voice_data;	/**< Dane poナてczenia gナPsowego (\c GG_EVENT_DCC_VOICE_DATA) */
+	struct gg_dcc7 *dcc7_new;	/**< Nowe poナてczenie bezpoナ孑ednie (\c GG_EVENT_DCC7_NEW) */
+	enum gg_error_t dcc7_error;	/**< Bナてd poナてczenia bezpoナ孑edniego (\c GG_EVENT_DCC7_ERROR) */
+	struct gg_event_dcc7_connected dcc7_connected;	/**< Informacja o zestawieniu poナてczenia bezpoナ孑edniego (\c GG_EVENT_DCC7_CONNECTED) */
+	struct gg_event_dcc7_reject dcc7_reject;	/**< Odrzucono poナてczenia bezpoナ孑edniego (\c GG_EVENT_DCC7_REJECT) */
+	struct gg_event_dcc7_accept dcc7_accept;	/**< Zaakceptowano poナてczenie bezpoナ孑ednie (\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 liカcie kontaktw -- GG_EVENT_NOTIFY */
-
-		enum gg_failure_t failure;	/* bウアd poウアczenia -- GG_EVENT_FAILURE */
-
-		struct gg_dcc *dcc_new;		/* nowe poウアczenie bezpoカrednie -- GG_EVENT_DCC_NEW */
-
-		int dcc_error;			/* bウアd poウアczenia bezpoカredniego -- GG_EVENT_DCC_ERROR */
-
-		gg_pubdir50_t pubdir50;		/* wynik operacji zwiアzanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */
-
-		struct {			/* @msg odebrano wiadomoカ -- 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;	/* iloカ odbiorcw konferencji */
-			uin_t *recipients;	/* odbiorcy konferencji */
-
-			int formats_length;	/* dウugoカ informacji o formatowaniu tekstu */
-			void *formats;		/* informacje o formatowaniu tekstu */
-		} msg;
-
-		struct {			/* @notify_descr informacje o liカcie kontaktw z opisami stanu -- GG_EVENT_NOTIFY_DESCR */
-			struct gg_notify_reply *notify;	/* informacje o liカcie 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 liカcie 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 wiadomoカci -- GG_EVENT_ACK */
-			uin_t recipient;	/* numer odbiorcy */
-			int status;		/* stan dor鹹zenia wiadomoカci */
-			int seq;		/* numer sekwencyjny wiadomoカci */
-		} ack;
-
-		struct {			/* @dcc_voice_data otrzymano dane dシwi麑owe -- GG_EVENT_DCC_VOICE_DATA */
-			uint8_t *data;		/* dane dシwi麑owe */
-			int length;		/* iloカ danych dシwi麑owych */
-		} dcc_voice_data;
-
-		struct {			/* @userlist odpowiedシ listy kontaktw serwera */
-			char type;		/* rodzaj odpowiedzi */
-			char *reply;		/* treカ odpowiedzi */
-		} userlist;
-
-		struct {			/* @image_request proカba o obrazek */
-			uin_t sender;		/* nadawca proカby */
-			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 obsウugi 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 obsウugi 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 opisujアca kryteria wyszukiwania dla gg_search(). nieaktualne,
- * zastアpione 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 obsウugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje
- * zachowujア pewien poziom abstrakcji, ソeby uniknア zmian ABI przy zmianach
- * w protokole.
- *
- * NIE NALEッY SIハ ODWO」YWAニ DO PモL gg_pubdir50_t BEZPOヲREDNIO!
- */
 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 udaウo */
-	uin_t uin;		/* otrzymany numerek. 0 jeカli bウアd */
+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ナFト */
+	GG_PUBDIR50_ACTIVE,	/**< Osoba dostト冪na (tylko wyszukiwanie) */
+	GG_PUBDIR50_START,	/**< Numer poczトtkowy wyszukiwania (tylko wyszukiwanie) */
+	GG_PUBDIR50_FAMILYNAME,	/**< Nazwisko rodowe (tylko wysyナBnie informacji o sobie) */
+	GG_PUBDIR50_FAMILYCITY,	/**< Miejscowoナ崙 pochodzenia (tylko wysyナBnie informacji o sobie) */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartoナ崙 pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolnト pナFト.
+ */
+enum {
+	GG_PUBDIR50_GENDER_FEMALE,	/**< Kobieta */
+	GG_PUBDIR50_GENDER_MALE,	/**< Mト卩シczyzna */
 };
 
-/* oglne funkcje, nie powinny by uソywane */
+/**
+ * \ingroup pubdir50
+ *
+ * Wartoナ崙 pola GG_PUBDIR50_GENDER przy wysyナBniu 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ト冪ne */
+};
+
+#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ナて囘u */
+};
+
 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 dotyczアce 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 hasウa 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 hasウa */
-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;		/* pウe */
-	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 dotyczアce 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 dotyczアce komunikacji mi鹽zy klientami.
- */
-extern int gg_dcc_port;			/* port, na ktrym nasウuchuje klient */
-extern unsigned long gg_dcc_ip;		/* adres, na ktrym nasウuchuje 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ナPsowego przed wersjト Gadu-Gadu 5.0.5 */
+#define GG_DCC_VOICE_FRAME_LENGTH_505 326	/**< Rozmiar pakietu gナPsowego 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
-
-/*
- * jeカli chcemy sobie podebugowa, wystarczy ustawi `gg_debug_level'.
- * niestety w miar przybywania wpisw `gg_debug(...)' nie chciaウo mi
- * si ustawia odpowiednich leveli, wi鹹 wi麑szoカ szウa do _MISC.
- */
-extern int gg_debug_level;	/* poziom debugowania. mapa bitowa staウych GG_DEBUG_* */
 
-/*
- * moソna poda wskaシnik do funkcji obsウugujアcej wywoウania gg_debug().
- * nieoficjalne, nieudokumentowane, moソe si zmieni. jeカli 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);
 
-/*
- * moソna poda plik, do ktrego b鹽ア 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ナ嫩i pakietテウw */
+#define GG_DEBUG_FUNCTION 8	/**< Rejestracja wywoナBナ 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;		/* wウアcza obsウug proxy */
-extern char *gg_proxy_host;		/* okreカla adres serwera proxy */
-extern int gg_proxy_port;		/* okreカla port serwera proxy */
-extern char *gg_proxy_username;		/* okreカla nazw uソytkownika przy autoryzacji serwera proxy */
-extern char *gg_proxy_password;		/* okreカla hasウo uソytkownika przy autoryzacji serwera proxy */
-extern int gg_proxy_http_only;		/* wウアcza obsウug proxy wyウアcznie dla usウug 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)
- * uソywany 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;
-/*
- * -------------------------------------------------------------------------
- * poniソej znajdujア si wewn黎rzne sprawy biblioteki. zwykウy klient nie
- * powinien ich w ogle rusza, bo i nie ma po co. wszystko moソna zaウatwi
- * procedurami wyソszego poziomu, ktrych definicje znajdujア si na poczアtku
- * tego pliku.
- * -------------------------------------------------------------------------
- */
+enum {
+	GG_PUBDIR50_WRITE,	/**< WysナBnie 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ナZ zachowane dla zgodnoナ嫩i 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ナV.
+ */
+enum {
+	GG_FEATURE_MSG77,	/**< Klient ナシyczy sobie otrzymywaト wiadomoナ嫩i zgodnie z protokoナFm 7.7 */
+	GG_FEATURE_STATUS77,	/**< Klient ナシyczy sobie otrzymywaト zmiany stanu zgodnie z protokoナFm 7.7 */
+	GG_FEATURE_DND_FFC,	/**< Klient obsナVguje statusy "nie przeszkadzaト" i "poGGadaj ze mnト" */
+	GG_FEATURE_IMAGE_DESCR,	/**< Klient obsナVguje 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;		/* dウugoカ reszty pakietu */
+	uint32_t length;		/* dナVgoナ崙 reszty pakietu */
 } GG_PACKED;
 
 #define GG_WELCOME 0x0001
 #define GG_NEED_EMAIL 0x0014
 
 struct gg_welcome {
-	uint32_t key;			/* klucz szyfrowania hasウa */
+	uint32_t key;			/* klucz szyfrowania hasナB */
 } GG_PACKED;
 
 #define GG_LOGIN 0x000c
 
 struct gg_login {
-	uint32_t uin;			/* mj numerek */
-	uint32_t hash;			/* hash hasウa */
-	uint32_t status;		/* status na dzie dobry */
+	uint32_t uin;			/* mテウj numerek */
+	uint32_t hash;			/* hash hasナB */
+	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 sウucham */
+	uint32_t local_ip;		/* mテウj adres ip */
+	uint16_t local_port;		/* port, na ktテウrym sナVcham */
 } GG_PACKED;
 
 #define GG_LOGIN_EXT 0x0013
 
 struct gg_login_ext {
-	uint32_t uin;			/* mj numerek */
-	uint32_t hash;			/* hash hasウa */
-	uint32_t status;		/* status na dzie dobry */
+	uint32_t uin;			/* mテウj numerek */
+	uint32_t hash;			/* hash hasナB */
+	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 sウucham */
-	uint32_t external_ip;		/* zewn黎rzny adres ip */
-	uint16_t external_port;		/* zewn黎rzny port */
+	uint32_t local_ip;		/* mテウj adres ip */
+	uint16_t local_port;		/* port, na ktテウrym sナVcham */
+	uint32_t external_ip;		/* zewnト冲rzny adres ip */
+	uint16_t external_port;		/* zewnト冲rzny port */
 } GG_PACKED;
 
 #define GG_LOGIN60 0x0015
 
 struct gg_login60 {
-	uint32_t uin;			/* mj numerek */
-	uint32_t hash;			/* hash hasウa */
-	uint32_t status;		/* status na dzie dobry */
+	uint32_t uin;			/* mテウj numerek */
+	uint32_t hash;			/* hash hasナB */
+	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 sウucham */
-	uint32_t external_ip;		/* zewn黎rzny adres ip */
-	uint16_t external_port;		/* zewn黎rzny port */
+	uint32_t local_ip;		/* mテウj adres ip */
+	uint16_t local_port;		/* port, na ktテウrym sナVcham */
+	uint32_t external_ip;		/* zewnト冲rzny adres ip */
+	uint16_t external_port;		/* zewnト冲rzny 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ナB */
+	uint8_t hash[64];		/* hash hasナB dopeナOiony 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ナVcham */
+	uint32_t external_ip;		/* zewnト冲rzny adres ip (???) */
+	uint16_t external_port;		/* zewnト冲rzny 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 wysウania zapytania */
+	uint32_t seq;			/* czas wysナBnia zapytania */
 } GG_PACKED;
 
 #define GG_PUBDIR50_REPLY 0x000e
 
 struct gg_pubdir50_reply {
 	uint8_t type;			/* GG_PUBDIR50_* */
-	uint32_t seq;			/* czas wysウania zapytania */
+	uint32_t seq;			/* czas wysナBnia zapytania */
 } GG_PACKED;
 
 #define GG_NEW_STATUS 0x0002
 
-#define GG_STATUS_NOT_AVAIL 0x0001		/* niedost麪ny */
-#define GG_STATUS_NOT_AVAIL_DESCR 0x0015	/* niedost麪ny z opisem (4.8) */
-#define GG_STATUS_AVAIL 0x0002			/* dost麪ny */
-#define GG_STATUS_AVAIL_DESCR 0x0004		/* dost麪ny z opisem (4.9) */
-#define GG_STATUS_BUSY 0x0003			/* zaj黎y */
-#define GG_STATUS_BUSY_DESCR 0x0005		/* zaj黎y 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ト冪ny */
+	GG_STATUS_NOT_AVAIL_DESCR,	/**< Niedostト冪ny z opisem */
+	GG_STATUS_FFC,			/**< PoGGadaj ze mnト */
+	GG_STATUS_FFC_DESCR,		/**< PoGGadaj ze mnト z opisem */
+	GG_STATUS_AVAIL,		/**< Dostト冪ny */
+	GG_STATUS_AVAIL_DESCR,		/**< Dostト冪ny z opisem */
+	GG_STATUS_BUSY,			/**< Zajト冲y */
+	GG_STATUS_BUSY_DESCR,		/**< Zajト冲y z opisem */
+	GG_STATUS_DND,			/**< Nie przeszkadzaト */
+	GG_STATUS_DND_DESCR,		/**< Nie przeszakdzaト z opisem */
+	GG_STATUS_INVISIBLE,		/**< Niewidoczny (tylko wナBsny status) */
+	GG_STATUS_INVISIBLE_DESCR,	/**< Niewidoczny z opisem (tylko wナBsny status) */
+	GG_STATUS_BLOCKED,		/**< Zablokowany (tylko status innych) */
+	GG_STATUS_IMAGE_MASK,		/**< Flaga bitowa oznaczajトca opis graficzny (tylko jeナ嬪i wybrano \c GG_FEATURE_IMAGE_DESCR) */
+	GG_STATUS_DESCR_MASK,		/**< Flaga bitowa oznaczajトca status z opisem (tylko jeナ嬪i wybrano \c GG_FEATURE_IMAGE_DESCR) */
+	GG_STATUS_FRIENDS_MASK,		/**< Flaga bitowa dostト冪noナ嫩i 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ナVgoナ嫩 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 uwzgl鹽nienia trybu tylko dla znajomych */
-#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK)
+/* GG_S() stan bez uwzglト囘nienia dodatkowych flag */
+#define GG_S(x) ((x) & GG_STATUS_MASK)
 
-/* GG_S_A() dost麪ny */
-#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_FF() chト冲ny do rozmowy */
+#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR)
 
-/* GG_S_NA() niedost麪ny */
-#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
+/* GG_S_AV() dostト冪ny */
+#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() zaj黎y */
-#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ト冪ny */
+#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ト冪ny lub chト冲ny do rozmowy */
+#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x))
+
+/* GG_S_B() zajト冲y 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 blokujアcy */
+/* 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 liカcie */
+	uint8_t dunno1;				/* rodzaj wpisu w liナ嫩ie */
 } GG_PACKED;
 
-#define GG_USER_OFFLINE 0x01	/* b鹽ziemy niewidoczni dla uソytkownika */
-#define GG_USER_NORMAL 0x03	/* zwykウy uソytkownik */
-#define GG_USER_BLOCKED 0x04	/* zablokowany uソytkownik */
+#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ナZ 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 sウucha klient */
+	uint16_t remote_port;		/* port, na ktテウrym sナVcha 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 sウucha klient */
+	uint16_t remote_port;		/* port, na ktテウrym sナVcha 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 sウucha klient */
+	uint16_t remote_port;		/* port, na ktテウrym sナVcha 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ナVcha 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ナVcha 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ナ嫩i wstecz */
+
+#else
 
-#define GG_MSG_MAXSIZE 2000
+/**
+ * Klasy wiadomoナ嫩i. Wartoナ嫩i sト maskami bitowymi, ktテウre w wiト冖szoナ嫩i
+ * 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ナVgoナ崙 wiadomoナ嫩i.
+ *
+ * \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ナ嫩i od wartoナ嫩i 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ナ嫩i.
+ *
+ * \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ナBdowa czerwona koloru */
+	uint8_t green;		/**< SkナBdowa zielona koloru */
+	uint8_t blue;		/**< SkナBdowa niebieska koloru */
+} GG_PACKED;
+
+/**
+ * Strukturya opisujトca obrazek wstawiony do wiadomoナ嫩i dla atrubutu
+ * \c GG_FONT_IMAGE.
+ *
+ * \ingroup messages
+ */
+struct gg_msg_richtext_image {
+	uint16_t unknown1;	/**< Nieznane pole o wartoナ嫩i 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ト冂zenia wiadomoナ嫩i.
+ *
+ * \ingroup messages
+ */
+enum
+{
+	GG_ACK_DELIVERED,	/**< Wiadomoナ崙 dostarczono. */
+	GG_ACK_QUEUED,		/**< Wiadomoナ崙 zakolejkowano z powodu niedostト冪noナ嫩i odbiorcy. */
+	GG_ACK_BLOCKED,		/**< Wiadomoナ崙 zablokowana przez serwer (spam, ナ孩iトteczne ograniczenia itd.) */
+	GG_ACK_MBOXFULL,	/**< Wiadomoナ嫩i nie dostarczono z powodu zapeナOionej kolejki wiadomoナ嫩i odbiorcy. */
+	GG_ACK_NOT_DELIVERED	/**< Wiadomoナ嫩i 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, staウe, 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 dokウadnie protokoウu. nie wiemy, co czemu odpowiada.
- * nazwy sア niepowaソne i tymczasowe.
+ * pテウki co, nie znamy dokナBdnie protokoナV. 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		/* wi鹹 mu damy */
+#define GG_DCC_HAVE_FILE 0x0001		/* wiト冂 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	/* wysyウamy plik */
+#define GG_DCC_CATCH_FILE 0x0002	/* wysyナBmy 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ナ孑ednie */
+#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ナ孑ednie juナシ trwa, nie umiem obsナVナシyト wiト冂ej */
+#define GG_DCC7_REJECT_USER 0x00000002	/**< Uナシytkownik odrzuciナ poナてczenie */
+#define GG_DCC7_REJECT_VERSION 0x00000006	/**< Druga strona ma wersjト klienta nieobsナVgujトcト poナてczeナ bezpoナ孑ednich 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ナPsu */
+#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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/obsolete.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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 wzgl鹽u
- * 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ト囘u
+ * na zmiany w protokole. Programy konsolidowane ze starszych wersjami
+ * bibliotek powinny nadal mieト moナシliwoナ崙 dziaナBnia, mimo ograniczonej
+ * funkcjonalnoナ嫩i.
  */
 
+/** \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	Tue Mar 16 12:07:06 2010 +0900
@@ -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ト凛yk: GG8_LANG */
+	uint8_t hash_type;		/* rodzaj hashowania hasナB */
+	uint8_t hash[64];		/* hash hasナB dopeナOiony zerami */
+	uint32_t status;		/* status na dzieナ dobry */
+	uint32_t flags;			/* flagi (przeznaczenie nieznane) */
+	uint32_t features;		/* opcje protokoナV (GG8_FEATURES) */
+	uint32_t local_ip;		/* mテウj adres ip */
+	uint16_t local_port;		/* port, na ktテウrym sナVcham */
+	uint32_t external_ip;		/* zewnト冲rzny adres ip (???) */
+	uint16_t external_port;		/* zewnト冲rzny 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ナ孑ednich poナてczeナ */
+	uint16_t remote_port;	/* port bezpoナ孑ednich 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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/pubdir.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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ナVga 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 uソytkownika protokoウem GG 6.0. wymaga wczeカniejszego
- * pobrania tokenu za pomocア funkcji gg_token().
+ * Wymaga wczeナ嬾iejszego pobrania tokenu za pomocト \c gg_token().
  *
- *  - email - adres e-mail klienta
- *  - password - hasウo klienta
- *  - tokenid - identyfikator tokenu
- *  - tokenval - wartoカ tokenu
- *  - async - poウアczenie asynchroniczne
+ * \param email Adres e-mail
+ * \param password HasナP
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartoナ崙 tokenu
+ * \param async Flaga poナてczenia asynchronicznego
  *
- * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni
- * funkcjア gg_register_free(), albo NULL jeカli wystアpiウ bウアd.
+ * \return Struktura \c gg_http lub \c NULL w przypadku bナて囘u
+ *
+ * \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ナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia.
+ *
+ * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE.
+ * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u
+ * znajdzie siト w polu \c error.
+ *
+ * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * usuwa konto uソytkownika z serwera protokoウem GG 6.0
+ * \param h Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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 - hasウo klienta
- *  - tokenid - identyfikator tokenu
- *  - tokenval - wartoカ tokenu
- *  - async - poウアczenie asynchroniczne
+ * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura poナてczenia
  *
- * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni
- * funkcjア gg_unregister_free(), albo NULL jeカli wystアpiウ bウアd.
+ * \ingroup register
+ */
+void gg_register_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Usuwa uナシytkownika.
+ *
+ * Wymaga wczeナ嬾iejszego pobrania tokenu za pomocト \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param password HasナP
+ * \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ナて囘u
+ *
+ * \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ナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia.
+ *
+ * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE.
+ * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u
+ * znajdzie siト w polu \c error.
+ *
+ * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura poナてczenia
  *
- * wysyウa ソアdanie zmiany hasウa zgodnie z protokoウem GG 6.0. wymaga
- * wczeカniejszego pobrania tokenu za pomocア funkcji gg_token().
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \ingroup unregister
+ */
+int gg_unregister_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do \c gg_pubdir_free().
  *
- *  - uin - numer
- *  - email - adres e-mail
- *  - passwd - stare hasウo
- *  - newpasswd - nowe hasウo
- *  - tokenid - identyfikator tokenu
- *  - tokenval - wartoカ tokenu
- *  - async - poウアczenie 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ナP uナシytkownika.
  *
- * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni
- * funkcjア gg_change_passwd_free(), albo NULL jeカli wystアpiウ bウアd.
+ * Wymaga wczeナ嬾iejszego pobrania tokenu za pomocト \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail
+ * \param passwd Obecne hasナP
+ * \param newpasswd Nowe hasナP
+ * \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ナて囘u
+ *
+ * \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ナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia.
+ *
+ * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE.
+ * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u
+ * znajdzie siト w polu \c error.
+ *
+ * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * wysyウa ソアdanie przypomnienia hasウa e-mailem.
+ * \param h Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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 - poウアczenie asynchroniczne
- *  - tokenid - identyfikator tokenu
- *  - tokenval - wartoカ tokenu
+ * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura poナてczenia
  *
- * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni
- * funkcjア gg_remind_passwd_free(), albo NULL jeカli wystアpiウ bウアd.
+ * \ingroup passwd
+ */
+void gg_change_passwd_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * WysyナB hasナP uナシytkownika na e-mail.
+ *
+ * Wymaga wczeナ嬾iejszego 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ナて囘u
+ *
+ * \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ナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia.
  *
- * przy asynchronicznych operacjach na katalogu publicznym naleソy wywoウywa
- * t funkcj przy zmianach na obserwowanym deskryptorze.
+ * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE.
+ * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u
+ * znajdzie siト w polu \c error.
+ *
+ * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  *
- *  - h - struktura opisujアca poウアczenie
+ * \ingroup remind
+ */
+int gg_remind_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura poナてczenia
  *
- * jeカli wszystko poszウo dobrze to 0, inaczej -1. operacja b鹽zie
- * zakoczona, jeカli h->state == GG_STATE_DONE. jeカli wystアpi jakiカ
- * bウアd, to b鹽zie tam GG_STATE_ERROR i odpowiedni kod bウ鹽u w h->error.
+ * \ingroup remind
+ */
+void gg_remind_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia.
+ *
+ * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE.
+ * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u
+ * znajdzie siト w polu \c error.
+ *
+ * \param h Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  */
 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ト囘ny do tworzenia nowego i usuwania uナシytkownika,
+ * zmiany hasナB itd.
  *
- * pobiera z serwera token do autoryzacji zakウadania konta, usuwania
- * konta i zmiany hasウa.
+ * \param async Flaga poナてczenia asynchronicznego
  *
- * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni
- * funkcjア gg_token_free(), albo NULL jeカli wystアpiウ bウアd.
+ * \return Struktura \c gg_http lub \c NULL w przypadku bナて囘u
+ *
+ * \ingroup token
  */
 struct gg_http *gg_token(int async)
 {
@@ -511,17 +681,18 @@
 	return h;
 }
 
-/*
- * gg_token_watch_fd()
- *
- * przy asynchronicznych operacjach zwiアzanych z tokenem naleソy wywoウywa
- * t funkcj przy zmianach na obserwowanym deskryptorze.
+/**
+ * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia.
  *
- *  - h - struktura opisujアca poウアczenie
+ * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE.
+ * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u
+ * znajdzie siト w polu \c error.
  *
- * jeカli wszystko poszウo dobrze to 0, inaczej -1. operacja b鹽zie
- * zakoczona, jeカli h->state == GG_STATE_DONE. jeカli wystアpi jakiカ
- * bウアd, to b鹽zie tam GG_STATE_ERROR i odpowiedni kod bウ鹽u w h->error.
+ * \param h Struktura poナてczenia
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \ingroup token
  */
 int gg_token_watch_fd(struct gg_http *h)
 {
@@ -547,8 +718,8 @@
 	if (h->state != GG_STATE_PARSING)
 		return 0;
 	
-	/* 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
+	/* jeナ嬪i h->data jest puste, to ナ嫩iトgaliナ嬶y tokenid i url do niego,
+	 * ale jeナ嬪i 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;
 		}
 		
-		/* dostaliカmy tokenid i wszystkie niezb鹽ne informacje,
-		 * wi鹹 pobierzmy obrazek z tokenem */
+		/* dostaliナ嬶y tokenid i wszystkie niezbト囘ne informacje,
+		 * wiト冂 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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/gg/lib/pubdir50.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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,11 +14,15 @@
  *
  *  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ナVga katalogu publicznego od wersji Gadu-Gadu 5.x
+ */
 
 #include <errno.h>
 #include <stdlib.h>
@@ -26,12 +30,17 @@
 #include <time.h>
 #include <glib.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 pami鹹i.
+ * \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku bナて囘u.
+ *
+ * \ingroup pubdir50
  */
 gg_pubdir50_t gg_pubdir50_new(int type)
 {
@@ -51,17 +60,16 @@
 	return res;
 }
 
-/*
- * gg_pubdir50_add_n()  // funkcja wewn黎rzna
- *
- * funkcja dodaje lub zast麪uje istniejアce pole do zapytania lub odpowiedzi.
+/**
+ * \internal Dodaje lub zastト冪uje pole zapytania lub odpowiedzi katalogu
+ * publicznego.
  *
- *  - req - wskaシnik 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  */
 static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
 {
@@ -111,31 +119,31 @@
 	return 0;
 }
 
-/*
- * gg_pubdir50_add()
- *
- * funkcja dodaje pole do zapytania.
+/**
+ * Dodaje pole zapytania.
  *
- *  - req - wskaシnik opisu zapytania,
- *  - field - nazwa pola,
- *  - value - wartoカ pola,
+ * \param req Zapytanie
+ * \param field Nazwa pola
+ * \param value Wartoナ崙 pola
  *
- * 0/-1
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ *
+ * \ingroup pubdir50
  */
 int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
 {
@@ -152,12 +160,12 @@
 	return 0;
 }
 
-/*
- * gg_pubdir50_free()
+/**
+ * Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego.
  *
- * zwalnia pami po zapytaniu lub rezultacie szukania uソytkownika.
+ * \param s Zapytanie lub odpowiedナコ
  *
- *  - s - zwalniana zmienna,
+ * \ingroup pubdir50
  */
 void gg_pubdir50_free(gg_pubdir50_t s)
 {
@@ -175,15 +183,15 @@
 	free(s);
 }
 
-/*
- * gg_pubdir50()
- *
- * wysyウa zapytanie katalogu publicznego do serwera.
+/**
+ * WysyナB zapytanie katalogu publicznego do serwera.
  *
- *  - sess - sesja,
- *  - req - zapytanie.
+ * \param sess Struktura sesji
+ * \param req Zapytanie
  *
- * numer sekwencyjny wyszukiwania lub 0 w przypadku bウ鹽u.
+ * \return Numer sekwencyjny zapytania lub 0 w przypadku bナて囘u
+ *
+ * \ingroup pubdir50
  */
 uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
 {
@@ -192,16 +200,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;
 	}
@@ -211,30 +219,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)
@@ -246,26 +305,26 @@
 }
 
 /*
- * gg_pubdir50_handle_reply()  // funkcja wewn黎rzna
- *
- * analizuje przychodzアcy 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 - dウugoカ pakietu odpowiedzi
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param packet Pakiet odpowiedzi
+ * \param length DナVgoナ崙 pakietu odpowiedzi
  *
- * 0/-1
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
  */
-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;
@@ -300,11 +359,11 @@
 			break;
 	}
 
-	/* brak wynikw? */
+	/* brak wynikテウw? */
 	if (length == 5)
 		return 0;
 
-	/* pomi poczアtek odpowiedzi */
+	/* pomiナ poczトtek odpowiedzi */
 	p = packet + 5;
 
 	while (p < end) {
@@ -312,7 +371,7 @@
 
 		field = p;
 
-		/* sprawdシ, czy nie mamy podziaウu na kolejne pole */
+		/* sprawdナコ, czy nie mamy podziaナV na kolejne pole */
 		if (!*field) {
 			num++;
 			field++;
@@ -321,22 +380,22 @@
 		value = NULL;
 		
 		for (p = field; p < end; p++) {
-			/* jeカli mamy koniec tekstu... */
+			/* jeナ嬪i mamy koniec tekstu... */
 			if (!*p) {
-				/* ...i jeszcze nie mieliカmy wartoカci pola to
-				 * wiemy, ソe po tym zerze jest wartoカ... */
+				/* ...i jeszcze nie mieliナ嬶y wartoナ嫩i pola to
+				 * wiemy, ナシe po tym zerze jest wartoナ崙... */
 				if (!value)
 					value = p + 1;
 				else
 					/* ...w przeciwym wypadku koniec
-					 * wartoカci i moソemy wychodzi
-					 * grzecznie z p黎li */
+					 * wartoナ嫩i i moナシemy wychodziト
+					 * grzecznie z pト冲li */
 					break;
 			}
 		}
 		
-		/* sprawdシmy, czy pole nie wychodzi poza pakiet, ソeby nie
-		 * mie segfaultw, jeカli serwer przestanie zakacza pakietw
+		/* sprawdナコmy, czy pole nie wychodzi poza pakiet, ナシeby nie
+		 * mieト segfaultテウw, jeナ嬪i serwer przestanie zakaナczaト pakietテウw
 		 * przez \0 */
 
 		if (p == end) {
@@ -346,14 +405,30 @@
 
 		p++;
 
-		/* jeカli dostaliカmy namier na nast麪ne wyniki, to znaczy ソe
-		 * mamy koniec wynikw i nie jest to kolejna osoba. */
+		/* jeナ嬪i dostaliナ嬶y namier na nastト冪ne 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);
+			}
 		}
 	}	
 
@@ -366,16 +441,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, jeカli nie znaleziono.
+ * \return Wartoナ崙 pola lub \c NULL jeナ嬪i nie znaleziono
+ *
+ * \ingroup pubdir50
  */
 const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
 {
@@ -400,57 +475,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ナて囘u
  *
- * iloカ lub -1 w przypadku bウ鹽u.
+ * \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ナて囘u
  *
- * iloカ lub -1 w przypadku bウ鹽u.
+ * \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 naleソy rozpoczア kolejne wyszukiwanie, jeカli
- * zaleソy nam na kolejnych wynikach.
+ * DナVナシsze odpowiedzi katalogu publicznego sト wysyナBne przez serwer
+ * w mniejszych paczkach. Po otrzymaniu odpowiedzi, jeナ嬪i numer kolejnego
+ * wyszukiwania jest wiト冖szy od zera, dalsze wyniki moナシna otrzymaト przez
+ * wywoナBnie kolejnego zapytania z okreナ嬪onym numerem poczトtkowym.
  *
- *  - res - odpowiedシ
+ * \param res Odpowiedナコ
  *
- * numer lub -1 w przypadku bウ鹽u.
+ * \return Numer lub -1 w przypadku bナて囘u
+ *
+ * \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ナて囘u
  *
- * numer lub -1 w przypadku bウ鹽u.
+ * \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	Tue Mar 16 12:07:06 2010 +0900
@@ -0,0 +1,1065 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woナコny <speedy@ziew.org>
+ *                          Arkadiusz Miナ嫐iewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliナгki <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テウナCieナシnoナ崙.
+ *
+ * Jeナ嬪i dany system dostarcza \c gethostbyname_r, uナシywa siト tej wersji, jeナ嬪i
+ * nie, to zwykナFj \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ト冂i
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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テウナCieナシnoナ崙.
+ *
+ * Jeナ嬪i dany system dostarcza \c gethostbyname_r, uナシywa siト tej wersji, jeナ嬪i
+ * nie, to zwykナFj \c gethostbyname.
+ *
+ * \param hostname Nazwa serwera
+ *
+ * \return Zaalokowana struktura \c in_addr lub NULL w przypadku bナて囘u.
+ */
+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ナて囘u gg_gethostbyname_real() zwrテウci -1
+					 * i nie zmieni &addr. Tam jest juナシ INADDR_NONE,
+					 * wiト冂 nie musimy robiト nic wiト冂ej. */
+		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ナ嬪i 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナて囘u gg_gethostbyname_real() zwrテウci -1
+                         * i nie zmieni &addr. Tam jest juナシ INADDR_NONE,
+                         * wiト冂 nie musimy robiト nic wiト冂ej. */
+			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ナZwana 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ナBnia
+ */
+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ナZwana 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ナBnia
+ */
+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ナて囘u gg_gethostbyname_real() zwrテウci -1
+                 * i nie zmieni &addr. Tam jest juナシ INADDR_NONE,
+                 * wiト冂 nie musimy robiト nic wiト冂ej. */
+		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ナB analogicznie do \c gg_resolver_fork_start(), z tト rテウナシnicト,
+ * ナシe dziaナB na wトtkach, nie procesach. Jest dostト冪na 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナBsny 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナBsny 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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ナBsny 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ト冪ujト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ナ嫩iト 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ト冪ujト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ト冂ia sesji.
+ *
+ * WナBsny 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ナBト otrzymany adres IP w postaci sieciowej (big-endian) do
+ * deskryptora. Jeナ嬪i rozwiトzywanie nazwy siト nie powiedzie, naleナシy wysナBト
+ * \c INADDR_NONE. Nastト冪nie zostanie wywoナBna funkcja zwalniajトca zasoby
+ * z parametrem \c force rテウwnym \c 0. Gdyby sesja zostaナB zakoナczona przed
+ * rozwiトzaniem nazwy, np. za pomocト funkcji \c gg_logoff(), funkcja
+ * zwalniajトca zasoby zostanie wywoナBna z parametrem \c force rテウwnym \c 1.
+ *
+ * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u
+ */
+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	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Tue Mar 16 12:07:06 2010 +0900
@@ -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トでUu 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トでU SHA1 z ziarna i hasトケツB.
+ *
+ * \param password HasトケツP
+ * \param seed Ziarno
+ * \param result Bufor na wynik funkcji skrトでUu (20 bajtトでX)
+ */
+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トでU SHA1 z pliku.
+ *
+ * \param fd Deskryptor pliku
+ * \param result Wskaトケナ殤ik na skrトでU
+ *
+ * \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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/irc/irc.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/bosh.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/buddy.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/data.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/disco.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/jabber.c	Tue Mar 16 12:07:06 2010 +0900
@@ -74,7 +74,10 @@
 
 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);
@@ -869,7 +872,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;
@@ -3318,40 +3322,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,
@@ -3360,7 +3391,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 |
@@ -3368,7 +3399,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 |
@@ -3376,7 +3407,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 |
@@ -3384,7 +3415,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 |
@@ -3392,7 +3423,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 |
@@ -3400,7 +3431,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 |
@@ -3408,14 +3439,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 |
@@ -3423,24 +3454,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 */
 
 /**
@@ -3489,14 +3535,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) {
@@ -3560,7 +3638,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),
@@ -3575,7 +3699,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,
@@ -3589,14 +3712,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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/jabber.h	Tue Mar 16 12:07:06 2010 +0900
@@ -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;
@@ -376,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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/jutil.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/libxmpp.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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;
 
@@ -354,7 +257,7 @@
 	purple_account_user_split_set_reverse(split, FALSE);
 	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
 
-	split = purple_account_user_split_new(_("Resource"), NULL, '/');
+	split = purple_account_user_split_new(_("Resource"), "", '/');
 	purple_account_user_split_set_reverse(split, FALSE);
 	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
 
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/message.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/parser.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/pep.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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);
 }
 
--- a/libpurple/protocols/jabber/presence.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/presence.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/presence.h	Tue Mar 16 12:07:06 2010 +0900
@@ -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/usermood.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/usermood.c	Tue Mar 16 12:07:06 2010 +0900
@@ -170,68 +170,6 @@
 	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;
 
@@ -256,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 NULL;
-	}
+	return moods;
 }
\ No newline at end of file
--- a/libpurple/protocols/jabber/usermood.h	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/jabber/usermood.h	Tue Mar 16 12:07:06 2010 +0900
@@ -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/msnp9/Makefile.am	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,785 +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 = botch_utf((gchar *)data, len, &msg->body_len); /* yaz */
-	}
-	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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2368 +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;
-	gchar *tmp;
-	gsize dummy;
-
-	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-	account = purple_connection_get_account(gc);
-
-	if(entry && strlen(entry)) {
-		tmp = botch_utf(entry, strlen(entry), &dummy);
-		alias = purple_url_encode(tmp);
-		g_free(tmp);
-	}
-	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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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, new_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 = sanitize_utf(body, body_len, &new_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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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	Thu Mar 04 15:19:39 2010 +0900
+++ /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/oscar/libaim.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/oscar/libaim.c	Tue Mar 16 12:07:06 2010 +0900
@@ -31,7 +31,6 @@
 	OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE,
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
-	/* The mimimum icon size below is not needed in AIM 6.0 */
 	{"gif,jpeg,bmp,ico", 0, 0, 100, 100, 7168, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
 	oscar_list_icon_aim,		/* list_icon */
 	oscar_list_emblem,		/* list_emblems */
--- a/libpurple/protocols/oscar/libicq.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/oscar/libicq.c	Tue Mar 16 12:07:06 2010 +0900
@@ -41,8 +41,7 @@
 	OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE,
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
-	{"gif,jpeg,bmp,ico", 48, 48, 52, 64, 7168,
-		PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY},	/* icon_spec */
+	{"gif,jpeg,bmp,ico", 0, 0, 100, 100, 7168, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */
 	oscar_list_icon_icq,		/* list_icon */
 	oscar_list_emblem,		/* list_emblems */
 	oscar_status_text,		/* status_text */
--- a/libpurple/protocols/oscar/oscar.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/oscar/oscar.c	Tue Mar 16 12:07:06 2010 +0900
@@ -1601,6 +1601,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;
@@ -3830,7 +3831,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/protocols/yahoo/libymsg.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/yahoo/libymsg.c	Tue Mar 16 12:07:06 2010 +0900
@@ -1860,6 +1860,7 @@
 		return;
 	}
 	else if (len > 0 && ret_data && *ret_data) {
+		PurpleAccount *account = purple_connection_get_account(gc);
 		gchar **split_data = g_strsplit(ret_data, "\r\n", -1);
 		int totalelements = 0;
 		int response_no = -1;
@@ -1867,11 +1868,13 @@
 
 		totalelements = g_strv_length(split_data);
 
-		if(totalelements == 1)
+		if(totalelements == 1) { /* Received an error code */
 			response_no = strtol(split_data[0], NULL, 10);
-		else if(totalelements >= 2) {
+		} else if(totalelements == 2 || totalelements == 3 ) { /* received valid data */
 			response_no = strtol(split_data[0], NULL, 10);
 			token = g_strdup(split_data[1] + strlen("ymsgr="));
+		} else { /* It looks like a transparent proxy has returned a document we don't want */
+			response_no = -1;
 		}
 
 		g_strfreev(split_data);
@@ -1890,8 +1893,8 @@
 				case 1212:
 					/* Password incorrect */
 					/* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */
-					if (!purple_account_get_remember_password(purple_connection_get_account(gc)))
-						purple_account_set_password(purple_connection_get_account(gc), NULL);
+					if (!purple_account_get_remember_password(account))
+						purple_account_set_password(account, NULL);
 					error_reason = g_strdup(_("Incorrect password"));
 					error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
 					break;
@@ -1933,7 +1936,6 @@
 		else {
 			/* OK to login, correct information provided */
 			PurpleUtilFetchUrlData *url_data = NULL;
-			PurpleAccount *account = purple_connection_get_account(gc);
 			char *url = NULL;
 			gboolean yahoojp = yahoo_is_japan(account);
 			gboolean proxy_ssl = purple_account_get_bool(account, "proxy_ssl", FALSE);
@@ -2737,8 +2739,9 @@
 	PurpleAccount *account;
 	YahooFriend *f;
 
-	/* if status is not 1 ie YAHOO_STATUS_BRB, the packet bounced back, so contains our own ip */
-	if(!(pkt->status == YAHOO_STATUS_BRB))
+	/* if status is not YAHOO_STATUS_BRB or YAHOO_STATUS_P2P, the packet bounced back,
+	 * so it contains our own ip */
+	if(pkt->status != YAHOO_STATUS_BRB && pkt->status != YAHOO_STATUS_P2P)
 		return ;
 
 	while (l) {
--- a/libpurple/protocols/yahoo/libymsg.h	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/protocols/yahoo/libymsg.h	Tue Mar 16 12:07:06 2010 +0900
@@ -119,6 +119,7 @@
 	YAHOO_STATUS_ONVACATION,
 	YAHOO_STATUS_OUTTOLUNCH,
 	YAHOO_STATUS_STEPPEDOUT,
+	YAHOO_STATUS_P2P = 11,
 	YAHOO_STATUS_INVISIBLE = 12,
 	YAHOO_STATUS_CUSTOM = 99,
 	YAHOO_STATUS_IDLE = 999,
--- a/libpurple/prpl.h	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/prpl.h	Tue Mar 16 12:07:06 2010 +0900
@@ -572,8 +572,7 @@
 
 	/**
 	 * Returns an array of "PurpleMood"s, with the last one having
-	 * "mood" set to @c NULL, or NULL if the account does not support setting
-	 * a mood.
+	 * "mood" set to @c NULL.
 	 */
 	PurpleMood *(*get_moods)(PurpleAccount *account);
 };
--- a/libpurple/signals.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/signals.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/signals.h	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/upnp.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/win32/global.mak	Tue Mar 16 12:07:06 2010 +0900
@@ -10,8 +10,8 @@
 
 # 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
@@ -105,7 +105,7 @@
 GMSGFMT ?= $(WIN32_DEV_TOP)/gettext-0.17/bin/msgfmt
 MAKENSIS ?= makensis.exe
 MAKENSISOPT ?= /
-PERL ?= /cygdrive/c/perl/bin/perl
+PERL ?= perl
 WINDRES ?= windres
 STRIP ?= strip
 
--- a/libpurple/win32/rules.mak	Thu Mar 04 15:19:39 2010 +0900
+++ b/libpurple/win32/rules.mak	Tue Mar 16 12:07:06 2010 +0900
@@ -4,7 +4,7 @@
 	$(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@ -c $<
 
 %.c: %.xs
-	$(PERL) $(EXTUTILS)/xsubpp -typemap $(EXTUTILS)/typemap -typemap $(PURPLE_PERL_TOP)/common/typemap $< > $@
+	$(PERL) -MExtUtils::ParseXS -e 'ExtUtils::ParseXS::process_file(filename => "$<", output => "$@", typemap => "$(PURPLE_PERL_TOP)/common/typemap");'
 
 %.o: %.rc
 	$(WINDRES) -I$(PURPLE_TOP) -i $< -o $@
--- a/pidgin/Makefile.mingw	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/Makefile.mingw	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/gtkblist.c	Tue Mar 16 12:07:06 2010 +0900
@@ -3389,6 +3389,253 @@
 			!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"));
 }
 
+static char *get_mood_icon_path(const char *mood)
+{
+	char *path;
+
+	if (!strcmp(mood, "busy")) {
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin",
+		                        "status", "16", "busy.png", NULL);
+	} else if (!strcmp(mood, "hiptop")) {
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin",
+		                        "emblems", "16", "hiptop.png", NULL);
+	} else {
+		char *filename = g_strdup_printf("%s.png", mood);
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin",
+		                        "emotes", "small", filename, NULL);
+		g_free(filename);
+	}
+	return path;
+}
+
+static void
+update_status_with_mood(PurpleAccount *account, const gchar *mood,
+    const gchar *text)
+{
+	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);
+	}
+}
+
+static void
+edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields)
+{
+	PurpleRequestField *mood_field, *text_field;
+	GList *l;
+
+	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(mood_field, l->data);
+		const char *text = purple_request_field_string_get_value(text_field);
+
+		if (gc) {
+			PurpleAccount *account = purple_connection_get_account(gc);
+
+			update_status_with_mood(account, mood, text);
+		} else {
+			GList *accounts = purple_accounts_get_all_active();
+
+			for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) {
+				PurpleAccount *account = (PurpleAccount *) accounts->data;
+				PurpleConnection *gc = purple_account_get_connection(account);
+
+				if (gc->flags && PURPLE_CONNECTION_SUPPORT_MOODS) {
+					update_status_with_mood(account, mood, text);
+				}
+			}
+		}
+	}
+}
+	
+static void
+global_moods_for_each(gpointer key, gpointer value, gpointer user_data)
+{
+	GList **out_moods = (GList **) user_data;
+	PurpleMood *mood = (PurpleMood *) value;
+	
+	*out_moods = g_list_append(*out_moods, mood);
+}
+
+static PurpleMood *
+get_global_moods(void)
+{
+	GHashTable *global_moods =
+		g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+	GHashTable *mood_counts =
+		g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+	GList *accounts = purple_accounts_get_all_active();
+	PurpleMood *result = NULL;
+	GList *out_moods = NULL;
+	int i = 0;
+	int num_accounts = 0;
+	
+	for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) {
+		PurpleAccount *account = (PurpleAccount *) accounts->data;
+		PurpleConnection *gc = purple_account_get_connection(account);
+
+		if (gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) {
+			PurplePluginProtocolInfo *prpl_info =
+				PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+			PurpleMood *mood = NULL;
+			
+			for (mood = prpl_info->get_moods(account) ;
+			    mood->mood != NULL ; mood++) {
+				int mood_count =
+						GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood));
+
+				if (!g_hash_table_lookup(global_moods, mood->mood)) {
+					g_hash_table_insert(global_moods, g_strdup(mood->mood), mood);
+				}
+				g_hash_table_insert(mood_counts, g_strdup(mood->mood),
+				    GINT_TO_POINTER(mood_count + 1));
+			}
+
+			num_accounts++;
+		}
+	}
+
+	g_hash_table_foreach(global_moods, global_moods_for_each, &out_moods);
+	result = g_new0(PurpleMood, g_hash_table_size(global_moods) + 1);
+
+	while (out_moods) {
+		PurpleMood *mood = (PurpleMood *) out_moods->data;
+		int in_num_accounts = 
+			GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood));
+
+		if (in_num_accounts == num_accounts) {
+			/* mood is present in all accounts supporting moods */
+			result[i].mood = mood->mood;
+			result[i].description = mood->description;
+			i++;
+		}
+		out_moods = g_list_delete_link(out_moods, out_moods);
+	}
+
+	g_hash_table_destroy(global_moods);
+	g_hash_table_destroy(mood_counts);
+
+	return result;
+}
+
+/* get current set mood for all mood-supporting accounts, or NULL if not set
+ or not set to the same on all */
+static const gchar *
+get_global_mood_status(void)
+{
+	GList *accounts = purple_accounts_get_all_active();
+	const gchar *found_mood = NULL;
+	
+	for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) {
+		PurpleAccount *account = (PurpleAccount *) accounts->data;
+
+		if (purple_account_get_connection(account)->flags &
+		    PURPLE_CONNECTION_SUPPORT_MOODS) {
+			PurplePresence *presence = purple_account_get_presence(account);
+			PurpleStatus *status = purple_presence_get_status(presence, "mood");
+			const gchar *curr_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+
+			if (found_mood != NULL && !purple_strequal(curr_mood, found_mood)) {
+				/* found a different mood */
+				found_mood = NULL;
+				break;
+			} else {
+				found_mood = curr_mood;
+			}
+		}
+	}
+
+	return found_mood;
+}
+
+static void
+set_mood_cb(GtkWidget *widget, PurpleAccount *account)
+{
+	const char *current_mood;
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *g;
+	PurpleRequestField *f;
+	PurpleConnection *gc = NULL;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurpleMood *mood;
+	PurpleMood *global_moods = get_global_moods();
+	
+	if (account) {
+		PurplePresence *presence = purple_account_get_presence(account);
+		PurpleStatus *status = purple_presence_get_status(presence, "mood");
+		gc = purple_account_get_connection(account);
+		g_return_if_fail(gc->prpl != NULL);
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+		current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+	} else {
+		current_mood = get_global_mood_status();
+	}
+
+	fields = purple_request_fields_new();
+	g = purple_request_field_group_new(NULL);
+	f = purple_request_field_list_new("mood", _("Please select your mood from the list"));
+
+	purple_request_field_list_add(f, _("None"), "");
+	if (current_mood == NULL)
+		purple_request_field_list_add_selected(f, _("None"));
+
+	/* TODO: rlaager wants this sorted. */
+	for (mood = account ? prpl_info->get_moods(account) : global_moods;
+	     mood->mood != NULL ; mood++) {
+		char *path;
+
+		if (mood->mood == NULL || mood->description == NULL)
+			continue;
+
+		path = get_mood_icon_path(mood->mood);
+		purple_request_field_list_add_icon(f, _(mood->description),
+				path, (gpointer)mood->mood);
+		g_free(path);
+
+		if (current_mood && !strcmp(current_mood, mood->mood))
+			purple_request_field_list_add_selected(f, _(mood->description));
+	}
+	purple_request_field_group_add_field(g, f);
+
+	purple_request_fields_add_group(fields, g);
+
+	/* if the connection allows setting a mood message */
+	if (gc && (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),
+                              _("Cancel"), NULL,
+                              gc ? purple_connection_get_account(gc) : NULL,
+                              NULL, NULL, gc);
+
+	g_free(global_moods);
+}
+
+static void
+set_mood_show(void)
+{
+	set_mood_cb(NULL, NULL);
+}
 
 /***************************************************
  *            Crap                                 *
@@ -3428,6 +3675,7 @@
 	{ N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 2, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS },
 	{ N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
 	{ N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL },
+	{ N_("/Tools/Set _Mood"), "<CTL>M", set_mood_show, 0, "<Item>", NULL },
 	{ "/Tools/sep2", NULL, NULL, 0, "<Separator>", NULL },
 	{ N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_TRANSFER },
 	{ N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "<Item>", NULL },
@@ -3760,25 +4008,6 @@
 	return pb;
 }
 
-static char *get_mood_icon_path(const char *mood)
-{
-	char *path;
-
-	if (!strcmp(mood, "busy")) {
-		path = g_build_filename(DATADIR, "pixmaps", "pidgin",
-		                        "status", "16", "busy.png", NULL);
-	} else if (!strcmp(mood, "hiptop")) {
-		path = g_build_filename(DATADIR, "pixmaps", "pidgin",
-		                        "emblems", "16", "hiptop.png", NULL);
-	} else {
-		char *filename = g_strdup_printf("%s.png", mood);
-		path = g_build_filename(DATADIR, "pixmaps", "pidgin",
-		                        "emotes", "small", filename, NULL);
-		g_free(filename);
-	}
-	return path;
-}
-
 GdkPixbuf *
 pidgin_blist_get_emblem(PurpleBlistNode *node)
 {
@@ -7821,82 +8050,7 @@
 	purple_account_set_enabled(account, PIDGIN_UI, FALSE);
 }
 
-static void
-edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields)
-{
-	PurpleRequestField *f;
-	GList *l;
-
-	f = purple_request_fields_get_field(fields, "mood");
-	l = purple_request_field_list_get_selected(f);
-
-	if (l) {
-		const char *mood = purple_request_field_list_get_data(f, l->data);
-		PurpleAccount *account = purple_connection_get_account(gc);
-
-		if (mood != NULL && !purple_strequal(mood, "")) {
-			purple_account_set_status(account, "mood", TRUE,
-			                          PURPLE_MOOD_NAME, mood,
-			                          NULL);
-		} else {
-			purple_account_set_status(account, "mood", FALSE, NULL);
-		}
-	}
-}
-
-static void
-set_mood_cb(GtkWidget *widget, PurpleAccount *account)
-{
-	PurplePresence *presence = purple_account_get_presence(account);
-	PurpleStatus *status = purple_presence_get_status(presence, "mood");
-	const char *current_mood;
-	PurpleRequestFields *fields;
-	PurpleRequestFieldGroup *g;
-	PurpleRequestField *f;
-	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);
-
-	current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
-
-	fields = purple_request_fields_new();
-	g = purple_request_field_group_new(NULL);
-	f = purple_request_field_list_new("mood", _("Please select your mood from the list"));
-
-	purple_request_field_list_add(f, _("None"), "");
-	if (current_mood == NULL)
-		purple_request_field_list_add_selected(f, _("None"));
-
-	/* TODO: rlaager wants this sorted. */
-	for (mood = prpl_info->get_moods(account);
-	     mood->mood != NULL ; mood++) {
-		char *path;
-
-		if (mood->mood == NULL || mood->description == NULL)
-			continue;
-
-		path = get_mood_icon_path(mood->mood);
-		purple_request_field_list_add_icon(f, _(mood->description),
-				path, (gpointer)mood->mood);
-		g_free(path);
-
-		if (current_mood && !strcmp(current_mood, mood->mood))
-			purple_request_field_list_add_selected(f, _(mood->description));
-	}
-	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),
-                              _("Cancel"), NULL,
-                              purple_connection_get_account(gc),
-                              NULL, NULL, gc);
-}
+
 
 void
 pidgin_blist_update_accounts_menu(void)
@@ -8023,7 +8177,7 @@
 		    (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) ||
 			 PURPLE_PLUGIN_HAS_ACTIONS(plugin))) {
 			if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) &&
-			    prpl_info->get_moods(account) != NULL) {
+			    gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) {
 				GList *types;
 
 				for (types = purple_account_get_status_types(account);
--- a/pidgin/gtkdialogs.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/gtkdialogs.c	Tue Mar 16 12:07:06 2010 +0900
@@ -76,7 +76,6 @@
 	{"John 'rekkanoryo' Bailey",           N_("bug master"),      NULL},
 	{"Ethan 'Paco-Paco' Blanton",          NULL,                  NULL},
 	{"Hylke Bons",                         N_("artist"),          "hylkebons@gmail.com"},
-	{"Thomas Butter",                      NULL,                  NULL},
 	/* feel free to not translate this */
 	{N_("Ka-Hing Cheung"),                 NULL,                  NULL},
 	{"Sadrul Habib Chowdhury",             NULL,                  NULL},
@@ -87,8 +86,6 @@
 	{"Richard 'rlaager' Laager",           NULL,                  "rlaager@pidgin.im"},
 	{"Sulabh 'sulabh_m' Mahajan",          NULL,                  NULL},
 	{"Richard 'wabz' Nelson",              NULL,                  NULL},
-	{"Christopher 'siege' O'Brien",        NULL,                  "taliesein@users.sf.net"},
-	{"Bartosz Oler",                       NULL,                  NULL},
 	{"Etan 'deryni' Reisner",              NULL,                  NULL},
 	{"Tim 'marv' Ringenbach",              NULL,                  NULL},
 	{"Michael 'Maiku' Ruprecht",           N_("voice and video"), NULL},
@@ -114,12 +111,15 @@
 /* Order: Alphabetical by Last Name */
 static const struct developer retired_developers[] = {
 	{"Herman Bloggs",               N_("win32 port"),          "herman@bluedigits.com"},
+	{"Thomas Butter",               NULL,                      NULL},
 	{"Jim Duchek",                  N_("maintainer"),          "jim@linuxpimps.com"},
 	{"Rob Flynn",                   N_("maintainer"),          NULL},
 	{"Adam Fritzler",               N_("libfaim maintainer"),  NULL},
 	{"Christian 'ChipX86' Hammond", N_("webmaster"),           NULL},
 	/* If "lazy bum" translates literally into a serious insult, use something else or omit it. */
 	{"Syd Logan",                   N_("hacker and designated driver [lazy bum]"), NULL},
+	{"Christopher 'siege' O'Brien", NULL,                      "taliesein@users.sf.net"},
+	{"Bartosz Oler",                NULL,                      NULL},
 	{"Megan 'Cae' Schneider",       N_("support/QA"),          NULL},
 	{"Jim Seymour",                 N_("XMPP"),                NULL},
 	{"Mark Spencer",                N_("original author"),     "markster@marko.net"},
--- a/pidgin/gtkimhtml.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/gtkimhtml.c	Tue Mar 16 12:07:06 2010 +0900
@@ -195,7 +195,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
@@ -1201,6 +1201,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 */
@@ -1261,13 +1269,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");
 }
 
@@ -1693,8 +1704,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/plugins/perl/common/Makefile.mingw	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/plugins/perl/common/Makefile.mingw	Tue Mar 16 12:07:06 2010 +0900
@@ -12,7 +12,6 @@
 DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES))
 
 TARGET = Pidgin
-EXTUTILS ?= C:/perl/lib/ExtUtils
 
 ##
 ## INCLUDE PATHS
--- a/pidgin/plugins/xmppconsole.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/plugins/xmppconsole.c	Tue Mar 16 12:07:06 2010 +0900
@@ -845,7 +845,7 @@
 	                                              /**  summary        */
 	N_("Send and receive raw XMPP stanzas."),
 	                                              /**  description    */
-	N_("This plugin is useful for debbuging XMPP servers or clients."),
+	N_("This plugin is useful for debugging XMPP servers or clients."),
 	"Sean Egan <seanegan@gmail.com>",             /**< author         */
 	PURPLE_WEBSITE,                               /**< homepage       */
 
--- a/pidgin/win32/gtkdocklet-win32.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/gtkdocklet-win32.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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/nsis/available.lst	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/available.lst	Tue Mar 16 12:07:06 2010 +0900
@@ -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,Fran軋is Classique,fr_FR-classique_1-3-2.zip
+fr,FR,fr_FR-1990,Fran軋is R馭orme 1990,fr_FR-1990_1-3-2.zip
+fr,FR,fr_FR,Fran軋is R馭orme 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
--- a/pidgin/win32/nsis/generate_gtk_zip.sh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/generate_gtk_zip.sh	Tue Mar 16 12:07:06 2010 +0900
@@ -14,31 +14,23 @@
 CONTENTS_FILE=$INSTALL_DIR/CONTENTS
 
 #This needs to be changed every time there is any sort of change.
-BUNDLE_VERSION=2.14.7.0
+BUNDLE_VERSION=2.16.6.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"
+ATK="http://ftp.acc.umu.se/pub/gnome/binaries/win32/atk/1.26/atk_1.26.0-1_win32.zip ATK 1.26.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_7-1_win32.zip libjpeg 7-1"
-#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.22/pango_1.22.4-1_win32.zip Pango 1.22.4-1"
+GTK="http://ftp.acc.umu.se/pub/gnome/binaries/win32/gtk+/2.16/gtk+_2.16.6-2_win32.zip GTK+ 2.16.6-2"
+LIBPNG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.4.0-1_win32.zip libpng 1.4.0-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"
+ALL="ATK CAIRO EXPAT FONTCONFIG FREETYPE GETTEXT GLIB GTK LIBPNG PANGO ZLIB"
 
-if [ ! -e $STAGE_DIR ]; then
-	mkdir $STAGE_DIR
-fi
+mkdir -p $STAGE_DIR
 cd $STAGE_DIR
 
 rm -rf $INSTALL_DIR
--- a/pidgin/win32/nsis/langmacros.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/langmacros.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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,37 +66,11 @@
   ; 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 TRANSLATIONS_SECTION_TITLE	${CUR_LANG}
-
   !undef CUR_LANG
 !macroend
 
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Tue Mar 16 12:07:06 2010 +0900
@@ -54,7 +54,6 @@
 ;Defines
 
 !define PIDGIN_NSIS_INCLUDE_PATH		"."
-!define PIDGIN_INSTALLER_DEPS			"..\..\..\..\win32-dev\pidgin-inst-deps-20100223"
 
 ; Remove these and the stuff that uses them at some point
 !define OLD_GAIM_REG_KEY			"SOFTWARE\gaim"
@@ -72,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}"
@@ -303,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
@@ -418,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} ""
@@ -508,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
@@ -521,95 +519,15 @@
 ;--------------------------------
 ;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
@@ -773,9 +691,16 @@
     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"
@@ -807,10 +732,13 @@
     ; Remove the local GTK+ copy (if we're not just upgrading)
     ${GetParameters} $R0
     ClearErrors
-    ${GetOptions} "$R0" "/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"
@@ -849,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
 
 ;--------------------------------
@@ -1239,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
 
@@ -1343,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
@@ -1399,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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/afrikaans.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/arabic.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/basque.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/catalan.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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.  (駸 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			"Galキl鑚"
-!define PIDGIN_SPELLCHECK_DANISH			"Dan鑚"
-!define PIDGIN_SPELLCHECK_GERMAN			"Alemany"
-!define PIDGIN_SPELLCHECK_GREEK			"Grec"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Angl鑚"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Espanyol"
-!define PIDGIN_SPELLCHECK_FAROESE			"Fero鑚"
-!define PIDGIN_SPELLCHECK_FRENCH			"Franc鑚"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Itali"
-!define PIDGIN_SPELLCHECK_DUTCH			"Holand鑚"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Noruec"
-!define PIDGIN_SPELLCHECK_POLISH			"Polon鑚"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugu鑚"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Roman鑚"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rus"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Eslovac"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Suec"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ucran鑚"
 
--- a/pidgin/win32/nsis/translations/dutch.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/dutch.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/english.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -54,31 +54,7 @@
 ; 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"
 
--- a/pidgin/win32/nsis/translations/finnish.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/finnish.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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 ep與nnistui"
-!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		"f蒿ri"
-!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		"ven臻"
-!define PIDGIN_SPELLCHECK_SLOVAK		"slovakia"
-!define PIDGIN_SPELLCHECK_SWEDISH		"ruotsi"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ukraina"
 
--- a/pidgin/win32/nsis/translations/french.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/french.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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 n馗essaire 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			"Tch鑷ue"
-!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		"Esp駻anto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Espagnol"
-!define PIDGIN_SPELLCHECK_FAROESE			"F駻ingien"
-!define PIDGIN_SPELLCHECK_FRENCH			"Fran軋is"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Italien"
-!define PIDGIN_SPELLCHECK_DUTCH			"Hollandais"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norv馮ien"
-!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			"Su馘ois"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainien"
--- a/pidgin/win32/nsis/translations/german.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/german.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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		"D舅isch"
-!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		"Holl舅disch"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norwegisch"
-!define PIDGIN_SPELLCHECK_POLISH		"Polnisch"
-!define PIDGIN_SPELLCHECK_PORTUGUESE	"Portugiesisch"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rum舅isch"
-!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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/hebrew.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/hungarian.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -49,29 +49,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Helyesr疽-ellenrz駸 t疥ogat疽a"
 !define PIDGIN_SPELLCHECK_ERROR			"Hiba a helyesr疽-ellenrz駸 telept駸e kzben"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Hiba a helyesr疽-ellenrz駸i szt疵 telept駸e kzben"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Helyesr疽-ellenrz駸 t疥ogat疽a. (Internetkapcsolat szks馮es a telept駸hez)"
-!define ASPELL_INSTALL_FAILED			"A telept駸 sikertelen"
-!define PIDGIN_SPELLCHECK_BRETON			"Breton"
-!define PIDGIN_SPELLCHECK_CATALAN			"Katal疣"
-!define PIDGIN_SPELLCHECK_CZECH			"Cseh"
-!define PIDGIN_SPELLCHECK_WELSH			"Walesi"
-!define PIDGIN_SPELLCHECK_DANISH			"D疣"
-!define PIDGIN_SPELLCHECK_GERMAN			"N駑et"
-!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		"Norv馮"
-!define PIDGIN_SPELLCHECK_POLISH			"Lengyel"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portug疝"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rom疣"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Orosz"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Szlov疚"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Sv馘"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukr疣"
 
--- a/pidgin/win32/nsis/translations/italian.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/italian.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/japanese.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -45,29 +45,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/kurdish.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/kurdish.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -44,29 +44,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Desteka kontrola rastnivs"
 !define PIDGIN_SPELLCHECK_ERROR			"Di sazkirina kontrola rastnivs de 軻wt derket."
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Di sazkirina ferhenga rastnivs de 軻wt derket."
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Desteka kontrola rastnivs.  (Ji bo sazkirin nternet p黔st 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			"Sw鹽"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrayn"
 
--- a/pidgin/win32/nsis/translations/lithuanian.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/lithuanian.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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		"Vokie鑛 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		"Ukrainie鑛 kalba"
 
--- a/pidgin/win32/nsis/translations/norwegian.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/norwegian.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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 p虧revd 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			"F誡ysk"
-!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/persian.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/persian.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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/portuguese-br.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/portuguese-br.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -43,28 +43,4 @@
 !define URI_HANDLERS_SECTION_TITLE             "Handlers para endere輟s"
 !define PIDGIN_SPELLCHECK_SECTION_TITLE        "Suporte a verifica鈬o ortogr畴ica"
 !define PIDGIN_SPELLCHECK_ERROR                "Erro ao instalar a verifica鈬o ortogr畴ica"
-!define PIDGIN_SPELLCHECK_DICT_ERROR           "Erro ao instalar o dicion疵io da verifica鈬o ortogr畴ica"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION  "Suporte a verifica鈬o ortogr畴ica (A instala鈬o necessita de conex縊 a internet)"
-!define ASPELL_INSTALL_FAILED                  "Falha na instala鈬o"
-!define PIDGIN_SPELLCHECK_BRETON               "Bret縊"
-!define PIDGIN_SPELLCHECK_CATALAN              "Catal縊"
-!define PIDGIN_SPELLCHECK_CZECH                "Tcheco"
-!define PIDGIN_SPELLCHECK_WELSH                "Gal黌" 
-!define PIDGIN_SPELLCHECK_DANISH               "Dinamarqu黌"
-!define PIDGIN_SPELLCHECK_GERMAN               "Alem縊" 
-!define PIDGIN_SPELLCHECK_GREEK                "Grego"
-!define PIDGIN_SPELLCHECK_ENGLISH              "Ingl黌"
-!define PIDGIN_SPELLCHECK_ESPERANTO            "Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH              "Espanhol"
-!define PIDGIN_SPELLCHECK_FAROESE              "Fero黌"
-!define PIDGIN_SPELLCHECK_FRENCH               "Franc黌"
-!define PIDGIN_SPELLCHECK_ITALIAN              "Italiano"
-!define PIDGIN_SPELLCHECK_DUTCH                "Holand黌"
-!define PIDGIN_SPELLCHECK_NORWEGIAN            "Noruegu黌" 
-!define PIDGIN_SPELLCHECK_POLISH               "Polon黌"
-!define PIDGIN_SPELLCHECK_PORTUGUESE           "Portugu黌"
-!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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/simp-chinese.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/slovak.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -43,29 +43,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Podpora kontroly pravopisu"
 !define PIDGIN_SPELLCHECK_ERROR			"Chyba pri in嗾al當ii kontroly pravopisu"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Chyba pri in嗾al當ii slovnka kontroly pravopisu"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Podpora kontroly pravopisu (Nutn pripojenie k Internetu)"
-!define ASPELL_INSTALL_FAILED			"In嗾al當ia zlyhala"
-!define PIDGIN_SPELLCHECK_BRETON			"Bretnsky"
-!define PIDGIN_SPELLCHECK_CATALAN			"Katal疣sky"
-!define PIDGIN_SPELLCHECK_CZECH			"ネesk"
-!define PIDGIN_SPELLCHECK_WELSH			"Welshsk"
-!define PIDGIN_SPELLCHECK_DANISH			"D疣sky"
-!define PIDGIN_SPELLCHECK_GERMAN			"Nemeck"
-!define PIDGIN_SPELLCHECK_GREEK			"Gr馗ky"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Anglick"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperantsk"
-!define PIDGIN_SPELLCHECK_SPANISH			"角anielsk"
-!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			"Poセsk"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugalsk"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumunsk"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rusk"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slovensk"
-!define PIDGIN_SPELLCHECK_SWEDISH			"革馘sky"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrajinsk"
 
--- a/pidgin/win32/nsis/translations/slovenian.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/slovenian.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -49,29 +49,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Podpora preverjanja 鑽kovanja"
 !define PIDGIN_SPELLCHECK_ERROR			"Napaka pri name夊anju preverjanja 鑽kovanja"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Napaka pri name夊anju slovarja za preverjanje 鑽kovanja"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Podpora preverjanja 鑽kovanja.  (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			"鐺嗅i"
-!define PIDGIN_SPELLCHECK_WELSH			"vel嗅i"
-!define PIDGIN_SPELLCHECK_DANISH		"danski"
-!define PIDGIN_SPELLCHECK_GERMAN		"nem嗅i"
-!define PIDGIN_SPELLCHECK_GREEK			"gr嗅i"
-!define PIDGIN_SPELLCHECK_ENGLISH		"angle嗅i"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"esperantski"
-!define PIDGIN_SPELLCHECK_SPANISH		"嗔anski"
-!define PIDGIN_SPELLCHECK_FAROESE		"farojski"
-!define PIDGIN_SPELLCHECK_FRENCH		"francoski"
-!define PIDGIN_SPELLCHECK_ITALIAN		"italijanski"
-!define PIDGIN_SPELLCHECK_DUTCH			"nizozemski"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"norve嗅i"
-!define PIDGIN_SPELLCHECK_POLISH		"poljski"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"portugalski"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"romunski"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"ruski"
-!define PIDGIN_SPELLCHECK_SLOVAK		"slova嗅i"
-!define PIDGIN_SPELLCHECK_SLOVENIAN		"slovenski"
-!define PIDGIN_SPELLCHECK_SWEDISH		"嘛edski"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ukrajinski"
--- a/pidgin/win32/nsis/translations/swedish.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/swedish.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -49,28 +49,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Std fr r舩tstavning"
 !define PIDGIN_SPELLCHECK_ERROR			"Fel vid installation fr r舩tstavning"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Fel vid installation av r舩tstavningsordlista"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Std fr R舩tstavning.  (Internetanslutning kr舸s 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		"F舐iska"
-!define PIDGIN_SPELLCHECK_FRENCH		"Franska"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italienska"
-!define PIDGIN_SPELLCHECK_DUTCH			"Nederl舅dska"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norska"
-!define PIDGIN_SPELLCHECK_POLISH		"Polska"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugisiska"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rum舅ska"
-!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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/trad-chinese.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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クヒカキヲウコサレコクウsスu。^。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テケクsョq、"
-!define PIDGIN_SPELLCHECK_FRENCH		"ェk、"
-!define PIDGIN_SPELLCHECK_ITALIAN		"キN、jァQ、"
-!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		"ッQァJト、"
 
--- a/pidgin/win32/nsis/translations/valencian.nsh	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/nsis/translations/valencian.nsh	Tue Mar 16 12:07:06 2010 +0900
@@ -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			"Gal駸"
-!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	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/winpidgin.c	Tue Mar 16 12:07:06 2010 +0900
@@ -188,6 +188,18 @@
 	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) {
 	/* need to be able to fit MAX_PATH + "PIDGIN_ASPELL_DIR=\\Aspell\\bin" in path2 */
 	TCHAR path[MAX_PATH + 1];
@@ -233,18 +245,6 @@
 	}
 }
 
-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 TCHAR* winpidgin_lcid_to_posix(LCID lcid) {
 	TCHAR *posix = NULL;
 	int lang_id = PRIMARYLANGID(lcid);
--- a/pidgin/win32/wspell.c	Thu Mar 04 15:19:39 2010 +0900
+++ b/pidgin/win32/wspell.c	Tue Mar 16 12:07:06 2010 +0900
@@ -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	Thu Mar 04 15:19:39 2010 +0900
+++ b/po/POTFILES.in	Tue Mar 16 12:07:06 2010 +0900
@@ -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