Merge with default. soc.2008.masterpassword

Fri, 25 Jan 2013 02:22:38 -0500

author
Elliott Sales de Andrade <qulogic@pidgin.im>
date
Fri, 25 Jan 2013 02:22:38 -0500
branch
soc.2008.masterpassword
changeset 34108
7a07b6857540
parent 34107
efe6019c9e91 (current diff)
parent 33678
9b535f78ad07 (diff)
child 34109
6d04ad258555

Merge with default.

COPYRIGHT file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
libpurple/Makefile.am file | annotate | diff | comparison | revisions
libpurple/ciphers/md5.c file | annotate | diff | comparison | revisions
libpurple/ciphers/sha1.c file | annotate | diff | comparison | revisions
libpurple/ciphers/sha256.c file | annotate | diff | comparison | revisions
libpurple/connection.c file | annotate | diff | comparison | revisions
libpurple/core.c file | annotate | diff | comparison | revisions
libpurple/example/nullclient.c file | annotate | diff | comparison | revisions
libpurple/protocols/gg/gg.c file | annotate | diff | comparison | revisions
libpurple/protocols/gg/oauth/oauth-purple.c file | annotate | diff | comparison | revisions
libpurple/protocols/jabber/jabber.c file | annotate | diff | comparison | revisions
libpurple/protocols/msn/session.c file | annotate | diff | comparison | revisions
libpurple/protocols/mxit/actions.c file | annotate | diff | comparison | revisions
libpurple/protocols/mxit/cipher.c file | annotate | diff | comparison | revisions
libpurple/protocols/mxit/login.c file | annotate | diff | comparison | revisions
libpurple/protocols/oscar/oscar.c file | annotate | diff | comparison | revisions
libpurple/protocols/sametime/sametime.c file | annotate | diff | comparison | revisions
libpurple/protocols/yahoo/libymsg.c file | annotate | diff | comparison | revisions
pidgin/gtkaccount.c file | annotate | diff | comparison | revisions
pidgin/gtkprefs.c file | annotate | diff | comparison | revisions
share/ca-certs/Microsoft_Internet_Authority.pem file | annotate | diff | comparison | revisions
share/ca-certs/Microsoft_Secure_Server_Authority.pem file | annotate | diff | comparison | revisions
share/ca-certs/Verisign_RSA_Secure_Server_CA.pem file | annotate | diff | comparison | revisions
--- a/.hgignore	Thu Aug 23 01:27:48 2012 -0400
+++ b/.hgignore	Fri Jan 25 02:22:38 2013 -0500
@@ -7,8 +7,10 @@
 .*/perl/common/[^/]+\.c$
 .*/perl/common/blib.*
 .*/perl/common/pm_to_blib$
+.*/perl/common/MYMETA\.(json|yml)
 .*~$
 .*\.a$
+.*\.asc$
 .*\.bak$
 .*\.bs$
 .*\.def$
--- a/COPYRIGHT	Thu Aug 23 01:27:48 2012 -0400
+++ b/COPYRIGHT	Fri Jan 25 02:22:38 2013 -0500
@@ -17,6 +17,7 @@
 
 Copyright (C) 1998-2012 by the following:
 
+Mark
 Saleem Abdulrasool
 Jakub Adam
 Dave Ahlswede
@@ -74,6 +75,7 @@
 Éric Boumaour
 Chris Boyle
 Stanislav Brabec
+Bartosz Brachaczek
 Quentin Brandon
 Derrick J Brashear
 Mauro Sérgio Ferreira Brasil
@@ -176,6 +178,7 @@
 Gavan Fantom (gavan)
 Leonardo Fernandes
 David Fiander
+Michael Fiedler
 Ryan Flegel
 Rob Flynn <gaim@robflynn.com>
 Rob Foehl (rwf)
@@ -256,6 +259,7 @@
 Vitaliy Ischenko
 Intel Corporation
 Andrew Ivanov
+Momchil Ivanov
 Scott Jackson
 Hans Petter Jansson
 David Jedelsky
@@ -417,6 +421,7 @@
 Ari Pollak
 Stephen Pope
 Cristi Posoiu
+Alexei Potashnik
 Nathan Poznick
 Jory A. Pratt
 David Preece
@@ -458,6 +463,7 @@
 Thanumalayan S.
 Jonathan Sailor
 Elliott Sales de Andrade
+Catalin Salgau
 Tomasz Sałaciński <tsalacinski@gmail.com>
 Pradyumna Sampath
 Arvind Samptur
@@ -510,6 +516,7 @@
 Lex Spoon
 Chris Stafford
 Kevin Stange
+Ferdinand Stehle
 Joshua Stein
 Jakub Steiner
 Richard Stellingwerff
--- a/ChangeLog	Thu Aug 23 01:27:48 2012 -0400
+++ b/ChangeLog	Fri Jan 25 02:22:38 2013 -0500
@@ -7,6 +7,9 @@
 	  either 2 or 3 will attempt to build with specifically 2.x or 3.x
 	  support.  The default is 'auto', which will first look for 3.x
 	  development headers and then 2.x development headers.
+	* Add email notification in the docklet area. (Alexei) (#3571)
+	* Add a pref to select the type messages in conversation that triggers 
+	  the docklet notification. (Momchil) (#12598)
 
 	Finch:
 	* Support the conversation-extended signal for extending the
@@ -57,21 +60,82 @@
 	* Fix a crash at startup with large contact list. Avatar support for
 	  buddies will be disabled till 3.0.0. (#15226, #14305)
 
+	General:
+	* The configure script will now exit with status 1 when specifying
+	  invalid protocol plugins using the --with-static-prpls and
+	  --with-dynamic-prpls arguments. (Michael Fiedler) (#15316)
+
+	libpurple:
+	* Don't link directly to libgcrypt when building with GnuTLS support.
+	  (Bartosz Brachaczek) (#15329)
+	* Fix UPnP mappings on routers that return empty <URLBase/> elements
+	  in their response. (Ferdinand Stehle) (#15373)
+	* Tcl plugin uses saner, race-free plugin loading.
+	* Fix the Tcl signals-test plugin for savedstatus-changed.
+	  (Andrew Shadura) (#15443)
+
+	Pidgin:
+	* Make Pidgin more friendly to non-X11 GTK+, such as MacPorts' +no_x11
+	  variant.
+
 	Gadu-Gadu:
 	* Fix a crash at startup with large contact list. Avatar support for
 	  buddies will be disabled till 3.0.0. (#15226, #14305)
 
+	IRC:
+	* Support for SASL authentication.  (Thijs Alkemade, Andy Spencer)
+	  (#13270)
+	* Print topic setter information at channel join. (#13317)
+
 	MSN:
+	* Fix SSL certificate issue when signing into MSN for some users.
 	* Fix a crash when removing a user before its icon is loaded. (Mark
 	  Barfield) (#15217)
 
+	MXit:
+	* Display farewell messages in a different colour to distinguish
+	  them from normal messages.
+	* Add support for typing notification.
+	* Add support for the Relationship Status profile attribute.
+	* Remove all reference to Hidden Number.
+	* Ignore new invites to join a GroupChat if you're already joined, or
+	  still have a pending invite.
+	* The buddy's name was not centered vertically in the buddy-list if they
+	  did not have a status-message or mood set.
+
 	Yahoo!:
 	* Fix a double-free in profile/picture loading code. (Mihai Serban)
 	  (#15053)
+	* Fix retrieving server-side buddy aliases. (Catalin Salgu) (#15381)
 
 	Plugins:
 	* The Voice/Video Settings plugin supports using the sndio GStreamer
-	 backends. (Brad Smith) (#14414)
+	  backends. (Brad Smith) (#14414)
+	* Fix a crash in the Contact Availability Detection plugin. (Mark)
+	  (#15327)
+	* Make the Message Notification plugin more friendly to non-X11 GTK+,
+	  such as MacPorts' +no_x11 variant.
+
+	Windows-Specific Changes:
+	* Compile with secure flags (#15290)
+	* Installer downloads GTK+ Runtime and Debug Symbols more securely.
+	  (#15277)
+	* Updates to a number of dependencies, some of which have security
+	  related fixes. (#14571, #15285, #15286)
+		* ATK 1.32.0-2
+		* Cyrus SASL 2.1.25
+		* expat 2.1.0-1
+		* freetype 2.4.10-1
+		* gettext 0.18.1.1-2
+		* Glib 2.28.8-1
+		* libpng 1.4.12-1
+		* libxml2 2.9.0-1
+		* NSS 3.13.6 and NSPR 4.9.2
+		* Pango 1.29.4-1
+		* SILC 1.1.10
+		* zlib 1.2.5-2
+	* Patch libmeanwhile (sametime library) to fix crash. (Jonathan Rice)
+	  (#12637)
 
 version 2.10.6 (07/06/2012):
 	Pidgin:
--- a/ChangeLog.API	Thu Aug 23 01:27:48 2012 -0400
+++ b/ChangeLog.API	Fri Jan 25 02:22:38 2013 -0500
@@ -3,6 +3,7 @@
 version 3.0.0 (??/??/????):
 	libpurple:
 		Added:
+		* displaying-emails-clear signal (notification signal)
 		* pidgin_create_webview
 		* purple_account_is_disconnecting
 		* purple_account_get_ui_data
@@ -23,6 +24,8 @@
 		* purple_conversation_message_get_alias
 		* purple_conversation_message_get_conv
 		* purple_contact_get_contact_size
+		* purple_notify_emails_pending
+		* purple_notify_emails_present
 		* purple_notify_searchresult_column_get_title
 		* purple_notify_searchresult_column_is_visible
 		* purple_notify_searchresult_column_set_visible
@@ -72,12 +75,14 @@
 		* Various WebKit-related functions in gtkwebview.h
 		* xmlnode_get_default_namespace
 		* xmlnode_strip_prefixes
+		* PidginDockletFlag
 
 		Changed:
 		* purple_account_add_buddy now takes an invite message as the last
 		  parameter
 		* purple_account_add_buddies now takes an invite message as the last
 		  parameter
+		* purple_buddy_icon_unref no longer has a return value
 		* purple_certificate_check_signature_chain now returns a list of failing
 		  PurpleCertificate*s as the second parameter
 		* purple_connection_error now takes a PurpleConnectionError
@@ -85,6 +90,8 @@
 		* purple_conversation_get_gc renamed to
 		  purple_conversation_get_connection
 		* purple_dnsquery_a now takes a PurpleAccount as the first parameter
+		* purple_imgstore_add renamed to purple_imgstore_new
+		* purple_imgstore_add_with_id renamed to purple_imgstore_new_with_id
 		* purple_network_listen now takes the protocol family as the second
 		  parameter
 		* purple_network_listen now takes a boolean indicating external port
@@ -114,6 +121,9 @@
 		* purple_xfer_set_bytes_sent now takes a goffset as the bytes_sent
 		  parameter
 		* purple_xfer_set_size now takes a goffset as the size parameter
+		* PurpleCertificateVerificationStatus enumeration is now merged with
+		  internal flags, thus removing PURPLE_CERTIFICATE_INVALID and
+		  replacing it with more precise errors.
 		* PurpleConnectionUiOps.report_disconnect now passes a
 		  PurpleConnectionError as the second parameter
 		* PurpleXfer.bytes_remaining is now a goffset
@@ -157,6 +167,9 @@
 		* PidginConversation.sg
 		* purple_account_add_buddies_with_invite
 		* purple_account_add_buddy_with_invite
+		* purple_blist_load
+		* purple_blist_new
+		* purple_set_blist
 		* purple_blist_update_buddy_icon
 		* purple_buddy_get_local_alias
 		* purple_buddy_icons_has_custom_icon
@@ -188,6 +201,7 @@
 		* purple_plugins_unregister_load_notify_cb
 		* purple_plugins_unregister_probe_notify_cb
 		* purple_plugins_unregister_unload_notify_cb
+		* purple_pounces_load
 		* purple_presence_add_status
 		* purple_presence_add_list
 		* purple_proxy_connect_socks5
@@ -212,6 +226,7 @@
 		* purple_util_fetch_url_request, instead.
 		* purple_util_fetch_url_request_len_with_account.  Use
 		  purple_util_fetch_url_request, instead.
+		* PurpleCertificateVerificationStatus.PURPLE_CERTIFICATE_INVALID
 		* PurpleConnectionUiOps.report_disconnect_reason
 		* PurplePluginProtocolInfo.add_buddy_with_invite
 		* PurplePluginProtocolInfo.add_buddies_with_invite
--- a/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -6,7 +6,7 @@
 		HACKING \
 		Makefile.mingw \
 		PLUGIN_HOWTO \
-		README.MTN \
+		README.hg \
 		README.mingw \
 		config.h.mingw \
 		doxy2devhelp.xsl \
@@ -54,13 +54,13 @@
 # ... and have no changes in the working copy. (this isn't really necessary with hg because hg id appends a "+")
 	test "x`hg st -mard`" = x
 
-packages:
+sign-packages: dist
 	gpg -ab pidgin-$(PACKAGE_VERSION).tar.gz
 	gpg -ab pidgin-$(PACKAGE_VERSION).tar.bz2
 	gpg --verify pidgin-$(PACKAGE_VERSION).tar.gz.asc pidgin-$(PACKAGE_VERSION).tar.gz
 	gpg --verify pidgin-$(PACKAGE_VERSION).tar.bz2.asc pidgin-$(PACKAGE_VERSION).tar.bz2
 
-release: commit-check version-check distcheck packages
+release: commit-check version-check distcheck sign-packages
 
 if INSTALL_I18N
 PO_DIR=po
--- a/Makefile.mingw	Thu Aug 23 01:27:48 2012 -0400
+++ b/Makefile.mingw	Fri Jan 25 02:22:38 2013 -0500
@@ -31,11 +31,21 @@
     exit; \
 }' VERSION)
 
-GTK_INSTALL_VERSION = 2.16.6.0
+GTK_INSTALL_VERSION = 2.16.6.1
+
+authenticode_sign = $(MONO_SIGNCODE) \
+		    -spc "$(SIGNCODE_SPC)" -v "$(SIGNCODE_PVK)" \
+		    -a sha1 -$$ commercial \
+		    -n "$(2)" -i "https://pidgin.im" \
+		    -t "http://timestamp.verisign.com/scripts/timstamp.dll" -tr 10 \
+		    $(1)
+
+gpg_sign = $(GPG_SIGN) -ab $(1) && $(GPG_SIGN) --verify $(1).asc
 
 STRIPPED_RELEASE_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-win32bin
 DEBUG_SYMBOLS_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-dbgsym
 
+PIDGIN_INST_DEP_DIR="$(WIN32_DEV_TOP)/pidgin-inst-deps-20120910"
 
 # Any *.dll or *.exe files included in win32-install-dir that we don't compile
 # should be included in this list so they don't get stripped
@@ -55,6 +65,7 @@
 	libplc4.dll \
 	libplds4.dll \
 	libsasl.dll \
+	libssp-0.dll \
 	libxml2-2.dll \
 	nss3.dll \
 	nssckbi.dll \
@@ -66,7 +77,7 @@
 	saslLOGIN.dll \
 	saslPLAIN.dll \
 	libsilc-1-1-2.dll \
-	libsilcclient-1-1-2.dll \
+	libsilcclient-1-1-3.dll \
 	smime3.dll \
 	softokn3.dll \
 	sqlite3.dll \
@@ -77,7 +88,7 @@
 
 include $(PIDGIN_COMMON_RULES)
 
-.PHONY: all docs install installer installer_offline installer_zip debug_symbols_zip installers clean uninstall create_release_install_dir generate_installer_includes $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT)
+.PHONY: all docs install installer installer_offline installer_zip debug_symbols_zip installers clean uninstall create_release_install_dir generate_installer_includes $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT) gtk_runtime_zip
 
 all: $(PIDGIN_CONFIG_H) $(PIDGIN_REVISION_H)
 	$(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE)
@@ -98,12 +109,13 @@
 	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/enchant/*.dll $(PIDGIN_INSTALL_DIR)/spellcheck/lib/enchant
-	cp $(WIN32_DEV_TOP)/pidgin-inst-deps-20100315/exchndl.dll $(PIDGIN_INSTALL_DIR)
+	cp $(PIDGIN_INST_DEP_DIR)/exchndl.dll $(PIDGIN_INSTALL_DIR)
+	cp $(GCC_SSP_TOP)/bin/libssp-0.dll $(PIDGIN_INSTALL_DIR)
 
-pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip:
-	pidgin/win32/nsis/generate_gtk_zip.sh `pwd`
+gtk_runtime_zip:
+	pidgin/win32/nsis/generate_gtk_zip.sh "`pwd`" "$(GPG_SIGN)"
 
-generate_installer_includes: create_release_install_dir pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip debug_symbols_zip $(PIDGIN_TREE_TOP)/pidgin/win32/nsis/nsis_translations.desktop
+generate_installer_includes: create_release_install_dir gtk_runtime_zip debug_symbols_zip $(PIDGIN_TREE_TOP)/pidgin/win32/nsis/nsis_translations.desktop
 	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 {} ';' \
@@ -137,18 +149,32 @@
 	find $(STRIPPED_RELEASE_DIR) \( -name '*.dll' -o -name '*.exe' \) \
 	 -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) \
 	 -exec $(STRIP) --strip-unneeded {} ';'
+	$(call authenticode_sign, $(STRIPPED_RELEASE_DIR)/pidgin.exe, "Pidgin $(PIDGIN_VERSION)")
 
 installer: generate_installer_includes
-	$(MAKENSIS) -V3 -DPIDGIN_VERSION="$(PIDGIN_VERSION)" -DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" -DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
+	$(eval $@_DEBUG_SYMBOLS_SHA1SUM := $(shell sha1sum $(DEBUG_SYMBOLS_DIR).zip | sed -e "s/\ .*$$//"))
+	$(eval $@_GTK_SHA1SUM := $(shell sha1sum pidgin/win32/nsis/gtk-runtime-$(GTK_INSTALL_VERSION).zip | sed -e "s/\ .*$$//"))
+	$(MAKENSIS) -V3 -DPIDGIN_VERSION="$(PIDGIN_VERSION)" -DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" \
+		-DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" \
+		-DDEBUG_SYMBOLS_SHA1SUM="$($@_DEBUG_SYMBOLS_SHA1SUM)" -DGTK_SHA1SUM="$($@_GTK_SHA1SUM)"\
+		pidgin/win32/nsis/pidgin-installer.nsi
+	$(call authenticode_sign, pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe, "Pidgin Installer")
 	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe ./
+	$(call gpg_sign, pidgin-$(PIDGIN_VERSION).exe)
 
 installer_offline: generate_installer_includes
-	$(MAKENSIS) -V3 -DPIDGIN_VERSION="$(PIDGIN_VERSION)" -DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" -DOFFLINE_INSTALLER -DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
+	$(MAKENSIS) -V3 -DPIDGIN_VERSION="$(PIDGIN_VERSION)" -DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" \
+		-DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" \
+		-DOFFLINE_INSTALLER \
+		pidgin/win32/nsis/pidgin-installer.nsi
+	$(call authenticode_sign, pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe, "Pidgin Installer")
 	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe ./
+	$(call gpg_sign, pidgin-$(PIDGIN_VERSION)-offline.exe)
 
 installer_zip: create_release_install_dir
 	rm -f pidgin-$(PIDGIN_VERSION)-win32-bin.zip
 	zip -9 -r pidgin-$(PIDGIN_VERSION)-win32-bin.zip $(STRIPPED_RELEASE_DIR)
+	$(call gpg_sign, pidgin-$(PIDGIN_VERSION)-win32-bin.zip)
 
 debug_symbols_zip: install
 	rm -rf $(DEBUG_SYMBOLS_DIR) $(DEBUG_SYMBOLS_DIR).zip
@@ -156,7 +182,9 @@
 	tar -cf - `find $(PIDGIN_INSTALL_DIR) \( -name '*.dll' -o -name '*.exe' \) \
 	 -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) -print` \
 	 | tar --strip 2 --xform s/$$/.dbgsym/ -xC $(DEBUG_SYMBOLS_DIR) -f -
+	cp $(MEANWHILE_TOP)/bin/libmeanwhile-1.dll.unstripped $(DEBUG_SYMBOLS_DIR)/libmeanwhile-1.dll.dbgsym
 	zip -9 -r $(DEBUG_SYMBOLS_DIR).zip $(DEBUG_SYMBOLS_DIR) 
+	$(call gpg_sign, $(DEBUG_SYMBOLS_DIR).zip)
 
 installers: installer installer_offline debug_symbols_zip installer_zip
 
--- a/autogen.sh	Thu Aug 23 01:27:48 2012 -0400
+++ b/autogen.sh	Fri Jan 25 02:22:38 2013 -0500
@@ -160,5 +160,8 @@
 ###############################################################################
 # Run configure
 ###############################################################################
-echo "running ./configure ${CONFIGURE_FLAGS} $@"
-./configure ${CONFIGURE_FLAGS} $@
+if test -z "$NOCONFIGURE"; then
+	echo "running ./configure ${CONFIGURE_FLAGS} $@"
+	./configure ${CONFIGURE_FLAGS} $@
+fi
+
--- a/configure.ac	Thu Aug 23 01:27:48 2012 -0400
+++ b/configure.ac	Fri Jan 25 02:22:38 2013 -0500
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_PREREQ([2.50])
+AC_PREREQ([2.63])
 
 # UPDATING VERSION NUMBERS FOR RELEASES
 #
@@ -74,9 +74,8 @@
 
 AC_CANONICAL_HOST
 AC_CONFIG_HEADERS([config.h])
-AM_INIT_AUTOMAKE([1.9 -Wno-portability dist-bzip2])
-dnl TODO: Always use AM_SILENT_RULES when we depend on automake >= 1.11
-m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+AM_INIT_AUTOMAKE([1.11 -Wno-portability dist-bzip2])
+AM_SILENT_RULES([yes])
 
 PURPLE_MAJOR_VERSION=purple_major_version
 PURPLE_MINOR_VERSION=purple_minor_version
@@ -111,8 +110,8 @@
 AC_PROG_CC
 AM_PROG_CC_C_O
 AC_PROG_CXX
-AC_DISABLE_STATIC
-AC_PROG_LIBTOOL
+LT_PREREQ([2.2.6])
+LT_INIT([disable-static])
 LIBTOOL="$LIBTOOL --silent"
 AC_PROG_INSTALL
 PKG_PROG_PKG_CONFIG
@@ -159,7 +158,7 @@
 	[AC_DEFINE([HAVE_GETADDRINFO], [1],
 		[Define to 1 if you have the getaddrinfo function.])],
 	[AC_CHECK_LIB(socket, getaddrinfo,
-		[AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lsnl $LIBS"], , , -lnsl)])
+		[AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lnsl $LIBS"], , -lnsl)])
 AC_CHECK_FUNCS(inet_ntop)
 AC_CHECK_FUNCS(getifaddrs)
 dnl Check for socklen_t (in Unix98)
@@ -209,7 +208,7 @@
   AC_MSG_ERROR([unable to find the ceil() function])
 ])
 
-AC_MSG_CHECKING(for fileno())
+AC_MSG_CHECKING([for fileno()])
 AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <stdio.h>
 
@@ -233,7 +232,7 @@
 	AC_MSG_RESULT(no)
 ])
 
-AC_MSG_CHECKING(for the %z format string in strftime())
+AC_MSG_CHECKING([for the %z format string in strftime()])
 AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
@@ -298,10 +297,10 @@
 dnl #######################################################################
 dnl # Disable creation and installation of translation files
 dnl #######################################################################
-AC_ARG_ENABLE(nls, AC_HELP_STRING([--disable-nls], [disable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes)
+AC_ARG_ENABLE(nls, AS_HELP_STRING([--disable-nls], [disable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes)
 
 if test x$enable_i18n = xyes; then
-	AC_PROG_INTLTOOL
+	IT_PROG_INTLTOOL
 	GETTEXT_PACKAGE=pidgin
 	AC_SUBST(GETTEXT_PACKAGE)
 
@@ -333,16 +332,16 @@
 AM_CONDITIONAL(INSTALL_I18N, test "x$enable_i18n" = "xyes")
 
 dnl #######################################################################
-dnl # Check for GLib 2.16 (required)
+dnl # Check for GLib 2.20 (required)
 dnl #######################################################################
 # TODO: gmodule-2.0 is only needed if enable_plugins is 'yes'.  It
 #       might be nice to change this check so that it's not required
 #       if enable_plugins is 'no'.
-PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.16.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
+PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.20.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
 	AC_MSG_RESULT(no)
 	AC_MSG_ERROR([
 
-You must have GLib 2.16.0 or newer development headers installed to build.
+You must have GLib 2.20.0 or newer development headers installed to build.
 
 If you have these installed already you may need to install pkg-config so
 I can find them.
@@ -354,7 +353,7 @@
 AC_SUBST(GLIB_GENMARSHAL)
 
 AC_ARG_WITH([extraversion],
-			AC_HELP_STRING([--with-extraversion=STRING],
+			AS_HELP_STRING([--with-extraversion=STRING],
 						   [extra version number to be displayed in Help->About and --help (for packagers)]),
 						   EXTRA_VERSION=$withval)
 
@@ -364,19 +363,19 @@
 	AC_DEFINE_UNQUOTED(DISPLAY_VERSION, "$VERSION", [display version info])
 fi
 
-AC_ARG_ENABLE(missing-dependencies, [AC_HELP_STRING([--disable-missing-dependencies],
+AC_ARG_ENABLE(missing-dependencies, [AS_HELP_STRING([--disable-missing-dependencies],
 		[skip missing dependencies instead of aborting configure])],
 	force_deps="$enableval", force_deps="yes")
 
 AC_ARG_WITH(x, [],
 	with_x="$withval", with_x="yes")
-AC_ARG_ENABLE(gtkui, [AC_HELP_STRING([--disable-gtkui],
+AC_ARG_ENABLE(gtkui, [AS_HELP_STRING([--disable-gtkui],
 		[compile without GTK+ user interface])],
 	enable_gtkui="$enableval", enable_gtkui="yes")
-AC_ARG_WITH(gtk, [AC_HELP_STRING([--with-gtk=<version>],
+AC_ARG_WITH(gtk, [AS_HELP_STRING([--with-gtk=<version>],
 		[compile with GTK+ 2 or 3 user interface (default: auto)])],
 	with_gtk="$withval", with_gtk="auto")
-AC_ARG_ENABLE(consoleui, [AC_HELP_STRING([--disable-consoleui],
+AC_ARG_ENABLE(consoleui, [AS_HELP_STRING([--disable-consoleui],
 		[compile without console user interface])],
 	[enable_consoleui=$enableval force_finch=$enableval], [enable_consoleui=yes force_finch=no])
 
@@ -384,35 +383,35 @@
 dnl # Check for GTK+ 2.10 and other things used by the GTK UI
 dnl #######################################################################
 AC_ARG_ENABLE(screensaver,
-	[AC_HELP_STRING([--disable-screensaver],
+	[AS_HELP_STRING([--disable-screensaver],
 		[compile without X screensaver extension (used to detect idleness)])],
 	enable_screensaver="$enableval", enable_screensaver="yes")
 AC_ARG_ENABLE(sm,
-	[AC_HELP_STRING([--disable-sm],
+	[AS_HELP_STRING([--disable-sm],
 		[compile without X session management support])],
 	enable_sm="$enableval", enable_sm="yes")
 AC_ARG_ENABLE(startup-notification,
-	[AC_HELP_STRING([--disable-startup-notification],
+	[AS_HELP_STRING([--disable-startup-notification],
 		[compile without startup notification support])],
 	enable_startup_notification="$enableval", enable_startup_notification="yes")
 AC_ARG_ENABLE(gtkspell,
-	[AC_HELP_STRING([--disable-gtkspell],
+	[AS_HELP_STRING([--disable-gtkspell],
 		[compile without GtkSpell automatic spell checking])],
 	enable_gtkspell="$enableval", enable_gtkspell="yes")
 AC_ARG_ENABLE(gevolution,
-	[AC_HELP_STRING([--enable-gevolution],
+	[AS_HELP_STRING([--enable-gevolution],
 		[compile with the Evolution plugin])],
 	enable_gevolution="$enableval", enable_gevolution="no")
 AC_ARG_ENABLE(cap,
-	[AC_HELP_STRING([--enable-cap],
+	[AS_HELP_STRING([--enable-cap],
 		[compile with Contact Availability Prediction plugin])],
 	enable_cap="$enableval", enable_cap="no")
 AC_ARG_ENABLE(gestures,
-	[AC_HELP_STRING([--disable-gestures],
+	[AS_HELP_STRING([--disable-gestures],
 		[compile without the gestures plugin])],
 	enable_gestures="$enableval", enable_gestures="yes")
 AC_ARG_ENABLE(gcr,
-	[AC_HELP_STRING([--enable-gcr],
+	[AS_HELP_STRING([--enable-gcr],
 		[compile with GCR certificate widgets])],
 	enable_gcr="$enableval", enable_gcr="no")
 
@@ -464,6 +463,9 @@
 	AC_SUBST(GTK_CFLAGS)
 	AC_SUBST(GTK_LIBS)
 
+	GTK_PC_MODULE="gtk+-${with_gtk}.0"
+	AC_SUBST(GTK_PC_MODULE)
+
 	dnl We only really need Pango >= 1.4 for decent RTL support
 	PKG_CHECK_MODULES(PANGO, [pango >= 1.4.0],
 			AC_DEFINE(HAVE_PANGO14, 1, [Define if we have Pango 1.4 or newer.]),:)
@@ -716,7 +718,7 @@
 dnl #######################################################################
 GNT_LIBS=""
 GNT_CFLAGS=""
-AC_ARG_WITH(ncurses-headers, [AC_HELP_STRING([--with-ncurses-headers=DIR],
+AC_ARG_WITH(ncurses-headers, [AS_HELP_STRING([--with-ncurses-headers=DIR],
 		[compile finch against the ncurses includes in DIR])],
 		[ac_ncurses_includes="$withval"], [ac_ncurses_includes=""])
 if test "x$enable_consoleui" = "xyes"; then
@@ -817,14 +819,25 @@
 AC_SUBST(LIBXML_LIBS)
 
 dnl #######################################################################
+dnl # Check for JSON-GLib (required)
+dnl #######################################################################
+
+PKG_CHECK_MODULES([JSON], [json-glib-1.0 >= 0.14.0], , [
+	AC_MSG_RESULT(no)
+	AC_MSG_ERROR([
+You must have JSON-GLib >= 0.14.0 development headers installed to build.
+])])
+
+AC_SUBST(JSON_CFLAGS)
+AC_SUBST(JSON_LIBS)
+
+dnl #######################################################################
 dnl # Check for zlib (required)
 dnl #######################################################################
 
 PKG_CHECK_MODULES(ZLIB, [zlib >= 1.2.0], , [
-	AC_MSG_RESULT(no)
-	AC_MSG_ERROR([
-You must have zlib >= 1.2.0 development headers installed to build.
-])])
+	AC_SEARCH_LIBS([deflate], [z], [], AC_MSG_ERROR([You must have zlib >= 1.2.0 development headers installed to build.]), [])
+])
 
 AC_SUBST(ZLIB_CFLAGS)
 AC_SUBST(ZLIB_LIBS)
@@ -839,42 +852,102 @@
 dnl #######################################################################
 dnl # Check for GStreamer
 dnl #######################################################################
-dnl
-dnl TODO: Depend on gstreamer >= 0.10.10, and remove the conditional use of
-dnl       gst_registry_fork_set_enabled.
 AC_ARG_ENABLE(gstreamer,
-	[AC_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])],
+	[AS_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])],
 	enable_gst="$enableval", enable_gst="yes")
+AC_ARG_WITH(gstreamer, [AS_HELP_STRING([--with-gstreamer=<version>],
+		[compile with GStreamer 0.10 or 1.0 interface (default: auto)])],
+	with_gstreamer="$withval", with_gstreamer="auto")
 if test "x$enable_gst" != "xno"; then
-	PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [
-		AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds])
-		AC_SUBST(GSTREAMER_CFLAGS)
-		AC_SUBST(GSTREAMER_LIBS)
-		AC_CHECK_LIB(gstreamer-0.10, gst_registry_fork_set_enabled,
-			[ AC_DEFINE(GST_CAN_DISABLE_FORKING, [],
-			  [Define if gst_registry_fork_set_enabled exists])],
-			[], [$GSTREAMER_LIBS])
-	], [
-		AC_MSG_RESULT(no)
-		enable_gst="no"
-		if test "x$force_deps" = "xyes" ; then
-			AC_MSG_ERROR([
+	if test "x$with_gstreamer" == "xauto"; then
+		PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0], [
+			AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds])
+			with_gstreamer="1.0"
+			AC_SUBST(GSTREAMER_CFLAGS)
+			AC_SUBST(GSTREAMER_LIBS)
+			dnl Check whether forking stuff is required for this version.
+		], [
+			PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [
+				AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds])
+				with_gstreamer="0.10"
+				AC_SUBST(GSTREAMER_CFLAGS)
+				AC_SUBST(GSTREAMER_LIBS)
+			], [
+				AC_MSG_RESULT(no)
+				enable_gst="no"
+				if test "x$force_deps" = "xyes" ; then
+					AC_MSG_ERROR([
 GStreamer development headers not found.
 Use --disable-gstreamer if you do not need GStreamer (sound) support.
 ])
-		fi])
+				fi
+			])
+		])
+	elif test "x$with_gstreamer" == "x1.0"; then
+		PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0], [
+			AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer 1.0 for playing sounds])
+			AC_SUBST(GSTREAMER_CFLAGS)
+			AC_SUBST(GSTREAMER_LIBS)
+		], [
+			AC_MSG_RESULT(no)
+			enable_gst="no"
+			if test "x$force_deps" = "xyes" ; then
+				AC_MSG_ERROR([
+GStreamer development headers not found.
+Use --disable-gstreamer if you do not need GStreamer (sound) support.
+])
+			fi
+		])
+	elif test "x$with_gstreamer" == "x0.10"; then
+		PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [
+			AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer 0.10 for playing sounds])
+			AC_SUBST(GSTREAMER_CFLAGS)
+			AC_SUBST(GSTREAMER_LIBS)
+		], [
+			AC_MSG_RESULT(no)
+			enable_gst="no"
+			if test "x$force_deps" = "xyes" ; then
+				AC_MSG_ERROR([
+GStreamer development headers not found.
+Use --disable-gstreamer if you do not need GStreamer (sound) support.
+])
+			fi
+		])
+	else
+		AC_MSG_ERROR([--with-gstreamer must specify one of 0.10, 1.0 or auto.])
+	fi
+fi
+
+dnl #######################################################################
+dnl # Check for GStreamer Video
+dnl #######################################################################
+if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x1.0"; then
+	AC_ARG_ENABLE(gstreamer-video,
+		[AS_HELP_STRING([--disable-gstreamer-video], [compile without GStreamer 1.0 Video Overlay support])],
+			enable_gstvideo="$enableval", enable_gstvideo="yes")
+	if test "x$enable_gstvideo" != "xno"; then
+		PKG_CHECK_MODULES(GSTVIDEO, [gstreamer-video-1.0], [
+			AC_DEFINE(USE_GSTVIDEO, 1, [Use GStreamer Video Overlay support])
+			AC_SUBST(GSTVIDEO_CFLAGS)
+			AC_SUBST(GSTVIDEO_LIBS)
+		], [
+			enable_gstvideo="no"
+		])
+	fi
+else
+	enable_gstvideo="no"
 fi
 
 dnl #######################################################################
 dnl # Check for GStreamer Interfaces
 dnl #######################################################################
-if test "x$enable_gst" != "xno"; then
+if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x0.10"; then
 	AC_ARG_ENABLE(gstreamer-interfaces,
-		[AC_HELP_STRING([--disable-gstreamer-interfaces], [compile without GStreamer interface support])],
+		[AS_HELP_STRING([--disable-gstreamer-interfaces], [compile without GStreamer 0.10 interface support])],
 			enable_gstinterfaces="$enableval", enable_gstinterfaces="yes")
 	if test "x$enable_gstinterfaces" != "xno"; then
 		PKG_CHECK_MODULES(GSTINTERFACES, [gstreamer-interfaces-0.10], [
-			AC_DEFINE(USE_GSTINTERFACES, 1, [Use GStreamer interfaces for X overlay support])
+			AC_DEFINE(USE_GSTINTERFACES, 1, [Use GStreamer 0.10 interfaces for X overlay support])
 			AC_SUBST(GSTINTERFACES_CFLAGS)
 			AC_SUBST(GSTINTERFACES_LIBS)
 		], [
@@ -889,32 +962,43 @@
 dnl # Check for Farstream
 dnl #######################################################################
 AC_ARG_ENABLE(farstream,
-	[AC_HELP_STRING([--disable-farstream], [compile without farstream support])],
+	[AS_HELP_STRING([--disable-farstream], [compile without farstream support])],
 	enable_farstream="$enableval", enable_farstream="yes")
 if test "x$enable_farstream" != "xno"; then
-	PKG_CHECK_MODULES(FARSTREAM, [farstream-0.1], [
-		AC_SUBST(FARSTREAM_CFLAGS)
-		AC_SUBST(FARSTREAM_LIBS)
- 	], [
-		# Try farsight.
-		PKG_CHECK_MODULES(FARSTREAM, [farsight2-0.10 >= 0.0.9], [
-		        AC_DEFINE(HAVE_FARSIGHT, 1, [Use Farsight instead of Farstream])
+	if test "x$with_gstreamer" == "x1.0"; then
+		PKG_CHECK_MODULES(FARSTREAM, [farstream-0.2], [
+			AC_SUBST(FARSTREAM_CFLAGS)
+			AC_SUBST(FARSTREAM_LIBS)
+ 		], [
+		        enable_farstream="no"
+		])
+	else
+		PKG_CHECK_MODULES(FARSTREAM, [farstream-0.1], [
 			AC_SUBST(FARSTREAM_CFLAGS)
 			AC_SUBST(FARSTREAM_LIBS)
-		], [
-		        enable_farstream="no"
-		])
- 	])
- fi
+ 		], [
+			# Try farsight.
+			PKG_CHECK_MODULES(FARSTREAM, [farsight2-0.10 >= 0.0.9], [
+			        AC_DEFINE(HAVE_FARSIGHT, 1, [Use Farsight instead of Farstream])
+				AC_SUBST(FARSTREAM_CFLAGS)
+				AC_SUBST(FARSTREAM_LIBS)
+			], [
+			        enable_farstream="no"
+			])
+ 		])
+	fi
+fi
 
 dnl #######################################################################
 dnl # Check for Voice and Video support
 dnl #######################################################################
 AC_ARG_ENABLE(vv,
-	[AC_HELP_STRING([--disable-vv], [compile without voice and video support])],
+	[AS_HELP_STRING([--disable-vv], [compile without voice and video support])],
 	enable_vv="$enableval", enable_vv="yes")
 if test "x$enable_vv" != "xno"; then
-	if test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno"; then
+	if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x1.0" -a "x$enable_gstvideo" != "xno" -a "x$enable_farstream" != "xno"; then
+		AC_DEFINE(USE_VV, 1, [Use voice and video])
+	elif test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x0.10" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno"; then
 		AC_DEFINE(USE_VV, 1, [Use voice and video])
 	else
 		enable_vv="no"
@@ -927,14 +1011,14 @@
 		fi
 	fi
 fi
-AM_CONDITIONAL(USE_VV, test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno")
+AM_CONDITIONAL(USE_VV, test "x$enable_vv" != "xno")
 
 dnl #######################################################################
 dnl # Check for Internationalized Domain Name support
 dnl #######################################################################
 
 AC_ARG_ENABLE(idn,
-	[AC_HELP_STRING([--disable-idn], [compile without IDN support])],
+	[AS_HELP_STRING([--disable-idn], [compile without IDN support])],
 	[enable_idn="$enableval" force_idn=$enableval], [enable_idn="yes" force_idn=no])
 if test "x$enable_idn" != "xno"; then
 	PKG_CHECK_MODULES(IDN, libidn >= 0.0.0, [
@@ -957,7 +1041,7 @@
 dnl # Check for Meanwhile headers (for Sametime)
 dnl #######################################################################
 AC_ARG_ENABLE(meanwhile,
-	[AC_HELP_STRING([--disable-meanwhile],
+	[AS_HELP_STRING([--disable-meanwhile],
 		[compile without meanwhile (required for Sametime support)])],
 	enable_meanwhile="$enableval", enable_meanwhile="yes")
 if test "x$enable_meanwhile" = "xyes"; then
@@ -979,13 +1063,13 @@
 dnl # Check for Native Avahi headers (for Bonjour)
 dnl #######################################################################
 AC_ARG_ENABLE(avahi,
-	[AC_HELP_STRING([--disable-avahi],
+	[AS_HELP_STRING([--disable-avahi],
 		[compile without avahi (required for Bonjour support)])],
 	enable_avahi="$enableval", enable_avahi="yes")
 
 if test "x$enable_avahi" = "xyes"; then
-	AC_ARG_WITH(avahi-client-includes, [AC_HELP_STRING([--with-avahi-client-includes=DIR], [compile the Bonjour plugin against the Avahi Client includes in DIR])], [ac_avahi_client_includes="$withval"], [ac_avahi_client_includes="no"])
-	AC_ARG_WITH(avahi-client-libs, [AC_HELP_STRING([--with-avahi-client-libs=DIR], [compile the Bonjour plugin against the Avahi Client libs in DIR])], [ac_avahi_client_libs="$withval"], [ac_avahi_client_libs="no"])
+	AC_ARG_WITH(avahi-client-includes, [AS_HELP_STRING([--with-avahi-client-includes=DIR], [compile the Bonjour plugin against the Avahi Client includes in DIR])], [ac_avahi_client_includes="$withval"], [ac_avahi_client_includes="no"])
+	AC_ARG_WITH(avahi-client-libs, [AS_HELP_STRING([--with-avahi-client-libs=DIR], [compile the Bonjour plugin against the Avahi Client libs in DIR])], [ac_avahi_client_libs="$withval"], [ac_avahi_client_libs="no"])
 	AVAHI_CFLAGS=""
 	AVAHI_LIBS=""
 
@@ -1032,8 +1116,8 @@
 dnl #######################################################################
 dnl # Check for SILC client includes and libraries
 dnl #######################################################################
-AC_ARG_WITH(silc-includes, [AC_HELP_STRING([--with-silc-includes=DIR], [compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"])
-AC_ARG_WITH(silc-libs, [AC_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"])
+AC_ARG_WITH(silc-includes, [AS_HELP_STRING([--with-silc-includes=DIR], [compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"])
+AC_ARG_WITH(silc-libs, [AS_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"])
 SILC_CFLAGS=""
 SILC_LIBS=""
 have_silc="no"
@@ -1079,8 +1163,8 @@
 dnl #######################################################################
 dnl # Check for Gadu-Gadu client includes and libraries
 dnl #######################################################################
-AC_ARG_WITH(gadu-includes, [AC_HELP_STRING([--with-gadu-includes=DIR], [compile the Gadu-Gadu plugin against includes in DIR])], [ac_gadu_includes="$withval"], [ac_gadu_includes="no"])
-AC_ARG_WITH(gadu-libs, [AC_HELP_STRING([--with-gadu-libs=DIR], [compile the Gadu-Gadu plugin against the libs in DIR])], [ac_gadu_libs="$withval"], [ac_gadu_libs="no"])
+AC_ARG_WITH(gadu-includes, [AS_HELP_STRING([--with-gadu-includes=DIR], [compile the Gadu-Gadu plugin against includes in DIR])], [ac_gadu_includes="$withval"], [ac_gadu_includes="no"])
+AC_ARG_WITH(gadu-libs, [AS_HELP_STRING([--with-gadu-libs=DIR], [compile the Gadu-Gadu plugin against the libs in DIR])], [ac_gadu_libs="$withval"], [ac_gadu_libs="no"])
 GADU_CFLAGS=""
 GADU_LIBS=""
 GADU_LIBGADU_VERSION=1.11.2
@@ -1183,7 +1267,7 @@
 AC_ARG_ENABLE(distrib,,,enable_distrib=no)
 AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes")
 DYNAMIC_PRPLS=all
-AC_ARG_WITH(static-prpls, [AC_HELP_STRING([--with-static-prpls], [Link to certain protocols statically])], [STATIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`], [STATIC_PRPLS=""])
+AC_ARG_WITH(static-prpls, [AS_HELP_STRING([--with-static-prpls], [Link to certain protocols statically])], [STATIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`], [STATIC_PRPLS=""])
 if test "x$STATIC_PRPLS" != "x" -a "x$DYNAMIC_PRPLS" = "xall"; then
 	DYNAMIC_PRPLS=""
 fi
@@ -1245,7 +1329,7 @@
 		simple)		static_simple=yes ;;
 		yahoo)		static_yahoo=yes ;;
 		zephyr)		static_zephyr=yes ;;
-		*)			echo "Invalid static protocol $i!!" ; exit ;;
+		*)			echo "Invalid static protocol $i!!" ; exit 1 ;;
 	esac
 done
 AM_CONDITIONAL(STATIC_BONJOUR, test "x$static_bonjour" = "xyes")
@@ -1266,7 +1350,7 @@
 AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init(void) { $load_proto },
 	[Loads static protocol plugin module initialization functions.])
 
-AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`])
+AC_ARG_WITH(dynamic_prpls, [AS_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`])
 if test "x$DYNAMIC_PRPLS" = "xall" ; then
 	DYNAMIC_PRPLS="bonjour gg irc jabber msn mxit myspace novell oscar sametime silc simple yahoo zephyr"
 fi
@@ -1299,19 +1383,19 @@
 		simple)		dynamic_simple=yes ;;
 		yahoo)		dynamic_yahoo=yes ;;
 		zephyr)		dynamic_zephyr=yes ;;
-		*)			echo "Invalid dynamic protocol $i!!" ; exit ;;
+		*)			echo "Invalid dynamic protocol $i!!" ; exit 1 ;;
 	esac
 done
 
-AC_ARG_ENABLE(plugins, [AC_HELP_STRING([--disable-plugins], [compile without plugin support])], , enable_plugins=yes)
-AC_ARG_WITH(krb4, [AC_HELP_STRING([--with-krb4=PREFIX], [compile Zephyr plugin with Kerberos 4 support])], kerberos="$withval", kerberos="no")
-AC_ARG_WITH(zephyr, [AC_HELP_STRING([--with-zephyr=PREFIX], [compile Zephyr plugin against external libzephyr])], zephyr="$withval", zephyr="no")
+AC_ARG_ENABLE(plugins, [AS_HELP_STRING([--disable-plugins], [compile without plugin support])], , enable_plugins=yes)
+AC_ARG_WITH(krb4, [AS_HELP_STRING([--with-krb4=PREFIX], [compile Zephyr plugin with Kerberos 4 support])], kerberos="$withval", kerberos="no")
+AC_ARG_WITH(zephyr, [AS_HELP_STRING([--with-zephyr=PREFIX], [compile Zephyr plugin against external libzephyr])], zephyr="$withval", zephyr="no")
 AM_CONDITIONAL(EXTERNAL_LIBZEPHYR, test "x$zephyr" != "xno")
 
 AC_CHECK_HEADERS(sys/utsname.h)
 AC_CHECK_FUNC(uname)
 
-AC_ARG_ENABLE(fortify, [AC_HELP_STRING([--disable-fortify], [compile without FORTIFY_SOURCE support])], , enable_fortify=yes)
+AC_ARG_ENABLE(fortify, [AS_HELP_STRING([--disable-fortify], [compile without FORTIFY_SOURCE support])], , enable_fortify=yes)
 
 DEBUG_CFLAGS="$DEBUG_CFLAGS -DPURPLE_DISABLE_DEPRECATED -DPIDGIN_DISABLE_DEPRECATED -DFINCH_DISABLE_DEPRECATED -DGNT_DISABLE_DEPRECATED"
 if test "x$GCC" = "xyes"; then
@@ -1404,8 +1488,8 @@
 dnl # Check for D-Bus libraries
 dnl #######################################################################
 
-AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--disable-dbus], [disable D-Bus support])], , enable_dbus=yes)
-AC_ARG_ENABLE(nm, [AC_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes)
+AC_ARG_ENABLE(dbus, [AS_HELP_STRING([--disable-dbus], [disable D-Bus support])], , enable_dbus=yes)
+AC_ARG_ENABLE(nm, [AS_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes)
 
 if test "x$enable_dbus" = "xyes" ; then
 	AC_CHECK_PROG(enable_dbus, dbus-binding-tool, yes, no)
@@ -1608,7 +1692,7 @@
 dnl in C (brrrr ...).
 
 AC_ARG_WITH([python],
-			AC_HELP_STRING([--with-python=PATH],
+			AS_HELP_STRING([--with-python=PATH],
 						   [which python interpreter to use for dbus code generation]),
 			PYTHON=$withval)
 
@@ -1642,7 +1726,7 @@
 dnl # although a newer dbus is installed.  But I have tried to order the
 dnl # directory searching to keep this situation at a minimum.
 dnl ###########################################################################
-AC_ARG_WITH(dbus-services, [AC_HELP_STRING([--with-dbus-services=<dir>], [where the D-Bus services directory is located.])])
+AC_ARG_WITH(dbus-services, [AS_HELP_STRING([--with-dbus-services=<dir>], [where the D-Bus services directory is located.])])
 
 DBUS_SERVICES_DIR=""
 
@@ -1723,7 +1807,7 @@
 dnl #######################################################################
 dnl # Check for Mono support
 dnl #######################################################################
-AC_ARG_ENABLE(mono, [AC_HELP_STRING([--enable-mono], [compile with Mono runtime support (experimental)])], , enable_mono=no)
+AC_ARG_ENABLE(mono, [AS_HELP_STRING([--enable-mono], [compile with Mono runtime support (experimental)])], , enable_mono=no)
 if test x"$enable_mono" = x"yes" ; then
 	PKG_CHECK_MODULES(MONO, mono, [
 		AC_SUBST(MONO_CFLAGS)
@@ -1764,7 +1848,7 @@
 dnl #######################################################################
 dnl # Check for Perl support
 dnl #######################################################################
-AC_ARG_ENABLE(perl, [AC_HELP_STRING([--disable-perl], [compile without perl scripting])], , enable_perl=yes)
+AC_ARG_ENABLE(perl, [AS_HELP_STRING([--disable-perl], [compile without perl scripting])], , enable_perl=yes)
 
 if test "$enable_plugins" = no ; then
 	enable_perl=no
@@ -1887,7 +1971,7 @@
 dnl # Thanks go to Evolution for the checks.
 dnl #######################################################################
 
-AC_ARG_WITH(system-ssl-certs, [AC_HELP_STRING([--with-system-ssl-certs=<dir>], [directory containing system-wide SSL CA certificates])], [ssl_certificates_dir=$withval])
+AC_ARG_WITH(system-ssl-certs, [AS_HELP_STRING([--with-system-ssl-certs=<dir>], [directory containing system-wide SSL CA certificates])], [ssl_certificates_dir=$withval])
 
 SSL_CERTIFICATES_DIR=""
 if ! test -z "$ssl_certificates_dir" ; then
@@ -1957,7 +2041,7 @@
 	fi
 
 	AC_ARG_WITH(gnutls-libs,
-		[AC_HELP_STRING([--with-gnutls-libs=PREFIX], [location of GnuTLS libraries.])],
+		[AS_HELP_STRING([--with-gnutls-libs=PREFIX], [location of GnuTLS libraries.])],
 		[ with_gnutls_libs="$withval" ])
 
 	if test "x$with_gnutls_libs"     != "xno" -a \
@@ -1972,8 +2056,8 @@
 
 		AC_CACHE_CHECK([for GnuTLS libraries], ac_cv_gnutls_libs,
 		[
-			LIBS="$LIBS $with_gnutls_libs -lgnutls -lgcrypt"
-			AC_TRY_LINK_FUNC(gnutls_init, ac_cv_gnutls_libs="yes", ac_cv_gnutls_libs="no")
+			LIBS="$LIBS $with_gnutls_libs -lgnutls"
+			AC_LINK_IFELSE([AC_LANG_CALL([], [gnutls_init])], ac_cv_gnutls_libs="yes", ac_cv_gnutls_libs="no")
 			LIBS="$LIBS_save"
 		])
 
@@ -1981,7 +2065,7 @@
 			AC_DEFINE(HAVE_GNUTLS, 1, [Define if you have GnuTLS])
 			AC_DEFINE(HAVE_SSL)
 			msg_gnutls="GnuTLS"
-			GNUTLS_LIBS="$with_gnutls_libs -lgnutls -lgcrypt"
+			GNUTLS_LIBS="$with_gnutls_libs -lgnutls"
 
 			enable_gnutls="yes"
 		else
@@ -2044,19 +2128,19 @@
 	looked_for_nss="yes"
 
 	AC_ARG_WITH(nspr-includes,
-		[AC_HELP_STRING([--with-nspr-includes=PREFIX], [specify location of Mozilla nspr4 includes.])],
+		[AS_HELP_STRING([--with-nspr-includes=PREFIX], [specify location of Mozilla nspr4 includes.])],
 		[with_nspr_includes="$withval"])
 
 	AC_ARG_WITH(nspr-libs,
-		[AC_HELP_STRING([--with-nspr-libs=PREFIX], [specify location of Mozilla nspr4 libs.])],
+		[AS_HELP_STRING([--with-nspr-libs=PREFIX], [specify location of Mozilla nspr4 libs.])],
 		[with_nspr_libs="$withval"])
 
 	AC_ARG_WITH(nss-includes,
-		[AC_HELP_STRING([--with-nss-includes=PREFIX], [specify location of Mozilla nss3 includes.])],
+		[AS_HELP_STRING([--with-nss-includes=PREFIX], [specify location of Mozilla nss3 includes.])],
 		[with_nss_includes="$withval"])
 
 	AC_ARG_WITH(nss-libs,
-		[AC_HELP_STRING([--with-nss-libs=PREFIX], [specify location of Mozilla nss3 libs.])],
+		[AS_HELP_STRING([--with-nss-libs=PREFIX], [specify location of Mozilla nss3 libs.])],
 		[with_nss_libs="$withval"])
 
 
@@ -2176,7 +2260,7 @@
 					LDFLAGS="$LDFLAGS"
 				fi
 
-				AC_TRY_LINK_FUNC(PR_Init,
+				AC_LINK_IFELSE([AC_LANG_CALL([], [PR_Init])],
 					[ac_cv_moz_nspr_libs="yes"],
 					[ac_cv_moz_nspr_libs="no"])
 
@@ -2264,7 +2348,7 @@
 				LDFLAGS="$LDFLAGS -L$with_nspr_libs -L$with_nss_libs"
 				LIBS="$nsslibs $nsprlibs"
 
-				AC_TRY_LINK_FUNC(NSS_Init,
+				AC_LINK_IFELSE([AC_LANG_CALL([], [NSS_Init])],
 					[ac_cv_moz_nss_libs="yes"],
 					[ac_cv_moz_nss_libs="no"])
 
@@ -2272,7 +2356,7 @@
 					nsslibs="-lssl3 -lsmime3 -lnss3 -lsoftokn3"
 					LDFLAGS="$LDFLAGS -L$with_nspr_libs -L$with_nss_libs"
 					LIBS="$LIBS $nsslibs"
-					AC_TRY_LINK_FUNC(NSS_Init,
+					AC_LINK_IFELSE([AC_LANG_CALL([], [NSS_Init])],
 						[ac_cv_moz_nss_libs="yes"],
 						[ac_cv_moz_nss_libs="no"])
 				fi
@@ -2344,9 +2428,9 @@
 dnl #######################################################################
 dnl # Check for Tcl
 dnl #######################################################################
-AC_ARG_ENABLE(tcl, [AC_HELP_STRING([--disable-tcl],
+AC_ARG_ENABLE(tcl, [AS_HELP_STRING([--disable-tcl],
 	[compile without Tcl scripting])], enable_tcl="$enableval", enable_tcl="yes")
-AC_ARG_WITH(tclconfig, [AC_HELP_STRING([--with-tclconfig=DIR],
+AC_ARG_WITH(tclconfig, [AS_HELP_STRING([--with-tclconfig=DIR],
 	[directory containing tclConfig.sh])])
 
 if test "$enable_plugins" = no; then
@@ -2423,9 +2507,9 @@
 dnl #######################################################################
 dnl # Check for Tk
 dnl #######################################################################
-AC_ARG_ENABLE(tk, [AC_HELP_STRING([--disable-tk],
+AC_ARG_ENABLE(tk, [AS_HELP_STRING([--disable-tk],
 	[compile without Tcl support for Tk])], enable_tk="$enableval", enable_tk="yes")
-AC_ARG_WITH(tkconfig, [AC_HELP_STRING([--with-tkconfig=DIR],
+AC_ARG_WITH(tkconfig, [AS_HELP_STRING([--with-tkconfig=DIR],
 	[directory containing tkConfig.sh])])
 
 if test "$enable_tcl" = yes -a "$enable_tk" = yes; then
@@ -2507,7 +2591,7 @@
 dnl AC_CHECK_SIZEOF(short)
 AC_CHECK_FUNCS(snprintf connect)
 AC_SUBST(SASL_LIBS)
-AC_ARG_ENABLE(cyrus-sasl, AC_HELP_STRING([--enable-cyrus-sasl], [enable Cyrus SASL support for jabberd]), enable_cyrus_sasl=$enableval, enable_cyrus_sasl=no)
+AC_ARG_ENABLE(cyrus-sasl, AS_HELP_STRING([--enable-cyrus-sasl], [enable Cyrus SASL support for jabberd]), enable_cyrus_sasl=$enableval, enable_cyrus_sasl=no)
 if test "x$enable_cyrus_sasl" = "xyes" ; then
 	AC_CHECK_LIB(sasl2, sasl_client_init, [
 			AM_CONDITIONAL(USE_CYRUS_SASL, true)
@@ -2658,14 +2742,14 @@
 dnl #######################################################################
 dnl # Disable pixmap installation
 dnl #######################################################################
-AC_ARG_ENABLE(pixmaps-install, AC_HELP_STRING([--disable-pixmaps-install], [disable installation of pixmap files - Pidgin still needs them!]), enable_pixmaps="$enableval", enable_pixmaps=yes)
+AC_ARG_ENABLE(pixmaps-install, AS_HELP_STRING([--disable-pixmaps-install], [disable installation of pixmap files - Pidgin still needs them!]), enable_pixmaps="$enableval", enable_pixmaps=yes)
 
 AM_CONDITIONAL(INSTALL_PIXMAPS, test "x$enable_pixmaps" = "xyes")
 
 dnl #######################################################################
 dnl # Tweak status tray icon installation directory
 dnl #######################################################################
-AC_ARG_ENABLE(trayicon-compat, AC_HELP_STRING([--enable-trayicon-compat], [install tray icons in location compatible with older releases of hicolor-icon-theme]), enable_traycompat="$enableval", enable_traycompat=no)
+AC_ARG_ENABLE(trayicon-compat, AS_HELP_STRING([--enable-trayicon-compat], [install tray icons in location compatible with older releases of hicolor-icon-theme]), enable_traycompat="$enableval", enable_traycompat=no)
 
 AM_CONDITIONAL(ENABLE_TRAYCOMPAT, test "x$enable_traycompat" = "xyes")
 
@@ -2673,15 +2757,15 @@
 dnl # Check for Doxygen and dot (part of GraphViz)
 dnl #######################################################################
 AC_ARG_ENABLE(doxygen,
-	[AC_HELP_STRING([--disable-doxygen],
+	[AS_HELP_STRING([--disable-doxygen],
 		[disable documentation with doxygen])],
 	enable_doxygen="$enableval", enable_doxygen="yes")
 AC_ARG_ENABLE(dot,
-	[AC_HELP_STRING([--disable-dot],
+	[AS_HELP_STRING([--disable-dot],
 		[disable graphs in doxygen via 'dot'])],
 	enable_dot="$enableval", enable_dot="yes")
 AC_ARG_ENABLE(devhelp,
-	[AC_HELP_STRING([--disable-devhelp],
+	[AS_HELP_STRING([--disable-devhelp],
 		[disable building index for devhelp documentation browser])],
 	enable_devhelp="$enableval", enable_devhelp="yes")
 
@@ -2726,7 +2810,7 @@
 AM_CONDITIONAL(HAVE_DOXYGEN, test "x$enable_doxygen" = "xyes")
 AM_CONDITIONAL(HAVE_XSLTPROC, test "x$enable_devhelp" = "xyes")
 
-AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug],
+AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug],
 	[compile with debugging support])], , enable_debug=no)
 
 if test "x$enable_debug" = "xyes" ; then
@@ -2822,6 +2906,7 @@
 echo Protocols to link statically.. : $STATIC_PRPLS
 echo
 echo Build with GStreamer support.. : $enable_gst
+echo Build for GStreamer version... : $with_gstreamer
 echo Build with D-Bus support...... : $enable_dbus
 echo Build with voice and video.... : $enable_vv
 if test "x$enable_dbus" = "xyes" ; then
--- a/finch/finch.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/finch.c	Fri Jan 25 02:22:38 2013 -0500
@@ -219,11 +219,7 @@
 	gnt_input_add,
 	g_source_remove,
 	NULL, /* input_get_error */
-#if GLIB_CHECK_VERSION(2,14,0)
 	g_timeout_add_seconds,
-#else
-	NULL,
-#endif
 
 	/* padding */
 	NULL,
@@ -381,19 +377,12 @@
 		abort();
 	}
 
-	/* TODO: Move blist loading into purple_blist_init() */
-	purple_set_blist(purple_blist_new());
-	purple_blist_load();
-
 	/* TODO: should this be moved into finch_prefs_init() ? */
 	finch_prefs_update_old();
 
 	/* load plugins we had when we quit */
 	purple_plugins_load_saved("/finch/plugins/loaded");
 
-	/* TODO: Move pounces loading into purple_pounces_init() */
-	purple_pounces_load();
-
 	if (opt_nologin)
 	{
 		/* Set all accounts to "offline" */
--- a/finch/gntsound.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/gntsound.c	Fri Jan 25 02:22:38 2013 -0500
@@ -553,7 +553,11 @@
 		return;
 	}
 
+#if GST_CHECK_VERSION(1,0,0)
 	play = gst_element_factory_make("playbin", "play");
+#else
+	play = gst_element_factory_make("playbin2", "play");
+#endif
 
 	if (play == NULL) {
 		return;
--- a/finch/libgnt/gnt.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/libgnt/gnt.h	Fri Jan 25 02:22:38 2013 -0500
@@ -40,19 +40,6 @@
 #include "gntkeys.h"
 
 /**
- * Get things to compile in Glib < 2.8
- */
-#if !GLIB_CHECK_VERSION(2,8,0)
-	#define G_PARAM_STATIC_NAME  G_PARAM_PRIVATE
-	#define G_PARAM_STATIC_NICK  G_PARAM_PRIVATE
-	#define G_PARAM_STATIC_BLURB  G_PARAM_PRIVATE
-#endif
-
-#if !GLIB_CHECK_VERSION(2,14,0)
-	#define g_timeout_add_seconds(time, callback, data)  g_timeout_add(time * 1000, callback, data)
-#endif
-
-/**
  * Initialize GNT.
  */
 void gnt_init(void);
--- a/finch/libgnt/gntcolors.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/libgnt/gntcolors.c	Fri Jan 25 02:22:38 2013 -0500
@@ -142,7 +142,6 @@
 		restore_colors();
 }
 
-#if GLIB_CHECK_VERSION(2,6,0)
 int
 gnt_colors_get_color(char *key)
 {
@@ -293,8 +292,6 @@
 	g_strfreev(keys);
 }
 
-#endif  /* GKeyFile */
-
 int gnt_color_pair(int pair)
 {
 	return (hascolors ? COLOR_PAIR(pair) :
--- a/finch/libgnt/gntcolors.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/libgnt/gntcolors.h	Fri Jan 25 02:22:38 2013 -0500
@@ -71,7 +71,6 @@
  */
 void gnt_uninit_colors(void);
 
-#if GLIB_CHECK_VERSION(2,6,0)
 /**
  * Parse color information from a file.
  *
@@ -96,7 +95,6 @@
  * @since 2.4.0
  */
 int gnt_colors_get_color(char *key);
-#endif
 
 /**
  * Return the appropriate character attribute for a specified color.
--- a/finch/libgnt/gntfilesel.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/libgnt/gntfilesel.c	Fri Jan 25 02:22:38 2013 -0500
@@ -66,106 +66,6 @@
 	}
 }
 
-#if !GLIB_CHECK_VERSION(2,8,0)
-/* ripped from glib/gfileutils.c */
-static gchar *
-g_build_path_va (const gchar  *separator,
-		gchar       **str_array)
-{
-	GString *result;
-	gint separator_len = strlen (separator);
-	gboolean is_first = TRUE;
-	gboolean have_leading = FALSE;
-	const gchar *single_element = NULL;
-	const gchar *next_element;
-	const gchar *last_trailing = NULL;
-	gint i = 0;
-
-	result = g_string_new (NULL);
-
-	next_element = str_array[i++];
-
-	while (TRUE) {
-		const gchar *element;
-		const gchar *start;
-		const gchar *end;
-
-		if (next_element) {
-			element = next_element;
-			next_element = str_array[i++];
-		} else
-			break;
-
-		/* Ignore empty elements */
-		if (!*element)
-			continue;
-
-		start = element;
-
-		if (separator_len) {
-			while (start &&
-					strncmp (start, separator, separator_len) == 0)
-				start += separator_len;
-		}
-
-		end = start + strlen (start);
-
-		if (separator_len) {
-			while (end >= start + separator_len &&
-					strncmp (end - separator_len, separator, separator_len) == 0)
-				end -= separator_len;
-
-			last_trailing = end;
-			while (last_trailing >= element + separator_len &&
-					strncmp (last_trailing - separator_len, separator, separator_len) == 0)
-				last_trailing -= separator_len;
-
-			if (!have_leading) {
-				/* If the leading and trailing separator strings are in the
-				 * same element and overlap, the result is exactly that element
-				 */
-				if (last_trailing <= start)
-					single_element = element;
-
-				g_string_append_len (result, element, start - element);
-				have_leading = TRUE;
-			} else
-				single_element = NULL;
-		}
-
-		if (end == start)
-			continue;
-
-		if (!is_first)
-			g_string_append (result, separator);
-
-		g_string_append_len (result, start, end - start);
-		is_first = FALSE;
-	}
-
-	if (single_element) {
-		g_string_free (result, TRUE);
-		return g_strdup (single_element);
-	} else {
-		if (last_trailing)
-			g_string_append (result, last_trailing);
-
-		return g_string_free (result, FALSE);
-	}
-}
-
-static gchar *
-g_build_pathv (const gchar  *separator,
-		gchar       **args)
-{
-	if (!args)
-		return NULL;
-
-	return g_build_path_va (separator, args);
-}
-
-#endif
-
 static char *
 process_path(const char *path)
 {
--- a/finch/libgnt/gntmain.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/libgnt/gntmain.c	Fri Jan 25 02:22:38 2013 -0500
@@ -675,7 +675,6 @@
 	return gnt_clipboard_get_string(clipboard);
 }
 
-#if GLIB_CHECK_VERSION(2,4,0)
 typedef struct
 {
 	void (*callback)(int status, gpointer data);
@@ -697,13 +696,11 @@
 	refresh();
 	refresh_screen();
 }
-#endif
 
 gboolean gnt_giveup_console(const char *wd, char **argv, char **envp,
 		gint *stin, gint *stout, gint *sterr,
 		void (*callback)(int status, gpointer data), gpointer data)
 {
-#if GLIB_CHECK_VERSION(2,4,0)
 	GPid pid = 0;
 	ChildProcess *cp = NULL;
 
@@ -721,18 +718,11 @@
 	g_child_watch_add(pid, reap_child, cp);
 
 	return TRUE;
-#else
-	return FALSE;
-#endif
 }
 
 gboolean gnt_is_refugee()
 {
-#if GLIB_CHECK_VERSION(2,4,0)
 	return (wm && wm->mode == GNT_KP_MODE_WAIT_ON_CHILD);
-#else
-	return FALSE;
-#endif
 }
 
 const char *C_(const char *x)
--- a/finch/libgnt/gntprogressbar.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/libgnt/gntprogressbar.c	Fri Jan 25 02:22:38 2013 -0500
@@ -36,16 +36,9 @@
 struct _GntProgressBar
 {
 	GntWidget parent;
-#if !GLIB_CHECK_VERSION(2,4,0)
-	GntProgressBarPrivate priv;
-#endif
 };
 
-#if GLIB_CHECK_VERSION(2,4,0)
 #define GNT_PROGRESS_BAR_GET_PRIVATE(o)   (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNT_TYPE_PROGRESS_BAR, GntProgressBarPrivate))
-#else
-#define GNT_PROGRESS_BAR_GET_PRIVATE(o)   &(GNT_PROGRESS_BAR(o)->priv)
-#endif
 
 static GntWidgetClass *parent_class = NULL;
 
@@ -128,9 +121,7 @@
 
 	parent_class = GNT_WIDGET_CLASS (klass);
 
-#if GLIB_CHECK_VERSION(2,4,0)
 	g_type_class_add_private (g_class, sizeof (GntProgressBarPrivate));
-#endif
 
 	parent_class->draw = gnt_progress_bar_draw;
 	parent_class->size_request = gnt_progress_bar_size_request;
--- a/finch/libgnt/gntstyle.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/libgnt/gntstyle.c	Fri Jan 25 02:22:38 2013 -0500
@@ -35,9 +35,7 @@
 
 #define MAX_WORKSPACES 99
 
-#if GLIB_CHECK_VERSION(2,6,0)
 static GKeyFile *gkfile;
-#endif
 
 static char * str_styles[GNT_STYLES];
 static int int_styles[GNT_STYLES];
@@ -50,7 +48,6 @@
 
 char *gnt_style_get_from_name(const char *group, const char *key)
 {
-#if GLIB_CHECK_VERSION(2,6,0)
 	const char *prg = g_get_prgname();
 	if ((group == NULL || *group == '\0') && prg &&
 			g_key_file_has_group(gkfile, prg))
@@ -58,15 +55,11 @@
 	if (!group)
 		group = "general";
 	return g_key_file_get_value(gkfile, group, key, NULL);
-#else
-	return NULL;
-#endif
 }
 
 int
 gnt_style_get_color(char *group, char *key)
 {
-#if GLIB_CHECK_VERSION(2,6,0)
 	int fg = 0, bg = 0;
 	gsize n;
 	char **vals;
@@ -79,14 +72,10 @@
 	}
 	g_strfreev(vals);
 	return ret;
-#else
-	return 0;
-#endif
 }
 
 char **gnt_style_get_string_list(const char *group, const char *key, gsize *length)
 {
-#if GLIB_CHECK_VERSION(2,6,0)
 	const char *prg = g_get_prgname();
 	if ((group == NULL || *group == '\0') && prg &&
 			g_key_file_has_group(gkfile, prg))
@@ -94,9 +83,6 @@
 	if (!group)
 		group = "general";
 	return g_key_file_get_string_list(gkfile, group, key, length, NULL);
-#else
-	return NULL;
-#endif
 }
 
 gboolean gnt_style_get_bool(GntStyle style, gboolean def)
@@ -134,7 +120,6 @@
 	return def;
 }
 
-#if GLIB_CHECK_VERSION(2,6,0)
 static void
 refine(char *text)
 {
@@ -175,11 +160,9 @@
 {
 	return (char *)gnt_key_translate(key);
 }
-#endif
 
 void gnt_style_read_workspaces(GntWM *wm)
 {
-#if GLIB_CHECK_VERSION(2,6,0)
 	int i;
 	gchar *name;
 	gsize c;
@@ -212,11 +195,10 @@
 			g_strfreev(titles);
 		}
 	}
-#endif
 }
+
 void gnt_style_read_actions(GType type, GntBindableClass *klass)
 {
-#if GLIB_CHECK_VERSION(2,6,0)
 	char *name;
 	GError *error = NULL;
 
@@ -264,12 +246,10 @@
 		g_strfreev(keys);
 	}
 	g_free(name);
-#endif
 }
 
 gboolean gnt_style_read_menu_accels(const char *name, GHashTable *table)
 {
-#if GLIB_CHECK_VERSION(2,6,0)
 	char *kname;
 	GError *error = NULL;
 	gboolean ret = FALSE;
@@ -322,13 +302,10 @@
 
 	g_free(kname);
 	return ret;
-#endif
-	return FALSE;
 }
 
 void gnt_styles_get_keyremaps(GType type, GHashTable *hash)
 {
-#if GLIB_CHECK_VERSION(2,6,0)
 	char *name;
 	GError *error = NULL;
 
@@ -373,10 +350,8 @@
 	}
 
 	g_free(name);
-#endif
 }
 
-#if GLIB_CHECK_VERSION(2,6,0)
 static void
 read_general_style(GKeyFile *kfile)
 {
@@ -419,11 +394,9 @@
 	}
 	g_strfreev(keys);
 }
-#endif
 
 void gnt_style_read_configure_file(const char *filename)
 {
-#if GLIB_CHECK_VERSION(2,6,0)
 	GError *error = NULL;
 	gkfile = g_key_file_new();
 
@@ -436,7 +409,6 @@
 	}
 	gnt_colors_parse(gkfile);
 	read_general_style(gkfile);
-#endif
 }
 
 void gnt_init_styles()
@@ -458,9 +430,7 @@
 		str_styles[i] = NULL;
 	}
 
-#if GLIB_CHECK_VERSION(2,6,0)
 	g_key_file_free(gkfile);
 	gkfile = NULL;
-#endif
 }
 
--- a/finch/libgnt/gntwm.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/libgnt/gntwm.c	Fri Jan 25 02:22:38 2013 -0500
@@ -37,14 +37,7 @@
 #endif
 
 #include <glib.h>
-#if GLIB_CHECK_VERSION(2,6,0)
-#	include <glib/gstdio.h>
-#else
-#	include <sys/types.h>
-#	include <sys/stat.h>
-#	include <fcntl.h>
-#	define g_fopen open
-#endif
+#include <glib/gstdio.h>
 #include <ctype.h>
 #include <gmodule.h>
 #include <stdlib.h>
@@ -337,7 +330,6 @@
 static void
 read_window_positions(GntWM *wm)
 {
-#if GLIB_CHECK_VERSION(2,6,0)
 	GKeyFile *gfile = g_key_file_new();
 	char *filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL);
 	GError *error = NULL;
@@ -379,7 +371,6 @@
 
 	g_free(filename);
 	g_key_file_free(gfile);
-#endif
 }
 
 static gboolean check_idle(gpointer n)
@@ -1753,31 +1744,6 @@
 	return FALSE;
 }
 
-#if !GLIB_CHECK_VERSION(2,4,0)
-struct
-{
-	gpointer data;
-	gpointer value;
-} table_find_data;
-
-static void
-table_find_helper(gpointer key, gpointer value, gpointer data)
-{
-	GHRFunc func = data;
-	if (func(key, value, table_find_data.data))
-		table_find_data.value = value;
-}
-
-static gpointer
-g_hash_table_find(GHashTable * table, GHRFunc func, gpointer data)
-{
-	table_find_data.data = data;
-	table_find_data.value = NULL;
-	g_hash_table_foreach(table, table_find_helper, func);
-	return table_find_data.value;
-}
-#endif
-
 static GntWS *
 new_widget_find_workspace(GntWM *wm, GntWidget *widget)
 {
--- a/finch/plugins/gnttinyurl.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/finch/plugins/gnttinyurl.c	Fri Jan 25 02:22:38 2013 -0500
@@ -21,6 +21,7 @@
 
 #include "internal.h"
 #include <glib.h>
+#include "obsolete.h"
 
 #define PLUGIN_STATIC_NAME	TinyURL
 #define PREFS_BASE          "/plugins/gnt/tinyurl"
--- a/libpurple/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -50,6 +50,7 @@
 	desktopitem.c \
 	eventloop.c \
 	ft.c \
+	http.c \
 	idle.c \
 	imgstore.c \
 	keyring.c \
@@ -66,6 +67,7 @@
 	network.c \
 	ntlm.c \
 	notify.c \
+	obsolete.c \
 	plugin.c \
 	pluginpref.c \
 	pounce.c \
@@ -118,6 +120,7 @@
 	desktopitem.h \
 	eventloop.h \
 	ft.h \
+	http.h \
 	idle.h \
 	imgstore.h \
 	keyring.h \
@@ -285,6 +288,7 @@
 
 noinst_HEADERS= \
 	internal.h \
+	obsolete.h \
 	media/backend-fs2.h \
 	valgrind.h
 
@@ -309,8 +313,10 @@
 	$(INTLLIBS) \
 	$(FARSTREAM_LIBS) \
 	$(GSTREAMER_LIBS) \
+	$(GSTVIDEO_LIBS) \
 	$(GSTINTERFACES_LIBS) \
 	$(IDN_LIBS) \
+	$(JSON_LIBS) \
 	ciphers/libpurple-ciphers.la \
 	-lm
 
@@ -325,9 +331,11 @@
 	$(LIBXML_CFLAGS) \
 	$(FARSTREAM_CFLAGS) \
 	$(GSTREAMER_CFLAGS) \
+	$(GSTVIDEO_CFLAGS) \
 	$(GSTINTERFACES_CFLAGS) \
 	$(IDN_CFLAGS) \
-	$(NETWORKMANAGER_CFLAGS)
+	$(NETWORKMANAGER_CFLAGS) \
+	$(JSON_CFLAGS)
 
 # INSTALL_SSL_CERTIFICATES is true when SSL_CERTIFICATES_DIR is empty.
 # We want to use SSL_CERTIFICATES_DIR when it's not empty.
--- a/libpurple/Makefile.mingw	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/Makefile.mingw	Fri Jan 25 02:22:38 2013 -0500
@@ -10,6 +10,19 @@
 TARGET = libpurple
 NEEDED_DLLS = $(LIBXML2_TOP)/bin/libxml2-2.dll
 
+ifeq ($(CYRUS_SASL), 1)
+NEEDED_DLLS += $(CYRUS_SASL_TOP)/bin/libsasl.dll
+
+CYRUS_SASL_PLUGINS = \
+                        $(CYRUS_SASL_TOP)/bin/sasl2/saslANONYMOUS.dll \
+                        $(CYRUS_SASL_TOP)/bin/sasl2/saslCRAMMD5.dll \
+                        $(CYRUS_SASL_TOP)/bin/sasl2/saslDIGESTMD5.dll \
+                        $(CYRUS_SASL_TOP)/bin/sasl2/saslGSSAPI.dll \
+                        $(CYRUS_SASL_TOP)/bin/sasl2/saslLOGIN.dll \
+                        $(CYRUS_SASL_TOP)/bin/sasl2/saslPLAIN.dll
+
+endif
+
 ##
 ## INCLUDE PATHS
 ##
@@ -39,10 +52,7 @@
 			ciphers/gchecksum.c \
 			ciphers/hmac.c \
 			ciphers/md4.c \
-			ciphers/md5.c \
 			ciphers/rc4.c \
-			ciphers/sha1.c \
-			ciphers/sha256.c \
 			circbuffer.c \
 			cmds.c \
 			connection.c \
@@ -126,6 +136,10 @@
 install_shallow: $(PURPLE_INSTALL_DIR) $(TARGET).dll
 	cp $(TARGET).dll $(PURPLE_INSTALL_DIR)
 	cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR)
+ifeq ($(CYRUS_SASL), 1)
+	mkdir -p $(PURPLE_INSTALL_DIR)/sasl2
+	cp $(CYRUS_SASL_PLUGINS) $(PURPLE_INSTALL_DIR)/sasl2
+endif
 
 install: install_shallow all
 	$(MAKE) -C $(PURPLE_PROTOS_TOP) -f $(MINGW_MAKEFILE) install
--- a/libpurple/accountopt.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/accountopt.c	Fri Jan 25 02:22:38 2013 -0500
@@ -27,6 +27,7 @@
 
 #include "accountopt.h"
 #include "util.h"
+#include "glibcompat.h"
 
 /**
  * An option for an account.
--- a/libpurple/blist.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/blist.c	Fri Jan 25 02:22:38 2013 -0500
@@ -597,9 +597,8 @@
 	}
 }
 
-/* TODO: Make static and rename to load_blist */
-void
-purple_blist_load()
+static void
+load_blist(void)
 {
 	xmlnode *purple, *blist, *privacy;
 
@@ -721,7 +720,8 @@
  * Public API functions                                                      *
  *****************************************************************************/
 
-PurpleBuddyList *purple_blist_new()
+void
+purple_blist_boot(void)
 {
 	PurpleBlistUiOps *ui_ops;
 	GList *account;
@@ -749,13 +749,9 @@
 	if (ui_ops != NULL && ui_ops->new_list != NULL)
 		ui_ops->new_list(gbl);
 
-	return gbl;
-}
-
-void
-purple_set_blist(PurpleBuddyList *list)
-{
-	purplebuddylist = list;
+	purplebuddylist = gbl;
+
+	load_blist();
 }
 
 PurpleBuddyList *
--- a/libpurple/blist.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/blist.h	Fri Jan 25 02:22:38 2013 -0500
@@ -252,22 +252,6 @@
 /*@{*/
 
 /**
- * Creates a new buddy list
- *
- * @return The new buddy list.
- * @deprecated In 3.0.0, this will be handled by purple_blist_init()
- */
-PurpleBuddyList *purple_blist_new(void);
-
-/**
- * Sets the main buddy list.
- *
- * @param blist The buddy list you want to use.
- * @deprecated In 3.0.0, this will be handled by purple_blist_init()
- */
-void purple_set_blist(PurpleBuddyList *blist);
-
-/**
  * Returns the main buddy list.
  *
  * @return The main buddy list.
@@ -1008,11 +992,6 @@
 /****************************************************************************************/
 
 /**
- * Loads the buddy list from ~/.purple/blist.xml.
- */
-void purple_blist_load(void);
-
-/**
  * Schedule a save of the blist.xml file.  This is used by the privacy
  * API whenever the privacy settings are changed.  If you make a change
  * to blist.xml using one of the functions in the buddy list API, then
@@ -1204,6 +1183,13 @@
 void purple_blist_init(void);
 
 /**
+ * Loads the buddy list.
+ *
+ * You shouldn't call this. purple_core_init() will do it for you.
+ */
+void purple_blist_boot(void);
+
+/**
  * Uninitializes the buddy list subsystem.
  */
 void purple_blist_uninit(void);
--- a/libpurple/buddyicon.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/buddyicon.c	Fri Jan 25 02:22:38 2013 -0500
@@ -42,7 +42,7 @@
 	                                the icon data.                       */
 	char *username;            /**< The username the icon belongs to.    */
 	char *checksum;            /**< The protocol checksum.               */
-	int ref_count;             /**< The buddy icon reference count.      */
+	unsigned int ref_count;    /**< The buddy icon reference count.      */
 };
 
 /**
@@ -75,7 +75,7 @@
 static GHashTable *icon_data_cache = NULL;
 
 /**
- * This hash table contains references counts for how many times each
+ * This hash table contains reference counts for how many times each
  * icon in the ~/.purple/icons/ directory is being used.  It's pretty
  * crazy.  It maintains the reference count across sessions, too, so
  * if you exit Pidgin then this hash table is reconstructed the next
@@ -156,7 +156,7 @@
 	if (!purple_buddy_icons_is_caching())
 		return;
 
-	dirname  = purple_buddy_icons_get_cache_dir();
+	dirname = purple_buddy_icons_get_cache_dir();
 	path = g_build_filename(dirname, purple_imgstore_get_filename(img), NULL);
 
 	if (!g_file_test(dirname, G_FILE_TEST_IS_DIR))
@@ -189,7 +189,7 @@
 	if (GPOINTER_TO_INT(g_hash_table_lookup(icon_file_cache, filename)))
 		return;
 
-	dirname  = purple_buddy_icons_get_cache_dir();
+	dirname = purple_buddy_icons_get_cache_dir();
 	path = g_build_filename(dirname, filename, NULL);
 
 	if (g_file_test(path, G_FILE_TEST_EXISTS))
@@ -243,36 +243,26 @@
 }
 
 static PurpleStoredImage *
-purple_buddy_icon_data_new(guchar *icon_data, size_t icon_len, const char *filename)
+purple_buddy_icon_data_new(guchar *icon_data, size_t icon_len)
 {
 	char *file;
 	PurpleStoredImage *img;
 
 	g_return_val_if_fail(icon_data != NULL, NULL);
-	g_return_val_if_fail(icon_len  > 0,     NULL);
+	g_return_val_if_fail(icon_len > 0, NULL);
 
-	if (filename == NULL)
-	{
-		file = purple_util_get_image_filename(icon_data, icon_len);
-		if (file == NULL)
-		{
-			g_free(icon_data);
-			return NULL;
-		}
-	}
-	else
-		file = g_strdup(filename);
+	file = purple_util_get_image_filename(icon_data, icon_len);
 
-	if ((img = g_hash_table_lookup(icon_data_cache, file)))
-	{
+	img = g_hash_table_lookup(icon_data_cache, file);
+	if (img) {
 		g_free(file);
 		g_free(icon_data);
 		return purple_imgstore_ref(img);
 	}
 
-	img = purple_imgstore_add(icon_data, icon_len, file);
+	img = purple_imgstore_new(icon_data, icon_len, file);
 
-	/* This will take ownership of file and g_free it either now or later. */
+	/* This will take ownership of file and free it as needed */
 	g_hash_table_insert(icon_data_cache, file, img);
 
 	purple_buddy_icon_data_cache(img);
@@ -352,13 +342,13 @@
 	return icon;
 }
 
-PurpleBuddyIcon *
+void
 purple_buddy_icon_unref(PurpleBuddyIcon *icon)
 {
 	if (icon == NULL)
-		return NULL;
+		return;
 
-	g_return_val_if_fail(icon->ref_count > 0, NULL);
+	g_return_if_fail(icon->ref_count > 0);
 
 	icon->ref_count--;
 
@@ -375,11 +365,7 @@
 
 		PURPLE_DBUS_UNREGISTER_POINTER(icon);
 		g_slice_free(PurpleBuddyIcon, icon);
-
-		return NULL;
 	}
-
-	return icon;
 }
 
 void
@@ -469,7 +455,7 @@
 	if (data != NULL)
 	{
 		if (len > 0)
-			icon->img = purple_buddy_icon_data_new(data, len, NULL);
+			icon->img = purple_buddy_icon_data_new(data, len);
 		else
 			g_free(data);
 	}
@@ -638,6 +624,7 @@
 
 	if ((icon_cache == NULL) || ((icon = g_hash_table_lookup(icon_cache, username)) == NULL))
 	{
+		/* The icon is not currently cached in memory--try reading from disk */
 		PurpleBuddy *b = purple_find_buddy(account, username);
 		const char *protocol_icon_file;
 		const char *dirname;
@@ -729,9 +716,8 @@
 	PurpleStoredImage *img = NULL;
 	char *old_icon;
 
-	if (icon_data != NULL && icon_len > 0)
-	{
-		img = purple_buddy_icon_data_new(icon_data, icon_len, NULL);
+	if (icon_data != NULL && icon_len > 0) {
+		img = purple_buddy_icon_data_new(icon_data, icon_len);
 	}
 
 	old_icon = g_strdup(purple_account_get_string(account, "buddy_icon", NULL));
@@ -864,7 +850,7 @@
 	old_img = g_hash_table_lookup(pointer_icon_cache, node);
 
 	if (icon_data != NULL && icon_len > 0) {
-		img = purple_buddy_icon_data_new(icon_data, icon_len, NULL);
+		img = purple_buddy_icon_data_new(icon_data, icon_len);
 	}
 
 	old_icon = g_strdup(purple_blist_node_get_string(node,
@@ -1093,7 +1079,7 @@
 	pointer_icon_cache = g_hash_table_new(g_direct_hash, g_direct_equal);
 
     if (!cache_dir)
-    	cache_dir = g_build_filename(purple_user_dir(), "icons", NULL);
+		cache_dir = g_build_filename(purple_user_dir(), "icons", NULL);
 
 	purple_signal_connect(purple_imgstore_get_handle(), "image-deleting",
 	                      purple_buddy_icons_get_handle(),
--- a/libpurple/buddyicon.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/buddyicon.h	Fri Jan 25 02:22:38 2013 -0500
@@ -49,8 +49,8 @@
 /**
  * Creates a new buddy icon structure and populates it.
  *
- * If the buddy icon already exists, you'll get a reference to that structure,
- * which will have been updated with the data supplied.
+ * If an icon for this account+username already exists, you'll get a reference
+ * to that structure, which will have been updated with the data supplied.
  *
  * @param account   The account the user is on.
  * @param username  The username the icon belongs to.
@@ -79,10 +79,8 @@
  * If the reference count reaches 0, the icon will be destroyed.
  *
  * @param icon The buddy icon.
- *
- * @return @a icon, or @c NULL if the reference count reached 0.
  */
-PurpleBuddyIcon *purple_buddy_icon_unref(PurpleBuddyIcon *icon);
+void purple_buddy_icon_unref(PurpleBuddyIcon *icon);
 
 /**
  * Updates every instance of this icon.
@@ -331,7 +329,7 @@
 /**
  * Sets whether or not buddy icon caching is enabled.
  *
- * @param caching TRUE of buddy icon caching should be enabled, or
+ * @param caching TRUE if buddy icon caching should be enabled, or
  *                FALSE otherwise.
  */
 void purple_buddy_icons_set_caching(gboolean caching);
--- a/libpurple/certificate.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/certificate.c	Fri Jan 25 02:22:38 2013 -0500
@@ -41,53 +41,8 @@
 /** List of registered Pools */
 static GList *cert_pools = NULL;
 
-/*
- * TODO: Merge this with PurpleCertificateVerificationStatus for 3.0.0 */
-typedef enum {
-	PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1,
-
-	/* Not an error */
-	PURPLE_CERTIFICATE_NO_PROBLEMS = 0,
-
-	/* Non-fatal */
-	PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF,
-
-	/* The certificate is self-signed. */
-	PURPLE_CERTIFICATE_SELF_SIGNED = 0x01,
-
-	/* The CA is not in libpurple's pool of certificates. */
-	PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02,
-
-	/* The current time is before the certificate's specified
-	 * activation time.
-	 */
-	PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04,
-
-	/* The current time is after the certificate's specified expiration time */
-	PURPLE_CERTIFICATE_EXPIRED = 0x08,
-
-	/* The certificate's subject name doesn't match the expected */
-	PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10,
-
-	/* No CA pool was found. This shouldn't happen... */
-	PURPLE_CERTIFICATE_NO_CA_POOL = 0x20,
-
-	/* Fatal */
-	PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000,
-
-	/* The signature chain could not be validated. Due to limitations in the
-	 * the current API, this also indicates one of the CA certificates in the
-	 * chain is expired (or not yet activated). FIXME 3.0.0 */
-	PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000,
-
-	/* The signature has been revoked. */
-	PURPLE_CERTIFICATE_REVOKED = 0x20000,
-
-	PURPLE_CERTIFICATE_LAST = 0x40000,
-} PurpleCertificateInvalidityFlags;
-
 static const gchar *
-invalidity_reason_to_string(PurpleCertificateInvalidityFlags flag)
+invalidity_reason_to_string(PurpleCertificateVerificationStatus flag)
 {
 	switch (flag) {
 		case PURPLE_CERTIFICATE_SELF_SIGNED:
@@ -121,6 +76,9 @@
 		case PURPLE_CERTIFICATE_REVOKED:
 			return _("The certificate has been revoked.");
 			break;
+		case PURPLE_CERTIFICATE_REJECTED:
+			return _("The certificate was rejected by the user.");
+			break;
 		case PURPLE_CERTIFICATE_UNKNOWN_ERROR:
 		default:
 			return _("An unknown certificate error occurred.");
@@ -700,7 +658,7 @@
 			  "VRQ on cert from %s rejected\n",
 			  vrq->subject_name);
 
-	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID);
+	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_REJECTED);
 }
 
 static void
@@ -1319,7 +1277,7 @@
 
 	purple_debug_warning("certificate/x509/tls_cached", "User REJECTED cert\n");
 
-	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID);
+	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_REJECTED);
 }
 
 /** Validates a certificate by asking the user
@@ -1351,11 +1309,11 @@
 
 static void
 x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
-                             PurpleCertificateInvalidityFlags flags);
+                             PurpleCertificateVerificationStatus flags);
 
 static void
 x509_tls_cached_complete(PurpleCertificateVerificationRequest *vrq,
-                         PurpleCertificateInvalidityFlags flags)
+                         PurpleCertificateVerificationStatus flags)
 {
 	PurpleCertificatePool *tls_peers;
 	PurpleCertificate *peer_crt = vrq->cert_chain->data;
@@ -1377,13 +1335,16 @@
 		secondary = g_strconcat(tmp, " ", error, NULL);
 		g_free(tmp);
 
+		purple_debug_error("certificate/x509/tls_cached",
+		                   "Unable to validate certificate: %s\n", secondary);
+
 		purple_notify_error(NULL, /* TODO: Probably wrong. */
 					_("SSL Certificate Error"),
 					_("Unable to validate certificate"),
 					secondary);
 		g_free(secondary);
 
-		purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID);
+		purple_certificate_verify_complete(vrq, flags);
 		return;
 	} else if (flags & PURPLE_CERTIFICATE_NON_FATALS_MASK) {
 		/* Non-fatal error. Prompt the user. */
@@ -1448,7 +1409,7 @@
 
 static void
 x509_tls_cached_cert_in_cache(PurpleCertificateVerificationRequest *vrq,
-                              PurpleCertificateInvalidityFlags flags)
+                              PurpleCertificateVerificationStatus flags)
 {
 	/* TODO: Looking this up by name over and over is expensive.
 	   Fix, please! */
@@ -1502,7 +1463,7 @@
  */
 static void
 x509_tls_cached_check_subject_name(PurpleCertificateVerificationRequest *vrq,
-                                   PurpleCertificateInvalidityFlags flags)
+                                   PurpleCertificateVerificationStatus flags)
 {
 	PurpleCertificate *peer_crt;
 	GList *chain = vrq->cert_chain;
@@ -1531,7 +1492,7 @@
  */
 static void
 x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
-                             PurpleCertificateInvalidityFlags flags)
+                             PurpleCertificateVerificationStatus flags)
 {
 	PurpleCertificatePool *ca;
 	PurpleCertificate *peer_crt;
@@ -1611,7 +1572,7 @@
 	 * CA, or is a trusted CA (based on fingerprint).
 	 */
 	/* If, for whatever reason, there is no Certificate Authority pool
-	   loaded, we'll verify the subject name and then warn about thsi. */
+	   loaded, we'll verify the subject name and then warn about this. */
 	if ( !ca ) {
 		purple_debug_error("certificate/x509/tls_cached",
 				   "No X.509 Certificate Authority pool "
@@ -1637,8 +1598,6 @@
 			  "Also checking for a CA with DN=%s\n",
 			  ca2_id);
 	ca_crts = g_slist_concat(x509_ca_get_certs(ca_id), x509_ca_get_certs(ca2_id));
-	g_free(ca_id);
-	g_free(ca2_id);
 	if ( NULL == ca_crts ) {
 		flags |= PURPLE_CERTIFICATE_CA_UNKNOWN;
 
@@ -1647,6 +1606,8 @@
 				  "found. I'll prompt the user, I guess.\n");
 
 		x509_tls_cached_check_subject_name(vrq, flags);
+		g_free(ca_id);
+		g_free(ca2_id);
 		return;
 	}
 
@@ -1681,12 +1642,19 @@
 		g_byte_array_free(ca_fpr, TRUE);
 	}
 
-	if (valid == FALSE)
+	if (valid == FALSE) {
+		purple_debug_error("certificate/x509/tls_cached",
+		                   "Unable to verify final certificate %s signed by %s. "
+		                   "Not a trusted root or signed by a trusted root.\n",
+		                   ca2_id, ca_id);
 		flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
+	}
 
 	g_slist_foreach(ca_crts, (GFunc)purple_certificate_destroy, NULL);
 	g_slist_free(ca_crts);
 	g_byte_array_free(last_fpr, TRUE);
+	g_free(ca_id);
+	g_free(ca2_id);
 
 	x509_tls_cached_check_subject_name(vrq, flags);
 }
@@ -1697,7 +1665,7 @@
 	const gchar *tls_peers_name = "tls_peers"; /* Name of local cache */
 	PurpleCertificatePool *tls_peers;
 	time_t now, activation, expiration;
-	PurpleCertificateInvalidityFlags flags = PURPLE_CERTIFICATE_NO_PROBLEMS;
+	PurpleCertificateVerificationStatus flags = PURPLE_CERTIFICATE_VALID;
 	gboolean ret;
 
 	g_return_if_fail(vrq);
--- a/libpurple/certificate.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/certificate.h	Fri Jan 25 02:22:38 2013 -0500
@@ -36,8 +36,49 @@
 
 typedef enum
 {
-	PURPLE_CERTIFICATE_INVALID = 0,
-	PURPLE_CERTIFICATE_VALID = 1
+	PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1,
+
+	/* Not an error */
+	PURPLE_CERTIFICATE_VALID = 0,
+
+	/* Non-fatal */
+	PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF,
+
+	/* The certificate is self-signed. */
+	PURPLE_CERTIFICATE_SELF_SIGNED = 0x01,
+
+	/* The CA is not in libpurple's pool of certificates. */
+	PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02,
+
+	/* The current time is before the certificate's specified
+	 * activation time.
+	 */
+	PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04,
+
+	/* The current time is after the certificate's specified expiration time */
+	PURPLE_CERTIFICATE_EXPIRED = 0x08,
+
+	/* The certificate's subject name doesn't match the expected */
+	PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10,
+
+	/* No CA pool was found. This shouldn't happen... */
+	PURPLE_CERTIFICATE_NO_CA_POOL = 0x20,
+
+	/* Fatal */
+	PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000,
+
+	/* The signature chain could not be validated. Due to limitations in the
+	 * the current API, this also indicates one of the CA certificates in the
+	 * chain is expired (or not yet activated). FIXME 3.0.0 */
+	PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000,
+
+	/* The signature has been revoked. */
+	PURPLE_CERTIFICATE_REVOKED = 0x20000,
+
+	/* The certificate was rejected by the user. */
+	PURPLE_CERTIFICATE_REJECTED = 0x40000,
+
+	PURPLE_CERTIFICATE_LAST = 0x80000,
 } PurpleCertificateVerificationStatus;
 
 typedef struct _PurpleCertificate PurpleCertificate;
--- a/libpurple/ciphers/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/ciphers/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -5,10 +5,7 @@
 	gchecksum.c \
 	hmac.c \
 	md4.c \
-	md5.c \
-	rc4.c \
-	sha1.c \
-	sha256.c
+	rc4.c
 
 INCLUDES = -I$(top_srcdir)/libpurple
 
--- a/libpurple/ciphers/gchecksum.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/ciphers/gchecksum.c	Fri Jan 25 02:22:38 2013 -0500
@@ -1,7 +1,5 @@
 #include <cipher.h>
 
-#if GLIB_CHECK_VERSION(2,16,0)
-
 static void
 purple_g_checksum_init(PurpleCipherContext *context, GChecksumType type)
 {
@@ -19,13 +17,7 @@
     checksum = purple_cipher_context_get_data(context);
     g_return_if_fail(checksum != NULL);
 
-#if GLIB_CHECK_VERSION(2,18,0)
     g_checksum_reset(checksum);
-#else
-    g_checksum_free(checksum);
-    checksum = g_checksum_new(type);
-    purple_cipher_context_set_data(context, checksum);
-#endif
 }
 
 static void
@@ -139,6 +131,3 @@
 PURPLE_G_CHECKSUM_IMPLEMENTATION(md5, MD5, G_CHECKSUM_MD5, 64);
 PURPLE_G_CHECKSUM_IMPLEMENTATION(sha1, SHA1, G_CHECKSUM_SHA1, 64);
 PURPLE_G_CHECKSUM_IMPLEMENTATION(sha256, SHA256, G_CHECKSUM_SHA256, 64);
-
-#endif /* GLIB_CHECK_VERSION(2,16,0) */
-
--- a/libpurple/ciphers/md5.c	Thu Aug 23 01:27:48 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,326 +0,0 @@
-/*
- * 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.
- *
- * Original md5
- * Copyright (C) 2001-2003  Christophe Devine <c.devine@cr0.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include <cipher.h>
-
-#if !GLIB_CHECK_VERSION(2,16,0)
-
-#define MD5_HMAC_BLOCK_SIZE 64
-
-struct MD5Context {
-	guint32 total[2];
-	guint32 state[4];
-	guchar buffer[64];
-};
-
-#define MD5_GET_GUINT32(n,b,i) {            \
-	(n) = ((guint32)(b) [(i)    ]      )    \
-	| ((guint32)(b) [(i) + 1] <<  8)    \
-	| ((guint32)(b) [(i) + 2] << 16)    \
-	| ((guint32)(b) [(i) + 3] << 24);   \
-}
-#define MD5_PUT_GUINT32(n,b,i) {            \
-	(b)[(i)    ] = (guchar)((n)      );     \
-	(b)[(i) + 1] = (guchar)((n) >>  8);     \
-	(b)[(i) + 2] = (guchar)((n) >> 16);     \
-	(b)[(i) + 3] = (guchar)((n) >> 24);     \
-}
-
-static size_t
-md5_get_block_size(PurpleCipherContext *context)
-{
-	/* This does not change (in this case) */
-	return MD5_HMAC_BLOCK_SIZE;
-}
-
-static void
-md5_init(PurpleCipherContext *context, gpointer extra) {
-	struct MD5Context *md5_context;
-
-	md5_context = g_new0(struct MD5Context, 1);
-
-	purple_cipher_context_set_data(context, md5_context);
-
-	purple_cipher_context_reset(context, extra);
-}
-
-static void
-md5_reset(PurpleCipherContext *context, gpointer extra) {
-	struct MD5Context *md5_context;
-
-	md5_context = purple_cipher_context_get_data(context);
-
-	md5_context->total[0] = 0;
-	md5_context->total[1] = 0;
-
-	md5_context->state[0] = 0x67452301;
-	md5_context->state[1] = 0xEFCDAB89;
-	md5_context->state[2] = 0x98BADCFE;
-	md5_context->state[3] = 0x10325476;
-
-	memset(md5_context->buffer, 0, sizeof(md5_context->buffer));
-}
-
-static void
-md5_uninit(PurpleCipherContext *context) {
-	struct MD5Context *md5_context;
-
-	purple_cipher_context_reset(context, NULL);
-
-	md5_context = purple_cipher_context_get_data(context);
-	memset(md5_context, 0, sizeof(*md5_context));
-
-	g_free(md5_context);
-	md5_context = NULL;
-}
-
-static void
-md5_process(struct MD5Context *md5_context, const guchar data[64]) {
-	guint32 X[16], A, B, C, D;
-
-	A = md5_context->state[0];
-	B = md5_context->state[1];
-	C = md5_context->state[2];
-	D = md5_context->state[3];
-
-	MD5_GET_GUINT32(X[ 0], data,  0);
-	MD5_GET_GUINT32(X[ 1], data,  4);
-	MD5_GET_GUINT32(X[ 2], data,  8);
-	MD5_GET_GUINT32(X[ 3], data, 12);
-	MD5_GET_GUINT32(X[ 4], data, 16);
-	MD5_GET_GUINT32(X[ 5], data, 20);
-	MD5_GET_GUINT32(X[ 6], data, 24);
-	MD5_GET_GUINT32(X[ 7], data, 28);
-	MD5_GET_GUINT32(X[ 8], data, 32);
-	MD5_GET_GUINT32(X[ 9], data, 36);
-	MD5_GET_GUINT32(X[10], data, 40);
-	MD5_GET_GUINT32(X[11], data, 44);
-	MD5_GET_GUINT32(X[12], data, 48);
-	MD5_GET_GUINT32(X[13], data, 52);
-	MD5_GET_GUINT32(X[14], data, 56);
-	MD5_GET_GUINT32(X[15], data, 60);
-
-#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
-#define P(a,b,c,d,k,s,t) {      \
-	a += F(b,c,d) + X[k] + t;   \
-	a = S(a,s) + b;             \
-}
-
-	/* first pass */
-#define F(x,y,z) (z ^ (x & (y ^ z)))
-	P(A, B, C, D,  0,  7, 0xD76AA478);
-	P(D, A, B, C,  1, 12, 0xE8C7B756);
-	P(C, D, A, B,  2, 17, 0x242070DB);
-	P(B, C, D, A,  3, 22, 0xC1BDCEEE);
-	P(A, B, C, D,  4,  7, 0xF57C0FAF);
-	P(D, A, B, C,  5, 12, 0x4787C62A);
-	P(C, D, A, B,  6, 17, 0xA8304613);
-	P(B, C, D, A,  7, 22, 0xFD469501);
-	P(A, B, C, D,  8,  7, 0x698098D8);
-	P(D, A, B, C,  9, 12, 0x8B44F7AF);
-	P(C, D, A, B, 10, 17, 0xFFFF5BB1);
-	P(B, C, D, A, 11, 22, 0x895CD7BE);
-	P(A, B, C, D, 12,  7, 0x6B901122);
-	P(D, A, B, C, 13, 12, 0xFD987193);
-	P(C, D, A, B, 14, 17, 0xA679438E);
-	P(B, C, D, A, 15, 22, 0x49B40821);
-#undef F
-
-	/* second pass */
-#define F(x,y,z) (y ^ (z & (x ^ y)))
-	P(A, B, C, D,  1,  5, 0xF61E2562);
-	P(D, A, B, C,  6,  9, 0xC040B340);
-	P(C, D, A, B, 11, 14, 0x265E5A51);
-	P(B, C, D, A,  0, 20, 0xE9B6C7AA);
-	P(A, B, C, D,  5,  5, 0xD62F105D);
-	P(D, A, B, C, 10,  9, 0x02441453);
-	P(C, D, A, B, 15, 14, 0xD8A1E681);
-	P(B, C, D, A,  4, 20, 0xE7D3FBC8);
-	P(A, B, C, D,  9,  5, 0x21E1CDE6);
-	P(D, A, B, C, 14,  9, 0xC33707D6);
-	P(C, D, A, B,  3, 14, 0xF4D50D87);
-	P(B, C, D, A,  8, 20, 0x455A14ED);
-	P(A, B, C, D, 13,  5, 0xA9E3E905);
-	P(D, A, B, C,  2,  9, 0xFCEFA3F8);
-	P(C, D, A, B,  7, 14, 0x676F02D9);
-	P(B, C, D, A, 12, 20, 0x8D2A4C8A);
-#undef F
-
-	/* third pass */
-#define F(x,y,z) (x ^ y ^ z)
-	P(A, B, C, D,  5,  4, 0xFFFA3942);
-	P(D, A, B, C,  8, 11, 0x8771F681);
-	P(C, D, A, B, 11, 16, 0x6D9D6122);
-	P(B, C, D, A, 14, 23, 0xFDE5380C);
-	P(A, B, C, D,  1,  4, 0xA4BEEA44);
-	P(D, A, B, C,  4, 11, 0x4BDECFA9);
-	P(C, D, A, B,  7, 16, 0xF6BB4B60);
-	P(B, C, D, A, 10, 23, 0xBEBFBC70);
-	P(A, B, C, D, 13,  4, 0x289B7EC6);
-	P(D, A, B, C,  0, 11, 0xEAA127FA);
-	P(C, D, A, B,  3, 16, 0xD4EF3085);
-	P(B, C, D, A,  6, 23, 0x04881D05);
-	P(A, B, C, D,  9,  4, 0xD9D4D039);
-	P(D, A, B, C, 12, 11, 0xE6DB99E5);
-	P(C, D, A, B, 15, 16, 0x1FA27CF8);
-	P(B, C, D, A,  2, 23, 0xC4AC5665);
-#undef F
-
-	/* forth pass */
-#define F(x,y,z) (y ^ (x | ~z))
-	P(A, B, C, D,  0,  6, 0xF4292244);
-	P(D, A, B, C,  7, 10, 0x432AFF97);
-	P(C, D, A, B, 14, 15, 0xAB9423A7);
-	P(B, C, D, A,  5, 21, 0xFC93A039);
-	P(A, B, C, D, 12,  6, 0x655B59C3);
-	P(D, A, B, C,  3, 10, 0x8F0CCC92);
-	P(C, D, A, B, 10, 15, 0xFFEFF47D);
-	P(B, C, D, A,  1, 21, 0x85845DD1);
-	P(A, B, C, D,  8,  6, 0x6FA87E4F);
-	P(D, A, B, C, 15, 10, 0xFE2CE6E0);
-	P(C, D, A, B,  6, 15, 0xA3014314);
-	P(B, C, D, A, 13, 21, 0x4E0811A1);
-	P(A, B, C, D,  4,  6, 0xF7537E82);
-	P(D, A, B, C, 11, 10, 0xBD3AF235);
-	P(C, D, A, B,  2, 15, 0x2AD7D2BB);
-	P(B, C, D, A,  9, 21, 0xEB86D391);
-#undef F
-#undef P
-#undef S
-
-	md5_context->state[0] += A;
-	md5_context->state[1] += B;
-	md5_context->state[2] += C;
-	md5_context->state[3] += D;
-	}
-
-static void
-md5_append(PurpleCipherContext *context, const guchar *data, size_t len) {
-	struct MD5Context *md5_context = NULL;
-	guint32 left = 0, fill = 0;
-
-	g_return_if_fail(context != NULL);
-
-	md5_context = purple_cipher_context_get_data(context);
-	g_return_if_fail(md5_context != NULL);
-
-	left = md5_context->total[0] & 0x3F;
-	fill = 64 - left;
-
-	md5_context->total[0] += len;
-	md5_context->total[0] &= 0xFFFFFFFF;
-
-	if(md5_context->total[0] < len)
-		md5_context->total[1]++;
-
-	if(left && len >= fill) {
-		memcpy((md5_context->buffer + left), data, fill);
-		md5_process(md5_context, md5_context->buffer);
-		len -= fill;
-		data += fill;
-		left = 0;
-	}
-
-	while(len >= 64) {
-		md5_process(md5_context, data);
-		len -= 64;
-		data += 64;
-	}
-
-	if(len) {
-		memcpy((md5_context->buffer + left), data, len);
-	}
-}
-
-	static gboolean
-md5_digest(PurpleCipherContext *context, size_t in_len, guchar digest[16],
-		size_t *out_len)
-{
-	struct MD5Context *md5_context = NULL;
-	guint32 last, pad;
-	guint32 high, low;
-	guchar message[8];
-	guchar padding[64] = {
-		0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-	};
-
-	g_return_val_if_fail(in_len >= 16, FALSE);
-
-	md5_context = purple_cipher_context_get_data(context);
-
-	high = (md5_context->total[0] >> 29)
-		| (md5_context->total[1] << 3);
-	low = (md5_context->total[0] << 3);
-
-	MD5_PUT_GUINT32(low, message, 0);
-	MD5_PUT_GUINT32(high, message, 4);
-
-	last = md5_context->total[0] & 0x3F;
-	pad = (last < 56) ? (56 - last) : (120 - last);
-
-	md5_append(context, padding, pad);
-	md5_append(context, message, 8);
-
-	MD5_PUT_GUINT32(md5_context->state[0], digest, 0);
-	MD5_PUT_GUINT32(md5_context->state[1], digest, 4);
-	MD5_PUT_GUINT32(md5_context->state[2], digest, 8);
-	MD5_PUT_GUINT32(md5_context->state[3], digest, 12);
-
-	if(out_len)
-		*out_len = 16;
-
-	return TRUE;
-}
-
-static PurpleCipherOps MD5Ops = {
-	NULL,			/* Set Option		*/
-	NULL,			/* Get Option		*/
-	md5_init,		/* init				*/
-	md5_reset,		/* reset			*/
-	md5_uninit,	/* uninit			*/
-	NULL,			/* set iv			*/
-	md5_append,	/* append			*/
-	md5_digest,		/* digest			*/
-	NULL,			/* encrypt			*/
-	NULL,			/* decrypt			*/
-	NULL,			/* set salt			*/
-	NULL,			/* get salt size	*/
-	NULL,			/* set key			*/
-	NULL,			/* get key size		*/
-	NULL,			/* set batch mode */
-	NULL,			/* get batch mode */
-	md5_get_block_size,	/* get block size */
-	NULL			/* set key with len */
-};
-
-PurpleCipherOps *
-purple_md5_cipher_get_ops(void) {
-	return &MD5Ops;
-}
-
-#endif /* !GLIB_CHECK_VERSION(2,16,0) */
-
--- a/libpurple/ciphers/sha1.c	Thu Aug 23 01:27:48 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,281 +0,0 @@
-/*
- * 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 <cipher.h>
-#include <util.h>
-
-#if !GLIB_CHECK_VERSION(2,16,0)
-
-#define SHA1_HMAC_BLOCK_SIZE    64
-#define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF)
-
-struct SHA1Context {
-	guint32 H[5];
-	guint32 W[80];
-
-	gint lenW;
-
-	guint32 sizeHi;
-	guint32 sizeLo;
-};
-
-static size_t
-sha1_get_block_size(PurpleCipherContext *context)
-{
-	/* This does not change (in this case) */
-	return SHA1_HMAC_BLOCK_SIZE;
-}
-
-static void
-sha1_hash_block(struct SHA1Context *sha1_ctx) {
-	gint i;
-	guint32 A, B, C, D, E, T;
-
-	for(i = 16; i < 80; i++) {
-		sha1_ctx->W[i] = SHA1_ROTL(sha1_ctx->W[i -  3] ^
-				sha1_ctx->W[i -  8] ^
-				sha1_ctx->W[i - 14] ^
-				sha1_ctx->W[i - 16], 1);
-	}
-
-	A = sha1_ctx->H[0];
-	B = sha1_ctx->H[1];
-	C = sha1_ctx->H[2];
-	D = sha1_ctx->H[3];
-	E = sha1_ctx->H[4];
-
-	for(i = 0; i < 20; i++) {
-		T = (SHA1_ROTL(A, 5) + (((C ^ D) & B) ^ D) + E + sha1_ctx->W[i] + 0x5A827999) & 0xFFFFFFFF;
-		E = D;
-		D = C;
-		C = SHA1_ROTL(B, 30);
-		B = A;
-		A = T;
-	}
-
-	for(i = 20; i < 40; i++) {
-		T = (SHA1_ROTL(A, 5) + (B ^ C ^ D) + E + sha1_ctx->W[i] + 0x6ED9EBA1) & 0xFFFFFFFF;
-		E = D;
-		D = C;
-		C = SHA1_ROTL(B, 30);
-		B = A;
-		A = T;
-	}
-
-	for(i = 40; i < 60; i++) {
-		T = (SHA1_ROTL(A, 5) + ((B & C) | (D & (B | C))) + E + sha1_ctx->W[i] + 0x8F1BBCDC) & 0xFFFFFFFF;
-		E = D;
-		D = C;
-		C = SHA1_ROTL(B, 30);
-		B = A;
-		A = T;
-	}
-
-	for(i = 60; i < 80; i++) {
-		T = (SHA1_ROTL(A, 5) + (B ^ C ^ D) + E + sha1_ctx->W[i] + 0xCA62C1D6) & 0xFFFFFFFF;
-		E = D;
-		D = C;
-		C = SHA1_ROTL(B, 30);
-		B = A;
-		A = T;
-	}
-
-	sha1_ctx->H[0] += A;
-	sha1_ctx->H[1] += B;
-	sha1_ctx->H[2] += C;
-	sha1_ctx->H[3] += D;
-	sha1_ctx->H[4] += E;
-}
-
-static void
-sha1_set_opt(PurpleCipherContext *context, const gchar *name, void *value) {
-	struct SHA1Context *ctx;
-
-	ctx = purple_cipher_context_get_data(context);
-
-	if(purple_strequal(name, "sizeHi")) {
-		ctx->sizeHi = GPOINTER_TO_INT(value);
-	} else if(purple_strequal(name, "sizeLo")) {
-		ctx->sizeLo = GPOINTER_TO_INT(value);
-	} else if(purple_strequal(name, "lenW")) {
-		ctx->lenW = GPOINTER_TO_INT(value);
-	}
-}
-
-static void *
-sha1_get_opt(PurpleCipherContext *context, const gchar *name) {
-	struct SHA1Context *ctx;
-
-	ctx = purple_cipher_context_get_data(context);
-
-	if(purple_strequal(name, "sizeHi")) {
-		return GINT_TO_POINTER(ctx->sizeHi);
-	} else if(purple_strequal(name, "sizeLo")) {
-		return GINT_TO_POINTER(ctx->sizeLo);
-	} else if(purple_strequal(name, "lenW")) {
-		return GINT_TO_POINTER(ctx->lenW);
-	}
-
-	return NULL;
-}
-
-static void
-sha1_init(PurpleCipherContext *context, void *extra) {
-	struct SHA1Context *sha1_ctx;
-
-	sha1_ctx = g_new0(struct SHA1Context, 1);
-
-	purple_cipher_context_set_data(context, sha1_ctx);
-
-	purple_cipher_context_reset(context, extra);
-}
-
-static void
-sha1_reset(PurpleCipherContext *context, void *extra) {
-	struct SHA1Context *sha1_ctx;
-	gint i;
-
-	sha1_ctx = purple_cipher_context_get_data(context);
-
-	g_return_if_fail(sha1_ctx);
-
-	sha1_ctx->lenW = 0;
-	sha1_ctx->sizeHi = 0;
-	sha1_ctx->sizeLo = 0;
-
-	sha1_ctx->H[0] = 0x67452301;
-	sha1_ctx->H[1] = 0xEFCDAB89;
-	sha1_ctx->H[2] = 0x98BADCFE;
-	sha1_ctx->H[3] = 0x10325476;
-	sha1_ctx->H[4] = 0xC3D2E1F0;
-
-	for(i = 0; i < 80; i++)
-		sha1_ctx->W[i] = 0;
-}
-
-static void
-sha1_uninit(PurpleCipherContext *context) {
-	struct SHA1Context *sha1_ctx;
-
-	purple_cipher_context_reset(context, NULL);
-
-	sha1_ctx = purple_cipher_context_get_data(context);
-
-	memset(sha1_ctx, 0, sizeof(struct SHA1Context));
-
-	g_free(sha1_ctx);
-	sha1_ctx = NULL;
-}
-
-static void
-sha1_append(PurpleCipherContext *context, const guchar *data, size_t len) {
-	struct SHA1Context *sha1_ctx;
-	gint i;
-
-	sha1_ctx = purple_cipher_context_get_data(context);
-
-	g_return_if_fail(sha1_ctx);
-
-	for(i = 0; i < len; i++) {
-		sha1_ctx->W[sha1_ctx->lenW / 4] <<= 8;
-		sha1_ctx->W[sha1_ctx->lenW / 4] |= data[i];
-
-		if((++sha1_ctx->lenW) % 64 == 0) {
-			sha1_hash_block(sha1_ctx);
-			sha1_ctx->lenW = 0;
-		}
-
-		sha1_ctx->sizeLo += 8;
-		sha1_ctx->sizeHi += (sha1_ctx->sizeLo < 8);
-	}
-}
-
-static gboolean
-sha1_digest(PurpleCipherContext *context, size_t in_len, guchar digest[20],
-            size_t *out_len)
-{
-	struct SHA1Context *sha1_ctx;
-	guchar pad0x80 = 0x80, pad0x00 = 0x00;
-	guchar padlen[8];
-	gint i;
-
-	g_return_val_if_fail(in_len >= 20, FALSE);
-
-	sha1_ctx = purple_cipher_context_get_data(context);
-
-	g_return_val_if_fail(sha1_ctx, FALSE);
-
-	padlen[0] = (guchar)((sha1_ctx->sizeHi >> 24) & 255);
-	padlen[1] = (guchar)((sha1_ctx->sizeHi >> 16) & 255);
-	padlen[2] = (guchar)((sha1_ctx->sizeHi >> 8) & 255);
-	padlen[3] = (guchar)((sha1_ctx->sizeHi >> 0) & 255);
-	padlen[4] = (guchar)((sha1_ctx->sizeLo >> 24) & 255);
-	padlen[5] = (guchar)((sha1_ctx->sizeLo >> 16) & 255);
-	padlen[6] = (guchar)((sha1_ctx->sizeLo >> 8) & 255);
-	padlen[7] = (guchar)((sha1_ctx->sizeLo >> 0) & 255);
-
-	/* pad with a 1, then zeroes, then length */
-	purple_cipher_context_append(context, &pad0x80, 1);
-	while(sha1_ctx->lenW != 56)
-		purple_cipher_context_append(context, &pad0x00, 1);
-	purple_cipher_context_append(context, padlen, 8);
-
-	for(i = 0; i < 20; i++) {
-		digest[i] = (guchar)(sha1_ctx->H[i / 4] >> 24);
-		sha1_ctx->H[i / 4] <<= 8;
-	}
-
-	purple_cipher_context_reset(context, NULL);
-
-	if(out_len)
-		*out_len = 20;
-
-	return TRUE;
-}
-
-static PurpleCipherOps SHA1Ops = {
-	sha1_set_opt,		/* Set Option		*/
-	sha1_get_opt,		/* Get Option		*/
-	sha1_init,		/* init				*/
-	sha1_reset,		/* reset			*/
-	sha1_uninit,		/* uninit			*/
-	NULL,			/* set iv			*/
-	sha1_append,		/* append			*/
-	sha1_digest,	/* digest			*/
-	NULL,			/* encrypt			*/
-	NULL,			/* decrypt			*/
-	NULL,			/* set salt			*/
-	NULL,			/* get salt size	*/
-	NULL,			/* set key			*/
-	NULL,			/* get key size		*/
-	NULL,			/* set batch mode */
-	NULL,			/* get batch mode */
-	sha1_get_block_size,	/* get block size */
-	NULL			/* set key with len */
-};
-
-PurpleCipherOps *
-purple_sha1_cipher_get_ops(void) {
-	return &SHA1Ops;
-}
-
-#endif /* !GLIB_CHECK_VERSION(2,16,0) */
-
--- a/libpurple/ciphers/sha256.c	Thu Aug 23 01:27:48 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-/*
- * 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 <cipher.h>
-
-#if !GLIB_CHECK_VERSION(2,16,0)
-
-#define SHA256_HMAC_BLOCK_SIZE  64
-#define SHA256_ROTR(X,n) ((((X) >> (n)) | ((X) << (32-(n)))) & 0xFFFFFFFF)
-
-static const guint32 sha256_K[64] =
-{
-	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
-	0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
-	0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
-	0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
-	0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
-	0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
-	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
-	0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
-};
-
-struct SHA256Context {
-	guint32 H[8];
-	guint32 W[64];
-
-	gint lenW;
-
-	guint32 sizeHi;
-	guint32 sizeLo;
-};
-
-static size_t
-sha256_get_block_size(PurpleCipherContext *context)
-{
-	/* This does not change (in this case) */
-	return SHA256_HMAC_BLOCK_SIZE;
-}
-
-static void
-sha256_hash_block(struct SHA256Context *sha256_ctx) {
-	gint i;
-	guint32 A, B, C, D, E, F, G, H, T1, T2;
-
-	for(i = 16; i < 64; i++) {
-		sha256_ctx->W[i] =
-			  (SHA256_ROTR(sha256_ctx->W[i-2], 17) ^ SHA256_ROTR(sha256_ctx->W[i-2],  19) ^ (sha256_ctx->W[i-2] >> 10))
-			+ sha256_ctx->W[i-7]
-			+ (SHA256_ROTR(sha256_ctx->W[i-15], 7) ^ SHA256_ROTR(sha256_ctx->W[i-15], 18) ^ (sha256_ctx->W[i-15] >> 3))
-			+ sha256_ctx->W[i-16];
-	}
-
-	A = sha256_ctx->H[0];
-	B = sha256_ctx->H[1];
-	C = sha256_ctx->H[2];
-	D = sha256_ctx->H[3];
-	E = sha256_ctx->H[4];
-	F = sha256_ctx->H[5];
-	G = sha256_ctx->H[6];
-	H = sha256_ctx->H[7];
-
-	for(i = 0; i < 64; i++) {
-		T1 = H
-			+ (SHA256_ROTR(E, 6) ^ SHA256_ROTR(E, 11) ^ SHA256_ROTR(E, 25))
-			+ ((E & F) ^ ((~E) & G))
-			+ sha256_K[i] + sha256_ctx->W[i];
-		T2 = (SHA256_ROTR(A, 2) ^ SHA256_ROTR(A, 13) ^ SHA256_ROTR(A, 22))
-			+ ((A & B) ^ (A & C) ^ (B & C));
-		H = G;
-		G = F;
-		F = E;
-		E = D + T1;
-		D = C;
-		C = B;
-		B = A;
-		A = T1 + T2;
-	}
-
-	sha256_ctx->H[0] += A;
-	sha256_ctx->H[1] += B;
-	sha256_ctx->H[2] += C;
-	sha256_ctx->H[3] += D;
-	sha256_ctx->H[4] += E;
-	sha256_ctx->H[5] += F;
-	sha256_ctx->H[6] += G;
-	sha256_ctx->H[7] += H;
-}
-
-static void
-sha256_set_opt(PurpleCipherContext *context, const gchar *name, void *value) {
-	struct SHA256Context *ctx;
-
-	ctx = purple_cipher_context_get_data(context);
-
-	if(!strcmp(name, "sizeHi")) {
-		ctx->sizeHi = GPOINTER_TO_INT(value);
-	} else if(!strcmp(name, "sizeLo")) {
-		ctx->sizeLo = GPOINTER_TO_INT(value);
-	} else if(!strcmp(name, "lenW")) {
-		ctx->lenW = GPOINTER_TO_INT(value);
-	}
-}
-
-static void *
-sha256_get_opt(PurpleCipherContext *context, const gchar *name) {
-	struct SHA256Context *ctx;
-
-	ctx = purple_cipher_context_get_data(context);
-
-	if(!strcmp(name, "sizeHi")) {
-		return GINT_TO_POINTER(ctx->sizeHi);
-	} else if(!strcmp(name, "sizeLo")) {
-		return GINT_TO_POINTER(ctx->sizeLo);
-	} else if(!strcmp(name, "lenW")) {
-		return GINT_TO_POINTER(ctx->lenW);
-	}
-
-	return NULL;
-}
-
-static void
-sha256_init(PurpleCipherContext *context, void *extra) {
-	struct SHA256Context *sha256_ctx;
-
-	sha256_ctx = g_new0(struct SHA256Context, 1);
-
-	purple_cipher_context_set_data(context, sha256_ctx);
-
-	purple_cipher_context_reset(context, extra);
-}
-
-static void
-sha256_reset(PurpleCipherContext *context, void *extra) {
-	struct SHA256Context *sha256_ctx;
-	gint i;
-
-	sha256_ctx = purple_cipher_context_get_data(context);
-
-	g_return_if_fail(sha256_ctx);
-
-	sha256_ctx->lenW = 0;
-	sha256_ctx->sizeHi = 0;
-	sha256_ctx->sizeLo = 0;
-
-	sha256_ctx->H[0] = 0x6a09e667;
-	sha256_ctx->H[1] = 0xbb67ae85;
-	sha256_ctx->H[2] = 0x3c6ef372;
-	sha256_ctx->H[3] = 0xa54ff53a;
-	sha256_ctx->H[4] = 0x510e527f;
-	sha256_ctx->H[5] = 0x9b05688c;
-	sha256_ctx->H[6] = 0x1f83d9ab;
-	sha256_ctx->H[7] = 0x5be0cd19;
-
-	for(i = 0; i < 64; i++)
-		sha256_ctx->W[i] = 0;
-}
-
-static void
-sha256_uninit(PurpleCipherContext *context) {
-	struct SHA256Context *sha256_ctx;
-
-	purple_cipher_context_reset(context, NULL);
-
-	sha256_ctx = purple_cipher_context_get_data(context);
-
-	memset(sha256_ctx, 0, sizeof(struct SHA256Context));
-
-	g_free(sha256_ctx);
-	sha256_ctx = NULL;
-}
-
-static void
-sha256_append(PurpleCipherContext *context, const guchar *data, size_t len) {
-	struct SHA256Context *sha256_ctx;
-	gint i;
-
-	sha256_ctx = purple_cipher_context_get_data(context);
-
-	g_return_if_fail(sha256_ctx);
-
-	for(i = 0; i < len; i++) {
-		sha256_ctx->W[sha256_ctx->lenW / 4] <<= 8;
-		sha256_ctx->W[sha256_ctx->lenW / 4] |= data[i];
-
-		if((++sha256_ctx->lenW) % 64 == 0) {
-			sha256_hash_block(sha256_ctx);
-			sha256_ctx->lenW = 0;
-		}
-
-		sha256_ctx->sizeLo += 8;
-		sha256_ctx->sizeHi += (sha256_ctx->sizeLo < 8);
-	}
-}
-
-static gboolean
-sha256_digest(PurpleCipherContext *context, size_t in_len, guchar digest[32],
-              size_t *out_len)
-{
-	struct SHA256Context *sha256_ctx;
-	guchar pad0x80 = 0x80, pad0x00 = 0x00;
-	guchar padlen[8];
-	gint i;
-
-	g_return_val_if_fail(in_len >= 32, FALSE);
-
-	sha256_ctx = purple_cipher_context_get_data(context);
-
-	g_return_val_if_fail(sha256_ctx, FALSE);
-
-	padlen[0] = (guchar)((sha256_ctx->sizeHi >> 24) & 255);
-	padlen[1] = (guchar)((sha256_ctx->sizeHi >> 16) & 255);
-	padlen[2] = (guchar)((sha256_ctx->sizeHi >> 8) & 255);
-	padlen[3] = (guchar)((sha256_ctx->sizeHi >> 0) & 255);
-	padlen[4] = (guchar)((sha256_ctx->sizeLo >> 24) & 255);
-	padlen[5] = (guchar)((sha256_ctx->sizeLo >> 16) & 255);
-	padlen[6] = (guchar)((sha256_ctx->sizeLo >> 8) & 255);
-	padlen[7] = (guchar)((sha256_ctx->sizeLo >> 0) & 255);
-
-	/* pad with a 1, then zeroes, then length */
-	purple_cipher_context_append(context, &pad0x80, 1);
-	while(sha256_ctx->lenW != 56)
-		purple_cipher_context_append(context, &pad0x00, 1);
-	purple_cipher_context_append(context, padlen, 8);
-
-	for(i = 0; i < 32; i++) {
-		digest[i] = (guchar)(sha256_ctx->H[i / 4] >> 24);
-		sha256_ctx->H[i / 4] <<= 8;
-	}
-
-	purple_cipher_context_reset(context, NULL);
-
-	if(out_len)
-		*out_len = 32;
-
-	return TRUE;
-}
-
-static PurpleCipherOps SHA256Ops = {
-	sha256_set_opt,			/* Set Option		*/
-	sha256_get_opt,			/* Get Option		*/
-	sha256_init,	/* init				*/
-	sha256_reset,	/* reset			*/
-	sha256_uninit,	/* uninit			*/
-	NULL,			/* set iv			*/
-	sha256_append,	/* append			*/
-	sha256_digest,	/* digest			*/
-	NULL,			/* encrypt			*/
-	NULL,			/* decrypt			*/
-	NULL,			/* set salt			*/
-	NULL,			/* get salt size	*/
-	NULL,			/* set key			*/
-	NULL,			/* get key size		*/
-	NULL,			/* set batch mode */
-	NULL,			/* get batch mode */
-	sha256_get_block_size,	/* get block size */
-	NULL			/* set key with len */
-};
-
-PurpleCipherOps *
-purple_sha256_cipher_get_ops(void) {
-	return &SHA256Ops;
-}
-
-#endif /* !GLIB_CHECK_VERSION(2,16,0) */
-
--- a/libpurple/connection.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/connection.c	Fri Jan 25 02:22:38 2013 -0500
@@ -31,6 +31,7 @@
 #include "connection.h"
 #include "dbus-maybe.h"
 #include "debug.h"
+#include "http.h"
 #include "log.h"
 #include "notify.h"
 #include "prefs.h"
@@ -251,6 +252,7 @@
 
 	update_keepalive(gc, FALSE);
 
+	purple_http_conn_cancel_all(gc);
 	purple_proxy_connect_cancel_with_handle(gc);
 
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
--- a/libpurple/core.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/core.c	Fri Jan 25 02:22:38 2013 -0500
@@ -33,6 +33,7 @@
 #include "debug.h"
 #include "dnsquery.h"
 #include "ft.h"
+#include "http.h"
 #include "idle.h"
 #include "imgstore.h"
 #include "keyring.h"
@@ -176,6 +177,7 @@
 	purple_stun_init();
 	purple_xfers_init();
 	purple_idle_init();
+	purple_http_init();
 	purple_smileys_init();
 	/*
 	 * Call this early on to try to auto-detect our IP address and
@@ -189,6 +191,9 @@
 	/* The UI may have registered some theme types, so refresh them */
 	purple_theme_manager_refresh();
 
+	/* Load the buddy list after UI init */
+	purple_blist_boot();
+
 	return TRUE;
 }
 
@@ -224,6 +229,7 @@
 
 	/* Save .xml files, remove signals, etc. */
 	purple_smileys_uninit();
+	purple_http_uninit();
 	purple_idle_uninit();
 	purple_pounces_uninit();
 	purple_blist_uninit();
@@ -260,9 +266,11 @@
 #endif
 
 	purple_cmds_uninit();
-	/* Everything after util_uninit cannot try to write things to the confdir */
+	purple_log_uninit();
+	/* Everything after util_uninit cannot try to write things to the
+	 * confdir nor use purple_escape_js
+	 */
 	purple_util_uninit();
-	purple_log_uninit();
 
 	purple_signals_uninit();
 
--- a/libpurple/dnsquery.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/dnsquery.c	Fri Jan 25 02:22:38 2013 -0500
@@ -80,8 +80,7 @@
 };
 
 static GSList *free_dns_children = NULL;
-/* TODO: Make me a GQueue when we require >= glib 2.4 */
-static GSList *queued_requests = NULL;
+static GQueue *queued_requests = NULL;
 
 static int number_of_dns_children = 0;
 
@@ -581,12 +580,11 @@
 	PurpleDnsQueryData *query_data;
 	PurpleDnsQueryResolverProcess *resolver;
 
-	if (queued_requests == NULL)
+	if (g_queue_is_empty(queued_requests))
 		/* No more DNS queries, yay! */
 		return;
 
-	query_data = queued_requests->data;
-	queued_requests = g_slist_delete_link(queued_requests, queued_requests);
+	query_data = g_queue_pop_head(queued_requests);
 
 	/*
 	 * If we have any children, attempt to have them perform the DNS
@@ -610,7 +608,7 @@
 		if (number_of_dns_children >= MAX_DNS_CHILDREN)
 		{
 			/* Apparently all our children are busy */
-			queued_requests = g_slist_prepend(queued_requests, query_data);
+			g_queue_push_head(queued_requests, query_data);
 			return;
 		}
 
@@ -696,7 +694,7 @@
 static void
 resolve_host(PurpleDnsQueryData *query_data)
 {
-	queued_requests = g_slist_append(queued_requests, query_data);
+	g_queue_push_tail(queued_requests, query_data);
 
 	handle_next_queued_request();
 }
@@ -953,7 +951,7 @@
 		ops->destroy(query_data);
 
 #if defined(PURPLE_DNSQUERY_USE_FORK)
-	queued_requests = g_slist_remove(queued_requests, query_data);
+	g_queue_remove(queued_requests, query_data);
 
 	if (query_data->resolver != NULL)
 		/*
@@ -1030,6 +1028,9 @@
 void
 purple_dnsquery_init(void)
 {
+#if defined(PURPLE_DNSQUERY_USE_FORK)
+	queued_requests = g_queue_new();
+#endif
 }
 
 void
@@ -1041,5 +1042,9 @@
 		purple_dnsquery_resolver_destroy(free_dns_children->data);
 		free_dns_children = g_slist_remove(free_dns_children, free_dns_children->data);
 	}
+
+	g_queue_free(queued_requests);
+	queued_requests = NULL;
 #endif /* end PURPLE_DNSQUERY_USE_FORK */
 }
+
--- a/libpurple/example/nullclient.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/example/nullclient.c	Fri Jan 25 02:22:38 2013 -0500
@@ -103,11 +103,7 @@
 	glib_input_add,
 	g_source_remove,
 	NULL,
-#if GLIB_CHECK_VERSION(2,14,0)
 	g_timeout_add_seconds,
-#else
-	NULL,
-#endif
 
 	/* padding */
 	NULL,
@@ -218,19 +214,12 @@
 		abort();
 	}
 
-	/* Create and load the buddylist. */
-	purple_set_blist(purple_blist_new());
-	purple_blist_load();
-
 	/* Load the preferences. */
 	purple_prefs_load();
 
 	/* Load the desired plugins. The client should save the list of loaded plugins in
 	 * the preferences using purple_plugins_save_loaded(PLUGIN_SAVE_PREF) */
 	purple_plugins_load_saved(PLUGIN_SAVE_PREF);
-
-	/* Load the pounces. */
-	purple_pounces_load();
 }
 
 static void
--- a/libpurple/ft.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/ft.c	Fri Jan 25 02:22:38 2013 -0500
@@ -303,7 +303,7 @@
 	if (print_thumbnail && thumbnail_data) {
 		gchar *message_with_img;
 		gpointer data = g_memdup(thumbnail_data, size);
-		int id = purple_imgstore_add_with_id(data, size, NULL);
+		int id = purple_imgstore_new_with_id(data, size, NULL);
 
 		message_with_img =
 			g_strdup_printf("<img src='" PURPLE_STORED_IMAGE_PROTOCOL "%d'> %s",
@@ -319,7 +319,7 @@
 }
 
 void
-purple_xfer_conversation_write(PurpleXfer *xfer, gchar *message,
+purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message,
 	gboolean is_error)
 {
 	purple_xfer_conversation_write_internal(xfer, message, is_error, FALSE);
--- a/libpurple/ft.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/ft.h	Fri Jan 25 02:22:38 2013 -0500
@@ -705,7 +705,7 @@
  * @param message The message to display.
  * @param is_error Is this an error message?.
  */
-void purple_xfer_conversation_write(PurpleXfer *xfer, char *message, gboolean is_error);
+void purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message, gboolean is_error);
 
 /**
  * Allows the UI to signal it's ready to send/receive data (depending on
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/glibcompat.h	Fri Jan 25 02:22:38 2013 -0500
@@ -0,0 +1,53 @@
+/* pidgin
+ *
+ * Pidgin 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 _PIDGINGLIBCOMPAT_H_
+#define _PIDGINGLIBCOMPAT_H_
+
+/* This file is internal to Pidgin. Do not use!
+ * Also, any public API should not depend on this file.
+ */
+
+#if !GLIB_CHECK_VERSION(2, 28, 0)
+
+static inline gint64 g_get_monotonic_time(void)
+{
+	GTimeVal time_s;
+
+	g_get_current_time(&time_s);
+
+	return ((gint64)time_s.tv_sec << 32) | time_s.tv_usec;
+}
+
+static inline void g_list_free_full(GList *list, GDestroyNotify free_func)
+{
+	g_list_foreach(list, (GFunc)free_func, NULL);
+	g_list_free(list);
+}
+
+static inline void g_slist_free_full(GSList *list, GDestroyNotify free_func)
+{
+	g_slist_foreach(list, (GFunc)free_func, NULL);
+	g_slist_free(list);
+}
+
+#endif /* 2.28.0 */
+
+#endif /* _PIDGINGLIBCOMPAT_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/http.c	Fri Jan 25 02:22:38 2013 -0500
@@ -0,0 +1,2357 @@
+/**
+ * @file http.c HTTP API
+ * @ingroup core
+ */
+
+/* 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 "http.h"
+
+#include "internal.h"
+
+#include "debug.h"
+#include "ntlm.h"
+
+#define PURPLE_HTTP_URL_CREDENTIALS_CHARS "a-z0-9.,~_/*!&%?=+\\^-"
+#define PURPLE_HTTP_MAX_RECV_BUFFER_LEN 10240
+#define PURPLE_HTTP_MAX_READ_BUFFER_LEN 10240
+
+#define PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS 20
+#define PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT 30
+
+typedef struct _PurpleHttpURL PurpleHttpURL;
+
+typedef struct _PurpleHttpSocket PurpleHttpSocket;
+
+typedef struct _PurpleHttpHeaders PurpleHttpHeaders;
+
+struct _PurpleHttpSocket
+{
+	gboolean is_ssl;
+	PurpleSslConnection *ssl_connection;
+	PurpleProxyConnectData *raw_connection;
+	int fd;
+	guint inpa;
+};
+
+struct _PurpleHttpRequest
+{
+	int ref_count;
+
+	gchar *url;
+	gchar *method;
+	PurpleHttpHeaders *headers;
+	PurpleHttpCookieJar *cookie_jar;
+
+	gchar *contents;
+	int contents_length;
+	PurpleHttpContentReader contents_reader;
+	gpointer contents_reader_data;
+	PurpleHttpContentWriter response_writer;
+	gpointer response_writer_data;
+
+	int timeout;
+	int max_redirects;
+	gboolean http11;
+	int max_length;
+};
+
+struct _PurpleHttpConnection
+{
+	PurpleConnection *gc;
+	PurpleHttpCallback callback;
+	gpointer user_data;
+	gboolean is_reading;
+
+	PurpleHttpURL *url;
+	PurpleHttpRequest *request;
+	PurpleHttpResponse *response;
+
+	PurpleHttpSocket socket;
+	GString *request_header;
+	int request_header_written, request_contents_written;
+	gboolean main_header_got, headers_got;
+	GString *response_buffer;
+
+	GString *contents_reader_buffer;
+	gboolean contents_reader_requested;
+
+	int redirects_count;
+	int data_length_got;
+
+	int length_expected, length_got;
+
+	gboolean is_chunked, in_chunk, chunks_done;
+	int chunk_length, chunk_got;
+
+	GList *link_global, *link_gc;
+
+	guint timeout_handle;
+
+	PurpleHttpProgressWatcher watcher;
+	gpointer watcher_user_data;
+	guint watcher_interval_threshold;
+	gint64 watcher_last_call;
+	guint watcher_delayed_handle;
+};
+
+struct _PurpleHttpResponse
+{
+	int code;
+	gchar *error;
+
+	GString *contents;
+	PurpleHttpHeaders *headers;
+};
+
+struct _PurpleHttpURL
+{
+	gchar *protocol;
+	gchar *user;
+	gchar *password;
+	gchar *host;
+	int port;
+	gchar *path;
+	gchar *fragment;
+};
+
+struct _PurpleHttpHeaders
+{
+	GList *list;
+	GHashTable *by_name;
+};
+
+typedef struct
+{
+	time_t expires;
+	gchar *value;
+} PurpleHttpCookie;
+
+struct _PurpleHttpCookieJar
+{
+	int ref_count;
+
+	GHashTable *tab;
+};
+
+static time_t purple_http_rfc1123_to_time(const gchar *str);
+
+static PurpleHttpConnection * purple_http_connection_new(
+	PurpleHttpRequest *request, PurpleConnection *gc);
+static void purple_http_connection_terminate(PurpleHttpConnection *hc);
+static void purple_http_conn_notify_progress_watcher(PurpleHttpConnection *hc);
+
+static PurpleHttpResponse * purple_http_response_new(void);
+static void purple_http_response_free(PurpleHttpResponse *response);
+
+static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar,
+	GList *values);
+static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar);
+gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar);
+
+static PurpleHttpURL * purple_http_url_parse(const char *url);
+static void purple_http_url_free(PurpleHttpURL *parsed_url);
+static void purple_http_url_relative(PurpleHttpURL *base_url,
+	PurpleHttpURL *relative_url);
+static gchar * purple_http_url_print(PurpleHttpURL *parsed_url);
+
+static GRegex *purple_http_re_url, *purple_http_re_url_host,
+	*purple_http_re_rfc1123;
+
+/**
+ * Values: pointers to running PurpleHttpConnection.
+ */
+static GList *purple_http_hc_list;
+
+/**
+ * Keys: pointers to PurpleConnection.
+ * Values: GList of pointers to running PurpleHttpConnection.
+ */
+static GHashTable *purple_http_hc_by_gc;
+
+/**
+ * Keys: pointers to PurpleHttpConnection.
+ * Values: pointers to links in purple_http_hc_list.
+ */
+static GHashTable *purple_http_hc_by_ptr;
+
+/*** Helper functions *********************************************************/
+
+static time_t purple_http_rfc1123_to_time(const gchar *str)
+{
+	static const gchar *months[13] = {"jan", "feb", "mar", "apr", "may", "jun",
+		"jul", "aug", "sep", "oct", "nov", "dec", NULL};
+	GMatchInfo *match_info;
+	gchar *d_date, *d_month, *d_year, *d_time;
+	int month;
+	gchar *iso_date;
+	time_t t;
+
+	g_return_val_if_fail(str != NULL, 0);
+
+	g_regex_match(purple_http_re_rfc1123, str, 0, &match_info);
+	if (!g_match_info_matches(match_info)) {
+		g_match_info_free(match_info);
+		return 0;
+	}
+	g_match_info_free(match_info);
+
+	d_date = g_match_info_fetch(match_info, 1);
+	d_month = g_match_info_fetch(match_info, 2);
+	d_year = g_match_info_fetch(match_info, 3);
+	d_time = g_match_info_fetch(match_info, 4);
+
+	month = 0;
+	while (months[month] != NULL)
+	{
+		if (0 == g_ascii_strcasecmp(d_month, months[month]))
+			break;
+		month++;
+	}
+	month++;
+
+	iso_date = g_strdup_printf("%s-%02d-%sT%s+00:00", 
+		d_year, month, d_date, d_time);
+
+	g_free(d_date);
+	g_free(d_month);
+	g_free(d_year);
+	g_free(d_time);
+
+	if (month > 12) {
+		purple_debug_warning("http", "Invalid month: %s\n", d_month);
+		g_free(iso_date);
+		return 0;
+	}
+
+	t = purple_str_to_time(iso_date, TRUE, NULL, NULL, NULL);
+
+	g_free(iso_date);
+
+	return t;
+}
+
+/*** Headers collection *******************************************************/
+
+static PurpleHttpHeaders * purple_http_headers_new(void);
+static void purple_http_headers_free(PurpleHttpHeaders *hdrs);
+static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key,
+	const gchar *value);
+static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs);
+static GList * purple_http_headers_get_all_by_name(
+	PurpleHttpHeaders *hdrs, const gchar *key);
+static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs,
+	const gchar *key);
+static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs,
+	const gchar *key, int *dst);
+static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs,
+	const gchar *key, const gchar *value);
+static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs);
+
+static PurpleHttpHeaders * purple_http_headers_new(void)
+{
+	PurpleHttpHeaders *hdrs = g_new0(PurpleHttpHeaders, 1);
+
+	hdrs->by_name = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+		(GDestroyNotify)g_list_free);
+
+	return hdrs;
+}
+
+static void purple_http_headers_free_kvp(PurpleKeyValuePair *kvp)
+{
+	g_free(kvp->key);
+	g_free(kvp->value);
+	g_free(kvp);
+}
+
+static void purple_http_headers_free(PurpleHttpHeaders *hdrs)
+{
+	if (hdrs == NULL)
+		return;
+
+	g_hash_table_destroy(hdrs->by_name);
+	g_list_free_full(hdrs->list,
+		(GDestroyNotify)purple_http_headers_free_kvp);
+	g_free(hdrs);
+}
+
+static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key,
+	const gchar *value)
+{
+	PurpleKeyValuePair *kvp;
+	GList *named_values, *new_values;
+	gchar *key_low;
+
+	g_return_if_fail(hdrs != NULL);
+	g_return_if_fail(key != NULL);
+	g_return_if_fail(value != NULL);
+
+	kvp = g_new0(PurpleKeyValuePair, 1);
+	kvp->key = g_strdup(key);
+	kvp->value = g_strdup(value);
+	hdrs->list = g_list_append(hdrs->list, kvp);
+
+	key_low = g_ascii_strdown(key, -1);
+	named_values = g_hash_table_lookup(hdrs->by_name, key_low);
+	new_values = g_list_append(named_values, kvp->value);
+	if (named_values)
+		g_free(key_low);
+	else
+		g_hash_table_insert(hdrs->by_name, key_low, new_values);
+}
+
+static void purple_http_headers_remove(PurpleHttpHeaders *hdrs,
+	const gchar *key)
+{
+	GList *it, *curr;
+
+	g_return_if_fail(hdrs != NULL);
+	g_return_if_fail(key != NULL);
+
+	if (!g_hash_table_remove(hdrs->by_name, key))
+		return;
+
+	/* Could be optimized to O(1). */
+	it = g_list_first(hdrs->list);
+	while (it)
+	{
+		PurpleKeyValuePair *kvp = it->data;
+		curr = it;
+		it = g_list_next(it);
+		if (g_ascii_strcasecmp(kvp->key, key) != 0)
+			continue;
+
+		hdrs->list = g_list_delete_link(hdrs->list, curr);
+		purple_http_headers_free_kvp(kvp);
+	}
+}
+
+static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs)
+{
+	return hdrs->list;
+}
+
+/* return const */
+static GList * purple_http_headers_get_all_by_name(
+	PurpleHttpHeaders *hdrs, const gchar *key)
+{
+	GList *values;
+	gchar *key_low;
+
+	g_return_val_if_fail(hdrs != NULL, NULL);
+	g_return_val_if_fail(key != NULL, NULL);
+
+	key_low = g_ascii_strdown(key, -1);
+	values = g_hash_table_lookup(hdrs->by_name, key_low);
+	g_free(key_low);
+
+	return values;
+}
+
+static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs,
+	const gchar *key)
+{
+	const GList *values = purple_http_headers_get_all_by_name(hdrs, key);
+
+	if (!values)
+		return NULL;
+
+	return values->data;
+}
+
+static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs,
+	const gchar *key, int *dst)
+{
+	int val;
+	const gchar *str;
+
+	str = purple_http_headers_get(hdrs, key);
+	if (!str)
+		return FALSE;
+
+	if (1 != sscanf(str, "%d", &val))
+		return FALSE;
+
+	*dst = val;
+	return TRUE;
+}
+
+static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs,
+	const gchar *key, const gchar *value)
+{
+	const gchar *str;
+
+	str = purple_http_headers_get(hdrs, key);
+	if (str == NULL || value == NULL)
+		return str == value;
+
+	return (g_ascii_strcasecmp(str, value) == 0);
+}
+
+static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs)
+{
+	const GList *hdr;
+
+	GString *s = g_string_new("");
+	
+	hdr = purple_http_headers_get_all(hdrs);
+	while (hdr) {
+		PurpleKeyValuePair *kvp = hdr->data;
+		hdr = g_list_next(hdr);
+
+		g_string_append_printf(s, "%s: %s%s", kvp->key,
+			(gchar*)kvp->value, hdr ? "\n" : "");
+	}
+	
+	return g_string_free(s, FALSE);
+}
+
+/*** HTTP protocol backend ****************************************************/
+
+static void _purple_http_disconnect(PurpleHttpConnection *hc);
+
+static void _purple_http_gen_headers(PurpleHttpConnection *hc);
+static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd);
+static void _purple_http_recv(gpointer _hc, gint fd,
+	PurpleInputCondition cond);
+static void _purple_http_recv_ssl(gpointer _hc,
+	PurpleSslConnection *ssl_connection, PurpleInputCondition cond);
+static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond);
+
+static void _purple_http_connected_raw(gpointer _hc, gint source,
+	const gchar *error_message);
+static void _purple_http_connected_ssl(gpointer _hc,
+	PurpleSslConnection *ssl_connection, PurpleInputCondition cond);
+static void _purple_http_connected_ssl_error(
+	PurpleSslConnection *ssl_connection, PurpleSslErrorType error,
+	gpointer _hc);
+
+/* closes current connection (if exists), estabilishes one and proceeds with
+ * request */
+static gboolean _purple_http_reconnect(PurpleHttpConnection *hc);
+
+static void _purple_http_error(PurpleHttpConnection *hc, const char *format,
+	...) G_GNUC_PRINTF(2, 3);
+
+static void _purple_http_error(PurpleHttpConnection *hc, const char *format,
+	...)
+{
+	va_list args;
+
+	va_start(args, format);
+	hc->response->error = g_strdup_vprintf(format, args);
+	va_end(args);
+
+	purple_http_conn_cancel(hc);
+}
+
+static void _purple_http_gen_headers(PurpleHttpConnection *hc)
+{
+	GString *h;
+	PurpleHttpURL *url;
+	const GList *hdr;
+	PurpleHttpRequest *req;
+	PurpleHttpHeaders *hdrs;
+	gchar *request_url, *tmp_url = NULL;
+
+	PurpleProxyInfo *proxy;
+	gboolean proxy_http = FALSE;
+	const gchar *proxy_username, *proxy_password;
+
+	g_return_if_fail(hc != NULL);
+
+	if (hc->request_header != NULL)
+		return;
+
+	req = hc->request;
+	url = hc->url;
+	hdrs = req->headers;
+	proxy = purple_proxy_get_setup(hc->gc ?
+		purple_connection_get_account(hc->gc) : NULL);
+
+	proxy_http = (purple_proxy_info_get_type(proxy) == PURPLE_PROXY_HTTP ||
+		purple_proxy_info_get_type(proxy) == PURPLE_PROXY_USE_ENVVAR);
+	/* this is HTTP proxy, but used with tunelling with CONNECT */
+	if (proxy_http && url->port != 80)
+		proxy_http = FALSE;
+
+	hc->request_header = h = g_string_new("");
+	hc->request_header_written = 0;
+	hc->request_contents_written = 0;
+
+	if (proxy_http)
+		request_url = tmp_url = purple_http_url_print(url);
+	else
+		request_url = url->path;
+
+	g_string_append_printf(h, "%s %s HTTP/%s\r\n",
+		req->method ? req->method : "GET",
+		request_url,
+		req->http11 ? "1.1" : "1.0");
+
+	if (tmp_url)
+		g_free(tmp_url);
+
+	if (!purple_http_headers_get(hdrs, "host"))
+		g_string_append_printf(h, "Host: %s\r\n", url->host);
+	if (!purple_http_headers_get(hdrs, "connection"))
+		g_string_append(h, "Connection: close\r\n");
+	if (!purple_http_headers_get(hdrs, "accept"))
+		g_string_append(h, "Accept: */*\r\n");
+
+	if (req->contents_length > 0 && !purple_http_headers_get(hdrs,
+		"content-length"))
+		g_string_append_printf(h, "Content-Length: %u\r\n",
+			req->contents_length);
+
+	if (proxy_http)
+		g_string_append(h, "Proxy-Connection: close\r\n");
+
+	proxy_username = purple_proxy_info_get_username(proxy);
+	if (proxy_http && proxy_username != NULL && proxy_username[0] != '\0') {
+		gchar *proxy_auth, *ntlm_type1, *tmp;
+		int len;
+
+		proxy_password = purple_proxy_info_get_password(proxy);
+		if (proxy_password == NULL)
+			proxy_password = "";
+
+		tmp = g_strdup_printf("%s:%s", proxy_username, proxy_password);
+		len = strlen(tmp);
+		proxy_auth = purple_base64_encode((const guchar *)tmp, len);
+		memset(tmp, 0, len);
+		g_free(tmp);
+
+		ntlm_type1 = purple_ntlm_gen_type1(purple_get_host_name(), "");
+
+		g_string_append_printf(h, "Proxy-Authorization: Basic %s\r\n",
+			proxy_auth);
+		g_string_append_printf(h, "Proxy-Authorization: NTLM %s\r\n",
+			ntlm_type1);
+		g_string_append(h, "Proxy-Connection: Close\r\n");
+
+		memset(proxy_auth, 0, strlen(proxy_auth));
+		g_free(proxy_auth);
+		g_free(ntlm_type1);
+	}
+
+	hdr = purple_http_headers_get_all(hdrs);
+	while (hdr) {
+		PurpleKeyValuePair *kvp = hdr->data;
+		hdr = g_list_next(hdr);
+
+		g_string_append_printf(h, "%s: %s\r\n",
+			kvp->key, (gchar*)kvp->value);
+	}
+
+	if (!purple_http_cookie_jar_is_empty(req->cookie_jar)) {
+		gchar * cookies = purple_http_cookie_jar_gen(req->cookie_jar);
+		g_string_append_printf(h, "Cookie: %s\r\n", cookies);
+		g_free(cookies);
+	}
+
+	g_string_append_printf(h, "\r\n");
+
+	if (purple_debug_is_unsafe() && purple_debug_is_verbose()) {
+		purple_debug_misc("http", "Generated request headers:\n%s",
+			h->str);
+	}
+}
+
+static gboolean _purple_http_recv_headers(PurpleHttpConnection *hc,
+	const gchar *buf, int len)
+{
+	gchar *eol, *delim;
+
+	if (hc->headers_got) {
+		purple_debug_error("http", "Headers already got\n");
+		_purple_http_error(hc, _("Error parsing HTTP"));
+		return FALSE;
+	}
+
+	g_string_append_len(hc->response_buffer, buf, len);
+	if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) {
+		purple_debug_error("http",
+			"Buffer too big when parsing headers\n");
+		_purple_http_error(hc, _("Error parsing HTTP"));
+		return FALSE;
+	}
+
+	while ((eol = strstr(hc->response_buffer->str, "\r\n"))
+		!= NULL) {
+		gchar *hdrline = hc->response_buffer->str;
+		int hdrline_len = eol - hdrline;
+
+		hdrline[hdrline_len] = '\0';
+
+		if (hdrline[0] == '\0') {
+			if (!hc->main_header_got) {
+				hc->response->code = 0;
+				purple_debug_warning("http",
+					"Main header not present\n");
+				_purple_http_error(hc, _("Error parsing HTTP"));
+				return FALSE;
+			}
+			hc->headers_got = TRUE;
+			if (purple_debug_is_verbose())
+				purple_debug_misc("http", "Got headers end\n");
+		} else if (!hc->main_header_got) {
+			hc->main_header_got = TRUE;
+			delim = strchr(hdrline, ' ');
+			if (delim == NULL || 1 != sscanf(delim + 1, "%d",
+				&hc->response->code)) {
+				purple_debug_warning("http",
+					"Invalid response code\n");
+				_purple_http_error(hc, _("Error parsing HTTP"));
+				return FALSE;
+			}
+			if (purple_debug_is_verbose())
+				purple_debug_misc("http",
+					"Got main header with code %d\n",
+					hc->response->code);
+		} else {
+			if (purple_debug_is_verbose() &&
+				purple_debug_is_unsafe())
+				purple_debug_misc("http", "Got header: %s\n",
+					hdrline);
+			delim = strchr(hdrline, ':');
+			if (delim == NULL || delim == hdrline) {
+				purple_debug_warning("http",
+					"Bad header delimiter\n");
+				_purple_http_error(hc, _("Error parsing HTTP"));
+				return FALSE;
+			}
+			*delim++ = '\0';
+			while (*delim == ' ')
+				delim++;
+			
+			purple_http_headers_add(hc->response->headers, hdrline, delim);
+		}
+
+		g_string_erase(hc->response_buffer, 0, hdrline_len + 2);
+		if (hc->headers_got)
+			break;
+	}
+	return TRUE;
+}
+
+static gboolean _purple_http_recv_body_data(PurpleHttpConnection *hc,
+	const gchar *buf, int len)
+{
+	int current_offset = hc->data_length_got;
+
+	if (hc->request->max_length >= 0) {
+		if (hc->data_length_got + len > hc->request->max_length) {
+			len = hc->request->max_length - hc->data_length_got;
+			hc->length_expected = hc->length_got;
+		}
+	}
+	hc->data_length_got += len;
+
+	if (len == 0)
+		return TRUE;
+
+	if (hc->request->response_writer != NULL) {
+		gboolean succ;
+		succ = hc->request->response_writer(hc, hc->response, buf,
+			current_offset, len, hc->request->response_writer_data);
+		if (!succ) {
+			purple_debug_error("http",
+				"Cannot write using callback\n");
+			_purple_http_error(hc,
+				_("Error handling retrieved data"));
+			return FALSE;
+		}
+	} else {
+		if (hc->response->contents == NULL)
+			hc->response->contents = g_string_new("");
+		g_string_append_len(hc->response->contents, buf, len);
+	}
+
+	purple_http_conn_notify_progress_watcher(hc);
+	return TRUE;
+}
+
+static gboolean _purple_http_recv_body_chunked(PurpleHttpConnection *hc,
+	const gchar *buf, int len)
+{
+	gchar *eol, *line;
+	int line_len;
+
+	if (hc->chunks_done)
+		return FALSE;
+	if (!hc->response_buffer)
+		hc->response_buffer = g_string_new("");
+
+	g_string_append_len(hc->response_buffer, buf, len);
+	if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) {
+		purple_debug_error("http",
+			"Buffer too big when searching for chunk\n");
+		_purple_http_error(hc, _("Error parsing HTTP"));
+		return FALSE;
+	}
+
+	while (hc->response_buffer->len > 0) {
+		if (hc->in_chunk) {
+			int got_now = hc->response_buffer->len;
+			if (hc->chunk_got + got_now > hc->chunk_length)
+				got_now = hc->chunk_length - hc->chunk_got;
+			hc->chunk_got += got_now;
+			
+			if (!_purple_http_recv_body_data(hc,
+				hc->response_buffer->str, got_now))
+				return FALSE;
+
+			g_string_erase(hc->response_buffer, 0, got_now);
+			hc->in_chunk = (hc->chunk_got < hc->chunk_length);
+
+			if (purple_debug_is_verbose())
+				purple_debug_misc("http", "Chunk (%d/%d)\n",
+					hc->chunk_got, hc->chunk_length);
+
+			continue;
+		}
+
+		line = hc->response_buffer->str;
+		eol = strstr(line, "\r\n");
+		if (eol == line) {
+			g_string_erase(hc->response_buffer, 0, 2);
+			line = hc->response_buffer->str;
+			eol = strstr(line, "\r\n");
+		}
+		if (eol == NULL) {
+			/* waiting for more data (unlikely, but possible) */
+			if (hc->response_buffer->len > 20) {
+				purple_debug_warning("http", "Chunk length not "
+					"found (buffer too large)\n");
+				_purple_http_error(hc, _("Error parsing HTTP"));
+				return FALSE;
+			}
+			return TRUE;
+		}
+		line_len = eol - line;
+
+		if (1 != sscanf(line, "%x", &hc->chunk_length)) {
+			if (purple_debug_is_unsafe())
+				purple_debug_warning("http",
+					"Chunk length not found in [%s]\n",
+					line);
+			else
+				purple_debug_warning("http",
+					"Chunk length not found\n");
+			_purple_http_error(hc, _("Error parsing HTTP"));
+			return FALSE;
+		}
+		hc->chunk_got = 0;
+		hc->in_chunk = TRUE;
+
+		if (purple_debug_is_verbose())
+			purple_debug_misc("http", "Found chunk of length %d\n", hc->chunk_length);
+
+		g_string_erase(hc->response_buffer, 0, line_len + 2);
+
+		if (hc->chunk_length == 0) {
+			hc->chunks_done = TRUE;
+			hc->in_chunk = FALSE;
+			return TRUE;
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean _purple_http_recv_body(PurpleHttpConnection *hc,
+	const gchar *buf, int len)
+{
+	if (hc->is_chunked)
+	{
+		hc->length_got += len;
+		return _purple_http_recv_body_chunked(hc, buf, len);
+	}
+
+	if (hc->length_expected >= 0 &&
+		len + hc->length_got > hc->length_expected)
+		len = hc->length_expected - hc->length_got;
+	hc->length_got += len;
+
+	return _purple_http_recv_body_data(hc, buf, len);
+}
+
+static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd)
+{
+	PurpleHttpSocket *hs = &hc->socket;
+	int len;
+	gchar buf[4096];
+	gboolean got_anything;
+
+	if (hs->is_ssl)
+		len = purple_ssl_read(hs->ssl_connection, buf, sizeof(buf));
+	else
+		len = read(fd, buf, sizeof(buf));
+	got_anything = (len > 0);
+
+	if (len < 0 && errno == EAGAIN)
+		return FALSE;
+
+	if (len < 0) {
+		_purple_http_error(hc, _("Error reading from %s: %s"),
+			hc->url->host, g_strerror(errno));
+		return FALSE;
+	}
+
+	/* EOF */
+	if (len == 0) {
+		if (hc->length_expected >= 0 &&
+			hc->length_got < hc->length_expected) {
+			purple_debug_warning("http", "No more data while reading"
+				" contents\n");
+			_purple_http_error(hc, _("Error parsing HTTP"));
+			return FALSE;
+		}
+		if (hc->headers_got)
+			hc->length_expected = hc->length_got;
+		else {
+			purple_debug_warning("http", "No more data while "
+				"parsing headers\n");
+			_purple_http_error(hc, _("Error parsing HTTP"));
+			return FALSE;
+		}
+	}
+
+	if (!hc->headers_got && len > 0) {
+		if (!_purple_http_recv_headers(hc, buf, len))
+			return FALSE;
+		len = 0;
+		if (hc->headers_got) {
+			if (!purple_http_headers_get_int(hc->response->headers,
+				"Content-Length", &hc->length_expected))
+				hc->length_expected = -1;
+			hc->is_chunked = (purple_http_headers_match(
+				hc->response->headers,
+				"Transfer-Encoding", "chunked"));
+		}
+		if (hc->headers_got && hc->response_buffer &&
+			hc->response_buffer->len > 0) {
+			int buffer_len = hc->response_buffer->len;
+			gchar *buffer = g_string_free(hc->response_buffer, FALSE);
+			hc->response_buffer = NULL;
+			_purple_http_recv_body(hc, buffer, buffer_len);
+		}
+		if (!hc->headers_got)
+			return got_anything;
+	}
+
+	if (len > 0) {
+		if (!_purple_http_recv_body(hc, buf, len))
+			return FALSE;
+	}
+
+	if (hc->is_chunked && hc->chunks_done)
+		hc->length_expected = hc->length_got;
+
+	if (hc->length_expected >= 0 && hc->length_got >= hc->length_expected) {
+		const gchar *redirect;
+
+		if (!hc->headers_got) {
+			hc->response->code = 0;
+			purple_debug_warning("http", "No headers got\n");
+			_purple_http_error(hc, _("Error parsing HTTP"));
+			return FALSE;
+		}
+
+		if (purple_debug_is_unsafe() && purple_debug_is_verbose()) {
+			gchar *hdrs = purple_http_headers_dump(
+				hc->response->headers);
+			purple_debug_misc("http", "Got response headers: %s\n",
+				hdrs);
+			g_free(hdrs);
+		}
+
+		purple_http_cookie_jar_parse(hc->request->cookie_jar,
+			purple_http_headers_get_all_by_name(
+				hc->response->headers, "Set-Cookie"));
+
+		if (purple_debug_is_unsafe() && purple_debug_is_verbose() &&
+			!purple_http_cookie_jar_is_empty(
+				hc->request->cookie_jar)) {
+			gchar *cookies = purple_http_cookie_jar_dump(
+				hc->request->cookie_jar);
+			purple_debug_misc("http", "Cookies: %s\n", cookies);
+			g_free(cookies);
+		}
+
+		if (hc->response->code == 407) {
+			_purple_http_error(hc, _("Invalid proxy credentials"));
+			return FALSE;
+		}
+
+		redirect = purple_http_headers_get(hc->response->headers,
+			"location");
+		if (redirect && (hc->request->max_redirects == -1 ||
+			hc->request->max_redirects > hc->redirects_count)) {
+			PurpleHttpURL *url = purple_http_url_parse(redirect);
+
+			hc->redirects_count++;
+
+			if (!url) {
+				if (purple_debug_is_unsafe())
+					purple_debug_warning("http",
+						"Invalid redirect to %s\n",
+						redirect);
+				else
+					purple_debug_warning("http",
+						"Invalid redirect\n");
+				_purple_http_error(hc, _("Error parsing HTTP"));
+			}
+
+			purple_http_url_relative(hc->url, url);
+			purple_http_url_free(url);
+
+			_purple_http_reconnect(hc);
+			return FALSE;
+		}
+
+		_purple_http_disconnect(hc);
+		purple_http_connection_terminate(hc);
+		return FALSE;
+	}
+
+	return got_anything;
+}
+
+static void _purple_http_recv(gpointer _hc, gint fd, PurpleInputCondition cond)
+{
+	PurpleHttpConnection *hc = _hc;
+
+	while (_purple_http_recv_loopbody(hc, fd));
+}
+
+static void _purple_http_recv_ssl(gpointer _hc,
+	PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
+{
+	_purple_http_recv(_hc, -1, cond);
+}
+
+static void _purple_http_send_got_data(PurpleHttpConnection *hc,
+	gboolean success, gboolean eof, size_t stored)
+{
+	int estimated_length;
+
+	g_return_if_fail(hc != NULL);
+
+	if (!success) {
+		_purple_http_error(hc, _("Error requesting data to write"));
+		return;
+	}
+
+	hc->contents_reader_requested = FALSE;
+	g_string_set_size(hc->contents_reader_buffer, stored);
+	if (!eof)
+		return;
+
+	estimated_length = hc->request_contents_written + stored;
+
+	if (hc->request->contents_length != -1 &&
+		hc->request->contents_length != estimated_length) {
+		purple_debug_warning("http",
+			"Invalid amount of data has been written\n");
+	}
+	hc->request->contents_length = estimated_length;
+}
+
+static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond)
+{
+	PurpleHttpConnection *hc = _hc;
+	PurpleHttpSocket *hs = &hc->socket;
+	int written, write_len;
+	const gchar *write_from;
+	gboolean writing_headers;
+
+	/* Waiting for data. This could be written more efficiently, by removing
+	 * (and later, adding) hs->inpa. */
+	if (hc->contents_reader_requested)
+		return;
+
+	_purple_http_gen_headers(hc);
+
+	writing_headers =
+		(hc->request_header_written < hc->request_header->len);
+	if (writing_headers) {
+		write_from = hc->request_header->str +
+			hc->request_header_written;
+		write_len = hc->request_header->len -
+			hc->request_header_written;
+	} else if (hc->request->contents_reader) {
+		if (hc->contents_reader_requested)
+			return; /* waiting for data */
+		if (!hc->contents_reader_buffer)
+			hc->contents_reader_buffer = g_string_new("");
+		if (hc->contents_reader_buffer->len == 0) {
+			hc->contents_reader_requested = TRUE;
+			g_string_set_size(hc->contents_reader_buffer,
+				PURPLE_HTTP_MAX_READ_BUFFER_LEN);
+			hc->request->contents_reader(hc,
+				hc->contents_reader_buffer->str,
+				hc->request_contents_written,
+				PURPLE_HTTP_MAX_READ_BUFFER_LEN,
+				hc->request->contents_reader_data,
+				_purple_http_send_got_data);
+			return;
+		}
+		write_from = hc->contents_reader_buffer->str;
+		write_len = hc->contents_reader_buffer->len;
+	} else {
+		write_from = hc->request->contents +
+			hc->request_contents_written;
+		write_len = hc->request->contents_length -
+			hc->request_contents_written;
+	}
+
+	if (write_len == 0) {
+		purple_debug_warning("http", "Nothing to write\n");
+		written = 0;
+	} else if (hs->is_ssl)
+		written = purple_ssl_write(hs->ssl_connection,
+			write_from, write_len);
+	else
+		written = write(hs->fd, write_from, write_len);
+
+	if (written < 0 && errno == EAGAIN)
+		return;
+
+	if (written < 0) {
+		_purple_http_error(hc, _("Error writing to %s: %s"),
+			hc->url->host, g_strerror(errno));
+		return;
+	}
+
+	if (writing_headers) {
+		hc->request_header_written += written;
+		purple_http_conn_notify_progress_watcher(hc);
+		if (hc->request_header_written < hc->request_header->len)
+			return;
+		if (hc->request->contents_length > 0)
+			return;
+	} else {
+		hc->request_contents_written += written;
+		purple_http_conn_notify_progress_watcher(hc);
+		if (hc->contents_reader_buffer)
+			g_string_erase(hc->contents_reader_buffer, 0, written);
+		if (hc->request_contents_written < hc->request->contents_length)
+			return;
+	}
+
+	/* request is completely written, let's read the response */
+	hc->is_reading = TRUE;
+	purple_input_remove(hs->inpa);
+	hs->inpa = 0;
+	if (hs->is_ssl)
+		purple_ssl_input_add(hs->ssl_connection,
+			_purple_http_recv_ssl, hc);
+	else
+		hs->inpa = purple_input_add(hs->fd, PURPLE_INPUT_READ,
+			_purple_http_recv, hc);
+}
+
+static void _purple_http_connected_raw(gpointer _hc, gint fd,
+	const gchar *error_message)
+{
+	PurpleHttpConnection *hc = _hc;
+	PurpleHttpSocket *hs = &hc->socket;
+
+	hs->raw_connection = NULL;
+
+	if (fd == -1) {
+		_purple_http_error(hc, _("Unable to connect to %s: %s"),
+			hc->url->host, error_message);
+		return;
+	}
+
+	hs->fd = fd;
+	hs->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE,
+		_purple_http_send, hc);
+}
+
+static void _purple_http_connected_ssl(gpointer _hc,
+	PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
+{
+	PurpleHttpConnection *hc = _hc;
+	PurpleHttpSocket *hs = &hc->socket;
+
+	hs->fd = hs->ssl_connection->fd;
+	hs->inpa = purple_input_add(hs->fd, PURPLE_INPUT_WRITE,
+		_purple_http_send, hc);
+}
+
+static void _purple_http_connected_ssl_error(
+	PurpleSslConnection *ssl_connection, PurpleSslErrorType error,
+	gpointer _hc)
+{
+	PurpleHttpConnection *hc = _hc;
+	PurpleHttpSocket *hs = &hc->socket;
+
+	hs->ssl_connection = NULL;
+	_purple_http_error(hc, _("Unable to connect to %s: %s"),
+		hc->url->host, purple_ssl_strerror(error));
+}
+
+static void _purple_http_disconnect(PurpleHttpConnection *hc)
+{
+	PurpleHttpSocket *hs;
+
+	g_return_if_fail(hc != NULL);
+
+	hs = &hc->socket;
+
+	if (hc->request_header)
+		g_string_free(hc->request_header, TRUE);
+	hc->request_header = NULL;
+	if (hc->response_buffer)
+		g_string_free(hc->response_buffer, TRUE);
+	hc->response_buffer = NULL;
+
+	if (hs->inpa != 0)
+		purple_input_remove(hs->inpa);
+
+	if (hs->is_ssl) {
+		if (hs->ssl_connection != NULL)
+			purple_ssl_close(hs->ssl_connection);
+	} else {
+		if (hs->raw_connection != NULL)
+			purple_proxy_connect_cancel(hs->raw_connection);
+		if (hs->fd > 0)
+			close(hs->fd);
+	}
+
+	memset(hs, 0, sizeof(PurpleHttpSocket));
+}
+
+static gboolean _purple_http_reconnect(PurpleHttpConnection *hc)
+{
+	PurpleHttpURL *url;
+	gboolean is_ssl = FALSE;
+	PurpleAccount *account = NULL;
+
+	g_return_val_if_fail(hc != NULL, FALSE);
+	g_return_val_if_fail(hc->url != NULL, FALSE);
+
+	_purple_http_disconnect(hc);
+
+	if (purple_debug_is_verbose()) {
+		if (purple_debug_is_unsafe()) {
+			gchar *url = purple_http_url_print(hc->url);
+			purple_debug_misc("http", "Connecting to %s...\n", url);
+			g_free(url);
+		} else
+			purple_debug_misc("http", "Connecting to %s...\n",
+				hc->url->host);
+	}
+
+	if (hc->gc)
+		account = purple_connection_get_account(hc->gc);
+
+	url = hc->url;
+	if (url->protocol[0] == '\0' ||
+		g_ascii_strcasecmp(url->protocol, "http") == 0) {
+		/* do nothing */
+	} else if (g_ascii_strcasecmp(url->protocol, "https") == 0) {
+		is_ssl = TRUE;
+	} else {
+		_purple_http_error(hc, _("Unsupported protocol: %s"),
+			url->protocol);
+		return FALSE;
+	}
+
+	hc->socket.is_ssl = is_ssl;
+	if (is_ssl) {
+		if (!purple_ssl_is_supported()) {
+			_purple_http_error(hc, _("Unable to connect to %s: %s"),
+				url->host, _("Server requires TLS/SSL, "
+				"but no TLS/SSL support was found."));
+			return FALSE;
+		}
+		hc->socket.ssl_connection = purple_ssl_connect(account,
+			url->host, url->port,
+			_purple_http_connected_ssl,
+			_purple_http_connected_ssl_error, hc);
+/* TODO
+		purple_ssl_set_compatibility_level(hc->socket.ssl_connection,
+			PURPLE_SSL_COMPATIBILITY_SECURE);
+*/
+	} else {
+		hc->socket.raw_connection = purple_proxy_connect(hc->gc, account,
+			url->host, url->port,
+			_purple_http_connected_raw, hc);
+	}
+
+	if (hc->socket.ssl_connection == NULL &&
+		hc->socket.raw_connection == NULL) {
+		_purple_http_error(hc, _("Unable to connect to %s"), url->host);
+		return FALSE;
+	}
+
+	purple_http_headers_free(hc->response->headers);
+	hc->response->headers = purple_http_headers_new();
+	hc->response_buffer = g_string_new("");
+	hc->main_header_got = FALSE;
+	hc->headers_got = FALSE;
+	if (hc->response->contents != NULL)
+		g_string_free(hc->response->contents, TRUE);
+	hc->response->contents = NULL;
+	hc->length_got = 0;
+	hc->data_length_got = 0;
+	hc->length_expected = -1;
+	hc->is_chunked = FALSE;
+	hc->in_chunk = FALSE;
+	hc->chunks_done = FALSE;
+
+	purple_http_conn_notify_progress_watcher(hc);
+
+	return TRUE;
+}
+
+/*** Performing HTTP requests *************************************************/
+
+static gboolean purple_http_request_timeout(gpointer _hc)
+{
+	PurpleHttpConnection *hc = _hc;
+
+	purple_debug_warning("http", "Timeout reached for request %p\n", hc);
+
+	purple_http_conn_cancel(hc);
+
+	return FALSE;
+}
+
+PurpleHttpConnection * purple_http_get(PurpleConnection *gc, const gchar *url,
+	PurpleHttpCallback callback, gpointer user_data)
+{
+	PurpleHttpRequest *request;
+	PurpleHttpConnection *hc;
+
+	g_return_val_if_fail(url != NULL, NULL);
+
+	request = purple_http_request_new(url);
+	hc = purple_http_request(gc, request, callback, user_data);
+	purple_http_request_unref(request);
+
+	return hc;
+}
+
+PurpleHttpConnection * purple_http_request(PurpleConnection *gc,
+	PurpleHttpRequest *request, PurpleHttpCallback callback,
+	gpointer user_data)
+{
+	PurpleHttpConnection *hc;
+
+	g_return_val_if_fail(request != NULL, NULL);
+
+	hc = purple_http_connection_new(request, gc);
+	hc->callback = callback;
+	hc->user_data = user_data;
+
+	if (purple_debug_is_unsafe())
+		purple_debug_misc("http", "Performing new request %p for %s.\n",
+			hc, request->url);
+	else
+		purple_debug_misc("http", "Performing new request %p.\n", hc);
+
+	hc->url = purple_http_url_parse(request->url);
+	if (!hc->url || hc->url->host[0] == '\0') {
+		purple_debug_error("http", "Invalid URL requested.\n");
+		purple_http_connection_terminate(hc);
+		return NULL;
+	}
+
+	_purple_http_reconnect(hc);
+
+	hc->timeout_handle = purple_timeout_add_seconds(request->timeout,
+		purple_http_request_timeout, hc);
+
+	return hc;
+}
+
+/*** HTTP connection API ******************************************************/
+
+static void purple_http_connection_free(PurpleHttpConnection *hc);
+static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc);
+
+static PurpleHttpConnection * purple_http_connection_new(
+	PurpleHttpRequest *request, PurpleConnection *gc)
+{
+	PurpleHttpConnection *hc = g_new0(PurpleHttpConnection, 1);
+
+	hc->request = request;
+	purple_http_request_ref(request);
+	hc->response = purple_http_response_new();
+
+	hc->link_global = purple_http_hc_list =
+		g_list_prepend(purple_http_hc_list, hc);
+	g_hash_table_insert(purple_http_hc_by_ptr, hc, hc->link_global);
+	if (gc) {
+		GList *gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc);
+		g_hash_table_steal(purple_http_hc_by_gc, gc);
+		hc->link_gc = gc_list = g_list_prepend(gc_list, hc);
+		g_hash_table_insert(purple_http_hc_by_gc, gc, gc_list);
+		hc->gc = gc;
+	}
+
+	return hc;
+}
+
+static void purple_http_connection_free(PurpleHttpConnection *hc)
+{
+	if (hc->timeout_handle)
+		purple_timeout_remove(hc->timeout_handle);
+	if (hc->watcher_delayed_handle)
+		purple_timeout_remove(hc->watcher_delayed_handle);
+
+	purple_http_url_free(hc->url);
+	purple_http_request_unref(hc->request);
+	purple_http_response_free(hc->response);
+
+	if (hc->contents_reader_buffer)
+		g_string_free(hc->contents_reader_buffer, TRUE);
+
+	if (hc->request_header)
+		g_string_free(hc->request_header, TRUE);
+
+	purple_http_hc_list = g_list_delete_link(purple_http_hc_list,
+		hc->link_global);
+	g_hash_table_remove(purple_http_hc_by_ptr, hc);
+	if (hc->gc) {
+		GList *gc_list, *gc_list_new;
+		gc_list = g_hash_table_lookup(purple_http_hc_by_gc, hc->gc);
+		g_assert(gc_list != NULL);
+
+		gc_list_new = g_list_delete_link(gc_list, hc->link_gc);
+		if (gc_list != gc_list_new) {
+			g_hash_table_steal(purple_http_hc_by_gc, hc->gc);
+			if (gc_list_new)
+				g_hash_table_insert(purple_http_hc_by_gc,
+					hc->gc, gc_list_new);
+		}
+	}
+
+	g_free(hc);
+}
+
+/* call callback and do the cleanup */
+static void purple_http_connection_terminate(PurpleHttpConnection *hc)
+{
+	g_return_if_fail(hc != NULL);
+
+	purple_debug_misc("http", "Request %p performed %s.\n", hc,
+		purple_http_response_is_successfull(hc->response) ?
+		"successfully" : "without success");
+
+	if (hc->callback)
+		hc->callback(hc, hc->response, hc->user_data);
+
+	purple_http_connection_free(hc);
+}
+
+void purple_http_conn_cancel(PurpleHttpConnection *http_conn)
+{
+	if (http_conn == NULL)
+		return;
+
+	http_conn->response->code = 0;
+	_purple_http_disconnect(http_conn);
+	purple_http_connection_terminate(http_conn);
+}
+
+void purple_http_conn_cancel_all(PurpleConnection *gc)
+{
+	GList *gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc);
+
+	while (gc_list) {
+		PurpleHttpConnection *hc = gc_list->data;
+		gc_list = g_list_next(gc_list);
+		purple_http_conn_cancel(hc);
+	}
+
+	if (NULL != g_hash_table_lookup(purple_http_hc_by_gc, gc))
+		purple_debug_error("http", "Couldn't cancel all connections "
+			"related to gc=%p\n", gc);
+}
+
+gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn)
+{
+	if (http_conn == NULL)
+		return FALSE;
+	return (NULL != g_hash_table_lookup(purple_http_hc_by_ptr, http_conn));
+}
+
+PurpleHttpRequest * purple_http_conn_get_request(PurpleHttpConnection *http_conn)
+{
+	g_return_val_if_fail(http_conn != NULL, NULL);
+
+	return http_conn->request;
+}
+
+PurpleHttpCookieJar * purple_http_conn_get_cookie_jar(
+	PurpleHttpConnection *http_conn)
+{
+	return purple_http_request_get_cookie_jar(purple_http_conn_get_request(
+		http_conn));
+}
+
+PurpleConnection * purple_http_conn_get_purple_connection(
+	PurpleHttpConnection *http_conn)
+{
+	g_return_val_if_fail(http_conn != NULL, NULL);
+
+	return http_conn->gc;
+}
+
+void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn,
+	PurpleHttpProgressWatcher watcher, gpointer user_data,
+	guint interval_threshold)
+{
+	g_return_if_fail(http_conn != NULL);
+
+	http_conn->watcher = watcher;
+	http_conn->watcher_user_data = user_data;
+	http_conn->watcher_interval_threshold = interval_threshold;
+}
+
+static void purple_http_conn_notify_progress_watcher(
+	PurpleHttpConnection *hc)
+{
+	gint64 now;
+	gboolean reading_state;
+	int processed, total;
+
+	g_return_if_fail(hc != NULL);
+
+	if (hc->watcher == NULL)
+		return;
+
+	reading_state = hc->is_reading;
+	if (reading_state) {
+		total = hc->length_expected;
+		processed = hc->length_got;
+	} else {
+		total = hc->request->contents_length;
+		processed = hc->request_contents_written;
+		if (total == 0)
+			total = -1;
+	}
+	if (total != -1 && total < processed) {
+		purple_debug_warning("http", "Processed too much\n");
+		total = processed;
+	}
+
+	now = g_get_monotonic_time();
+	if (hc->watcher_last_call + hc->watcher_interval_threshold
+		> now && processed != total) {
+		if (hc->watcher_delayed_handle)
+			return;
+		hc->watcher_delayed_handle = purple_timeout_add_seconds(
+			1 + hc->watcher_interval_threshold / 1000000,
+			purple_http_conn_notify_progress_watcher_timeout, hc);
+		return;
+	}
+
+	if (hc->watcher_delayed_handle)
+		purple_timeout_remove(hc->watcher_delayed_handle);
+	hc->watcher_delayed_handle = 0;
+
+	hc->watcher_last_call = now;
+	hc->watcher(hc, reading_state, processed, total, hc->watcher_user_data);
+}
+
+static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc)
+{
+	PurpleHttpConnection *hc = _hc;
+
+	purple_http_conn_notify_progress_watcher(hc);
+
+	return FALSE;
+}
+
+/*** Cookie jar API ***********************************************************/
+
+static PurpleHttpCookie * purple_http_cookie_new(const gchar *value);
+void purple_http_cookie_free(PurpleHttpCookie *cookie);
+
+static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar,
+	const gchar *name, const gchar *value, time_t expires);
+
+static PurpleHttpCookie * purple_http_cookie_new(const gchar *value)
+{
+	PurpleHttpCookie *cookie = g_new0(PurpleHttpCookie, 1);
+
+	cookie->value = g_strdup(value);
+	cookie->expires = -1;
+
+	return cookie;
+}
+
+void purple_http_cookie_free(PurpleHttpCookie *cookie)
+{
+	g_free(cookie->value);
+	g_free(cookie);
+}
+
+void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar);
+
+PurpleHttpCookieJar * purple_http_cookie_jar_new(void)
+{
+	PurpleHttpCookieJar *cjar = g_new0(PurpleHttpCookieJar, 1);
+
+	cjar->ref_count = 1;
+	cjar->tab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+		(GDestroyNotify)purple_http_cookie_free);
+
+	return cjar;
+}
+
+void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar)
+{
+	g_hash_table_destroy(cookie_jar->tab);
+	g_free(cookie_jar);
+}
+
+void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar)
+{
+	g_return_if_fail(cookie_jar != NULL);
+
+	cookie_jar->ref_count++;
+}
+
+PurpleHttpCookieJar * purple_http_cookie_jar_unref(
+	PurpleHttpCookieJar *cookie_jar)
+{
+	if (cookie_jar == NULL)
+		return NULL;
+
+	g_return_val_if_fail(cookie_jar->ref_count > 0, NULL);
+
+	cookie_jar->ref_count--;
+	if (cookie_jar->ref_count > 0)
+		return cookie_jar;
+
+	purple_http_cookie_jar_free(cookie_jar);
+	return NULL;
+}
+
+static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar,
+	GList *values)
+{
+	values = g_list_first(values);
+	while (values) {
+		const gchar *cookie = values->data;
+		const gchar *eqsign, *semicolon;
+		gchar *name, *value;
+		time_t expires = -1;
+		values = g_list_next(values);
+
+		eqsign = strchr(cookie, '=');
+		semicolon = strchr(cookie, ';');
+
+		if (eqsign == NULL || eqsign == cookie ||
+			(semicolon != NULL && semicolon < eqsign)) {
+			if (purple_debug_is_unsafe())
+				purple_debug_warning("http",
+					"Invalid cookie: [%s]\n", cookie);
+			else
+				purple_debug_warning("http", "Invalid cookie.");
+		}
+
+		name = g_strndup(cookie, eqsign - cookie);
+		eqsign++;
+		if (semicolon != NULL)
+			value = g_strndup(eqsign, semicolon - eqsign);
+		else
+			value = g_strdup(eqsign);
+
+		if (semicolon != NULL) {
+			GMatchInfo *match_info;
+			GRegex *re_expires = g_regex_new(
+				"expires=([a-z0-9, :]+)",
+				G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
+				G_REGEX_MATCH_NOTEMPTY, NULL);
+
+			g_regex_match(re_expires, semicolon, 0, &match_info);
+			if (g_match_info_matches(match_info)) {
+				gchar *expire_date =
+					g_match_info_fetch(match_info, 1);
+				expires = purple_http_rfc1123_to_time(
+					expire_date);
+				g_free(expire_date);
+			}
+			g_match_info_free(match_info);
+
+			g_regex_unref(re_expires);
+		}
+
+		purple_http_cookie_jar_set_ext(cookie_jar, name, value, expires);
+
+		g_free(name);
+		g_free(value);
+	}
+}
+
+static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar)
+{
+	GHashTableIter it;
+	gchar *key;
+	PurpleHttpCookie *cookie;
+	GString *str;
+	time_t now = time(NULL);
+
+	g_return_val_if_fail(cookie_jar != NULL, NULL);
+
+	str = g_string_new("");
+
+	g_hash_table_iter_init(&it, cookie_jar->tab);
+	while (g_hash_table_iter_next(&it, (gpointer*)&key,
+		(gpointer*)&cookie)) {
+		if (cookie->expires != -1 && cookie->expires <= now)
+			continue;
+		g_string_append_printf(str, "%s=%s; ", key, cookie->value);
+	}
+
+	if (str->len > 0)
+		g_string_truncate(str, str->len - 2);
+	return g_string_free(str, FALSE);
+}
+
+void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar,
+	const gchar *name, const gchar *value)
+{
+	purple_http_cookie_jar_set_ext(cookie_jar, name, value, -1);
+}
+
+static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar,
+	const gchar *name, const gchar *value, time_t expires)
+{
+	g_return_if_fail(cookie_jar != NULL);
+	g_return_if_fail(name != NULL);
+
+	if (expires != -1 && time(NULL) >= expires)
+		value = NULL;
+
+	if (value != NULL) {
+		PurpleHttpCookie *cookie = purple_http_cookie_new(value);
+		cookie->expires = expires;
+		g_hash_table_insert(cookie_jar->tab, g_strdup(name), cookie);
+	} else
+		g_hash_table_remove(cookie_jar->tab, name);
+}
+
+const gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar,
+	const gchar *name)
+{
+	PurpleHttpCookie *cookie;
+
+	g_return_val_if_fail(cookie_jar != NULL, NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	cookie = g_hash_table_lookup(cookie_jar->tab, name);
+	if (!cookie)
+		return NULL;
+
+	return cookie->value;
+}
+
+gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar)
+{
+	GHashTableIter it;
+	gchar *key;
+	PurpleHttpCookie *cookie;
+	GString *str = g_string_new("");
+
+	g_hash_table_iter_init(&it, cjar->tab);
+	while (g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&cookie))
+		g_string_append_printf(str, "%s: %s (expires: %lld)\n", key,
+			cookie->value, (long long int)cookie->expires);
+
+	if (str->len > 0)
+		g_string_truncate(str, str->len - 1);
+	return g_string_free(str, FALSE);
+}
+
+gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar)
+{
+	g_return_val_if_fail(cookie_jar != NULL, TRUE);
+
+	return g_hash_table_size(cookie_jar->tab) == 0;
+}
+
+/*** Request API **************************************************************/
+
+static void purple_http_request_free(PurpleHttpRequest *request);
+
+PurpleHttpRequest * purple_http_request_new(const gchar *url)
+{
+	PurpleHttpRequest *request;
+
+	g_return_val_if_fail(url != NULL, NULL);
+
+	request = g_new0(PurpleHttpRequest, 1);
+
+	request->ref_count = 1;
+	request->url = g_strdup(url);
+	request->headers = purple_http_headers_new();
+	request->cookie_jar = purple_http_cookie_jar_new();
+
+	request->timeout = PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT;
+	request->max_redirects = PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS;
+	request->http11 = TRUE;
+	request->max_length = -1;
+
+	return request;
+}
+
+static void purple_http_request_free(PurpleHttpRequest *request)
+{
+	purple_http_headers_free(request->headers);
+	purple_http_cookie_jar_unref(request->cookie_jar);
+	g_free(request->url);
+	g_free(request);
+}
+
+void purple_http_request_ref(PurpleHttpRequest *request)
+{
+	g_return_if_fail(request != NULL);
+
+	request->ref_count++;
+}
+
+PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request)
+{
+	if (request == NULL)
+		return NULL;
+
+	g_return_val_if_fail(request->ref_count > 0, NULL);
+
+	request->ref_count--;
+	if (request->ref_count > 0)
+		return request;
+
+	purple_http_request_free(request);
+	return NULL;
+}
+
+void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url)
+{
+	g_return_if_fail(request != NULL);
+	g_return_if_fail(url != NULL);
+
+	g_free(request->url);
+	request->url = g_strdup(url);
+}
+
+const gchar * purple_http_request_get_url(PurpleHttpRequest *request)
+{
+	g_return_val_if_fail(request != NULL, NULL);
+
+	return request->url;
+}
+
+void purple_http_request_set_method(PurpleHttpRequest *request, const gchar *method)
+{
+	g_return_if_fail(request != NULL);
+
+	g_free(request->method);
+	request->method = g_strdup(method);
+}
+
+const gchar * purple_http_request_get_method(PurpleHttpRequest *request)
+{
+	g_return_val_if_fail(request != NULL, NULL);
+
+	return request->method;
+}
+
+void purple_http_request_set_contents(PurpleHttpRequest *request,
+	const gchar *contents, int length)
+{
+	g_return_if_fail(request != NULL);
+	g_return_if_fail(length >= -1);
+
+	request->contents_reader = NULL;
+	request->contents_reader_data = NULL;
+
+	g_free(request->contents);
+	if (contents == NULL || length == 0) {
+		request->contents = NULL;
+		request->contents_length = 0;
+		return;
+	}
+
+	if (length == -1)
+		length = strlen(contents);
+	request->contents = g_memdup(contents, length);
+	request->contents_length = length;
+}
+
+void purple_http_request_set_contents_reader(PurpleHttpRequest *request,
+	PurpleHttpContentReader reader, int contents_length, gpointer user_data)
+{
+	g_return_if_fail(request != NULL);
+	g_return_if_fail(reader != NULL);
+	g_return_if_fail(contents_length >= -1);
+
+	g_free(request->contents);
+	request->contents = NULL;
+	request->contents_length = contents_length;
+	request->contents_reader = reader;
+	request->contents_reader_data = user_data;
+}
+
+void purple_http_request_set_response_writer(PurpleHttpRequest *request,
+	PurpleHttpContentWriter writer, gpointer user_data)
+{
+	g_return_if_fail(request != NULL);
+
+	if (writer == NULL)
+		user_data = NULL;
+	request->response_writer = writer;
+	request->response_writer_data = user_data;
+}
+
+void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout)
+{
+	g_return_if_fail(request != NULL);
+
+	if (timeout < -1)
+		timeout = -1;
+
+	request->timeout = timeout;
+}
+
+int purple_http_request_get_timeout(PurpleHttpRequest *request)
+{
+	g_return_val_if_fail(request != NULL, -1);
+
+	return request->timeout;
+}
+
+void purple_http_request_set_max_redirects(PurpleHttpRequest *request,
+	int max_redirects)
+{
+	g_return_if_fail(request != NULL);
+
+	if (max_redirects < -1)
+		max_redirects = -1;
+
+	request->max_redirects = max_redirects;
+}
+
+int purple_http_request_get_max_redirects(PurpleHttpRequest *request)
+{
+	g_return_val_if_fail(request != NULL, -1);
+
+	return request->max_redirects;
+}
+
+void purple_http_request_set_cookie_jar(PurpleHttpRequest *request,
+	PurpleHttpCookieJar *cookie_jar)
+{
+	g_return_if_fail(request != NULL);
+	g_return_if_fail(cookie_jar != NULL);
+
+	purple_http_cookie_jar_ref(cookie_jar);
+	purple_http_cookie_jar_unref(request->cookie_jar);
+	request->cookie_jar = cookie_jar;
+}
+
+PurpleHttpCookieJar * purple_http_request_get_cookie_jar(
+	PurpleHttpRequest *request)
+{
+	g_return_val_if_fail(request != NULL, NULL);
+
+	return request->cookie_jar;
+}
+
+void purple_http_request_set_http11(PurpleHttpRequest *request, gboolean http11)
+{
+	g_return_if_fail(request != NULL);
+
+	request->http11 = http11;
+}
+
+gboolean purple_http_request_is_http11(PurpleHttpRequest *request)
+{
+	g_return_val_if_fail(request != NULL, FALSE);
+
+	return request->http11;
+}
+
+void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len)
+{
+	g_return_if_fail(request != NULL);
+
+	if (max_len < -1)
+		max_len = -1;
+
+	request->max_length = max_len;
+}
+
+int purple_http_request_get_max_len(PurpleHttpRequest *request)
+{
+	g_return_val_if_fail(request != NULL, -1);
+
+	return request->max_length;
+}
+
+void purple_http_request_header_set(PurpleHttpRequest *request,
+	const gchar *key, const gchar *value)
+{
+	g_return_if_fail(request != NULL);
+	g_return_if_fail(key != NULL);
+
+	purple_http_headers_remove(request->headers, key);
+	if (value)
+		purple_http_headers_add(request->headers, key, value);
+}
+
+void purple_http_request_header_set_printf(PurpleHttpRequest *request,
+	const gchar *key, const gchar *format, ...)
+{
+	va_list args;
+	gchar *value;
+
+	g_return_if_fail(request != NULL);
+	g_return_if_fail(key != NULL);
+	g_return_if_fail(format != NULL);
+
+	va_start(args, format);
+	value = g_strdup_vprintf(format, args);
+	va_end(args);
+
+	purple_http_request_header_set(request, key, value);
+	g_free(value);
+}
+
+void purple_http_request_header_add(PurpleHttpRequest *request,
+	const gchar *key, const gchar *value)
+{
+	g_return_if_fail(request != NULL);
+	g_return_if_fail(key != NULL);
+
+	purple_http_headers_add(request->headers, key, value);
+}
+
+/*** HTTP response API ********************************************************/
+
+static PurpleHttpResponse * purple_http_response_new(void)
+{
+	PurpleHttpResponse *response = g_new0(PurpleHttpResponse, 1);
+
+	return response;
+}
+
+static void purple_http_response_free(PurpleHttpResponse *response)
+{
+	if (response->contents != NULL)
+		g_string_free(response->contents, TRUE);
+	g_free(response->error);
+	purple_http_headers_free(response->headers);
+	g_free(response);
+}
+
+gboolean purple_http_response_is_successfull(PurpleHttpResponse *response)
+{
+	int code;
+
+	g_return_val_if_fail(response != NULL, FALSE);
+
+	code = response->code;
+
+	if (code <= 0)
+		return FALSE;
+
+	if (code / 100 == 2)
+		return TRUE;
+
+	return FALSE;
+}
+
+int purple_http_response_get_code(PurpleHttpResponse *response)
+{
+	g_return_val_if_fail(response != NULL, 0);
+
+	return response->code;
+}
+
+const gchar * purple_http_response_get_error(PurpleHttpResponse *response)
+{
+	g_return_val_if_fail(response != NULL, NULL);
+
+	return response->error;
+}
+
+gsize purple_http_response_get_data_len(PurpleHttpResponse *response)
+{
+	g_return_val_if_fail(response != NULL, 0);
+
+	if (response->contents == NULL)
+		return 0;
+
+	return response->contents->len;
+}
+
+const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len)
+{
+	const gchar *ret = "";
+
+	g_return_val_if_fail(response != NULL, NULL);
+
+	if (response->contents != NULL) {
+		ret = response->contents->str;
+		if(len)
+			*len = response->contents->len;
+	} else {
+		if(len)
+			*len = 0;
+	}
+
+	return ret;
+}
+
+const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response)
+{
+	g_return_val_if_fail(response != NULL, NULL);
+
+	return purple_http_headers_get_all(response->headers);
+}
+
+const GList * purple_http_response_get_headers_by_name(
+	PurpleHttpResponse *response, const gchar *name)
+{
+	g_return_val_if_fail(response != NULL, NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	return purple_http_headers_get_all_by_name(response->headers, name);
+}
+
+const gchar * purple_http_response_get_header(PurpleHttpResponse *response,
+	const gchar *name)
+{
+	g_return_val_if_fail(response != NULL, NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	return purple_http_headers_get(response->headers, name);
+}
+
+/*** URL functions ************************************************************/
+
+static PurpleHttpURL * purple_http_url_parse(const char *raw_url)
+{
+	PurpleHttpURL *url;
+	GMatchInfo *match_info;
+
+	gchar *host_full, *tmp;
+
+	g_return_val_if_fail(raw_url != NULL, NULL);
+
+	url = g_new0(PurpleHttpURL, 1);
+
+	if (!g_regex_match(purple_http_re_url, raw_url, 0, &match_info)) {
+		if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
+			purple_debug_warning("http",
+				"Invalid URL provided: %s\n",
+				raw_url);
+		}
+		return NULL;
+	}
+
+	url->protocol = g_match_info_fetch(match_info, 1);
+	host_full = g_match_info_fetch(match_info, 2);
+	url->path = g_match_info_fetch(match_info, 3);
+	url->fragment = g_match_info_fetch(match_info, 4);
+	g_match_info_free(match_info);
+
+	if (url->protocol[0] == '\0') {
+		g_free(url->protocol);
+		url->protocol = NULL;
+	} else if (url->protocol != NULL) {
+		tmp = url->protocol;
+		url->protocol = g_ascii_strdown(url->protocol, -1);
+		g_free(tmp);
+	}
+	if (host_full[0] == '\0') {
+		g_free(host_full);
+		host_full = NULL;
+	}
+	if (url->path[0] == '\0') {
+		g_free(url->path);
+		url->path = NULL;
+	}
+	if ((url->protocol == NULL) != (host_full == NULL))
+		purple_debug_warning("http", "Protocol or host not present "
+			"(unlikely case)\n");
+
+	if (host_full) {
+		gchar *port_str;
+
+		if (!g_regex_match(purple_http_re_url_host, host_full, 0,
+			&match_info)) {
+			if (purple_debug_is_verbose() &&
+				purple_debug_is_unsafe()) {
+				purple_debug_warning("http",
+					"Invalid host provided for URL: %s\n",
+					raw_url);
+			}
+
+			g_free(host_full);
+			purple_http_url_free(url);
+			return NULL;
+		}
+
+		url->user = g_match_info_fetch(match_info, 1);
+		url->password = g_match_info_fetch(match_info, 2);
+		url->host = g_match_info_fetch(match_info, 3);
+		port_str = g_match_info_fetch(match_info, 4);
+
+		if (port_str && port_str[0])
+			url->port = atoi(port_str);
+
+		if (url->user[0] == '\0') {
+			g_free(url->user);
+			url->user = NULL;
+		}
+		if (url->password[0] == '\0') {
+			g_free(url->password);
+			url->password = NULL;
+		}
+		if (url->host[0] == '\0') {
+			g_free(url->host);
+			url->host = NULL;
+		} else if (url->host != NULL) {
+			tmp = url->host;
+			url->host = g_ascii_strdown(url->host, -1);
+			g_free(tmp);
+		}
+
+		g_free(port_str);
+		g_match_info_free(match_info);
+
+		g_free(host_full);
+		host_full = NULL;
+	}
+
+	if (url->host != NULL) {
+		if (url->protocol == NULL)
+			url->protocol = g_strdup("http");
+		if (url->port == 0 && 0 == strcmp(url->protocol, "http"))
+			url->port = 80;
+		if (url->port == 0 && 0 == strcmp(url->protocol, "https"))
+			url->port = 443;
+		if (url->path == NULL)
+			url->path = g_strdup("/");
+		if (url->path[0] != '/')
+			purple_debug_warning("http",
+				"URL path doesn't start with slash\n");
+	}
+
+	return url;
+}
+
+static void purple_http_url_free(PurpleHttpURL *parsed_url)
+{
+	if (parsed_url == NULL)
+		return;
+
+	g_free(parsed_url->protocol);
+	g_free(parsed_url->user);
+	g_free(parsed_url->password);
+	g_free(parsed_url->host);
+	g_free(parsed_url->path);
+	g_free(parsed_url->fragment);
+	g_free(parsed_url);
+}
+
+static void purple_http_url_relative(PurpleHttpURL *base_url,
+	PurpleHttpURL *relative_url)
+{
+	g_return_if_fail(base_url != NULL);
+	g_return_if_fail(relative_url != NULL);
+
+	if (relative_url->host) {
+		g_free(base_url->protocol);
+		base_url->protocol = g_strdup(relative_url->protocol);
+		g_free(base_url->user);
+		base_url->user = g_strdup(relative_url->user);
+		g_free(base_url->password);
+		base_url->password = g_strdup(relative_url->password);
+		g_free(base_url->host);
+		base_url->host = g_strdup(relative_url->host);
+		base_url->port = relative_url->port;
+
+		g_free(base_url->path);
+		base_url->path = NULL;
+	}
+
+	if (relative_url->path) {
+		if (relative_url->path[0] == '/' ||
+			base_url->path == NULL) {
+			g_free(base_url->path);
+			base_url->path = g_strdup(relative_url->path);
+		} else {
+			gchar *last_slash = strrchr(base_url->path, '/');
+			gchar *tmp;
+			if (last_slash == NULL)
+				base_url->path[0] = '\0';
+			else
+				last_slash[1] = '\0';
+			tmp = base_url->path;
+			base_url->path = g_strconcat(base_url->path,
+				relative_url->path, NULL);
+			g_free(tmp);
+		}
+	}
+
+	g_free(base_url->fragment);
+	base_url->fragment = g_strdup(relative_url->fragment);
+}
+
+static gchar * purple_http_url_print(PurpleHttpURL *parsed_url)
+{
+	GString *url = g_string_new("");
+	gboolean before_host_printed = FALSE, host_printed = FALSE;
+	gboolean port_is_default = FALSE;
+
+	if (parsed_url->protocol) {
+		g_string_append_printf(url, "%s://", parsed_url->protocol);
+		before_host_printed = TRUE;
+		if (parsed_url->port == 80 && 0 == strcmp(parsed_url->protocol,
+			"http"))
+			port_is_default = TRUE;
+		if (parsed_url->port == 443 && 0 == strcmp(parsed_url->protocol,
+			"https"))
+			port_is_default = TRUE;
+	}
+	if (parsed_url->user || parsed_url->password) {
+		if (parsed_url->user)
+			g_string_append(url, parsed_url->user);
+		g_string_append_printf(url, ":%s", parsed_url->password);
+		g_string_append(url, "@");
+		before_host_printed = TRUE;
+	}
+	if (parsed_url->host || parsed_url->port) {
+		if (!parsed_url->host)
+			g_string_append_printf(url, "{???}:%d",
+				parsed_url->port);
+		else {
+			g_string_append(url, parsed_url->host);
+			if (!port_is_default)
+				g_string_append_printf(url, ":%d",
+					parsed_url->port);
+		}
+		host_printed = TRUE;
+	}
+	if (parsed_url->path) {
+		if (!host_printed && before_host_printed)
+			g_string_append(url, "{???}");
+		g_string_append(url, parsed_url->path);
+	}
+	if (parsed_url->fragment)
+		g_string_append_printf(url, "#%s", parsed_url->fragment);
+
+	return g_string_free(url, FALSE);
+}
+
+/*** HTTP Subsystem ***********************************************************/
+
+void purple_http_init(void)
+{
+	purple_http_re_url = g_regex_new("^"
+
+		"(?:" /* host part beginning */
+		"([a-z]+)\\:/*" /* protocol */
+		"([^/]+)" /* username, password, host, port */
+		")?" /* host part ending */
+
+		"([^#]*)" /* path */
+
+		"(?:#" "(.*)" ")?" /* fragment */
+
+		"$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
+		G_REGEX_MATCH_NOTEMPTY, NULL);
+
+	purple_http_re_url_host = g_regex_new("^"
+
+		"(?:" /* user credentials part beginning */
+		"([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+)" /* username */
+		"(?::([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+))" /* password */
+		"@)?" /* user credentials part ending */
+
+		"([a-z0-9.-]+)" /* host */
+		"(?::([0-9]+))?" /* port*/
+
+		"$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
+		G_REGEX_MATCH_NOTEMPTY, NULL);
+
+	purple_http_re_rfc1123 = g_regex_new(
+		"^[a-z]+, " /* weekday */
+		"([0-9]+) " /* date */
+		"([a-z]+) " /* month */
+		"([0-9]+) " /* year */
+		"([0-9]+:[0-9]+:[0-9]+) " /* time */
+		"(?:GMT|UTC)$",
+		G_REGEX_OPTIMIZE | G_REGEX_CASELESS,
+		G_REGEX_MATCH_NOTEMPTY, NULL);
+
+	purple_http_hc_list = NULL;
+	purple_http_hc_by_ptr = g_hash_table_new(g_direct_hash, g_direct_equal);
+	purple_http_hc_by_gc = g_hash_table_new_full(g_direct_hash,
+		g_direct_equal, NULL, (GDestroyNotify)g_list_free);
+}
+
+static void purple_http_foreach_conn_cancel(gpointer _hc, gpointer user_data)
+{
+	PurpleHttpConnection *hc = _hc;
+	purple_http_conn_cancel(hc);
+}
+
+void purple_http_uninit(void)
+{
+	g_regex_unref(purple_http_re_url);
+	purple_http_re_url = NULL;
+	g_regex_unref(purple_http_re_url_host);
+	purple_http_re_url_host = NULL;
+	g_regex_unref(purple_http_re_rfc1123);
+	purple_http_re_rfc1123 = NULL;
+
+	g_list_foreach(purple_http_hc_list, purple_http_foreach_conn_cancel,
+		NULL);
+
+	if (purple_http_hc_list != NULL ||
+		0 != g_hash_table_size(purple_http_hc_by_ptr) ||
+		0 != g_hash_table_size(purple_http_hc_by_gc))
+		purple_debug_warning("http",
+			"Couldn't cleanup all connections.\n");
+
+	g_list_free(purple_http_hc_list);
+	purple_http_hc_list = NULL;
+	g_hash_table_destroy(purple_http_hc_by_gc);
+	purple_http_hc_by_gc = NULL;
+	g_hash_table_destroy(purple_http_hc_by_ptr);
+	purple_http_hc_by_ptr = NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/http.h	Fri Jan 25 02:22:38 2013 -0500
@@ -0,0 +1,590 @@
+/**
+ * @file http.h HTTP API
+ * @ingroup core
+ */
+
+/* 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 _PURPLE_HTTP_H_
+#define _PURPLE_HTTP_H_
+
+#include <glib.h>
+
+#include "connection.h"
+
+/**
+ * A structure containing all data required to generate a single HTTP request.
+ */
+typedef struct _PurpleHttpRequest PurpleHttpRequest;
+
+/**
+ * A representation of actually running HTTP request. Can be used to cancel the
+ * request.
+ */
+typedef struct _PurpleHttpConnection PurpleHttpConnection;
+
+/**
+ * All information got with response for HTTP request.
+ */
+typedef struct _PurpleHttpResponse PurpleHttpResponse;
+
+/**
+ * An collection of cookies, got from HTTP response or provided for HTTP
+ * request.
+ */
+typedef struct _PurpleHttpCookieJar PurpleHttpCookieJar;
+
+/**
+ * An callback called after performing (successfully or not) HTTP request.
+ */
+typedef void (*PurpleHttpCallback)(PurpleHttpConnection *http_conn,
+	PurpleHttpResponse *response, gpointer user_data);
+
+/**
+ * An callback called after storing data requested by PurpleHttpContentReader.
+ */
+typedef void (*PurpleHttpContentReaderCb)(PurpleHttpConnection *http_conn,
+	gboolean success, gboolean eof, size_t stored);
+
+/**
+ * An callback for getting large request contents (ie. from file stored on
+ * disk).
+ *
+ * @param http_conn Connection, which requests data.
+ * @param buffer    Buffer to store data to (with offset ignored).
+ * @param offset    Position, from where to read data.
+ * @param length    Length of data to read.
+ * @param user_data The user data passed with callback function.
+ * @param cb        The function to call after storing data to buffer.
+ */
+typedef void (*PurpleHttpContentReader)(PurpleHttpConnection *http_conn,
+	gchar *buffer, size_t offset, size_t length, gpointer user_data,
+	PurpleHttpContentReaderCb cb);
+
+/**
+ * An callback for writting large response contents.
+ *
+ * @param http_conn Connection, which requests data.
+ * @param response  Response at point got so far (may change later).
+ * @param buffer    Buffer to read data from (with offset ignored).
+ * @param offset    Position of data got (its value is offset + length of
+ *                  previous call), can be safely ignored.
+ * @param length    Length of data read.
+ * @param user_data The user data passed with callback function.
+ * @return          TRUE, if succeeded, FALSE otherwise.
+ */
+typedef gboolean (*PurpleHttpContentWriter)(PurpleHttpConnection *http_conn,
+	PurpleHttpResponse *response, const gchar *buffer, size_t offset,
+	size_t length, gpointer user_data);
+
+/**
+ * An callback for watching HTTP connection progress.
+ *
+ * @param http_conn     The HTTP Connection.
+ * @param reading_state FALSE, is we are sending the request, TRUE, when reading
+ *                      the response.
+ * @param processed     The amount of data already processed.
+ * @param total         Total amount of data (in current state).
+ * @param user_data     The user data passed with callback function.
+ */
+typedef void (*PurpleHttpProgressWatcher)(PurpleHttpConnection *http_conn,
+	gboolean reading_state, int processed, int total, gpointer user_data);
+
+G_BEGIN_DECLS
+
+/**************************************************************************/
+/** @name Performing HTTP requests                                        */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Fetches the data from a URL with GET request, and passes it to a callback
+ * function.
+ *
+ * @param gc       The connection for which the request is needed, or NULL.
+ * @param url      The URL.
+ * @param callback The callback function.
+ * @param data     The user data to pass to the callback function.
+ * @return         The HTTP connection struct.
+ */
+PurpleHttpConnection * purple_http_get(PurpleConnection *gc, const gchar *url,
+	PurpleHttpCallback callback, gpointer user_data);
+
+/**
+ * Fetches a HTTP request and passes the response to a callback function.
+ * Provided request struct can be shared by multiple http requests but can not
+ * be modified when any of these is running.
+ *
+ * @param gc        The connection for which the request is needed, or NULL.
+ * @param request   The request.
+ * @param callback  The callback function.
+ * @param user_data The user data to pass to the callback function.
+ * @return          The HTTP connection struct.
+ */
+PurpleHttpConnection * purple_http_request(PurpleConnection *gc,
+	PurpleHttpRequest *request, PurpleHttpCallback callback,
+	gpointer user_data);
+
+/**************************************************************************/
+/** @name HTTP connection API                                             */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Cancel a pending HTTP request.
+ *
+ * @param http_conn The data returned when you initiated the HTTP request.
+ */
+void purple_http_conn_cancel(PurpleHttpConnection *http_conn);
+
+/**
+ * Cancels all HTTP connections associated with the specified handle.
+ *
+ * @param gc The handle.
+ */
+void purple_http_conn_cancel_all(PurpleConnection *gc);
+
+/**
+ * Checks, if provided HTTP request is running.
+ *
+ * @param http_conn The HTTP connection (may be invalid pointer).
+ * @return          TRUE, if provided connection is currently running.
+ */
+gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn);
+
+/**
+ * Gets PurpleHttpRequest used for specified HTTP connection.
+ *
+ * @param http_conn The HTTP connection.
+ * @return          The PurpleHttpRequest object.
+ */
+PurpleHttpRequest * purple_http_conn_get_request(
+	PurpleHttpConnection *http_conn);
+
+/**
+ * Gets cookie jar used within connection.
+ *
+ * @param http_conn The HTTP connection.
+ * @return          The cookie jar.
+ */
+PurpleHttpCookieJar * purple_http_conn_get_cookie_jar(
+	PurpleHttpConnection *http_conn);
+
+/**
+ * Gets PurpleConnection tied with specified HTTP connection.
+ *
+ * @param http_conn The HTTP connection.
+ * @return          The PurpleConnection object.
+ */
+PurpleConnection * purple_http_conn_get_purple_connection(
+	PurpleHttpConnection *http_conn);
+
+/**
+ * Sets the watcher, called after writing or reading data to/from HTTP stream.
+ * May be used for updating transfer progress gauge.
+ *
+ * @param http_conn          The HTTP connection.
+ * @param watcher            The watcher.
+ * @param user_data          The user data to pass to the callback function.
+ * @param interval_threshold Minimum interval (in microseconds) of calls to
+ *                           watcher.
+ */
+void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn,
+	PurpleHttpProgressWatcher watcher, gpointer user_data,
+	guint interval_threshold);
+
+/*@}*/
+
+
+/**************************************************************************/
+/** @name Cookie jar API                                                  */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Creates new cookie jar,
+ *
+ * @return empty cookie jar.
+ */
+PurpleHttpCookieJar * purple_http_cookie_jar_new(void);
+
+/**
+ * Increment the reference count.
+ *
+ * @param cookie_jar The cookie jar.
+ */
+void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar);
+
+/**
+ * Decrement the reference count.
+ *
+ * If the reference count reaches zero, the cookie jar will be freed.
+ *
+ * @param cookie_jar The cookie jar.
+ * @return @a cookie_jar or @c NULL if the reference count reached zero.
+ */
+PurpleHttpCookieJar * purple_http_cookie_jar_unref(
+	PurpleHttpCookieJar *cookie_jar);
+
+/**
+ * Sets the cookie.
+ *
+ * @param cookie_jar The cookie jar.
+ * @param name       Cookie name.
+ * @param value      Cookie contents.
+ */
+void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar,
+	const gchar *name, const gchar *value);
+
+/**
+ * Gets the cookie.
+ *
+ * @param cookie_jar The cookie jar.
+ * @param name       Cookie name.
+ * @return           Cookie contents, or NULL, if cookie doesn't exists.
+ */
+const gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar,
+	const gchar *name);
+
+/**
+ * Checks, if the cookie jar contains any cookies.
+ *
+ * @param cookie_jar The cookie jar.
+ * @return           TRUE, if cookie jar contains any cookie, FALSE otherwise.
+ */
+gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar);
+
+/*@}*/
+
+
+/**************************************************************************/
+/** @name HTTP Request API                                                */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Creates the new instance of HTTP request configuration.
+ *
+ * @param url The URL to request for.
+ * @return The new instance of HTTP request struct.
+ */
+PurpleHttpRequest * purple_http_request_new(const gchar *url);
+
+/**
+ * Increment the reference count.
+ *
+ * @param request The request.
+ */
+void purple_http_request_ref(PurpleHttpRequest *request);
+
+/**
+ * Decrement the reference count.
+ *
+ * If the reference count reaches zero, the http request struct will be freed.
+ *
+ * @param request The request.
+ * @return @a request or @c NULL if the reference count reached zero.
+ */
+PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request);
+
+/**
+ * Sets URL for HTTP request.
+ *
+ * @param request The request.
+ * @param url     The url.
+ */
+void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url);
+
+/**
+ * Gets URL set for the HTTP request.
+ *
+ * @param request The request.
+ * @return        URL set for this request.
+ */
+const gchar * purple_http_request_get_url(PurpleHttpRequest *request);
+
+/**
+ * Sets custom HTTP method used for the request.
+ *
+ * @param request The request.
+ * @param method  The method, or NULL for default.
+ */
+void purple_http_request_set_method(PurpleHttpRequest *request,
+	const gchar *method);
+
+/**
+ * Gets HTTP method set for the request.
+ *
+ * @param request The request.
+ * @return        The method.
+ */
+const gchar * purple_http_request_get_method(PurpleHttpRequest *request);
+
+/**
+ * Sets contents of HTTP request (for example, POST data).
+ *
+ * @param request  The request.
+ * @param contents The contents.
+ * @param length   The length of contents (-1 if it's a NULL-terminated string)
+ */
+void purple_http_request_set_contents(PurpleHttpRequest *request,
+	const gchar *contents, int length);
+
+/**
+ * Sets contents reader for HTTP request, used mainly for possible large
+ * uploads.
+ *
+ * @param request       The request.
+ * @param reader        The reader callback.
+ * @param contents_size The size of all contents.
+ * @param user_data     The user data to pass to the callback function.
+ */
+void purple_http_request_set_contents_reader(PurpleHttpRequest *request,
+	PurpleHttpContentReader reader, int contents_length, gpointer user_data);
+
+/**
+ * Set contents writer for HTTP response.
+ *
+ * @param request   The request.
+ * @param reader    The writer callback, or NULL to remove existing.
+ * @param user_data The user data to pass to the callback function.
+ */
+void purple_http_request_set_response_writer(PurpleHttpRequest *request,
+	PurpleHttpContentWriter writer, gpointer user_data);
+
+/**
+ * Set maximum amount of time, that request is allowed to run.
+ *
+ * @param request The request.
+ * @param timeout Time (in seconds) after that timeout will be cancelled,
+ *                -1 for infinite time.
+ */
+void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout);
+
+/**
+ * Get maximum amount of time, that request is allowed to run.
+ *
+ * @param request The request.
+ * @return        Timeout currently set (-1 for infinite).
+ */
+int purple_http_request_get_timeout(PurpleHttpRequest *request);
+
+/**
+ * Sets maximum amount of redirects.
+ *
+ * @param request       The request.
+ * @param max_redirects Maximum amount of redirects, or -1 for unlimited.
+ */
+void purple_http_request_set_max_redirects(PurpleHttpRequest *request,
+	int max_redirects);
+
+/**
+ * Gets maximum amount of redirects.
+ *
+ * @param request The request.
+ * @return        Current maximum amount of redirects (-1 for unlimited).
+ */
+int purple_http_request_get_max_redirects(PurpleHttpRequest *request);
+
+/**
+ * Sets cookie jar used for the request.
+ *
+ * @param request    The request.
+ * @param cookie_jar The cookie jar.
+ */
+void purple_http_request_set_cookie_jar(PurpleHttpRequest *request,
+	PurpleHttpCookieJar *cookie_jar);
+
+/**
+ * Gets cookie jar used for the request.
+ *
+ * @param request The request.
+ * @return        The cookie jar.
+ */
+PurpleHttpCookieJar * purple_http_request_get_cookie_jar(
+	PurpleHttpRequest *request);
+
+/**
+ * Sets HTTP version to use.
+ *
+ * @param request The request.
+ * @param http11  TRUE for HTTP/1.1, FALSE for HTTP/1.0.
+ */
+void purple_http_request_set_http11(PurpleHttpRequest *request,
+	gboolean http11);
+
+/**
+ * Gets used HTTP version.
+ *
+ * @param request The request.
+ * @return        TRUE, if we use HTTP/1.1, FALSE for HTTP/1.0.
+ */
+gboolean purple_http_request_is_http11(PurpleHttpRequest *request);
+
+/**
+ * Sets maximum length of response content to read.
+ *
+ * Headers length doesn't count here.
+ *
+ * @param request The request.
+ * @param max_len Maximum length of response to read (-1 for unlimited).
+ */
+void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len);
+
+/**
+ * Gets maximum length of response content to read.
+ *
+ * @param request The request.
+ * @return        Maximum length of response to read, or -1 if unlimited.
+ */
+int purple_http_request_get_max_len(PurpleHttpRequest *request);
+
+/**
+ * Sets (replaces, if exists) specified HTTP request header with provided value.
+ *
+ * @param key   A header to be set.
+ * @param value A value to set, or NULL to remove specified header from request.
+ *
+ * @see purple_http_request_header_add
+ */
+void purple_http_request_header_set(PurpleHttpRequest *request,
+	const gchar *key, const gchar *value);
+
+void purple_http_request_header_set_printf(PurpleHttpRequest *request,
+	const gchar *key, const gchar *format, ...) G_GNUC_PRINTF(3, 4);
+
+/**
+ * Adds (without replacing, if exists) an HTTP request header.
+ *
+ * @param key   A header to be set.
+ * @param value A value to set.
+ *
+ * @see purple_http_request_header_set
+ */
+void purple_http_request_header_add(PurpleHttpRequest *request,
+	const gchar *key, const gchar *value);
+
+/*@}*/
+
+/**************************************************************************/
+/** @name HTTP response API                                               */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Checks, if HTTP request was performed successfully.
+ *
+ * @param response The response.
+ * @return         TRUE, if request was performed successfully.
+ */
+gboolean purple_http_response_is_successfull(PurpleHttpResponse *response);
+
+/**
+ * Gets HTTP response code.
+ *
+ * @param response The response.
+ * @return         HTTP response code.
+ */
+int purple_http_response_get_code(PurpleHttpResponse *response);
+
+/**
+ * Gets error description.
+ *
+ * @param response The response.
+ * @return         Localized error description or NULL, if there was no error.
+ */
+const gchar * purple_http_response_get_error(PurpleHttpResponse *response);
+
+/**
+ * Gets HTTP response data length.
+ *
+ * @param response The response.
+ * @return         Data length;
+ */
+gsize purple_http_response_get_data_len(PurpleHttpResponse *response);
+
+/**
+ * Gets HTTP response data.
+ *
+ * Response data is not written, if writer callback was set for request.
+ *
+ * @param response The response.
+ * @param len      Return address for the size of the data.  Can be NULL.
+ * @return         The data.
+ */
+const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len);
+
+/**
+ * Gets all headers got with response.
+ *
+ * @param response The response.
+ * @return         GList of PurpleKeyValuePair, which keys are header field
+ *                 names (gchar*) and values are its contents (gchar*).
+ */
+const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response);
+
+/**
+ * Gets all headers with specified name got with response.
+ *
+ * @param response The response.
+ * @param name     The name of header field.
+ * @return         GList of header field records contents (gchar*).
+ */
+const GList * purple_http_response_get_headers_by_name(
+	PurpleHttpResponse *response, const gchar *name);
+
+/**
+ * Gets one header contents with specified name got with response.
+ *
+ * To get all headers with the same name, use
+ * purple_http_response_get_headers_by_name instead.
+ *
+ * @param response The response.
+ * @param name     The name of header field.
+ * @return         Header field contents or NULL, if there is no such one.
+ */
+const gchar * purple_http_response_get_header(PurpleHttpResponse *response,
+	const gchar *name);
+
+/*@}*/
+
+
+/**************************************************************************/
+/** @name HTTP Subsystem                                                  */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Initializes the http subsystem.
+ */
+void purple_http_init(void);
+
+/**
+ * Uninitializes the http subsystem.
+ */
+void purple_http_uninit(void);
+
+/*@}*/
+
+G_END_DECLS
+
+#endif /* _PURPLE_HTTP_H_ */
--- a/libpurple/imgstore.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/imgstore.c	Fri Jan 25 02:22:38 2013 -0500
@@ -36,20 +36,20 @@
 static unsigned int nextid = 0;
 
 /*
- * NOTE: purple_imgstore_add() creates these without zeroing the memory, so
+ * NOTE: purple_imgstore_new() creates these without zeroing the memory, so
  * NOTE: make sure to update that function when adding members.
  */
 struct _PurpleStoredImage
 {
 	int id;
 	guint8 refcount;
-	size_t size;		/**< The image data's size.	*/
-	char *filename;		/**< The filename (for the UI)	*/
-	gpointer data;		/**< The image data.		*/
+	size_t size;     /**< The image data's size. */
+	char *filename;  /**< The filename (for the UI) */
+	gpointer data;   /**< The image data. */
 };
 
 PurpleStoredImage *
-purple_imgstore_add(gpointer data, size_t size, const char *filename)
+purple_imgstore_new(gpointer data, size_t size, const char *filename)
 {
 	PurpleStoredImage *img;
 
@@ -82,30 +82,33 @@
 		g_error_free(err);
 		return NULL;
 	}
-	return purple_imgstore_add(data, len, path);
+	return purple_imgstore_new(data, len, path);
 }
 
 int
-purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename)
+purple_imgstore_new_with_id(gpointer data, size_t size, const char *filename)
 {
-	PurpleStoredImage *img = purple_imgstore_add(data, size, filename);
-	if (img) {
-		/*
-		 * Use the next unused id number.  We do it in a loop on the
-		 * off chance that nextid wraps back around to 0 and the hash
-		 * table still contains entries from the first time around.
-		 */
-		do {
-			img->id = ++nextid;
-		} while (img->id == 0 || g_hash_table_lookup(imgstore, &(img->id)) != NULL);
-
-		g_hash_table_insert(imgstore, &(img->id), img);
+	PurpleStoredImage *img = purple_imgstore_new(data, size, filename);
+	if (!img) {
+		return 0;
 	}
 
-	return (img ? img->id : 0);
+	/*
+	 * Use the next unused id number.  We do it in a loop on the
+	 * off chance that nextid wraps back around to 0 and the hash
+	 * table still contains entries from the first time around.
+	 */
+	do {
+		img->id = ++nextid;
+	} while (img->id == 0 || g_hash_table_lookup(imgstore, &(img->id)) != NULL);
+
+	g_hash_table_insert(imgstore, &(img->id), img);
+
+	return img->id;
 }
 
-PurpleStoredImage *purple_imgstore_find_by_id(int id) {
+PurpleStoredImage *purple_imgstore_find_by_id(int id)
+{
 	PurpleStoredImage *img = g_hash_table_lookup(imgstore, &id);
 
 	if (img != NULL)
@@ -114,7 +117,8 @@
 	return img;
 }
 
-gconstpointer purple_imgstore_get_data(PurpleStoredImage *img) {
+gconstpointer purple_imgstore_get_data(PurpleStoredImage *img)
+{
 	g_return_val_if_fail(img != NULL, NULL);
 
 	return img->data;
--- a/libpurple/imgstore.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/imgstore.h	Fri Jan 25 02:22:38 2013 -0500
@@ -1,5 +1,6 @@
 /**
- * @file imgstore.h IM Image Store API
+ * @file imgstore.h Utility functions for reference-counted in-memory
+ *       image data.
  * @ingroup core
  * @see @ref imgstore-signals
  */
@@ -39,66 +40,89 @@
 G_BEGIN_DECLS
 
 /**
- * Add an image to the store.
+ * Create a new PurpleStoredImage.
  *
- * The caller owns a reference to the image in the store, and must dereference
- * the image with purple_imgstore_unref() for it to be freed.
+ * The image is not added to the image store and no ID is assigned.  If you
+ * need to reference the image by an ID, use purple_imgstore_new_with_id()
+ * instead.
  *
- * No ID is allocated when using this function.  If you need to reference the
- * image by an ID, use purple_imgstore_add_with_id() instead.
+ * The caller owns a reference to this image and must dereference it with
+ * purple_imgstore_unref() for it to be freed.
  *
- * @param data		Pointer to the image data, which the imgstore will take
- *                      ownership of and free as appropriate.  If you want a
- *                      copy of the data, make it before calling this function.
- * @param size		Image data's size.
- * @param filename	Filename associated with image.  This is for your
+ * @param data      Pointer to the image data, which the imgstore will take
+ *                  ownership of and free as appropriate.  If you want a
+ *                  copy of the data, make it before calling this function.
+ * @param size      Image data's size.
+ * @param filename  Filename associated with image.  This is for your
  *                  convenience.  It could be the full path to the
  *                  image or, more commonly, the filename of the image
  *                  without any directory information.  It can also be
  *                  NULL, if you don't need to keep track of a filename.
+ *                  If you intend to use this filename to write the file to
+ *                  disk, make sure the filename is appropriately escaped.
+ *                  You may wish to use purple_escape_filename().
  *
- * @return The stored image.
+ * @return The image, or NULL if the image could not be created (because of
+ *         empty data or size).
  */
 PurpleStoredImage *
-purple_imgstore_add(gpointer data, size_t size, const char *filename);
+purple_imgstore_new(gpointer data, size_t size, const char *filename);
 
 /**
- * Create an image and add it to the store.
+ * Create a PurpleStoredImage using purple_imgstore_new() by reading the
+ * given filename from disk.
+ *
+ * The image is not added to the image store and no ID is assigned.  If you
+ * need to reference the image by an ID, use purple_imgstore_new_with_id()
+ * instead.
  *
- * @param path  The path to the image.
+ * Make sure the filename is appropriately escaped.  You may wish to use
+ * purple_escape_filename().  The PurpleStoredImage's filename will be set
+ * to the given path.
  *
- * @return  The stored image.
+ * The caller owns a reference to this image and must dereference it with
+ * purple_imgstore_unref() for it to be freed.
+ *
+ * @param path The path to the image.
+ *
+ * @return The image, or NULL if the image could not be created (because of
+ *         empty data or size).
  */
 PurpleStoredImage *
 purple_imgstore_new_from_file(const char *path);
 
 /**
- * Add an image to the store, allocating an ID.
- *
- * The caller owns a reference to the image in the store, and must dereference
- * the image with purple_imgstore_unref_by_id() or purple_imgstore_unref()
- * for it to be freed.
+ * Create a PurpleStoredImage using purple_imgstore_new() and add the
+ * image to the image store.  A unique ID will be assigned to the image.
  *
- * @param data		Pointer to the image data, which the imgstore will take
- *                      ownership of and free as appropriate.  If you want a
- *                      copy of the data, make it before calling this function.
- * @param size		Image data's size.
- * @param filename	Filename associated with image.  This is for your
+ * The caller owns a reference to the image and must dereference it with
+ * purple_imgstore_unref() or purple_imgstore_unref_by_id() for it to be
+ * freed.
+ *
+ * @param data      Pointer to the image data, which the imgstore will take
+ *                  ownership of and free as appropriate.  If you want a
+ *                  copy of the data, make it before calling this function.
+ * @param size      Image data's size.
+ * @param filename  Filename associated with image.  This is for your
  *                  convenience.  It could be the full path to the
  *                  image or, more commonly, the filename of the image
  *                  without any directory information.  It can also be
  *                  NULL, if you don't need to keep track of a filename.
-
+ *                  If you intend to use this filename to write the file to
+ *                  disk, make sure the filename is appropriately escaped.
+ *                  You may wish to use purple_escape_filename()
+ *
  * @return ID for the image.  This is a unique number that can be used
- *         within libpurple to reference the image.
+ *         within libpurple to reference the image.  0 is returned if the
+ *         image could not be created (because of empty data or size).
  */
-int purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename);
+int purple_imgstore_new_with_id(gpointer data, size_t size, const char *filename);
 
 /**
  * Retrieve an image from the store. The caller does not own a
  * reference to the image.
  *
- * @param id		The ID for the image.
+ * @param id The ID for the image.
  *
  * @return A pointer to the requested image, or NULL if it was not found.
  */
@@ -107,7 +131,7 @@
 /**
  * Retrieves a pointer to the image's data.
  *
- * @param img	The Image
+ * @param img The Image.
  *
  * @return A pointer to the data, which must not
  *         be freed or modified.
@@ -117,7 +141,7 @@
 /**
  * Retrieves the length of the image's data.
  *
- * @param img	The Image
+ * @param img The Image.
  *
  * @return The size of the data that the pointer returned by
  *         purple_imgstore_get_data points to.
@@ -125,9 +149,12 @@
 size_t purple_imgstore_get_size(PurpleStoredImage *img);
 
 /**
- * Retrieves a pointer to the image's filename.
+ * Retrieves a pointer to the image's filename.  If you intend to use this
+ * filename to write the file to disk, make sure the filename was
+ * appropriately escaped when you created the PurpleStoredImage.  You may
+ * wish to use purple_escape_filename().
  *
- * @param img	The image
+ * @param img The image.
  *
  * @return A pointer to the filename, which must not
  *         be freed or modified.
@@ -138,7 +165,7 @@
  * Looks at the magic numbers of the image data (the first few bytes)
  * and returns an extension corresponding to the image's file type.
  *
- * @param img  The image.
+ * @param img The image.
  *
  * @return The image's extension (for example "png") or "icon"
  *         if unknown.
@@ -174,7 +201,7 @@
  * purple_imgstore_ref(), so if you have a PurpleStoredImage, it'll
  * be more efficient to call purple_imgstore_ref() directly.
  *
- * @param id		The ID for the image.
+ * @param id The ID for the image.
  */
 void purple_imgstore_ref_by_id(int id);
 
@@ -185,7 +212,7 @@
  * purple_imgstore_unref(), so if you have a PurpleStoredImage, it'll
  * be more efficient to call purple_imgstore_unref() directly.
  *
- * @param id		The ID for the image.
+ * @param id The ID for the image.
  */
 void purple_imgstore_unref_by_id(int id);
 
--- a/libpurple/log.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/log.c	Fri Jan 25 02:22:38 2013 -0500
@@ -835,7 +835,10 @@
 						fclose(image_file);
 
 						/* Attempt to not leave half-written files around. */
-						unlink(path);
+						if (g_unlink(path)) {
+							purple_debug_error("log", "Error deleting partial "
+									"file %s: %s\n", path, g_strerror(errno));
+						}
 					}
 					else
 					{
@@ -1763,7 +1766,7 @@
 	index_tmp = g_strdup_printf("%s.XXXXXX", pathstr);
 	if ((index_fd = g_mkstemp(index_tmp)) == -1) {
 		purple_debug_error("log", "Failed to open index temp file: %s\n",
-		                 g_strerror(errno));
+		                   g_strerror(errno));
 		g_free(pathstr);
 		g_free(index_tmp);
 		index = NULL;
@@ -1771,7 +1774,7 @@
 		if ((index = fdopen(index_fd, "wb")) == NULL)
 		{
 			purple_debug_error("log", "Failed to fdopen() index temp file: %s\n",
-			                 g_strerror(errno));
+			                   g_strerror(errno));
 			close(index_fd);
 			if (index_tmp != NULL)
 			{
@@ -1827,7 +1830,6 @@
 					log->logger_data = data;
 					list = g_list_prepend(list, log);
 
-					/* XXX: There is apparently Is there a proper way to print a time_t? */
 					if (index != NULL)
 						fprintf(index, "%d\t%d\t%lu\n", data->offset, data->length, (unsigned long)log->time);
 				}
@@ -1887,9 +1889,8 @@
 			log->logger_data = data;
 			list = g_list_prepend(list, log);
 
-			/* XXX: Is there a proper way to print a time_t? */
 			if (index != NULL)
-				fprintf(index, "%d\t%d\t%d\n", data->offset, data->length, (int)log->time);
+				fprintf(index, "%d\t%d\t%lu\n", data->offset, data->length, (unsigned long)log->time);
 		}
 	}
 
@@ -2022,7 +2023,7 @@
 		/* Search the buddy list to find the account and to determine if this is a buddy. */
 		for (gnode = purple_blist_get_root();
 		     !found && gnode != NULL;
-			 gnode = purple_blist_node_get_sibling_next(gnode))
+		     gnode = purple_blist_node_get_sibling_next(gnode))
 		{
 			if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 				continue;
@@ -2036,7 +2037,7 @@
 
 				for (bnode = purple_blist_node_get_first_child(cnode);
 				     !found && bnode != NULL;
-					 bnode = purple_blist_node_get_sibling_next(bnode))
+				     bnode = purple_blist_node_get_sibling_next(bnode))
 				{
 					PurpleBuddy *buddy = (PurpleBuddy *)bnode;
 
--- a/libpurple/media/backend-fs2.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/media/backend-fs2.c	Fri Jan 25 02:22:38 2013 -0500
@@ -179,9 +179,17 @@
 purple_media_backend_fs2_init(PurpleMediaBackendFs2 *self)
 {}
 
+#if GST_CHECK_VERSION(1,0,0)
+static GstPadProbeReturn
+event_probe_cb(GstPad *srcpad, GstPadProbeInfo *info, gpointer unused)
+#else
 static gboolean
 event_probe_cb(GstPad *srcpad, GstEvent *event, gboolean release_pad)
+#endif
 {
+#if GST_CHECK_VERSION(1,0,0)
+	GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info);
+#endif
 	if (GST_EVENT_TYPE(event) == GST_EVENT_CUSTOM_DOWNSTREAM
 		&& gst_event_has_name(event, "purple-unlink-tee")) {
 
@@ -189,22 +197,40 @@
 
 		gst_pad_unlink(srcpad, gst_pad_get_peer(srcpad));
 
+#if GST_CHECK_VERSION(1,0,0)
+		gst_pad_remove_probe(srcpad,
+			g_value_get_ulong(gst_structure_get_value(s, "handler-id")));
+#else
 		gst_pad_remove_event_probe(srcpad,
 			g_value_get_uint(gst_structure_get_value(s, "handler-id")));
+#endif
 
 		if (g_value_get_boolean(gst_structure_get_value(s, "release-pad")))
 			gst_element_release_request_pad(GST_ELEMENT_PARENT(srcpad), srcpad);
 
+#if GST_CHECK_VERSION(1,0,0)
+		return GST_PAD_PROBE_DROP;
+#else
 		return FALSE;
+#endif
 	}
 
+#if GST_CHECK_VERSION(1,0,0)
+	return GST_PAD_PROBE_OK;
+#else
 	return TRUE;
+#endif
 }
 
 static void
 unlink_teepad_dynamic(GstPad *srcpad, gboolean release_pad)
 {
+#if GST_CHECK_VERSION(1,0,0)
+	gulong id = gst_pad_add_probe(srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+	                              event_probe_cb, NULL, NULL);
+#else
 	guint id = gst_pad_add_event_probe(srcpad, G_CALLBACK(event_probe_cb), NULL);
+#endif
 
 	if (GST_IS_GHOST_PAD(srcpad))
 		srcpad = gst_ghost_pad_get_target(GST_GHOST_PAD(srcpad));
@@ -213,7 +239,11 @@
 		gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM,
 			gst_structure_new("purple-unlink-tee",
 				"release-pad", G_TYPE_BOOLEAN, release_pad,
+#if GST_CHECK_VERSION(1,0,0)
+				"handler-id", G_TYPE_ULONG, id,
+#else
 				"handler-id", G_TYPE_UINT, id,
+#endif
 				NULL)));
 }
 
@@ -793,9 +823,12 @@
 	gdouble value_db;
 	gdouble percent;
 
-	list = gst_structure_get_value(
-				gst_message_get_structure(msg), value_name);
+	list = gst_structure_get_value(gst_message_get_structure(msg), value_name);
+#if GST_CHECK_VERSION(1,0,0)
+	value = g_value_array_get_nth(g_value_get_boxed(list), 0);
+#else
 	value = gst_value_list_get_value(list, 0);
+#endif
 	value_db = g_value_get_double(value);
 	percent = pow(10, value_db / 20);
 	return (percent > 1.0) ? 1.0 : percent;
@@ -809,11 +842,12 @@
 			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
 	GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
 	static guint level_id = 0;
+	const GstStructure *structure = gst_message_get_structure(msg);
 
 	if (level_id == 0)
 		level_id = g_signal_lookup("level", PURPLE_TYPE_MEDIA);
 
-	if (gst_structure_has_name(msg->structure, "level")) {
+	if (gst_structure_has_name(structure, "level")) {
 		GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
 		gchar *name;
 		gchar *participant = NULL;
@@ -868,12 +902,12 @@
 		return;
 
 #ifdef HAVE_FARSIGHT
-	if (gst_structure_has_name(msg->structure, "farsight-error")) {
+	if (gst_structure_has_name(structure, "farsight-error")) {
 #else
-	if (gst_structure_has_name(msg->structure, "farstream-error")) {
+	if (gst_structure_has_name(structure, "farstream-error")) {
 #endif
 		FsError error_no;
-		gst_structure_get_enum(msg->structure, "error-no",
+		gst_structure_get_enum(structure, "error-no",
 				FS_TYPE_ERROR, (gint*)&error_no);
 		switch (error_no) {
 			case FS_ERROR_NO_CODECS:
@@ -910,7 +944,7 @@
 #endif
 						error_no,
 						gst_structure_get_string(
-						msg->structure, "error-msg"));
+						structure, "error-msg"));
 				break;
 		}
 
@@ -924,7 +958,7 @@
 #endif
 			purple_media_end(priv->media, NULL, NULL);
 		}
-	} else if (gst_structure_has_name(msg->structure,
+	} else if (gst_structure_has_name(structure,
 #ifdef HAVE_FARSIGHT
 			"farsight-new-local-candidate")) {
 #else
@@ -939,9 +973,9 @@
 		PurpleMediaBackendFs2Stream *media_stream;
 		gchar *name;
 
-		value = gst_structure_get_value(msg->structure, "stream");
+		value = gst_structure_get_value(structure, "stream");
 		stream = g_value_get_object(value);
-		value = gst_structure_get_value(msg->structure, "candidate");
+		value = gst_structure_get_value(structure, "candidate");
 		local_candidate = g_value_get_boxed(value);
 
 		session = get_session_from_fs_stream(self, stream);
@@ -963,7 +997,7 @@
 		g_signal_emit_by_name(self, "new-candidate",
 				session->id, name, candidate);
 		g_object_unref(candidate);
-	} else if (gst_structure_has_name(msg->structure,
+	} else if (gst_structure_has_name(structure,
 #ifdef HAVE_FARSIGHT
 			"farsight-local-candidates-prepared")) {
 #else
@@ -975,7 +1009,7 @@
 		PurpleMediaBackendFs2Session *session;
 		gchar *name;
 
-		value = gst_structure_get_value(msg->structure, "stream");
+		value = gst_structure_get_value(structure, "stream");
 		stream = g_value_get_object(value);
 		session = get_session_from_fs_stream(self, stream);
 
@@ -985,7 +1019,7 @@
 
 		g_signal_emit_by_name(self, "candidates-prepared",
 				session->id, name);
-	} else if (gst_structure_has_name(msg->structure,
+	} else if (gst_structure_has_name(structure,
 #ifdef HAVE_FARSIGHT
 			"farsight-new-active-candidate-pair")) {
 #else
@@ -1000,13 +1034,11 @@
 		PurpleMediaCandidate *lcandidate, *rcandidate;
 		gchar *name;
 
-		value = gst_structure_get_value(msg->structure, "stream");
+		value = gst_structure_get_value(structure, "stream");
 		stream = g_value_get_object(value);
-		value = gst_structure_get_value(msg->structure,
-				"local-candidate");
+		value = gst_structure_get_value(structure, "local-candidate");
 		local_candidate = g_value_get_boxed(value);
-		value = gst_structure_get_value(msg->structure,
-				"remote-candidate");
+		value = gst_structure_get_value(structure, "remote-candidate");
 		remote_candidate = g_value_get_boxed(value);
 
 		g_object_get(stream, "participant", &participant, NULL);
@@ -1023,7 +1055,7 @@
 
 		g_object_unref(lcandidate);
 		g_object_unref(rcandidate);
-	} else if (gst_structure_has_name(msg->structure,
+	} else if (gst_structure_has_name(structure,
 #ifdef HAVE_FARSIGHT
 			"farsight-recv-codecs-changed")) {
 #else
@@ -1033,7 +1065,7 @@
 		GList *codecs;
 		FsCodec *codec;
 
-		value = gst_structure_get_value(msg->structure, "codecs");
+		value = gst_structure_get_value(structure, "codecs");
 		codecs = g_value_get_boxed(value);
 		codec = codecs->data;
 
@@ -1044,7 +1076,7 @@
 				"farstream-recv-codecs-changed: %s\n",
 #endif
 				codec->encoding_name);
-	} else if (gst_structure_has_name(msg->structure,
+	} else if (gst_structure_has_name(structure,
 #ifdef HAVE_FARSIGHT
 			"farsight-component-state-changed")) {
 #else
@@ -1055,9 +1087,9 @@
 		guint component;
 		const gchar *state;
 
-		value = gst_structure_get_value(msg->structure, "state");
+		value = gst_structure_get_value(structure, "state");
 		fsstate = g_value_get_enum(value);
-		value = gst_structure_get_value(msg->structure, "component");
+		value = gst_structure_get_value(structure, "component");
 		component = g_value_get_uint(value);
 
 		switch (fsstate) {
@@ -1092,7 +1124,7 @@
 #endif
 				"component: %u state: %s\n",
 				component, state);
-	} else if (gst_structure_has_name(msg->structure,
+	} else if (gst_structure_has_name(structure,
 #ifdef HAVE_FARSIGHT
 			"farsight-send-codec-changed")) {
 #else
@@ -1102,7 +1134,7 @@
 		FsCodec *codec;
 		gchar *codec_str;
 
-		value = gst_structure_get_value(msg->structure, "codec");
+		value = gst_structure_get_value(structure, "codec");
 		codec = g_value_get_boxed(value);
 		codec_str = fs_codec_to_string(codec);
 
@@ -1115,7 +1147,7 @@
 				codec_str);
 
 		g_free(codec_str);
-	} else if (gst_structure_has_name(msg->structure,
+	} else if (gst_structure_has_name(structure,
 #ifdef HAVE_FARSIGHT
 			"farsight-codecs-changed")) {
 #else
@@ -1125,7 +1157,7 @@
 		FsSession *fssession;
 		GList *sessions;
 
-		value = gst_structure_get_value(msg->structure, "session");
+		value = gst_structure_get_value(structure, "session");
 		fssession = g_value_get_object(value);
 		sessions = g_hash_table_get_values(priv->sessions);
 
@@ -1569,7 +1601,11 @@
 		srcpad = gst_element_get_static_pad(session->srcvalve, "src");
 		g_object_set(volume, "volume", input_volume, NULL);
 	} else {
+#if GST_CHECK_VERSION(1,0,0)
+		srcpad = gst_element_get_request_pad(session->tee, "src_%u");
+#else
 		srcpad = gst_element_get_request_pad(session->tee, "src%d");
+#endif
 	}
 
 	purple_debug_info("backend-fs2", "connecting pad: %s\n",
@@ -1787,14 +1823,10 @@
 			 *   audioresample ! audioconvert ! realsink
 			 */
 			stream->queue = gst_element_factory_make("queue", NULL);
-			stream->volume = gst_element_factory_make(
-					"volume", NULL);
-			g_object_set(stream->volume, "volume",
-					output_volume, NULL);
-			stream->level = gst_element_factory_make(
-					"level", NULL);
-			stream->src = gst_element_factory_make(
-					"liveadder", NULL);
+			stream->volume = gst_element_factory_make("volume", NULL);
+			g_object_set(stream->volume, "volume", output_volume, NULL);
+			stream->level = gst_element_factory_make("level", NULL);
+			stream->src = gst_element_factory_make("liveadder", NULL);
 			sink = purple_media_manager_get_element(
 					purple_media_get_manager(priv->media),
 					PURPLE_MEDIA_RECV_AUDIO, priv->media,
@@ -1813,10 +1845,12 @@
 			gst_element_link(stream->queue, stream->volume);
 			sink = stream->queue;
 		} else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) {
-			stream->src = gst_element_factory_make(
-					"fsfunnel", NULL);
-			sink = gst_element_factory_make(
-					"fakesink", NULL);
+#if GST_CHECK_VERSION(1,0,0)
+			stream->src = gst_element_factory_make("funnel", NULL);
+#else
+			stream->src = gst_element_factory_make("fsfunnel", NULL);
+#endif
+			sink = gst_element_factory_make("fakesink", NULL);
 			g_object_set(G_OBJECT(sink), "async", FALSE, NULL);
 			gst_bin_add(GST_BIN(priv->confbin), sink);
 			gst_element_set_state(sink, GST_STATE_PLAYING);
@@ -1830,7 +1864,11 @@
 		gst_element_link_many(stream->src, stream->tee, sink, NULL);
 	}
 
+#if GST_CHECK_VERSION(1,0,0)
+	sinkpad = gst_element_get_request_pad(stream->src, "sink_%u");
+#else
 	sinkpad = gst_element_get_request_pad(stream->src, "sink%d");
+#endif
 	gst_pad_link(srcpad, sinkpad);
 	gst_object_unref(sinkpad);
 
--- a/libpurple/mediamanager.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/mediamanager.c	Fri Jan 25 02:22:38 2013 -0500
@@ -44,7 +44,11 @@
 #else
 #include <farstream/fs-element-added-notifier.h>
 #endif
+#if GST_CHECK_VERSION(1,0,0)
+#include <gst/video/videooverlay.h>
+#else
 #include <gst/interfaces/xoverlay.h>
+#endif
 
 /** @copydoc _PurpleMediaManagerPrivate */
 typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate;
@@ -270,8 +274,11 @@
 		gst_bus_add_signal_watch(GST_BUS(bus));
 		g_signal_connect(G_OBJECT(bus), "message",
 				G_CALLBACK(pipeline_bus_call), manager);
-		gst_bus_set_sync_handler(bus,
-				gst_bus_sync_signal_handler, NULL);
+#if GST_CHECK_VERSION(1,0,0)
+		gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL, NULL);
+#else
+		gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL);
+#endif
 		gst_object_unref(bus);
 
 		filename = g_build_filename(purple_user_dir(),
@@ -401,20 +408,31 @@
 {
 	GstElement *parent = GST_ELEMENT_PARENT(pad);
 	GstIterator *iter;
+#if GST_CHECK_VERSION(1,0,0)
+	GValue tmp = G_VALUE_INIT;
+#endif
 	GstPad *remaining_pad;
 	GstIteratorResult result;
 
-	gst_element_release_request_pad(GST_ELEMENT_PARENT(pad), pad);
+	gst_element_release_request_pad(parent, pad);
 
 	iter = gst_element_iterate_src_pads(parent);
 
+#if GST_CHECK_VERSION(1,0,0)
+	result = gst_iterator_next(iter, &tmp);
+#else
 	result = gst_iterator_next(iter, (gpointer)&remaining_pad);
+#endif
 
 	if (result == GST_ITERATOR_DONE) {
 		gst_element_set_locked_state(parent, TRUE);
 		gst_element_set_state(parent, GST_STATE_NULL);
 		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent)), parent);
 	} else if (result == GST_ITERATOR_OK) {
+#if GST_CHECK_VERSION(1,0,0)
+		remaining_pad = g_value_get_object(&tmp);
+		g_value_reset(&tmp);
+#endif
 		gst_object_unref(remaining_pad);
 	}
 
@@ -452,7 +470,11 @@
 {
 #ifdef USE_VV
 	if (manager->priv->video_caps == NULL)
+#if GST_CHECK_VERSION(1,0,0)
+		manager->priv->video_caps = gst_caps_from_string("video/x-raw,"
+#else
 		manager->priv->video_caps = gst_caps_from_string("video/x-raw-yuv,"
+#endif
 			"width=[250,352], height=[200,288], framerate=[1/1,20/1]");
 	return manager->priv->video_caps;
 #else
@@ -535,7 +557,11 @@
 		g_free(id);
 
 		tee = gst_bin_get_by_name(GST_BIN(ret), "tee");
+#if GST_CHECK_VERSION(1,0,0)
+		pad = gst_element_get_request_pad(tee, "src_%u");
+#else
 		pad = gst_element_get_request_pad(tee, "src%d");
+#endif
 		gst_object_unref(tee);
 		ghost = gst_ghost_pad_new(NULL, pad);
 		gst_object_unref(pad);
@@ -726,9 +752,12 @@
 {
 	GstElement *sink;
 
-	if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT ||
-			!gst_structure_has_name(msg->structure,
-			"prepare-xwindow-id"))
+	if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT
+#if GST_CHECK_VERSION(1,0,0)
+	 || !gst_is_video_overlay_prepare_window_handle_message(msg))
+#else
+	 || !gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
+#endif
 		return;
 
 	sink = GST_ELEMENT(GST_MESSAGE_SRC(msg));
@@ -742,7 +771,10 @@
 			| G_SIGNAL_MATCH_DATA, 0, 0, NULL,
 			window_id_cb, ow);
 
-#if GST_CHECK_VERSION(0,10,31)
+#if GST_CHECK_VERSION(1,0,0)
+	gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg)),
+	                                    ow->window_id);
+#elif GST_CHECK_VERSION(0,10,31)
 	gst_x_overlay_set_window_handle(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)),
 	                                ow->window_id);
 #else
@@ -773,17 +805,19 @@
 				(participant == ow->participant)) &&
 				!strcmp(session_id, ow->session_id)) {
 			GstBus *bus;
-			GstElement *queue, *colorspace;
+			GstElement *queue, *convert;
 			GstElement *tee = purple_media_get_tee(media,
 					session_id, participant);
 
 			if (tee == NULL)
 				continue;
 
-			queue = gst_element_factory_make(
-					"queue", NULL);
-			colorspace = gst_element_factory_make(
-					"ffmpegcolorspace", NULL);
+			queue = gst_element_factory_make("queue", NULL);
+#if GST_CHECK_VERSION(1,0,0)
+			convert = gst_element_factory_make("videoconvert", NULL);
+#else
+			convert = gst_element_factory_make("ffmpegcolorspace", NULL);
+#endif
 			ow->sink = purple_media_manager_get_element(
 					manager, PURPLE_MEDIA_RECV_VIDEO,
 					ow->media, ow->session_id,
@@ -804,7 +838,7 @@
 			}
 
 			gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)),
-					queue, colorspace, ow->sink, NULL);
+					queue, convert, ow->sink, NULL);
 
 			bus = gst_pipeline_get_bus(GST_PIPELINE(
 					manager->priv->pipeline));
@@ -813,10 +847,10 @@
 			gst_object_unref(bus);
 
 			gst_element_set_state(ow->sink, GST_STATE_PLAYING);
-			gst_element_set_state(colorspace, GST_STATE_PLAYING);
+			gst_element_set_state(convert, GST_STATE_PLAYING);
 			gst_element_set_state(queue, GST_STATE_PLAYING);
-			gst_element_link(colorspace, ow->sink);
-			gst_element_link(queue, colorspace);
+			gst_element_link(convert, ow->sink);
+			gst_element_link(queue, convert);
 			gst_element_link(tee, queue);
 		}
 	}
--- a/libpurple/notify.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/notify.c	Fri Jan 25 02:22:38 2013 -0500
@@ -818,6 +818,9 @@
 						 purple_value_new(PURPLE_TYPE_POINTER),
 						 purple_value_new(PURPLE_TYPE_UINT));
 
+	purple_signal_register(handle, "displaying-emails-clear",
+						 purple_marshal_VOID, NULL, 0);
+
 	purple_signal_register(handle, "displaying-userinfo",
 						 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
--- a/libpurple/ntlm.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/ntlm.c	Fri Jan 25 02:22:38 2013 -0500
@@ -220,16 +220,39 @@
 	des_ecb_encrypt(plaintext, results + 16, key);
 }
 
+/*
+ * TODO: We think we should be using cryptographically secure random numbers
+ *       here.  We think the rand() function is probably bad.  We think
+ *       /dev/urandom is a step up, but using a random function from an SSL
+ *       library would probably be best.  In Windows we could possibly also
+ *       use CryptGenRandom.
+ */
 static void
-gensesskey(char *buffer, const char *oldkey)
+gensesskey(char *buffer)
 {
-	int i = 0;
-	if(oldkey == NULL) {
-		for(i=0; i<16; i++) {
-			buffer[i] = (char)(rand() & 0xff);
+	int fd;
+	int i;
+	ssize_t red = 0;
+
+	fd = open("/dev/urandom", O_RDONLY);
+	if (fd >= 0) {
+		red = read(fd, buffer, 16);
+		if (red < 0) {
+			purple_debug_warning("ntlm", "Error reading from /dev/urandom: %s."
+					"  Falling back to inferior method.\n", g_strerror(errno));
+			red = 0;
+		} else if (red < 16) {
+			purple_debug_warning("ntlm", "Tried reading 16 bytes from "
+					"/dev/urandom but only got %zd.  Falling back to "
+					"inferior method\n", red);
 		}
 	} else {
-		memcpy(buffer, oldkey, 16);
+		purple_debug_warning("ntlm", "Error opening /dev/urandom: %s."
+				"  Falling back to inferior method.\n", g_strerror(errno));
+	}
+
+	for (i = red; i < 16; i++) {
+		buffer[i] = (char)(rand() & 0xff);
 	}
 }
 
@@ -366,7 +389,7 @@
 	/* LCS Stuff */
 	if (flags) {
 		tmsg->flags = GUINT32_TO_LE(0x409082d4);
-		gensesskey(sesskey, NULL);
+		gensesskey(sesskey);
 		memcpy(tmp, sesskey, 0x10);
 	}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/obsolete.c	Fri Jan 25 02:22:38 2013 -0500
@@ -0,0 +1,776 @@
+/* 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 "obsolete.h"
+
+#include "internal.h"
+#include "debug.h"
+#include "http.h"
+#include "ntlm.h"
+
+struct _PurpleUtilFetchUrlData
+{
+	PurpleUtilFetchUrlCallback callback;
+	void *user_data;
+
+	struct
+	{
+		char *user;
+		char *passwd;
+		char *address;
+		int port;
+		char *page;
+
+	} website;
+
+	char *url;
+	int num_times_redirected;
+	gboolean full;
+	char *user_agent;
+	gboolean http11;
+	char *request;
+	gsize request_written;
+	gboolean include_headers;
+
+	gboolean is_ssl;
+	PurpleSslConnection *ssl_connection;
+	PurpleProxyConnectData *connect_data;
+	int fd;
+	guint inpa;
+
+	gboolean got_headers;
+	gboolean has_explicit_data_len;
+	char *webdata;
+	gsize len;
+	unsigned long data_len;
+	gssize max_len;
+	gboolean chunked;
+	PurpleAccount *account;
+
+	PurpleHttpConnection *wrapped_request;
+	gboolean cancelled;
+};
+
+typedef struct
+{
+	PurpleUtilFetchUrlData *url_data;
+	PurpleUtilFetchUrlCallback cb;
+	gpointer user_data;
+} PurpleUtilLegacyWrapData;
+
+static void purple_util_fetch_url_cb(PurpleHttpConnection *http_conn,
+	PurpleHttpResponse *response, gpointer _wrap_data)
+{
+	PurpleUtilLegacyWrapData *wrap_data = _wrap_data;
+	const char *data = NULL;
+	size_t len;
+
+	if (wrap_data->cb && !wrap_data->url_data->cancelled) {
+		data = purple_http_response_get_data(response, &len);
+
+		wrap_data->cb(wrap_data->url_data, wrap_data->user_data, data, len,
+			purple_http_response_get_error(response));
+	}
+
+	g_free(wrap_data->url_data);
+	g_free(wrap_data);
+}
+
+PurpleUtilFetchUrlData * purple_util_fetch_url(const gchar *url, gboolean full,
+	const gchar *user_agent, gboolean http11, gssize max_len,
+	PurpleUtilFetchUrlCallback cb, gpointer data)
+{
+	PurpleHttpRequest *request;
+	PurpleUtilFetchUrlData *url_data;
+	PurpleUtilLegacyWrapData *wrap_data;
+
+	if (FALSE)
+		return purple_util_fetch_url_request(NULL, url, full,
+			user_agent, http11, NULL, FALSE, max_len, cb, data);
+
+	wrap_data = g_new0(PurpleUtilLegacyWrapData, 1);
+	url_data = g_new0(PurpleUtilFetchUrlData, 1);
+	request = purple_http_request_new(url);
+
+	wrap_data->url_data = url_data;
+	wrap_data->cb = cb;
+	wrap_data->user_data = data;
+
+	if (user_agent)
+		purple_http_request_header_set(request,
+			"User-Agent", user_agent);
+	purple_http_request_set_http11(request, http11);
+	purple_http_request_set_max_len(request, max_len);
+
+	url_data->wrapped_request = purple_http_request(NULL, request,
+		purple_util_fetch_url_cb, wrap_data);
+
+	purple_http_request_unref(request);
+
+	return url_data;
+}
+
+/**
+ * The arguments to this function are similar to printf.
+ */
+static void
+purple_util_fetch_url_error(PurpleUtilFetchUrlData *gfud, const char *format, ...)
+{
+	gchar *error_message;
+	va_list args;
+
+	va_start(args, format);
+	error_message = g_strdup_vprintf(format, args);
+	va_end(args);
+
+	gfud->callback(gfud, gfud->user_data, NULL, 0, error_message);
+	g_free(error_message);
+	purple_util_fetch_url_cancel(gfud);
+}
+
+static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message);
+static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond);
+static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data);
+
+static gboolean
+parse_redirect(const char *data, size_t data_len,
+			   PurpleUtilFetchUrlData *gfud)
+{
+	gchar *s;
+	gchar *new_url, *temp_url, *end;
+	gboolean full;
+	int len;
+
+	if ((s = g_strstr_len(data, data_len, "\nLocation: ")) == NULL)
+		/* We're not being redirected */
+		return FALSE;
+
+	s += strlen("Location: ");
+	end = strchr(s, '\r');
+
+	/* Just in case :) */
+	if (end == NULL)
+		end = strchr(s, '\n');
+
+	if (end == NULL)
+		return FALSE;
+
+	len = end - s;
+
+	new_url = g_malloc(len + 1);
+	strncpy(new_url, s, len);
+	new_url[len] = '\0';
+
+	full = gfud->full;
+
+	if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL)
+	{
+		temp_url = new_url;
+
+		new_url = g_strdup_printf("%s:%d%s", gfud->website.address,
+								  gfud->website.port, temp_url);
+
+		g_free(temp_url);
+
+		full = FALSE;
+	}
+
+	purple_debug_info("util", "Redirecting to %s\n", new_url);
+
+	gfud->num_times_redirected++;
+	if (gfud->num_times_redirected >= 5)
+	{
+		purple_util_fetch_url_error(gfud,
+				_("Could not open %s: Redirected too many times"),
+				gfud->url);
+		return TRUE;
+	}
+
+	/*
+	 * Try again, with this new location.  This code is somewhat
+	 * ugly, but we need to reuse the gfud because whoever called
+	 * us is holding a reference to it.
+	 */
+	g_free(gfud->url);
+	gfud->url = new_url;
+	gfud->full = full;
+	g_free(gfud->request);
+	gfud->request = NULL;
+
+	if (gfud->is_ssl) {
+		gfud->is_ssl = FALSE;
+		purple_ssl_close(gfud->ssl_connection);
+		gfud->ssl_connection = NULL;
+	} else {
+		purple_input_remove(gfud->inpa);
+		gfud->inpa = 0;
+		close(gfud->fd);
+		gfud->fd = -1;
+	}
+	gfud->request_written = 0;
+	gfud->len = 0;
+	gfud->data_len = 0;
+
+	g_free(gfud->website.user);
+	g_free(gfud->website.passwd);
+	g_free(gfud->website.address);
+	g_free(gfud->website.page);
+	purple_url_parse(new_url, &gfud->website.address, &gfud->website.port,
+				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
+
+	if (purple_strcasestr(new_url, "https://") != NULL) {
+		gfud->is_ssl = TRUE;
+		gfud->ssl_connection = purple_ssl_connect(gfud->account,
+				gfud->website.address, gfud->website.port,
+				ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud);
+	} else {
+		gfud->connect_data = purple_proxy_connect(NULL, gfud->account,
+				gfud->website.address, gfud->website.port,
+				url_fetch_connect_cb, gfud);
+	}
+
+	if (gfud->ssl_connection == NULL && gfud->connect_data == NULL)
+	{
+		purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
+				gfud->website.address);
+	}
+
+	return TRUE;
+}
+
+static const char *
+find_header_content(const char *data, size_t data_len, const char *header, size_t header_len)
+{
+	const char *p = NULL;
+
+	if (header_len <= 0)
+		header_len = strlen(header);
+
+	/* Note: data is _not_ nul-terminated.  */
+	if (data_len > header_len) {
+		if (header[0] == '\n')
+			p = (g_ascii_strncasecmp(data, header + 1, header_len - 1) == 0) ? data : NULL;
+		if (!p)
+			p = purple_strcasestr(data, header);
+		if (p)
+			p += header_len;
+	}
+
+	/* If we can find the header at all, try to sscanf it.
+	 * Response headers should end with at least \r\n, so sscanf is safe,
+	 * if we make sure that there is indeed a \n in our header.
+	 */
+	if (p && g_strstr_len(p, data_len - (p - data), "\n")) {
+		return p;
+	}
+
+	return NULL;
+}
+
+static size_t
+parse_content_len(const char *data, size_t data_len)
+{
+	size_t content_len = 0;
+	const char *p = NULL;
+
+	p = find_header_content(data, data_len, "\nContent-Length: ", sizeof("\nContent-Length: ") - 1);
+	if (p) {
+		sscanf(p, "%" G_GSIZE_FORMAT, &content_len);
+		purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len);
+	}
+
+	return content_len;
+}
+
+static gboolean
+content_is_chunked(const char *data, size_t data_len)
+{
+	const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ", sizeof("\nTransfer-Encoding: ") - 1);
+	if (p && g_ascii_strncasecmp(p, "chunked", 7) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+/* Process in-place */
+static void
+process_chunked_data(char *data, gsize *len)
+{
+	gsize sz;
+	gsize newlen = 0;
+	char *p = data;
+	char *s = data;
+
+	while (*s) {
+		/* Read the size of this chunk */
+		if (sscanf(s, "%" G_GSIZE_MODIFIER "x", &sz) != 1)
+		{
+			purple_debug_error("util", "Error processing chunked data: "
+					"Expected data length, found: %s\n", s);
+			break;
+		}
+		if (sz == 0) {
+			/* We've reached the last chunk */
+			/*
+			 * TODO: The spec allows "footers" to follow the last chunk.
+			 *       If there is more data after this line then we should
+			 *       treat it like a header.
+			 */
+			break;
+		}
+
+		/* Advance to the start of the data */
+		s = strstr(s, "\r\n");
+		if (s == NULL)
+			break;
+		s += 2;
+
+		if (s + sz > data + *len) {
+			purple_debug_error("util", "Error processing chunked data: "
+					"Chunk size %" G_GSIZE_FORMAT " bytes was longer "
+					"than the data remaining in the buffer (%"
+					G_GSIZE_FORMAT " bytes)\n", sz, data + *len - s);
+		}
+
+		/* Move all data overtop of the chunk length that we read in earlier */
+		g_memmove(p, s, sz);
+		p += sz;
+		s += sz;
+		newlen += sz;
+		if (*s != '\r' && *(s + 1) != '\n') {
+			purple_debug_error("util", "Error processing chunked data: "
+					"Expected \\r\\n, found: %s\n", s);
+			break;
+		}
+		s += 2;
+	}
+
+	/* NULL terminate the data */
+	*p = 0;
+
+	*len = newlen;
+}
+
+static void
+url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond)
+{
+	PurpleUtilFetchUrlData *gfud = url_data;
+	int len;
+	char buf[4096];
+	char *data_cursor;
+	gboolean got_eof = FALSE;
+
+	/*
+	 * Read data in a loop until we can't read any more!  This is a
+	 * little confusing because we read using a different function
+	 * depending on whether the socket is ssl or cleartext.
+	 */
+	while ((gfud->is_ssl && ((len = purple_ssl_read(gfud->ssl_connection, buf, sizeof(buf))) > 0)) ||
+			(!gfud->is_ssl && (len = read(source, buf, sizeof(buf))) > 0))
+	{
+		if(gfud->max_len != -1 && (gfud->len + len) > gfud->max_len) {
+			purple_util_fetch_url_error(gfud, _("Error reading from %s: response too long (%d bytes limit)"),
+						    gfud->website.address, gfud->max_len);
+			return;
+		}
+
+		/* If we've filled up our buffer, make it bigger */
+		if((gfud->len + len) >= gfud->data_len) {
+			while((gfud->len + len) >= gfud->data_len)
+				gfud->data_len += sizeof(buf);
+
+			gfud->webdata = g_realloc(gfud->webdata, gfud->data_len);
+		}
+
+		data_cursor = gfud->webdata + gfud->len;
+
+		gfud->len += len;
+
+		memcpy(data_cursor, buf, len);
+
+		gfud->webdata[gfud->len] = '\0';
+
+		if(!gfud->got_headers) {
+			char *end_of_headers;
+
+			/* See if we've reached the end of the headers yet */
+			end_of_headers = strstr(gfud->webdata, "\r\n\r\n");
+			if (end_of_headers) {
+				char *new_data;
+				guint header_len = (end_of_headers + 4 - gfud->webdata);
+				size_t content_len;
+
+				purple_debug_misc("util", "Response headers: '%.*s'\n",
+					header_len, gfud->webdata);
+
+				/* See if we can find a redirect. */
+				if(parse_redirect(gfud->webdata, header_len, gfud))
+					return;
+
+				gfud->got_headers = TRUE;
+
+				/* No redirect. See if we can find a content length. */
+				content_len = parse_content_len(gfud->webdata, header_len);
+				gfud->chunked = content_is_chunked(gfud->webdata, header_len);
+
+				if (content_len == 0) {
+					/* We'll stick with an initial 8192 */
+					content_len = 8192;
+				} else {
+					gfud->has_explicit_data_len = TRUE;
+				}
+
+
+				/* If we're returning the headers too, we don't need to clean them out */
+				if (gfud->include_headers) {
+					gfud->data_len = content_len + header_len;
+					gfud->webdata = g_realloc(gfud->webdata, gfud->data_len);
+				} else {
+					size_t body_len = gfud->len - header_len;
+
+					content_len = MAX(content_len, body_len);
+
+					new_data = g_try_malloc(content_len);
+					if (new_data == NULL) {
+						purple_debug_error("util",
+								"Failed to allocate %" G_GSIZE_FORMAT " bytes: %s\n",
+								content_len, g_strerror(errno));
+						purple_util_fetch_url_error(gfud,
+								_("Unable to allocate enough memory to hold "
+								  "the contents from %s.  The web server may "
+								  "be trying something malicious."),
+								gfud->website.address);
+
+						return;
+					}
+
+					/* We may have read part of the body when reading the headers, don't lose it */
+					if (body_len > 0) {
+						memcpy(new_data, end_of_headers + 4, body_len);
+					}
+
+					/* Out with the old... */
+					g_free(gfud->webdata);
+
+					/* In with the new. */
+					gfud->len = body_len;
+					gfud->data_len = content_len;
+					gfud->webdata = new_data;
+				}
+			}
+		}
+
+		if(gfud->has_explicit_data_len && gfud->len >= gfud->data_len) {
+			got_eof = TRUE;
+			break;
+		}
+	}
+
+	if(len < 0) {
+		if(errno == EAGAIN) {
+			return;
+		} else {
+			purple_util_fetch_url_error(gfud, _("Error reading from %s: %s"),
+					gfud->website.address, g_strerror(errno));
+			return;
+		}
+	}
+
+	if((len == 0) || got_eof) {
+		gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1);
+		gfud->webdata[gfud->len] = '\0';
+
+		if (!gfud->include_headers && gfud->chunked) {
+			/* Process only if we don't want the headers. */
+			process_chunked_data(gfud->webdata, &gfud->len);
+		}
+
+		gfud->callback(gfud, gfud->user_data, gfud->webdata, gfud->len, NULL);
+		purple_util_fetch_url_cancel(gfud);
+	}
+}
+
+static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
+{
+	url_fetch_recv_cb(data, -1, cond);
+}
+
+/**
+ * This function is called when the socket is available to be written
+ * to.
+ *
+ * @param source The file descriptor that can be written to.  This can
+ *        be an http connection or it can be the SSL connection of an
+ *        https request.  So be careful what you use it for!  If it's
+ *        an https request then use purple_ssl_write() instead of
+ *        writing to it directly.
+ */
+static void
+url_fetch_send_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleUtilFetchUrlData *gfud;
+	int len, total_len;
+
+	gfud = data;
+
+	if (gfud->request == NULL) {
+
+		PurpleProxyInfo *gpi = purple_proxy_get_setup(gfud->account);
+		GString *request_str = g_string_new(NULL);
+
+		g_string_append_printf(request_str, "GET %s%s HTTP/%s\r\n"
+						    "Connection: close\r\n",
+			(gfud->full ? "" : "/"),
+			(gfud->full ? (gfud->url ? gfud->url : "") : (gfud->website.page ? gfud->website.page : "")),
+			(gfud->http11 ? "1.1" : "1.0"));
+
+		if (gfud->user_agent)
+			g_string_append_printf(request_str, "User-Agent: %s\r\n", gfud->user_agent);
+
+		/* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1
+		 * clients must know how to handle the "chunked" transfer encoding.
+		 * Purple doesn't know how to handle "chunked", so should always send
+		 * the Host header regardless, to get around some observed problems
+		 */
+		g_string_append_printf(request_str, "Accept: */*\r\n"
+						    "Host: %s\r\n",
+			(gfud->website.address ? gfud->website.address : ""));
+
+		if (purple_proxy_info_get_username(gpi) != NULL
+				&& (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR
+					|| purple_proxy_info_get_type(gpi) == PURPLE_PROXY_HTTP)) {
+			/* This chunk of code was copied from proxy.c http_start_connect_tunneling()
+			 * This is really a temporary hack - we need a more complete proxy handling solution,
+			 * so I didn't think it was worthwhile to refactor for reuse
+			 */
+			char *t1, *t2, *ntlm_type1;
+			char hostname[256];
+			int ret;
+
+			ret = gethostname(hostname, sizeof(hostname));
+			hostname[sizeof(hostname) - 1] = '\0';
+			if (ret < 0 || hostname[0] == '\0') {
+				purple_debug_warning("util", "proxy - gethostname() failed -- is your hostname set?");
+				strcpy(hostname, "localhost");
+			}
+
+			t1 = g_strdup_printf("%s:%s",
+				purple_proxy_info_get_username(gpi),
+				purple_proxy_info_get_password(gpi) ?
+					purple_proxy_info_get_password(gpi) : "");
+			t2 = purple_base64_encode((const guchar *)t1, strlen(t1));
+			g_free(t1);
+
+			ntlm_type1 = purple_ntlm_gen_type1(hostname, "");
+
+			g_string_append_printf(request_str,
+				"Proxy-Authorization: Basic %s\r\n"
+				"Proxy-Authorization: NTLM %s\r\n"
+				"Proxy-Connection: Keep-Alive\r\n",
+				t2, ntlm_type1);
+			g_free(ntlm_type1);
+			g_free(t2);
+		}
+
+		g_string_append(request_str, "\r\n");
+
+		gfud->request = g_string_free(request_str, FALSE);
+	}
+
+	if(purple_debug_is_unsafe())
+		purple_debug_misc("util", "Request: '%s'\n", gfud->request);
+	else
+		purple_debug_misc("util", "request constructed\n");
+
+	total_len = strlen(gfud->request);
+
+	if (gfud->is_ssl)
+		len = purple_ssl_write(gfud->ssl_connection, gfud->request + gfud->request_written,
+				total_len - gfud->request_written);
+	else
+		len = write(gfud->fd, gfud->request + gfud->request_written,
+				total_len - gfud->request_written);
+
+	if (len < 0 && errno == EAGAIN)
+		return;
+	else if (len < 0) {
+		purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"),
+				gfud->website.address, g_strerror(errno));
+		return;
+	}
+	gfud->request_written += len;
+
+	if (gfud->request_written < total_len)
+		return;
+
+	/* We're done writing our request, now start reading the response */
+	if (gfud->is_ssl) {
+		purple_input_remove(gfud->inpa);
+		gfud->inpa = 0;
+		purple_ssl_input_add(gfud->ssl_connection, ssl_url_fetch_recv_cb, gfud);
+	} else {
+		purple_input_remove(gfud->inpa);
+		gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb,
+			gfud);
+	}
+}
+
+static void
+url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message)
+{
+	PurpleUtilFetchUrlData *gfud;
+
+	gfud = url_data;
+	gfud->connect_data = NULL;
+
+	if (source == -1)
+	{
+		purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"),
+				(gfud->website.address ? gfud->website.address : ""), error_message);
+		return;
+	}
+
+	gfud->fd = source;
+
+	gfud->inpa = purple_input_add(source, PURPLE_INPUT_WRITE,
+								url_fetch_send_cb, gfud);
+	url_fetch_send_cb(gfud, source, PURPLE_INPUT_WRITE);
+}
+
+static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
+{
+	PurpleUtilFetchUrlData *gfud;
+
+	gfud = data;
+
+	gfud->inpa = purple_input_add(ssl_connection->fd, PURPLE_INPUT_WRITE,
+			url_fetch_send_cb, gfud);
+	url_fetch_send_cb(gfud, ssl_connection->fd, PURPLE_INPUT_WRITE);
+}
+
+static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data)
+{
+	PurpleUtilFetchUrlData *gfud;
+
+	gfud = data;
+	gfud->ssl_connection = NULL;
+
+	purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"),
+			(gfud->website.address ? gfud->website.address : ""),
+	purple_ssl_strerror(error));
+}
+
+PurpleUtilFetchUrlData *
+purple_util_fetch_url_request(PurpleAccount *account,
+		const char *url, gboolean full,	const char *user_agent, gboolean http11,
+		const char *request, gboolean include_headers, gssize max_len,
+		PurpleUtilFetchUrlCallback callback, void *user_data)
+{
+	PurpleUtilFetchUrlData *gfud;
+
+	g_return_val_if_fail(url      != NULL, NULL);
+	g_return_val_if_fail(callback != NULL, NULL);
+
+	if(purple_debug_is_unsafe())
+		purple_debug_info("util",
+				 "requested to fetch (%s), full=%d, user_agent=(%s), http11=%d\n",
+				 url, full, user_agent?user_agent:"(null)", http11);
+	else
+		purple_debug_info("util", "requesting to fetch a URL\n");
+
+	gfud = g_new0(PurpleUtilFetchUrlData, 1);
+
+	gfud->callback = callback;
+	gfud->user_data  = user_data;
+	gfud->url = g_strdup(url);
+	gfud->user_agent = g_strdup(user_agent);
+	gfud->http11 = http11;
+	gfud->full = full;
+	gfud->request = g_strdup(request);
+	gfud->include_headers = include_headers;
+	gfud->fd = -1;
+	gfud->max_len = max_len;
+	gfud->account = account;
+
+	purple_url_parse(url, &gfud->website.address, &gfud->website.port,
+				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
+
+	if (purple_strcasestr(url, "https://") != NULL) {
+		if (!purple_ssl_is_supported()) {
+			purple_util_fetch_url_error(gfud,
+					_("Unable to connect to %s: %s"),
+					gfud->website.address,
+					_("Server requires TLS/SSL, but no TLS/SSL support was found."));
+			return NULL;
+		}
+
+		gfud->is_ssl = TRUE;
+		gfud->ssl_connection = purple_ssl_connect(account,
+				gfud->website.address, gfud->website.port,
+				ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud);
+	} else {
+		gfud->connect_data = purple_proxy_connect(NULL, account,
+				gfud->website.address, gfud->website.port,
+				url_fetch_connect_cb, gfud);
+	}
+
+	if (gfud->ssl_connection == NULL && gfud->connect_data == NULL)
+	{
+		purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
+				gfud->website.address);
+		return NULL;
+	}
+
+	return gfud;
+}
+
+void
+purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *gfud)
+{
+	if (gfud->wrapped_request != NULL) {
+		gfud->cancelled = TRUE;
+		purple_http_conn_cancel(gfud->wrapped_request);
+		return;
+	}
+
+	if (gfud->ssl_connection != NULL)
+		purple_ssl_close(gfud->ssl_connection);
+
+	if (gfud->connect_data != NULL)
+		purple_proxy_connect_cancel(gfud->connect_data);
+
+	if (gfud->inpa > 0)
+		purple_input_remove(gfud->inpa);
+
+	if (gfud->fd >= 0)
+		close(gfud->fd);
+
+	g_free(gfud->website.user);
+	g_free(gfud->website.passwd);
+	g_free(gfud->website.address);
+	g_free(gfud->website.page);
+	g_free(gfud->url);
+	g_free(gfud->user_agent);
+	g_free(gfud->request);
+	g_free(gfud->webdata);
+
+	g_free(gfud);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/obsolete.h	Fri Jan 25 02:22:38 2013 -0500
@@ -0,0 +1,110 @@
+/**
+ * @file obsolete.h Obsolete code, to be removed
+ * @ingroup core
+ */
+
+/* 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 _PURPLE_OBSOLETE_H_
+#define _PURPLE_OBSOLETE_H_
+
+#include <glib.h>
+#include "account.h"
+
+/**************************************************************************/
+/** @name URI/URL Functions                                               */
+/**************************************************************************/
+/*@{*/
+
+/**
+  * An opaque structure representing a URL request. Can be used to cancel
+  * the request.
+  */
+typedef struct _PurpleUtilFetchUrlData PurpleUtilFetchUrlData;
+
+/**
+ * This is the signature used for functions that act as the callback
+ * to purple_util_fetch_url() or purple_util_fetch_url_request().
+ *
+ * @param url_data      The same value that was returned when you called
+ *                      purple_fetch_url() or purple_fetch_url_request().
+ * @param user_data     The user data that your code passed into either
+ *                      purple_util_fetch_url() or purple_util_fetch_url_request().
+ * @param url_text      This will be NULL on error.  Otherwise this
+ *                      will contain the contents of the URL.
+ * @param len           0 on error, otherwise this is the length of buf.
+ * @param error_message If something went wrong then this will contain
+ *                      a descriptive error message, and buf will be
+ *                      NULL and len will be 0.
+ */
+typedef void (*PurpleUtilFetchUrlCallback)(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message);
+
+/**
+ * Fetches the data from a URL, and passes it to a callback function.
+ *
+ * @param url        The URL.
+ * @param full       TRUE if this is the full URL, or FALSE if it's a
+ *                   partial URL.
+ * @param user_agent The user agent field to use, or NULL.
+ * @param http11     TRUE if HTTP/1.1 should be used to download the file.
+ * @param max_len    The maximum number of bytes to retrieve (-1 for unlimited)
+ * @param cb         The callback function.
+ * @param data       The user data to pass to the callback function.
+ */
+PurpleUtilFetchUrlData * purple_util_fetch_url(const gchar *url, gboolean full,
+	const gchar *user_agent, gboolean http11, gssize max_len,
+	PurpleUtilFetchUrlCallback cb, gpointer data);
+
+/**
+ * Fetches the data from a URL, and passes it to a callback function.
+ *
+ * @param account    The account for which the request is needed, or NULL.
+ * @param url        The URL.
+ * @param full       TRUE if this is the full URL, or FALSE if it's a
+ *                   partial URL.
+ * @param user_agent The user agent field to use, or NULL.
+ * @param http11     TRUE if HTTP/1.1 should be used to download the file.
+ * @param request    A HTTP request to send to the server instead of the
+ *                   standard GET
+ * @param include_headers
+ *                   If TRUE, include the HTTP headers in the response.
+ * @param max_len    The maximum number of bytes to retrieve (-1 for unlimited)
+ * @param callback   The callback function.
+ * @param data       The user data to pass to the callback function.
+ */
+PurpleUtilFetchUrlData *purple_util_fetch_url_request(
+		PurpleAccount *account, const gchar *url,
+		gboolean full, const gchar *user_agent, gboolean http11,
+		const gchar *request, gboolean include_headers, gssize max_len,
+		PurpleUtilFetchUrlCallback callback, gpointer data);
+
+/**
+ * Cancel a pending URL request started with either
+ * purple_util_fetch_url_request() or purple_util_fetch_url().
+ *
+ * @param url_data The data returned when you initiated the URL fetch.
+ */
+void purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *url_data);
+
+
+/*@}*/
+
+#endif /* _PURPLE_OBSOLETE_H_ */
--- a/libpurple/plugins/autoaccept.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/autoaccept.c	Fri Jan 25 02:22:38 2013 -0500
@@ -239,7 +239,7 @@
 	 *                                             --Mark Doliner, 2011-01-03
 	 */
 	if (!purple_prefs_exists(PREF_STRANGER)) {
-		if (purple_prefs_get_bool(PREF_STRANGER_OLD))
+		if (purple_prefs_exists(PREF_STRANGER_OLD) && purple_prefs_get_bool(PREF_STRANGER_OLD))
 			purple_prefs_add_int(PREF_STRANGER, FT_REJECT);
 		else
 			purple_prefs_set_int(PREF_STRANGER, FT_ASK);
--- a/libpurple/plugins/log_reader.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/log_reader.c	Fri Jan 25 02:22:38 2013 -0500
@@ -921,7 +921,6 @@
 		xmlnode *to;
 		enum name_guesses name_guessed = NAME_GUESS_UNKNOWN;
 		const char *their_name;
-		time_t time_unix;
 		struct tm *tm;
 		char *timestamp;
 		char *tmp;
@@ -1101,7 +1100,7 @@
 			text = g_string_append(text, ";\">");
 		}
 
-		time_unix = msn_logger_parse_timestamp(message, &tm);
+		msn_logger_parse_timestamp(message, &tm);
 
 		timestamp = g_strdup_printf("<font size=\"2\">(%02u:%02u:%02u)</font> ",
 				tm->tm_hour, tm->tm_min, tm->tm_sec);
@@ -2581,7 +2580,7 @@
 		/* Read talk.ini file to find the log directory. */
 		GError *error = NULL;
 
-#if 0 && GLIB_CHECK_VERSION(2,6,0) /* FIXME: Not tested yet. */
+#if 0 /* FIXME: Not tested yet. */
 		GKeyFile *key_file;
 
 		purple_debug_info("Trillian talk.ini read", "Reading %s\n", path);
@@ -2608,7 +2607,7 @@
 
 			g_key_file_free(key_file);
 		}
-#else /* !GLIB_CHECK_VERSION(2,6,0) */
+#else
 		gchar *contents = NULL;
 
 		purple_debug_info("Trillian talk.ini read",
@@ -2644,7 +2643,7 @@
 			g_free(contents);
 		}
 		g_free(path);
-#endif /* !GLIB_CHECK_VERSION(2,6,0) */
+#endif
 	} /* path */
 
 	if (!found) {
--- a/libpurple/plugins/perl/common/BuddyIcon.xs	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/perl/common/BuddyIcon.xs	Fri Jan 25 02:22:38 2013 -0500
@@ -7,7 +7,7 @@
 purple_buddy_icon_ref(icon)
 	Purple::Buddy::Icon icon
 
-Purple::Buddy::Icon
+void
 purple_buddy_icon_unref(icon)
 	Purple::Buddy::Icon icon
 
--- a/libpurple/plugins/perl/common/BuddyList.xs	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/perl/common/BuddyList.xs	Fri Jan 25 02:22:38 2013 -0500
@@ -35,10 +35,6 @@
 Purple::BuddyList
 purple_get_blist()
 
-void
-purple_set_blist(blist)
-	Purple::BuddyList blist
-
 MODULE = Purple::BuddyList  PACKAGE = Purple::Find  PREFIX = purple_find_
 PROTOTYPES: ENABLE
 
@@ -176,9 +172,6 @@
 	Purple::BuddyList::Group  group
 	Purple::BuddyList::Node node
 
-Purple::BuddyList
-purple_blist_new()
-
 void
 purple_blist_show()
 
@@ -237,9 +230,6 @@
 	Purple::BuddyList::Group  group
 
 void
-purple_blist_load()
-
-void
 purple_blist_schedule_save()
 
 void
--- a/libpurple/plugins/perl/common/Certificate.xs	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/perl/common/Certificate.xs	Fri Jan 25 02:22:38 2013 -0500
@@ -43,8 +43,20 @@
 
 	static const constiv *civ, const_iv[] = {
 #define const_iv(name) {#name, (IV)PURPLE_CERTIFICATE_##name}
-		const_iv(INVALID),
+		const_iv(UNKNOWN_ERROR),
 		const_iv(VALID),
+		const_iv(NON_FATALS_MASK),
+		const_iv(SELF_SIGNED),
+		const_iv(CA_UNKNOWN),
+		const_iv(NOT_ACTIVATED),
+		const_iv(EXPIRED),
+		const_iv(NAME_MISMATCH),
+		const_iv(NO_CA_POOL),
+		const_iv(FATALS_MASK),
+		const_iv(INVALID_CHAIN),
+		const_iv(REVOKED),
+		const_iv(REJECTED),
+		const_iv(LAST),
 	};
 
 	for (civ = const_iv + sizeof(const_iv) / sizeof(const_iv[0]); civ-- > const_iv; )
--- a/libpurple/plugins/perl/common/ImgStore.xs	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/perl/common/ImgStore.xs	Fri Jan 25 02:22:38 2013 -0500
@@ -4,13 +4,13 @@
 PROTOTYPES: ENABLE
 
 Purple::StoredImage
-purple_imgstore_add(data, size, filename)
+purple_imgstore_new(data, size, filename)
 	void *data
 	size_t size
 	const char *filename
 
 int
-purple_imgstore_add_with_id(data, size, filename)
+purple_imgstore_new_with_id(data, size, filename)
 	void *data
 	size_t size
 	const char *filename
--- a/libpurple/plugins/perl/common/Makefile.mingw	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/perl/common/Makefile.mingw	Fri Jan 25 02:22:38 2013 -0500
@@ -112,7 +112,7 @@
 	$(PERL) -MAutoSplit -e 'autosplit("lib/Purple.pm")'
 
 $(TARGET).dll: $(PURPLE_DLL).a $(PURPLE_PERL_DLL).a $(FALLBACKS) $(OBJECTS)
-	$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) -o $(TARGET).dll
+	$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll
 
 ##
 ## CLEAN
--- a/libpurple/plugins/perl/common/Pounce.xs	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/perl/common/Pounce.xs	Fri Jan 25 02:22:38 2013 -0500
@@ -121,9 +121,6 @@
 Purple::Handle
 purple_pounces_get_handle()
 
-gboolean
-purple_pounces_load()
-
 void
 purple_pounces_unregister_handler(ui)
 	const char *ui
--- a/libpurple/plugins/perl/common/Util.xs	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/perl/common/Util.xs	Fri Jan 25 02:22:38 2013 -0500
@@ -1,31 +1,5 @@
 #include "module.h"
 
-static void
-purple_perl_util_url_cb(PurpleUtilFetchUrlData *url_data, void *user_data,
-                        const gchar *url_text, size_t size,
-                        const gchar *error_message)
-{
-	SV *sv = (SV *)user_data;
-	dSP;
-	ENTER;
-	SAVETMPS;
-	PUSHMARK(SP);
-
-	XPUSHs(sv_2mortal(newSVpvn(url_text, size)));
-	PUTBACK;
-
-	call_sv(sv, G_EVAL | G_SCALAR);
-	SPAGAIN;
-
-	/* XXX Make sure this destroys it correctly and that we don't want
-	 * something like sv_2mortal(sv) or something else here instead. */
-	SvREFCNT_dec(sv);
-
-	PUTBACK;
-	FREETMPS;
-	LEAVE;
-}
-
 static void markup_find_tag_foreach(GQuark key_id, char *data, HV *hv) {
 	const char *key = NULL;
 	key = g_quark_to_string(key_id);
@@ -460,31 +434,6 @@
 MODULE = Purple::Util  PACKAGE = Purple::Util  PREFIX = purple_util_
 PROTOTYPES: ENABLE
 
- #XXX: expand...
-void
-purple_util_fetch_url(plugin, url, full, user_agent, http11, max_len, cb)
-	Purple::Plugin plugin
-	const char *url
-	gboolean full
-	const char *user_agent
-	gboolean http11
-	gssize max_len
-	SV * cb
-PREINIT:
-	PurpleUtilFetchUrlData *data;
-PPCODE:
-	/* XXX: i don't like this... only plugins can use it... */
-	SV *sv = purple_perl_sv_from_fun(plugin, cb);
-
-	if (sv != NULL) {
-		data = purple_util_fetch_url(url, full, user_agent, http11, max_len,
-		                      purple_perl_util_url_cb, sv);
-		XPUSHs(sv_2mortal(purple_perl_bless_object(data, "Purple::Util::FetchUrlData")));
-	} else {
-		purple_debug_warning("perl", "Callback not a valid type, only strings and coderefs allowed in purple_util_fetch_url.\n");
-		XSRETURN_UNDEF;
-	}
-
 void
 purple_util_set_user_dir(dir)
 	const char *dir
--- a/libpurple/plugins/perl/common/module.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/perl/common/module.h	Fri Jan 25 02:22:38 2013 -0500
@@ -271,7 +271,6 @@
 
 /* util.h */
 typedef PurpleInfoFieldFormatCallback	Purple__Util__InfoFieldFormatCallback;
-typedef PurpleUtilFetchUrlData	Purple__Util__FetchUrlData;
 typedef PurpleMenuAction *		Purple__Menu__Action;
 
 /* value.h */
--- a/libpurple/plugins/tcl/signal-test.tcl	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/tcl/signal-test.tcl	Fri Jan 25 02:22:38 2013 -0500
@@ -108,7 +108,7 @@
 	purple::debug -info "tcl signal" "plugin-unload [list $args]"
 }
 
-purple::signal connect [purple::savedstatuses handle] savedstatus-changed args {
+purple::signal connect [purple::savedstatus handle] savedstatus-changed args {
 	purple::debug -info "tcl signal" "savedstatus-changed [list $args]"
 	purple::debug -info "tcl signal" "purple::savedstatus current = [purple::savedstatus current]"
 }
--- a/libpurple/plugins/tcl/tcl.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/plugins/tcl/tcl.c	Fri Jan 25 02:22:38 2013 -0500
@@ -174,37 +174,17 @@
 	Tcl_Interp *interp;
 	Tcl_Parse parse;
 	Tcl_Obj *result, **listitems;
-	struct stat st;
-	FILE *fp;
-	char *buf, *cur;
+	char *buf;
 	const char *next;
-	int len, found = 0, err = 0, nelems;
+	int found = 0, err = 0, nelems;
+	gsize len;
 	gboolean status = FALSE;
-	if ((fp = g_fopen(plugin->path, "r")) == NULL)
-		return FALSE;
-	if (fstat(fileno(fp), &st)) {
-		fclose(fp);
+
+	if (!g_file_get_contents(plugin->path, &buf, &len, NULL)) {
+		purple_debug(PURPLE_DEBUG_INFO, "tcl", "Error opening plugin %s\n",
+			     plugin->path);
 		return FALSE;
 	}
-	len = st.st_size;
-
-	buf = g_malloc(len + 1);
-
-	cur = buf;
-	while (fgets(cur, GPOINTER_TO_INT(buf) - (buf - cur), fp)) {
-		cur += strlen(cur);
-		if (feof(fp))
-			break;
-	}
-
-	if (ferror(fp)) {
-		purple_debug(PURPLE_DEBUG_ERROR, "tcl", "error reading %s (%s)\n", plugin->path, g_strerror(errno));
-		g_free(buf);
-		fclose(fp);
-		return FALSE;
-	}
-
-	fclose(fp);
 
 	if ((interp = tcl_create_interp()) == NULL) {
 		return FALSE;
--- a/libpurple/pounce.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/pounce.c	Fri Jan 25 02:22:38 2013 -0500
@@ -572,7 +572,7 @@
 	NULL
 };
 
-gboolean
+static gboolean
 purple_pounces_load(void)
 {
 	gchar *filename = g_build_filename(purple_user_dir(), "pounces.xml", NULL);
@@ -1190,6 +1190,8 @@
 
 	purple_signal_connect(conv_handle, "received-im-msg",
 						handle, PURPLE_CALLBACK(received_message_cb), NULL);
+
+	purple_pounces_load();
 }
 
 void
--- a/libpurple/pounce.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/pounce.h	Fri Jan 25 02:22:38 2013 -0500
@@ -289,14 +289,6 @@
 PurplePounce *purple_find_pounce(const PurpleAccount *pouncer,
 							 const char *pouncee, PurplePounceEvent events);
 
-
-/**
- * Loads the pounces.
- *
- * @return @c TRUE if the pounces could be loaded.
- */
-gboolean purple_pounces_load(void);
-
 /**
  * Registers a pounce handler for a UI.
  *
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Fri Jan 25 02:22:38 2013 -0500
@@ -33,7 +33,7 @@
 static void
 bonjour_bytestreams_init(PurpleXfer *xfer);
 static void
-bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb);
+bonjour_bytestreams_connect(PurpleXfer *xfer);
 static void
 bonjour_xfer_init(PurpleXfer *xfer);
 static void
@@ -280,6 +280,25 @@
 	xep_iq_send_and_free(iq);
 }
 
+/**
+ * Frees the whole tree of an xml node
+ *
+ * First determines the root of the xml tree and then frees the whole tree
+ * from there.
+ *
+ * @param node	The node to free the tree from
+ */
+static void
+xmlnode_free_tree(xmlnode *node)
+{
+	g_return_if_fail(node != NULL);
+
+	while(xmlnode_get_parent(node))
+		node = xmlnode_get_parent(node);
+
+	xmlnode_free(node);
+}
+
 static void
 bonjour_free_xfer(PurpleXfer *xfer)
 {
@@ -310,6 +329,9 @@
 		g_free(xf->proxy_host);
 		g_free(xf->buddy_ip);
 		g_free(xf->sid);
+
+		xmlnode_free_tree(xf->streamhost);
+
 		g_free(xf);
 		purple_xfer_set_protocol_data(xfer, NULL);
 	}
@@ -441,7 +463,7 @@
 		if (si && (profile = xmlnode_get_attrib(si, "profile"))
 				&& !strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) {
 			const char *filename = NULL, *filesize_str = NULL;
-			int filesize = 0;
+			goffset filesize = 0;
 			xmlnode *file;
 
 			const char *sid = xmlnode_get_attrib(si, "id");
@@ -449,7 +471,7 @@
 			if ((file = xmlnode_get_child(si, "file"))) {
 				filename = xmlnode_get_attrib(file, "name");
 				if((filesize_str = xmlnode_get_attrib(file, "size")))
-					filesize = atoi(filesize_str);
+					filesize = g_ascii_strtoll(filesize_str, NULL, 10);
 			}
 
 			/* TODO: Make sure that it is advertising a bytestreams transfer */
@@ -547,19 +569,97 @@
 	return !strcmp(host, buddy_ip);
 }
 
+static inline gint
+xep_addr_differ(const char *buddy_ip, const char *host)
+{
+	return !xep_cmp_addr(host, buddy_ip);
+}
+
+/**
+ * Create and insert an identical twin
+ *
+ * Creates a copy of the specified node and inserts it right after
+ * this original node.
+ *
+ * @param node	The node to clone
+ * @return	A pointer to the new, cloned twin if successful
+ *		or NULL otherwise.
+ */
+static xmlnode *
+xmlnode_insert_twin_copy(xmlnode *node) {
+	xmlnode *copy;
+
+	g_return_val_if_fail(node != NULL, NULL);
+
+	copy = xmlnode_copy(node);
+	g_return_val_if_fail(copy != NULL, NULL);
+
+	copy->next = node->next;
+	node->next = copy;
+
+	return copy;
+}
+
+/**
+ * Tries to append an interface scope to an IPv6 link local address.
+ *
+ * If the given address is a link local IPv6 address (with no
+ * interface scope) then we try to determine all fitting interfaces
+ * from our Bonjour IP address list.
+ *
+ * For any such found matches we insert a copy of our current xml
+ * streamhost entry right after this streamhost entry and append
+ * the determined interface to the host address of this copy.
+ *
+ * @param cur_streamhost	The XML streamhost node we examine
+ * @param host	The host address to examine in text form
+ * @param pb	Buddy to get the list of link local IPv6 addresses
+ *		and their interface from
+ * @return	Returns TRUE if the specified 'host' address is a
+ *		link local IPv6 address with no interface scope.
+ *		Otherwise returns FALSE.
+ */
 static gboolean
-__xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, xmlnode *query,
+add_ipv6_link_local_ifaces(xmlnode *cur_streamhost, const char *host,
+			   const PurpleBuddy *pb) {
+	xmlnode *new_streamhost = NULL;
+	struct in6_addr in6_addr;
+	BonjourBuddy *bb;
+	GSList *ip_elem;
+
+	if (inet_pton(AF_INET6, host, &in6_addr) != 1 ||
+	    !IN6_IS_ADDR_LINKLOCAL(&in6_addr) ||
+	    strchr(host, '%'))
+		return FALSE;
+
+	bb = purple_buddy_get_protocol_data(pb);
+
+	for (ip_elem = bb->ips;
+	     (ip_elem = g_slist_find_custom(ip_elem, host, (GCompareFunc)&xep_addr_differ));
+	     ip_elem = ip_elem->next) {
+		purple_debug_info("bonjour", "Inserting an xmlnode twin copy for %s with new host address %s\n",
+				  host, (char*)ip_elem->data);
+		new_streamhost = xmlnode_insert_twin_copy(cur_streamhost);
+		xmlnode_set_attrib(new_streamhost, "host", ip_elem->data);
+	}
+
+	if (!new_streamhost)
+		purple_debug_info("bonjour", "No interface for this IPv6 link local address found: %s\n",
+				  host);
+
+	return TRUE;
+}
+
+static gboolean
+__xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, xmlnode *streamhost,
 			const char *iq_id)
 {
+	char *tmp_iq_id;
 	const char *jid, *host, *port;
 	int portnum;
-	xmlnode *streamhost;
 	XepXfer *xf = purple_xfer_get_protocol_data(xfer);
 
-	for(streamhost = xmlnode_get_child(query, "streamhost");
-			streamhost;
-			streamhost = xmlnode_get_next_twin(streamhost)) {
-
+	for(; streamhost; streamhost = xmlnode_get_next_twin(streamhost)) {
 		if(!(jid = xmlnode_get_attrib(streamhost, "jid")) ||
 		   !(host = xmlnode_get_attrib(streamhost, "host")) ||
 		   !(port = xmlnode_get_attrib(streamhost, "port")) ||
@@ -568,29 +668,36 @@
 			continue;
 		}
 
-		if(!xep_cmp_addr(host, xf->buddy_ip))
+		/* skip IPv6 link local addresses with no interface scope
+		 * (but try to add a new one with an interface scope then) */
+		if(add_ipv6_link_local_ifaces(streamhost, host, pb))
 			continue;
 
+		tmp_iq_id = g_strdup(iq_id);
 		g_free(xf->iq_id);
-		xf->iq_id = g_strdup(iq_id);
+		g_free(xf->jid);
+		g_free(xf->proxy_host);
+
+		xf->iq_id = tmp_iq_id;
 		xf->jid = g_strdup(jid);
-		xf->proxy_host = g_strdup(xf->buddy_ip);
+		xf->proxy_host = g_strdup(host);
 		xf->proxy_port = portnum;
+		xf->streamhost = streamhost;
+		xf->pb = pb;
 		purple_debug_info("bonjour", "bytestream offer parse"
 				  "jid=%s host=%s port=%d.\n", jid, host, portnum);
-		bonjour_bytestreams_connect(xfer, pb);
+		bonjour_bytestreams_connect(xfer);
 		return TRUE;
 	}
 
 	return FALSE;
 }
 
-
 void
 xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
 {
 	const char *type, *from, *iq_id, *sid;
-	xmlnode *query;
+	xmlnode *query, *streamhost;
 	BonjourData *bd;
 	PurpleXfer *xfer;
 
@@ -610,6 +717,10 @@
 	if(!type)
 		return;
 
+	query = xmlnode_copy(query);
+	if (!query)
+		return;
+
 	if(strcmp(type, "set")) {
 		purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type);
 		return;
@@ -621,7 +732,9 @@
 
 	sid = xmlnode_get_attrib(query, "sid");
 	xfer = bonjour_si_xfer_find(bd, sid, from);
-	if(xfer && __xep_bytestreams_parse(pb, xfer, query, iq_id))
+	streamhost = xmlnode_get_child(query, "streamhost");
+
+	if(xfer && streamhost && __xep_bytestreams_parse(pb, xfer, streamhost, iq_id))
 		return; /* success */
 
 	purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
@@ -864,15 +977,22 @@
 	XepIq *iq;
 	xmlnode *q_node, *tmp_node;
 	BonjourData *bd;
+	gboolean ret = FALSE;
 
 	xf->proxy_connection = NULL;
 
 	if(source < 0) {
-		purple_debug_error("bonjour", "Error connecting via SOCKS5 - %s\n",
-			error_message ? error_message : "(null)");
-		xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel");
-		/* Cancel the connection */
-		purple_xfer_cancel_local(xfer);
+		purple_debug_error("bonjour", "Error connecting via SOCKS5 to %s - %s\n",
+			xf->proxy_host, error_message ? error_message : "(null)");
+
+		tmp_node = xmlnode_get_next_twin(xf->streamhost);
+		ret = __xep_bytestreams_parse(xf->pb, xfer, tmp_node, xf->iq_id);
+
+		if (!ret) {
+			xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel");
+			/* Cancel the connection */
+			purple_xfer_cancel_local(xfer);
+		}
 		return;
 	}
 
@@ -894,8 +1014,9 @@
 }
 
 static void
-bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb)
+bonjour_bytestreams_connect(PurpleXfer *xfer)
 {
+	PurpleBuddy *pb;
 	PurpleAccount *account = NULL;
 	XepXfer *xf;
 	char dstaddr[41];
@@ -913,6 +1034,7 @@
 	if(!xf)
 		return;
 
+	pb = xf->pb;
 	name = purple_buddy_get_name(pb);
 	account = purple_buddy_get_account(pb);
 
--- a/libpurple/protocols/bonjour/bonjour_ft.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/bonjour/bonjour_ft.h	Fri Jan 25 02:22:38 2013 -0500
@@ -50,6 +50,8 @@
 	char *jid;
 	char *proxy_host;
 	int proxy_port;
+	xmlnode *streamhost;
+	PurpleBuddy *pb;
 };
 
 /**
--- a/libpurple/protocols/gg/avatar.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/gg/avatar.c	Fri Jan 25 02:22:38 2013 -0500
@@ -30,6 +30,8 @@
 #include "avatar.h"
 
 #include <debug.h>
+#include <glibcompat.h>
+#include <obsolete.h>
 
 #include "gg.h"
 #include "utils.h"
--- a/libpurple/protocols/gg/gg.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/gg/gg.c	Fri Jan 25 02:22:38 2013 -0500
@@ -696,8 +696,11 @@
 			}
 			break;
 		case GG_EVENT_CONN_FAILED:
-			purple_input_remove(info->inpa);
-			info->inpa = 0;
+			if (info->inpa > 0)
+			{
+				purple_input_remove(info->inpa);
+				info->inpa = 0;
+			}
 			purple_debug_info("gg", "Connection failure: %d\n",
 				ev->event.failure);
 			switch (ev->event.failure) {
--- a/libpurple/protocols/gg/image.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/gg/image.c	Fri Jan 25 02:22:38 2013 -0500
@@ -30,6 +30,7 @@
 #include "image.h"
 
 #include <debug.h>
+#include <glibcompat.h>
 
 #include "gg.h"
 #include "utils.h"
@@ -176,7 +177,7 @@
 	gchar *imgtag_replace;
 	GList *pending_messages_it;
 	
-	stored_id = purple_imgstore_add_with_id(
+	stored_id = purple_imgstore_new_with_id(
 		g_memdup(image_reply->image, image_reply->size),
 		image_reply->size,
 		image_reply->filename);
--- a/libpurple/protocols/gg/oauth/oauth-purple.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/gg/oauth/oauth-purple.c	Fri Jan 25 02:22:38 2013 -0500
@@ -34,6 +34,7 @@
 #include "../xml.h"
 
 #include <debug.h>
+#include <obsolete.h>
 
 #define GGP_OAUTH_RESPONSE_MAX 10240
 
--- a/libpurple/protocols/gg/pubdir-prpl.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/gg/pubdir-prpl.c	Fri Jan 25 02:22:38 2013 -0500
@@ -31,6 +31,7 @@
 
 #include <debug.h>
 #include <request.h>
+#include <obsolete.h>
 
 #include "oauth/oauth-purple.h"
 #include "xml.h"
--- a/libpurple/protocols/gg/roster.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/gg/roster.c	Fri Jan 25 02:22:38 2013 -0500
@@ -35,6 +35,7 @@
 #include "purplew.h"
 
 #include <debug.h>
+#include <glibcompat.h>
 
 #define GGP_ROSTER_SYNC_SETT "gg-synchronized"
 #define GGP_ROSTER_DEBUG 0
--- a/libpurple/protocols/gg/servconn.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/gg/servconn.c	Fri Jan 25 02:22:38 2013 -0500
@@ -32,6 +32,7 @@
 #include "utils.h"
 
 #include <debug.h>
+#include <glibcompat.h>
 
 #define GGP_SERVCONN_HISTORY_PREF "/plugins/prpl/gg/server_history"
 #define GGP_SERVCONN_HISTORY_MAXLEN 15
--- a/libpurple/protocols/gg/status.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/gg/status.c	Fri Jan 25 02:22:38 2013 -0500
@@ -132,6 +132,7 @@
 	if (status_message)
 	{
 		gchar *stripped = purple_markup_strip_html(status_message);
+		g_strstrip(stripped);
 		*message = ggp_status_validate_description(stripped);
 		g_free(stripped);
 	}
@@ -405,11 +406,16 @@
 	PurpleBuddy *buddy = purple_find_buddy(account, ggp_uin_to_str(uin));
 	const gchar *purple_status = ggp_status_to_purplestatus(status);
 	gchar *status_message = NULL;
+	gboolean is_own;
+	
+	is_own = 0 == g_strcmp0(ggp_uin_to_str(uin), purple_account_get_username(account));
 	
 	if (!buddy)
 	{
-		purple_debug_warning("gg", "ggp_status_got_others_buddy: "
-			"buddy %u not found\n", uin);
+		if (!is_own)
+			purple_debug_warning("gg",
+				"ggp_status_got_others_buddy: "
+				"buddy %u not found\n", uin);
 		return;
 	}
 	ggp_buddy_get_data(buddy)->blocked = (status == GG_STATUS_BLOCKED);
--- a/libpurple/protocols/irc/Makefile.mingw	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/irc/Makefile.mingw	Fri Jan 25 02:22:38 2013 -0500
@@ -54,6 +54,13 @@
 			-lintl \
 			-lpurple
 
+
+ifeq ($(CYRUS_SASL), 1)
+INCLUDE_PATHS += -I$(CYRUS_SASL_TOP)/include
+LIB_PATHS += -L$(CYRUS_SASL_TOP)/bin
+LIBS += -llibsasl
+endif
+
 include $(PIDGIN_COMMON_RULES)
 
 ##
--- a/libpurple/protocols/irc/irc.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/irc/irc.c	Fri Jan 25 02:22:38 2013 -0500
@@ -155,6 +155,7 @@
  	char *tosend= g_strdup(buf);
 
 	purple_signal_emit(_irc_plugin, "irc-sending-text", purple_account_get_connection(irc->account), &tosend);
+	
 	if (tosend == NULL)
 		return 0;
 
@@ -393,9 +394,17 @@
 	const char *username, *realname;
 	struct irc_conn *irc = purple_connection_get_protocol_data(gc);
 	const char *pass = purple_connection_get_password(gc);
+#ifdef HAVE_CYRUS_SASL
+	const gboolean use_sasl = purple_account_get_bool(irc->account, "sasl", FALSE);
+#endif
 
 	if (pass && *pass) {
-		buf = irc_format(irc, "v:", "PASS", pass);
+#ifdef HAVE_CYRUS_SASL
+		if (use_sasl)
+			buf = irc_format(irc, "vv:", "CAP", "REQ", "sasl");
+		else /* intended to fall through */
+#endif
+			buf = irc_format(irc, "v:", "PASS", pass);
 		if (irc_send(irc, buf) < 0) {
 			g_free(buf);
 			return FALSE;
@@ -529,6 +538,17 @@
 	g_free(irc->mode_chars);
 	g_free(irc->reqnick);
 
+#ifdef HAVE_CYRUS_SASL
+	if (irc->sasl_conn) {
+		sasl_dispose(&irc->sasl_conn);
+		irc->sasl_conn = NULL;
+	}
+	g_free(irc->sasl_cb);
+	if(irc->sasl_mechs)
+		g_string_free(irc->sasl_mechs, TRUE);
+#endif
+
+
 	g_free(irc);
 }
 
@@ -1047,6 +1067,16 @@
 	option = purple_account_option_bool_new(_("Use SSL"), "ssl", FALSE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
+#ifdef HAVE_CYRUS_SASL
+	option = purple_account_option_bool_new(_("Authenticate with SASL"), "sasl", FALSE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+	option = purple_account_option_bool_new(
+						_("Allow plaintext SASL auth over unencrypted connection"),
+						"auth_plain_in_clear", FALSE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+#endif
+
 	_irc_plugin = plugin;
 
 	purple_prefs_remove("/plugins/prpl/irc/quitmsg");
--- a/libpurple/protocols/irc/irc.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/irc/irc.h	Fri Jan 25 02:22:38 2013 -0500
@@ -25,6 +25,10 @@
 
 #include <glib.h>
 
+#ifdef HAVE_CYRUS_SASL
+#include <sasl/sasl.h>
+#endif
+
 #include "circbuffer.h"
 #include "ft.h"
 #include "roomlist.h"
@@ -93,6 +97,13 @@
 	char *mode_chars;
 	char *reqnick;
 	gboolean nickused;
+#ifdef HAVE_CYRUS_SASL
+	sasl_conn_t *sasl_conn;
+	const char *current_mech;
+	GString *sasl_mechs;
+	gboolean mech_works;
+	sasl_callback_t *sasl_cb;
+#endif
 };
 
 struct irc_buddy {
@@ -163,11 +174,19 @@
 void irc_msg_regonly(struct irc_conn *irc, const char *name, const char *from, char **args);
 void irc_msg_time(struct irc_conn *irc, const char *name, const char *from, char **args);
 void irc_msg_topic(struct irc_conn *irc, const char *name, const char *from, char **args);
+void irc_msg_topicinfo(struct irc_conn *irc, const char *name, const char *from, char **args);
 void irc_msg_unavailable(struct irc_conn *irc, const char *name, const char *from, char **args);
 void irc_msg_unknown(struct irc_conn *irc, const char *name, const char *from, char **args);
 void irc_msg_wallops(struct irc_conn *irc, const char *name, const char *from, char **args);
 void irc_msg_whois(struct irc_conn *irc, const char *name, const char *from, char **args);
 void irc_msg_who(struct irc_conn *irc, const char *name, const char *from, char **args);
+#ifdef HAVE_CYRUS_SASL
+void irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **args);
+void irc_msg_auth(struct irc_conn *irc, char *arg);
+void irc_msg_authok(struct irc_conn *irc, const char *name, const char *from, char **args);
+void irc_msg_authtryagain(struct irc_conn *irc, const char *name, const char *from, char **args);
+void irc_msg_authfail(struct irc_conn *irc, const char *name, const char *from, char **args);
+#endif
 
 void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args);
 
--- a/libpurple/protocols/irc/msgs.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/irc/msgs.c	Fri Jan 25 02:22:38 2013 -0500
@@ -32,6 +32,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#ifdef HAVE_CYRUS_SASL
+#include <sasl/sasl.h>
+#endif
+
 static char *irc_mask_nick(const char *mask);
 static char *irc_mask_userhost(const char *mask);
 static void irc_chat_remove_buddy(PurpleConversation *convo, char *data[2]);
@@ -42,6 +46,10 @@
                                    const char *from, const char *to,
                                    const char *rawmsg, gboolean notice);
 
+#ifdef HAVE_CYRUS_SASL
+static void irc_sasl_finish(struct irc_conn *irc);
+#endif
+
 static char *irc_mask_nick(const char *mask)
 {
 	char *end, *buf;
@@ -634,6 +642,38 @@
 	g_free(topic);
 }
 
+void irc_msg_topicinfo(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+	PurpleConversation *convo;
+	struct tm *tm;
+	time_t t;
+	char *msg, *timestamp, *datestamp;
+	
+	if (!args || !args[1] || !args[2] || !args[3])
+		return;
+
+	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account);
+	if (!convo) {
+		purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got topic info for %s, which doesn't exist\n", args[1]);
+		return;
+	}
+
+	t = (time_t)atol(args[3]);
+	if (t == 0) {
+		purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got apparently nonsensical topic timestamp %s\n", args[3]);
+		return;
+	}
+	tm = localtime(&t);
+
+	timestamp = g_strdup(purple_time_format(tm));
+	datestamp = g_strdup(purple_date_format_short(tm));
+	msg = g_strdup_printf("Topic for %s set by %s at %s on %s", args[1], args[2], timestamp, datestamp);
+	purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY, time(NULL));
+	g_free(timestamp);
+	g_free(datestamp);
+	g_free(msg);
+}
+
 void irc_msg_unknown(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
 	PurpleConnection *gc = purple_account_get_connection(irc->account);
@@ -1422,6 +1462,379 @@
 	g_free(msg);
 }
 
+#ifdef HAVE_CYRUS_SASL
+static int
+irc_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret)
+{
+	struct irc_conn *irc = ctx;
+	sasl_secret_t *sasl_secret;
+	const char *pw;
+	size_t len;
+
+	pw = purple_account_get_password(irc->account);
+
+	if (!conn || !secret || id != SASL_CB_PASS)
+		return SASL_BADPARAM;
+
+	len = strlen(pw);
+	/* Not an off-by-one because sasl_secret_t defines char data[1] */
+	/* TODO: This can probably be moved to glib's allocator */
+	sasl_secret = malloc(sizeof(sasl_secret_t) + len);
+	if (!sasl_secret)
+		return SASL_NOMEM;
+
+	sasl_secret->len = len;
+	strcpy((char*)sasl_secret->data, pw);
+
+	*secret = sasl_secret;
+	return SASL_OK;
+}
+
+static int
+irc_sasl_cb_log(void *context, int level, const char *message)
+{
+	if(level <= SASL_LOG_TRACE)
+		purple_debug_info("sasl", "%s\n", message);
+
+	return SASL_OK;
+}
+
+static int
+irc_sasl_cb_simple(void *ctx, int id, const char **res, unsigned *len)
+{
+	struct irc_conn *irc = ctx;
+	PurpleConnection *gc = purple_account_get_connection(irc->account);
+
+	switch(id) {
+		case SASL_CB_AUTHNAME:
+			*res = purple_connection_get_display_name(gc);
+			break;
+		case SASL_CB_USER:
+			*res = "";
+			break;
+		default:
+			return SASL_BADPARAM;
+	}
+	if (len) *len = strlen((char *)*res);
+	return SASL_OK;
+}
+
+static void
+irc_auth_start_cyrus(struct irc_conn *irc)
+{
+	int ret = 0;
+	char *buf;
+	sasl_security_properties_t secprops;
+	PurpleAccount *account = irc->account;
+	PurpleConnection *gc = purple_account_get_connection(account);
+
+	gboolean plaintext;
+	gboolean again = FALSE;
+
+	/* Set up security properties and options */
+	secprops.min_ssf = 0;
+	secprops.security_flags = SASL_SEC_NOANONYMOUS;
+
+	if (!irc->gsc) {
+		secprops.max_ssf = -1;
+		secprops.maxbufsize = 4096;
+		plaintext = purple_account_get_bool(account, "auth_plain_in_clear", FALSE);
+		if (!plaintext)
+			secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
+	} else {
+		secprops.max_ssf = 0;
+		secprops.maxbufsize = 0;
+		plaintext = TRUE;
+	}
+
+	secprops.property_names = 0;
+	secprops.property_values = 0;
+
+	do {
+		gchar *tmp = NULL;
+		again = FALSE;
+
+		ret = sasl_client_new("irc", irc->server, NULL, NULL, irc->sasl_cb, 0, &irc->sasl_conn);
+
+		if (ret != SASL_OK) {
+			purple_debug_error("irc", "sasl_client_new failed: %d\n", ret);
+			tmp = g_strdup_printf(_("Failed to initialize SASL authentication: %s"),
+				sasl_errdetail(irc->sasl_conn));
+			purple_connection_error_reason (gc,
+				PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
+			g_free(tmp);
+			return;
+		}
+
+		sasl_setprop(irc->sasl_conn, SASL_AUTH_EXTERNAL, irc->account->username);
+		sasl_setprop(irc->sasl_conn, SASL_SEC_PROPS, &secprops);
+
+		ret = sasl_client_start(irc->sasl_conn, irc->sasl_mechs->str, NULL, NULL, NULL, &irc->current_mech);
+
+		switch (ret) {
+			case SASL_OK:
+			case SASL_CONTINUE:
+				irc->mech_works = FALSE;
+				break;
+			case SASL_NOMECH:
+				purple_connection_error_reason (gc,
+					PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+					_("SASL authentication failed: No worthy authentication mechanisms found."));
+
+				irc_sasl_finish(irc);
+				return;
+			case SASL_BADPARAM:
+			case SASL_NOMEM:
+				tmp = g_strdup_printf(_("SASL authentication failed: %s"), sasl_errdetail(irc->sasl_conn));
+				purple_connection_error_reason (gc,
+					PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
+				g_free(tmp);
+
+				irc_sasl_finish(irc);
+				return;
+			default:
+				purple_debug_error("irc", "sasl_client_start failed: %s\n", sasl_errdetail(irc->sasl_conn));
+
+				if (irc->current_mech && *irc->current_mech) {
+					char *pos;
+					if ((pos = strstr(irc->sasl_mechs->str, irc->current_mech))) {
+						size_t index = pos - irc->sasl_mechs->str;
+						g_string_erase(irc->sasl_mechs, index, strlen(irc->current_mech));
+
+						/* Remove space which separated this mech from the next */
+						if ((irc->sasl_mechs->str)[index] == ' ') {
+							g_string_erase(irc->sasl_mechs, index, 1);
+						}
+					}
+
+					again = TRUE;
+				}
+				irc_sasl_finish(irc);
+		}
+	} while (again);
+
+	purple_debug_info("irc", "Using SASL: %s\n", irc->current_mech);
+
+	buf = irc_format(irc, "vv", "AUTHENTICATE", irc->current_mech);
+	irc_send(irc, buf);
+	g_free(buf);
+}
+
+/* SASL authentication */
+void
+irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+	int ret = 0;
+	int id = 0;
+	PurpleConnection *gc = purple_account_get_connection(irc->account);
+	const char *mech_list = NULL;
+
+	if (!args[1] || !args[2] || strncmp(args[2], "sasl ", 6))
+		return;
+	if (strncmp(args[1], "ACK", 4)) {
+		const char *tmp = _("SASL authentication failed: Server does not support SASL authentication.");
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, tmp);
+
+		irc_sasl_finish(irc);
+		return;
+	}
+
+	if ((ret = sasl_client_init(NULL)) != SASL_OK) {
+		const char *tmp = _("SASL authentication failed: Initializing SASL failed.");
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
+		return;
+	}
+
+	irc->sasl_cb = g_new0(sasl_callback_t, 5);
+
+	irc->sasl_cb[id].id = SASL_CB_AUTHNAME;
+	irc->sasl_cb[id].proc = irc_sasl_cb_simple;
+	irc->sasl_cb[id].context = (void *)irc;
+	id++;
+
+	irc->sasl_cb[id].id = SASL_CB_USER;
+	irc->sasl_cb[id].proc = irc_sasl_cb_simple;
+	irc->sasl_cb[id].context = (void *)irc;
+	id++;
+
+	irc->sasl_cb[id].id = SASL_CB_PASS;
+	irc->sasl_cb[id].proc = irc_sasl_cb_secret;
+	irc->sasl_cb[id].context = (void *)irc;
+	id++;
+
+	irc->sasl_cb[id].id = SASL_CB_LOG;
+	irc->sasl_cb[id].proc = irc_sasl_cb_log;
+	irc->sasl_cb[id].context = (void *)irc;
+	id++;
+
+	irc->sasl_cb[id].id = SASL_CB_LIST_END;
+
+	/* We need to do this to be able to list the mechanisms. */
+	ret = sasl_client_new("irc", irc->server, NULL, NULL, irc->sasl_cb, 0, &irc->sasl_conn);
+
+	sasl_listmech(irc->sasl_conn, NULL, "", " ", "", &mech_list, NULL, NULL);
+	purple_debug_info("irc", "SASL: we have available: %s\n", mech_list);
+
+	if (ret != SASL_OK) {
+		gchar *tmp;
+
+		purple_debug_error("irc", "sasl_client_new failed: %d\n", ret);
+		tmp = g_strdup_printf(_("Failed to initialize SASL authentication: %s"),
+			sasl_errdetail(irc->sasl_conn));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
+		g_free(tmp);
+
+		return;
+	}
+
+	irc->sasl_mechs = g_string_new(mech_list);
+
+	irc_auth_start_cyrus(irc);
+}
+
+void
+irc_msg_auth(struct irc_conn *irc, char *arg)
+{
+	PurpleConnection *gc = purple_account_get_connection(irc->account);
+	char *buf, *authinfo;
+	char *serverin = NULL;
+	unsigned serverinlen = 0;
+	const gchar *c_out;
+	unsigned int clen;
+	int ret;
+
+	irc->mech_works = TRUE;
+
+	if (!arg)
+		return;
+
+	if (arg[0] != '+') {
+		serverin = arg;
+		serverinlen = strlen(serverin);
+	}
+
+	ret = sasl_client_step(irc->sasl_conn, serverin, serverinlen,
+		NULL, &c_out, &clen);
+
+	if (ret != SASL_OK && ret != SASL_CONTINUE) {
+
+		gchar *tmp = g_strdup_printf(_("SASL authentication failed: %s"),
+			sasl_errdetail(irc->sasl_conn));
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, tmp);
+		g_free(tmp);
+
+		irc_sasl_finish(irc);
+
+		return;
+	}
+
+	authinfo = purple_base64_encode((const guchar*)c_out, clen);
+
+	buf = irc_format(irc, "vv", "AUTHENTICATE", authinfo);
+	irc_send(irc, buf);
+	g_free(buf);
+	g_free(authinfo);
+}
+
+void
+irc_msg_authok(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+	char *buf;
+
+	sasl_dispose(&irc->sasl_conn);
+	irc->sasl_conn = NULL;
+	purple_debug_info("irc", "Succesfully authenticated using SASL.\n");
+
+	/* Finish auth session */
+	buf = irc_format(irc, "vv", "CAP", "END");
+	irc_send(irc, buf);
+	g_free(buf);
+}
+
+void
+irc_msg_authtryagain(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+	PurpleConnection *gc = purple_account_get_connection(irc->account);
+
+	/* We already received at least one AUTHENTICATE reply from the
+	 * server. This suggests it supports this mechanism, but the
+	 * password was incorrect. It would be better to abort and inform
+	 * the user than to try again with a different mechanism, so they
+	 * aren't told the server supports no worthy mechanisms.
+	 */
+	if (irc->mech_works) {
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect Password"));
+
+		irc_sasl_finish(irc);
+
+		return;
+	}
+
+	if (irc->current_mech) {
+		char *pos;
+		if ((pos = strstr(irc->sasl_mechs->str, irc->current_mech))) {
+			size_t index = pos - irc->sasl_mechs->str;
+			g_string_erase(irc->sasl_mechs, index, strlen(irc->current_mech));
+
+			/* Remove space which separated this mech from the next */
+			if ((irc->sasl_mechs->str)[index] == ' ') {
+				g_string_erase(irc->sasl_mechs, index, 1);
+			}
+		}
+	}
+	if (*irc->sasl_mechs->str) {
+		sasl_dispose(&irc->sasl_conn);
+
+		purple_debug_info("irc", "Now trying with %s\n", irc->sasl_mechs->str);
+		irc_auth_start_cyrus(irc);
+	} else {
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+			_("SASL authentication failed: No worthy mechanisms found"));
+
+		irc_sasl_finish(irc);
+	}
+}
+
+void
+irc_msg_authfail(struct irc_conn *irc, const char *name, const char *from, char **args)
+{
+	PurpleConnection *gc = purple_account_get_connection(irc->account);
+
+	/* Only show an error if we did not abort ourselves. */
+	if (irc->sasl_conn) {
+		purple_debug_info("irc", "SASL authentication failed: %s", sasl_errdetail(irc->sasl_conn));
+
+		purple_connection_error_reason (gc,
+			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect Password"));
+	}
+
+	irc_sasl_finish(irc);
+}
+
+static void
+irc_sasl_finish(struct irc_conn *irc)
+{
+	char *buf;
+
+	sasl_dispose(&irc->sasl_conn);
+	irc->sasl_conn = NULL;
+
+	g_free(irc->sasl_cb);
+	irc->sasl_cb = NULL;
+
+	/* Auth failed, abort */
+	buf = irc_format(irc, "vv", "CAP", "END");
+	irc_send(irc, buf);
+	g_free(buf);
+}
+#endif
+
 void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args)
 {
 	return;
--- a/libpurple/protocols/irc/parse.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/irc/parse.c	Fri Jan 25 02:22:38 2013 -0500
@@ -73,7 +73,7 @@
 	{ "324", "ncv:", irc_msg_chanmode },	/* Channel modes		*/
 	{ "331", "nc:",	irc_msg_topic },	/* No channel topic		*/
 	{ "332", "nc:", irc_msg_topic },	/* Channel topic		*/
-	{ "333", "*", irc_msg_ignore },		/* Topic setter stuff		*/
+	{ "333", "ncvv", irc_msg_topicinfo },	/* Topic setter stuff		*/
 	{ "352", "ncvvvnv:", irc_msg_who },	/* Channel WHO			*/
 	{ "353", "nvc:", irc_msg_names },	/* Names list			*/
 	{ "366", "nc:", irc_msg_names },	/* End of names			*/
@@ -102,6 +102,14 @@
 	{ "501", "n:", irc_msg_badmode },	/* Unknown mode flag		*/
 	{ "506", "nc:", irc_msg_nosend },	/* Must identify to send	*/
 	{ "515", "nc:", irc_msg_regonly },	/* Registration required	*/
+#ifdef HAVE_CYRUS_SASL
+	{ "903", "*", irc_msg_authok},		/* SASL auth successful		*/
+	{ "904", "*", irc_msg_authtryagain },	/* SASL auth failed, can recover		*/
+	{ "905", "*", irc_msg_authfail },	/* SASL auth failed		*/
+	{ "906", "*", irc_msg_authfail },	/* SASL auth failed		*/
+	{ "907", "*", irc_msg_authfail },	/* SASL auth failed		*/
+	{ "cap", "vv:", irc_msg_cap },		/* SASL capable			*/
+#endif
 	{ "invite", "n:", irc_msg_invite },	/* Invited			*/
 	{ "join", ":", irc_msg_join },		/* Joined a channel		*/
 	{ "kick", "cn:", irc_msg_kick },	/* KICK				*/
@@ -678,6 +686,11 @@
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Disconnected."));
 		return;
+#ifdef HAVE_CYRUS_SASL
+	} else if (!strncmp(input, "AUTHENTICATE ", 13)) {
+		irc_msg_auth(irc, input + 13);
+		return;
+#endif
 	}
 
 	if (input[0] != ':' || (cur = strchr(input, ' ')) == NULL) {
--- a/libpurple/protocols/jabber/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -31,6 +31,8 @@
 			  google/gmail.h \
 			  google/google.c \
 			  google/google.h \
+			  google/google_p2p.c \
+			  google/google_p2p.h \
 			  google/google_presence.c \
 			  google/google_presence.h \
 			  google/google_roster.c \
@@ -113,8 +115,7 @@
 libjabber_la_SOURCES = $(JABBERSOURCES)
 libjabber_la_LIBADD  = $(GLIB_LIBS) $(SASL_LIBS) $(LIBXML_LIBS) $(IDN_LIBS)\
 	$(FARSIGHT_LIBS) \
-	$(GSTREAMER_LIBS) \
-	$(GSTINTERFACES_LIBS)
+	$(GSTREAMER_LIBS)
 
 libfacebook_la_SOURCES = libfacebook.c
 libfacebook_la_LIBADD = libjabber.la
@@ -135,5 +136,4 @@
 	$(IDN_CFLAGS) \
 	$(LIBXML_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
-	$(GSTREAMER_CFLAGS) \
-	$(GSTINTERFACES_CFLAGS)
+	$(GSTREAMER_CFLAGS)
--- a/libpurple/protocols/jabber/Makefile.mingw	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/Makefile.mingw	Fri Jan 25 02:22:38 2013 -0500
@@ -113,21 +113,9 @@
 			-lpurple
 
 ifeq ($(CYRUS_SASL), 1)
-CYRUS_SASL_TOP := $(WIN32_DEV_TOP)/cyrus-sasl-2.1.22-daa1
 INCLUDE_PATHS += -I$(CYRUS_SASL_TOP)/include
 LIB_PATHS += -L$(CYRUS_SASL_TOP)/bin
 LIBS += -llibsasl
-CYRUS_SASL_DLLS = \
-			$(CYRUS_SASL_TOP)/bin/libsasl.dll
-
-CYRUS_SASL_PLUGINS = \
-			$(CYRUS_SASL_TOP)/bin/sasl2/saslANONYMOUS.dll \
-			$(CYRUS_SASL_TOP)/bin/sasl2/saslCRAMMD5.dll \
-			$(CYRUS_SASL_TOP)/bin/sasl2/saslDIGESTMD5.dll \
-			$(CYRUS_SASL_TOP)/bin/sasl2/saslGSSAPI.dll \
-			$(CYRUS_SASL_TOP)/bin/sasl2/saslLOGIN.dll \
-			$(CYRUS_SASL_TOP)/bin/sasl2/saslPLAIN.dll
-
 endif
 
 include $(PIDGIN_COMMON_RULES)
@@ -144,11 +132,6 @@
 	cp $(GTALK_TARGET).dll $(DLL_INSTALL_DIR)
 	cp $(XMPP_TARGET).dll $(DLL_INSTALL_DIR)
 	cp $(TARGET).dll $(PURPLE_INSTALL_DIR)
-ifeq ($(CYRUS_SASL), 1)
-	mkdir -p $(PURPLE_INSTALL_DIR)/sasl2
-	cp $(CYRUS_SASL_DLLS) $(PURPLE_INSTALL_DIR)
-	cp $(CYRUS_SASL_PLUGINS) $(PURPLE_INSTALL_DIR)/sasl2
-endif
 
 $(OBJECTS): $(PURPLE_CONFIG_H)
 
--- a/libpurple/protocols/jabber/buddy.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/buddy.c	Fri Jan 25 02:22:38 2013 -0500
@@ -1203,7 +1203,7 @@
 						char *img_text;
 						char *hash;
 
-						jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(purple_imgstore_add_with_id(g_memdup(data, size), size, "logo.png")));
+						jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(purple_imgstore_new_with_id(g_memdup(data, size), size, "logo.png")));
 						img_text = g_strdup_printf("<img src='" PURPLE_STORED_IMAGE_PROTOCOL "%d'>",
 						                           GPOINTER_TO_INT(jbi->vcard_imgids->data));
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/google/google_p2p.c	Fri Jan 25 02:22:38 2013 -0500
@@ -0,0 +1,442 @@
+/**
+ * @file google_p2p.c
+ *
+ * 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 "internal.h"
+
+#include "google_p2p.h"
+#include "jingle/jingle.h"
+#include "debug.h"
+
+#include <string.h>
+
+struct _JingleGoogleP2PPrivate
+{
+	GList *local_candidates;
+	GList *remote_candidates;
+};
+
+#define JINGLE_GOOGLE_P2P_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2PPrivate))
+
+static void jingle_google_p2p_class_init (JingleGoogleP2PClass *klass);
+static void jingle_google_p2p_init (JingleGoogleP2P *google_p2p);
+static void jingle_google_p2p_finalize (GObject *object);
+static void jingle_google_p2p_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void jingle_google_p2p_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static JingleTransport *jingle_google_p2p_parse_internal(xmlnode *google_p2p);
+static xmlnode *jingle_google_p2p_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action);
+static void jingle_google_p2p_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+static GList *jingle_google_p2p_get_remote_candidates(JingleTransport *transport);
+
+static JingleTransportClass *parent_class = NULL;
+
+enum {
+	PROP_0,
+	PROP_LOCAL_CANDIDATES,
+	PROP_REMOTE_CANDIDATES,
+};
+
+static JingleGoogleP2PCandidate *
+jingle_google_p2p_candidate_copy(JingleGoogleP2PCandidate *candidate)
+{
+	JingleGoogleP2PCandidate *new_candidate = g_new0(JingleGoogleP2PCandidate, 1);
+	new_candidate->id = g_strdup(candidate->id);
+	new_candidate->address = g_strdup(candidate->address);
+	new_candidate->port = candidate->port;
+	new_candidate->preference = candidate->preference;
+	new_candidate->type = g_strdup(candidate->type);
+	new_candidate->protocol = g_strdup(candidate->protocol);
+	new_candidate->username = g_strdup(candidate->username);
+	new_candidate->password = g_strdup(candidate->password);
+	new_candidate->generation = candidate->generation;
+
+	new_candidate->rem_known = candidate->rem_known;
+
+	return new_candidate;
+}
+
+static void
+jingle_google_p2p_candidate_free(JingleGoogleP2PCandidate *candidate)
+{
+	g_free(candidate->id);
+	g_free(candidate->address);
+	g_free(candidate->type);
+	g_free(candidate->protocol);
+	g_free(candidate->username);
+	g_free(candidate->password);
+}
+
+GType
+jingle_google_p2p_candidate_get_type(void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		type = g_boxed_type_register_static("JingleGoogleP2PCandidate",
+				(GBoxedCopyFunc)jingle_google_p2p_candidate_copy,
+				(GBoxedFreeFunc)jingle_google_p2p_candidate_free);
+	}
+	return type;
+}
+
+JingleGoogleP2PCandidate *
+jingle_google_p2p_candidate_new(const gchar *id, guint generation,
+		const gchar *address, guint port, guint preference,
+		const gchar *type, const gchar *protocol,
+		const gchar *username, const gchar *password)
+{
+	JingleGoogleP2PCandidate *candidate = g_new0(JingleGoogleP2PCandidate, 1);
+	candidate->id = g_strdup(id);
+	candidate->address = g_strdup(address);
+	candidate->port = port;
+	candidate->preference = preference;
+	candidate->type = g_strdup(type);
+	candidate->protocol = g_strdup(protocol);
+	candidate->username = g_strdup(username);
+	candidate->password = g_strdup(password);
+	candidate->generation = generation;
+
+	candidate->rem_known = FALSE;
+	return candidate;
+}
+
+GType
+jingle_google_p2p_get_type(void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(JingleGoogleP2PClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) jingle_google_p2p_class_init,
+			NULL,
+			NULL,
+			sizeof(JingleGoogleP2P),
+			0,
+			(GInstanceInitFunc) jingle_google_p2p_init,
+			NULL
+		};
+		type = g_type_register_static(JINGLE_TYPE_TRANSPORT, "JingleGoogleP2P", &info, 0);
+	}
+	return type;
+}
+
+static void
+jingle_google_p2p_class_init(JingleGoogleP2PClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass *)klass;
+	parent_class = g_type_class_peek_parent(klass);
+
+	gobject_class->finalize = jingle_google_p2p_finalize;
+	gobject_class->set_property = jingle_google_p2p_set_property;
+	gobject_class->get_property = jingle_google_p2p_get_property;
+	klass->parent_class.to_xml = jingle_google_p2p_to_xml_internal;
+	klass->parent_class.parse = jingle_google_p2p_parse_internal;
+	klass->parent_class.transport_type = NS_GOOGLE_TRANSPORT_P2P;
+	klass->parent_class.add_local_candidate = jingle_google_p2p_add_local_candidate;
+	klass->parent_class.get_remote_candidates = jingle_google_p2p_get_remote_candidates;
+
+	g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES,
+			g_param_spec_pointer("local-candidates",
+			"Local candidates",
+			"The local candidates for this transport.",
+			G_PARAM_READABLE));
+
+	g_object_class_install_property(gobject_class, PROP_REMOTE_CANDIDATES,
+			g_param_spec_pointer("remote-candidates",
+			"Remote candidates",
+			"The remote candidates for this transport.",
+			G_PARAM_READABLE));
+
+	g_type_class_add_private(klass, sizeof(JingleGoogleP2PPrivate));
+}
+
+static void
+jingle_google_p2p_init(JingleGoogleP2P *google_p2p)
+{
+	google_p2p->priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(google_p2p);
+	google_p2p->priv->local_candidates = NULL;
+	google_p2p->priv->remote_candidates = NULL;
+}
+
+static void
+jingle_google_p2p_finalize(GObject *google_p2p)
+{
+/*	JingleGoogleP2PPrivate *priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(google_p2p); */
+	purple_debug_info("jingle","jingle_google_p2p_finalize\n");
+
+	G_OBJECT_CLASS(parent_class)->finalize(google_p2p);
+}
+
+static void
+jingle_google_p2p_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	JingleGoogleP2P *google_p2p;
+
+	g_return_if_fail(object != NULL);
+	g_return_if_fail(JINGLE_IS_GOOGLE_P2P(object));
+
+	google_p2p = JINGLE_GOOGLE_P2P(object);
+
+	switch (prop_id) {
+		case PROP_LOCAL_CANDIDATES:
+			google_p2p->priv->local_candidates = g_value_get_pointer(value);
+			break;
+		case PROP_REMOTE_CANDIDATES:
+			google_p2p->priv->remote_candidates = g_value_get_pointer(value);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+jingle_google_p2p_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	JingleGoogleP2P *google_p2p;
+
+	g_return_if_fail(object != NULL);
+	g_return_if_fail(JINGLE_IS_GOOGLE_P2P(object));
+
+	google_p2p = JINGLE_GOOGLE_P2P(object);
+
+	switch (prop_id) {
+		case PROP_LOCAL_CANDIDATES:
+			g_value_set_pointer(value, google_p2p->priv->local_candidates);
+			break;
+		case PROP_REMOTE_CANDIDATES:
+			g_value_set_pointer(value, google_p2p->priv->remote_candidates);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+jingle_google_p2p_add_local_candidate(JingleTransport *transport, const gchar *id,
+                                      guint generation, PurpleMediaCandidate *candidate)
+{
+	JingleGoogleP2P *google_p2p = JINGLE_GOOGLE_P2P(transport);
+	JingleGoogleP2PCandidate *google_p2p_candidate;
+	gchar *ip;
+	gchar *username;
+	gchar *password;
+	PurpleMediaCandidateType type;
+	PurpleMediaNetworkProtocol protocol;
+	GList *iter;
+
+	ip = purple_media_candidate_get_ip(candidate);
+	username = purple_media_candidate_get_username(candidate);
+	password = purple_media_candidate_get_password(candidate);
+	type = purple_media_candidate_get_candidate_type(candidate);
+	protocol = purple_media_candidate_get_protocol(candidate);
+
+	google_p2p_candidate = jingle_google_p2p_candidate_new(id, generation,
+			ip, purple_media_candidate_get_port(candidate),
+			purple_media_candidate_get_priority(candidate),
+			type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" :
+			type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" :
+			type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" :
+			type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" :
+			"",
+			protocol == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP ? "udp" : "tcp",
+			username, password);
+
+	g_free(password);
+	g_free(username);
+	g_free(ip);
+
+	for (iter = google_p2p->priv->local_candidates; iter; iter = g_list_next(iter)) {
+		JingleGoogleP2PCandidate *c = iter->data;
+		if (!strcmp(c->id, id)) {
+			generation = c->generation + 1;
+
+			g_boxed_free(JINGLE_TYPE_GOOGLE_P2P_CANDIDATE, c);
+			google_p2p->priv->local_candidates = g_list_delete_link(
+					google_p2p->priv->local_candidates, iter);
+
+			google_p2p_candidate->generation = generation;
+
+			google_p2p->priv->local_candidates = g_list_append(
+					google_p2p->priv->local_candidates, candidate);
+			return;
+		}
+	}
+
+	google_p2p->priv->local_candidates = g_list_append(
+			google_p2p->priv->local_candidates, google_p2p_candidate);
+}
+
+static GList *
+jingle_google_p2p_get_remote_candidates(JingleTransport *transport)
+{
+	JingleGoogleP2P *google_p2p = JINGLE_GOOGLE_P2P(transport);
+	GList *candidates = google_p2p->priv->remote_candidates;
+	GList *ret = NULL;
+
+	for (; candidates; candidates = g_list_next(candidates)) {
+		JingleGoogleP2PCandidate *candidate = candidates->data;
+		PurpleMediaCandidate *new_candidate = purple_media_candidate_new("", 0,
+				!strcmp(candidate->type, "host") ?
+					PURPLE_MEDIA_CANDIDATE_TYPE_HOST :
+					!strcmp(candidate->type, "srflx") ?
+						PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX :
+						!strcmp(candidate->type, "prflx") ?
+							PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX :
+							!strcmp(candidate->type, "relay") ?
+								PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0,
+				!strcmp(candidate->protocol, "udp") ?
+					PURPLE_MEDIA_NETWORK_PROTOCOL_UDP :
+					PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
+				candidate->address, candidate->port);
+		g_object_set(new_candidate,
+		             "username", candidate->username,
+		             "password", candidate->password,
+		             "priority", candidate->preference,
+		             NULL);
+		ret = g_list_append(ret, new_candidate);
+	}
+
+	return ret;
+}
+
+static JingleGoogleP2PCandidate *
+jingle_google_p2p_get_remote_candidate_by_id(JingleGoogleP2P *google_p2p,
+		const gchar *id)
+{
+	GList *iter = google_p2p->priv->remote_candidates;
+	for (; iter; iter = g_list_next(iter)) {
+		JingleGoogleP2PCandidate *candidate = iter->data;
+		if (!strcmp(candidate->id, id)) {
+			return candidate;
+		}
+	}
+	return NULL;
+}
+
+static void
+jingle_google_p2p_add_remote_candidate(JingleGoogleP2P *google_p2p, JingleGoogleP2PCandidate *candidate)
+{
+	JingleGoogleP2PPrivate *priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(google_p2p);
+	JingleGoogleP2PCandidate *google_p2p_candidate =
+			jingle_google_p2p_get_remote_candidate_by_id(google_p2p,
+					candidate->id);
+	if (google_p2p_candidate != NULL) {
+		priv->remote_candidates = g_list_remove(priv->remote_candidates,
+		                                        google_p2p_candidate);
+		g_boxed_free(JINGLE_TYPE_GOOGLE_P2P_CANDIDATE, google_p2p_candidate);
+	}
+	priv->remote_candidates = g_list_append(priv->remote_candidates, candidate);
+}
+
+static JingleTransport *
+jingle_google_p2p_parse_internal(xmlnode *google_p2p)
+{
+	JingleTransport *transport = parent_class->parse(google_p2p);
+	xmlnode *candidate = xmlnode_get_child(google_p2p, "candidate");
+	JingleGoogleP2PCandidate *google_p2p_candidate = NULL;
+
+	for (; candidate; candidate = xmlnode_get_next_twin(candidate)) {
+		const gchar *generation = xmlnode_get_attrib(candidate, "generation");
+		const gchar *id = xmlnode_get_attrib(candidate, "name");
+		const gchar *address = xmlnode_get_attrib(candidate, "address");
+		const gchar *port = xmlnode_get_attrib(candidate, "port");
+		const gchar *preference = xmlnode_get_attrib(candidate, "preference");
+		const gchar *type = xmlnode_get_attrib(candidate, "type");
+		const gchar *protocol = xmlnode_get_attrib(candidate, "protocol");
+		const gchar *username = xmlnode_get_attrib(candidate, "username");
+		const gchar *password = xmlnode_get_attrib(candidate, "password");
+
+		if (!generation || !id || !address || !port || !preference ||
+				!type || !protocol || !username || !password)
+			continue;
+
+		google_p2p_candidate = jingle_google_p2p_candidate_new(id,
+				atoi(generation),
+				address,
+				atoi(port),
+				atoi(preference),
+				type,
+				protocol,
+				username, password);
+		google_p2p_candidate->rem_known = TRUE;
+		jingle_google_p2p_add_remote_candidate(JINGLE_GOOGLE_P2P(transport), google_p2p_candidate);
+	}
+
+	return transport;
+}
+
+static xmlnode *
+jingle_google_p2p_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action)
+{
+	xmlnode *node = parent_class->to_xml(transport, content, action);
+
+	if (action == JINGLE_SESSION_INITIATE ||
+			action == JINGLE_SESSION_ACCEPT ||
+			action == JINGLE_TRANSPORT_INFO ||
+			action == JINGLE_CONTENT_ADD ||
+			action == JINGLE_TRANSPORT_REPLACE) {
+		JingleGoogleP2PPrivate *priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(transport);
+		GList *iter = priv->local_candidates;
+
+		for (; iter; iter = g_list_next(iter)) {
+			JingleGoogleP2PCandidate *candidate = iter->data;
+			xmlnode *xmltransport;
+			gchar *generation, *network, *port, *preference;
+
+			if (candidate->rem_known == TRUE)
+				continue;
+
+			candidate->rem_known = TRUE;
+
+			xmltransport = xmlnode_new_child(node, "candidate");
+			generation = g_strdup_printf("%d", candidate->generation);
+			network = g_strdup_printf("%d", candidate->network);
+			port = g_strdup_printf("%d", candidate->port);
+			preference = g_strdup_printf("%d", candidate->preference);
+
+			xmlnode_set_attrib(xmltransport, "generation", generation);
+			xmlnode_set_attrib(xmltransport, "name", candidate->id);
+			xmlnode_set_attrib(xmltransport, "address", candidate->address);
+			xmlnode_set_attrib(xmltransport, "network", network);
+			xmlnode_set_attrib(xmltransport, "port", port);
+			xmlnode_set_attrib(xmltransport, "preference", preference);
+			xmlnode_set_attrib(xmltransport, "protocol", candidate->protocol);
+			xmlnode_set_attrib(xmltransport, "type", candidate->type);
+			xmlnode_set_attrib(xmltransport, "username", candidate->username);
+			xmlnode_set_attrib(xmltransport, "password", candidate->password);
+
+			g_free(generation);
+			g_free(network);
+			g_free(port);
+			g_free(preference);
+		}
+	}
+
+	return node;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/google/google_p2p.h	Fri Jan 25 02:22:38 2013 -0500
@@ -0,0 +1,102 @@
+/**
+ * @file google_p2p.h
+ *
+ * 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 PURPLE_JABBER_JINGLE_GOOGLE_P2P_H
+#define PURPLE_JABBER_JINGLE_GOOGLE_P2P_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "jingle/transport.h"
+
+G_BEGIN_DECLS
+
+#define JINGLE_TYPE_GOOGLE_P2P            (jingle_google_p2p_get_type())
+#define JINGLE_TYPE_GOOGLE_P2P_CANDIDATE  (jingle_google_p2p_candidate_get_type())
+#define JINGLE_GOOGLE_P2P(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2P))
+#define JINGLE_GOOGLE_P2P_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2PClass))
+#define JINGLE_IS_GOOGLE_P2P(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), JINGLE_TYPE_GOOGLE_P2P))
+#define JINGLE_IS_GOOGLE_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JINGLE_TYPE_GOOGLE_P2P))
+#define JINGLE_GOOGLE_P2P_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2PClass))
+
+/** @copydoc _JingleGoogleP2P */
+typedef struct _JingleGoogleP2P JingleGoogleP2P;
+/** @copydoc _JingleGoogleP2PClass */
+typedef struct _JingleGoogleP2PClass JingleGoogleP2PClass;
+/** @copydoc _JingleGoogleP2PPrivate */
+typedef struct _JingleGoogleP2PPrivate JingleGoogleP2PPrivate;
+/** @copydoc _JingleGoogleP2PCandidate */
+typedef struct _JingleGoogleP2PCandidate JingleGoogleP2PCandidate;
+
+/** The Google P2P class */
+struct _JingleGoogleP2PClass
+{
+	JingleTransportClass parent_class;  /**< The parent class. */
+
+	xmlnode *(*to_xml) (JingleTransport *transport, xmlnode *content, JingleActionType action);
+	JingleTransport *(*parse) (xmlnode *transport);
+};
+
+/** The Google P2P class's private data */
+struct _JingleGoogleP2P
+{
+	JingleTransport parent;         /**< The parent of this object. */
+	JingleGoogleP2PPrivate *priv;   /**< The private data of this object. */
+};
+
+struct _JingleGoogleP2PCandidate
+{
+	gchar *id;
+	gchar *address;
+	guint port;
+	guint preference;
+	gchar *type;
+	gchar *protocol;
+	guint network;
+	gchar *username;
+	gchar *password;
+	guint generation;
+
+	gboolean rem_known; /* TRUE if the remote side knows
+	                     * about this candidate */
+};
+
+GType jingle_google_p2p_candidate_get_type(void);
+
+/**
+ * Gets the Google P2P class's GType
+ *
+ * @return The Google P2P class's GType.
+ */
+GType jingle_google_p2p_get_type(void);
+
+JingleGoogleP2PCandidate *jingle_google_p2p_candidate_new(const gchar *id,
+		guint generation, const gchar *address, guint port, guint preference,
+		const gchar *type, const gchar *protocol,
+		const gchar *username, const gchar *password);
+
+G_END_DECLS
+
+#endif /* PURPLE_JABBER_JINGLE_GOOGLE_P2P_H */
+
--- a/libpurple/protocols/jabber/google/relay.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/google/relay.c	Fri Jan 25 02:22:38 2013 -0500
@@ -20,6 +20,7 @@
 
 #include "internal.h"
 #include "debug.h"
+#include "obsolete.h"
 
 #include "relay.h"
 
--- a/libpurple/protocols/jabber/jabber.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jabber.c	Fri Jan 25 02:22:38 2013 -0500
@@ -44,6 +44,7 @@
 #include "util.h"
 #include "version.h"
 #include "xmlnode.h"
+#include "obsolete.h"
 
 #include "auth.h"
 #include "buddy.h"
--- a/libpurple/protocols/jabber/jingle/content.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/content.h	Fri Jan 25 02:22:38 2013 -0500
@@ -68,10 +68,6 @@
 	JingleContentPrivate *priv;      /**< The private data of this object. */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Gets the content class's GType
  *
@@ -111,10 +107,6 @@
 xmlnode *jingle_content_to_xml(JingleContent *content, xmlnode *jingle, JingleActionType action);
 void jingle_content_handle_action(JingleContent *content, xmlnode *xmlcontent, JingleActionType action);
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif /* PURPLE_JABBER_JINGLE_CONTENT_H */
--- a/libpurple/protocols/jabber/jingle/iceudp.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/iceudp.c	Fri Jan 25 02:22:38 2013 -0500
@@ -45,6 +45,8 @@
 static void jingle_iceudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
 static JingleTransport *jingle_iceudp_parse_internal(xmlnode *iceudp);
 static xmlnode *jingle_iceudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action);
+static void jingle_iceudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+static GList *jingle_iceudp_get_remote_candidates(JingleTransport *transport);
 
 static JingleTransportClass *parent_class = NULL;
 
@@ -58,10 +60,10 @@
 jingle_iceudp_candidate_copy(JingleIceUdpCandidate *candidate)
 {
 	JingleIceUdpCandidate *new_candidate = g_new0(JingleIceUdpCandidate, 1);
+	new_candidate->id = g_strdup(candidate->id);
 	new_candidate->component = candidate->component;
 	new_candidate->foundation = g_strdup(candidate->foundation);
 	new_candidate->generation = candidate->generation;
-	new_candidate->id = g_strdup(candidate->id);
 	new_candidate->ip = g_strdup(candidate->ip);
 	new_candidate->network = candidate->network;
 	new_candidate->port = candidate->port;
@@ -105,17 +107,18 @@
 }
 
 JingleIceUdpCandidate *
-jingle_iceudp_candidate_new(guint component, const gchar *foundation,
-		guint generation, const gchar *id, const gchar *ip,
+jingle_iceudp_candidate_new(const gchar *id,
+		guint component, const gchar *foundation,
+		guint generation, const gchar *ip,
 		guint network, guint port, guint priority,
 		const gchar *protocol, const gchar *type,
 		const gchar *username, const gchar *password)
 {
 	JingleIceUdpCandidate *candidate = g_new0(JingleIceUdpCandidate, 1);
+	candidate->id = g_strdup(id);
 	candidate->component = component;
 	candidate->foundation = g_strdup(foundation);
 	candidate->generation = generation;
-	candidate->id = g_strdup(id);
 	candidate->ip = g_strdup(ip);
 	candidate->network = network;
 	candidate->port = port;
@@ -165,6 +168,8 @@
 	klass->parent_class.to_xml = jingle_iceudp_to_xml_internal;
 	klass->parent_class.parse = jingle_iceudp_parse_internal;
 	klass->parent_class.transport_type = JINGLE_TRANSPORT_ICEUDP;
+	klass->parent_class.add_local_candidate = jingle_iceudp_add_local_candidate;
+	klass->parent_class.get_remote_candidates = jingle_iceudp_get_remote_candidates;
 
 	g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES,
 			g_param_spec_pointer("local-candidates",
@@ -246,36 +251,93 @@
 	}
 }
 
-void
-jingle_iceudp_add_local_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *candidate)
+static void
+jingle_iceudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate)
 {
-	GList *iter = iceudp->priv->local_candidates;
+	JingleIceUdp *iceudp = JINGLE_ICEUDP(transport);
+	PurpleMediaCandidateType type;
+	gchar *ip;
+	gchar *username;
+	gchar *password;
+	JingleIceUdpCandidate *iceudp_candidate;
+	GList *iter;
+
+	ip = purple_media_candidate_get_ip(candidate);
+	username = purple_media_candidate_get_username(candidate);
+	password = purple_media_candidate_get_password(candidate);
+	type = purple_media_candidate_get_candidate_type(candidate);
 
-	for (; iter; iter = g_list_next(iter)) {
+	iceudp_candidate = jingle_iceudp_candidate_new(id,
+			purple_media_candidate_get_component_id(candidate),
+			purple_media_candidate_get_foundation(candidate),
+			generation, ip, 0,
+			purple_media_candidate_get_port(candidate),
+			purple_media_candidate_get_priority(candidate), "udp",
+			type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" :
+			type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" :
+			type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" :
+			type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" :
+			"", username, password);
+	iceudp_candidate->reladdr = purple_media_candidate_get_base_ip(candidate);
+	iceudp_candidate->relport = purple_media_candidate_get_base_port(candidate);
+
+	g_free(password);
+	g_free(username);
+	g_free(ip);
+
+	for (iter = iceudp->priv->local_candidates; iter; iter = g_list_next(iter)) {
 		JingleIceUdpCandidate *c = iter->data;
-		if (!strcmp(c->id, candidate->id)) {
-			guint generation = c->generation + 1;
+		if (!strcmp(c->id, id)) {
+			generation = c->generation + 1;
 
 			g_boxed_free(JINGLE_TYPE_ICEUDP_CANDIDATE, c);
 			iceudp->priv->local_candidates = g_list_delete_link(
 					iceudp->priv->local_candidates, iter);
 
-			candidate->generation = generation;
+			iceudp_candidate->generation = generation;
 
 			iceudp->priv->local_candidates = g_list_append(
-					iceudp->priv->local_candidates, candidate);
+					iceudp->priv->local_candidates, iceudp_candidate);
 			return;
 		}
 	}
 
 	iceudp->priv->local_candidates = g_list_append(
-			iceudp->priv->local_candidates, candidate);
+			iceudp->priv->local_candidates, iceudp_candidate);
 }
 
-GList *
-jingle_iceudp_get_remote_candidates(JingleIceUdp *iceudp)
+static GList *
+jingle_iceudp_get_remote_candidates(JingleTransport *transport)
 {
-	return g_list_copy(iceudp->priv->remote_candidates);
+	JingleIceUdp *iceudp = JINGLE_ICEUDP(transport);
+	GList *candidates = iceudp->priv->remote_candidates;
+	GList *ret = NULL;
+
+	for (; candidates; candidates = g_list_next(candidates)) {
+		JingleIceUdpCandidate *candidate = candidates->data;
+		PurpleMediaCandidate *new_candidate = purple_media_candidate_new(
+					candidate->foundation, candidate->component,
+					!strcmp(candidate->type, "host") ?
+						PURPLE_MEDIA_CANDIDATE_TYPE_HOST :
+						!strcmp(candidate->type, "srflx") ?
+							PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX :
+							!strcmp(candidate->type, "prflx") ?
+								PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX :
+								!strcmp(candidate->type, "relay") ?
+									PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0,
+					PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
+					candidate->ip, candidate->port);
+		g_object_set(new_candidate,
+		             "base-ip", candidate->reladdr,
+		             "base-port", candidate->relport,
+		             "username", candidate->username,
+		             "password", candidate->password,
+		             "priority", candidate->priority,
+		             NULL);
+		ret = g_list_append(ret, new_candidate);
+	}
+
+	return ret;
 }
 
 static JingleIceUdpCandidate *
@@ -335,10 +397,10 @@
 			continue;
 
 		iceudp_candidate = jingle_iceudp_candidate_new(
+				id,
 				atoi(component),
 				foundation,
 				atoi(generation),
-				id,
 				ip,
 				atoi(network),
 				atoi(port),
--- a/libpurple/protocols/jabber/jingle/iceudp.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/iceudp.h	Fri Jan 25 02:22:38 2013 -0500
@@ -67,10 +67,10 @@
 
 struct _JingleIceUdpCandidate
 {
+	gchar *id;
 	guint component;
 	gchar *foundation;
 	guint generation;
-	gchar *id;
 	gchar *ip;
 	guint network;
 	guint port;
@@ -87,10 +87,6 @@
 				 * about this candidate */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 GType jingle_iceudp_candidate_get_type(void);
 
 /**
@@ -100,17 +96,11 @@
  */
 GType jingle_iceudp_get_type(void);
 
-JingleIceUdpCandidate *jingle_iceudp_candidate_new(guint component,
-		const gchar *foundation, guint generation, const gchar *id,
+JingleIceUdpCandidate *jingle_iceudp_candidate_new(const gchar *id,
+		guint component, const gchar *foundation, guint generation,
 		const gchar *ip, guint network, guint port, guint priority,
 		const gchar *protocol, const gchar *type,
 		const gchar *username, const gchar *password);
-void jingle_iceudp_add_local_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *candidate);
-GList *jingle_iceudp_get_remote_candidates(JingleIceUdp *iceudp);
-
-#ifdef __cplusplus
-}
-#endif
 
 G_END_DECLS
 
--- a/libpurple/protocols/jabber/jingle/jingle.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/jingle.c	Fri Jan 25 02:22:38 2013 -0500
@@ -29,6 +29,7 @@
 #include "content.h"
 #include "debug.h"
 #include "jingle.h"
+#include "google/google_p2p.h"
 #include "session.h"
 #include "iceudp.h"
 #include "rawudp.h"
@@ -58,6 +59,8 @@
 #ifdef USE_VV
 	else if (!strcmp(type, JINGLE_APP_RTP))
 		return JINGLE_TYPE_RTP;
+	else if (!strcmp(type, NS_GOOGLE_TRANSPORT_P2P))
+		return JINGLE_TYPE_GOOGLE_P2P;
 #endif
 #if 0
 	else if (!strcmp(type, JINGLE_APP_FT))
@@ -429,18 +432,14 @@
 	jingle_actions[action_type].handler(session, jingle);
 }
 
-static void
-jingle_terminate_sessions_gh(gpointer key, gpointer value, gpointer user_data)
-{
-	g_object_unref(value);
-}
-
 void
 jingle_terminate_sessions(JabberStream *js)
 {
-	if (js->sessions)
-		g_hash_table_foreach(js->sessions,
-				jingle_terminate_sessions_gh, NULL);
+	if (js->sessions) {
+		GList *list = g_hash_table_get_values(js->sessions);
+		for (; list; list = g_list_delete_link(list, list))
+			g_object_unref(list->data);
+	}
 }
 
 #ifdef USE_VV
--- a/libpurple/protocols/jabber/jingle/jingle.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/jingle.h	Fri Jan 25 02:22:38 2013 -0500
@@ -30,10 +30,6 @@
 
 G_BEGIN_DECLS
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 #define JINGLE "urn:xmpp:jingle:1"
 #define JINGLE_ERROR "urn:xmpp:jingle:errors:0"
 #define JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:1"
@@ -86,10 +82,6 @@
     const gchar *relay_username, const gchar *relay_password, guint *num_params);
 #endif
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif /* PURPLE_JABBER_JINGLE_H */
--- a/libpurple/protocols/jabber/jingle/rawudp.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/rawudp.c	Fri Jan 25 02:22:38 2013 -0500
@@ -45,6 +45,8 @@
 static void jingle_rawudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
 static JingleTransport *jingle_rawudp_parse_internal(xmlnode *rawudp);
 static xmlnode *jingle_rawudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action);
+static void jingle_rawudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+static GList *jingle_rawudp_get_remote_candidates(JingleTransport *transport);
 
 static JingleTransportClass *parent_class = NULL;
 
@@ -137,6 +139,8 @@
 	klass->parent_class.to_xml = jingle_rawudp_to_xml_internal;
 	klass->parent_class.parse = jingle_rawudp_parse_internal;
 	klass->parent_class.transport_type = JINGLE_TRANSPORT_RAWUDP;
+	klass->parent_class.add_local_candidate = jingle_rawudp_add_local_candidate;
+	klass->parent_class.get_remote_candidates = jingle_rawudp_get_remote_candidates;
 
 	g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES,
 			g_param_spec_pointer("local-candidates",
@@ -218,36 +222,58 @@
 	}
 }
 
-void
-jingle_rawudp_add_local_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate)
+static void
+jingle_rawudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate)
 {
-	GList *iter = rawudp->priv->local_candidates;
+	JingleRawUdp *rawudp = JINGLE_RAWUDP(transport);
+	gchar *ip;
+	JingleRawUdpCandidate *rawudp_candidate;
+	GList *iter;
 
-	for (; iter; iter = g_list_next(iter)) {
+	ip = purple_media_candidate_get_ip(candidate);
+	rawudp_candidate = jingle_rawudp_candidate_new(id, generation,
+			purple_media_candidate_get_component_id(candidate),
+			ip, purple_media_candidate_get_port(candidate));
+	g_free(ip);
+
+	for (iter = rawudp->priv->local_candidates; iter; iter = g_list_next(iter)) {
 		JingleRawUdpCandidate *c = iter->data;
-		if (!strcmp(c->id, candidate->id)) {
-			guint generation = c->generation + 1;
+		if (!strcmp(c->id, id)) {
+			generation = c->generation + 1;
 
 			g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, c);
 			rawudp->priv->local_candidates = g_list_delete_link(
 					rawudp->priv->local_candidates, iter);
 
-			candidate->generation = generation;
+			rawudp_candidate->generation = generation;
 
 			rawudp->priv->local_candidates = g_list_append(
-					rawudp->priv->local_candidates, candidate);
+					rawudp->priv->local_candidates, rawudp_candidate);
 			return;
 		}
 	}
 
 	rawudp->priv->local_candidates = g_list_append(
-			rawudp->priv->local_candidates, candidate);
+			rawudp->priv->local_candidates, rawudp_candidate);
 }
 
-GList *
-jingle_rawudp_get_remote_candidates(JingleRawUdp *rawudp)
+static GList *
+jingle_rawudp_get_remote_candidates(JingleTransport *transport)
 {
-	return g_list_copy(rawudp->priv->remote_candidates);
+	JingleRawUdp *rawudp = JINGLE_RAWUDP(transport);
+	GList *candidates = rawudp->priv->remote_candidates;
+	GList *ret = NULL;
+
+	for (; candidates; candidates = g_list_next(candidates)) {
+		JingleRawUdpCandidate *candidate = candidates->data;
+		ret = g_list_append(ret, purple_media_candidate_new("",
+					candidate->component,
+					PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
+					PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
+					candidate->ip, candidate->port));
+	}
+
+	return ret;
 }
 
 static JingleRawUdpCandidate *
--- a/libpurple/protocols/jabber/jingle/rawudp.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/rawudp.h	Fri Jan 25 02:22:38 2013 -0500
@@ -77,10 +77,6 @@
 				 * about this candidate */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 GType jingle_rawudp_candidate_get_type(void);
 
 /**
@@ -92,12 +88,6 @@
 
 JingleRawUdpCandidate *jingle_rawudp_candidate_new(const gchar *id,
 		guint generation, guint component, const gchar *ip, guint port);
-void jingle_rawudp_add_local_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate);
-GList *jingle_rawudp_get_remote_candidates(JingleRawUdp *rawudp);
-
-#ifdef __cplusplus
-}
-#endif
 
 G_END_DECLS
 
--- a/libpurple/protocols/jabber/jingle/rtp.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/rtp.c	Fri Jan 25 02:22:38 2013 -0500
@@ -28,6 +28,7 @@
 
 #include "jabber.h"
 #include "jingle.h"
+#include "google/google_p2p.h"
 #include "media.h"
 #include "mediamanager.h"
 #include "iceudp.h"
@@ -226,134 +227,23 @@
 	return media;
 }
 
-static JingleRawUdpCandidate *
-jingle_rtp_candidate_to_rawudp(JingleSession *session, guint generation,
-		PurpleMediaCandidate *candidate)
-{
-	gchar *id = jabber_get_next_id(jingle_session_get_js(session));
-	gchar *ip = purple_media_candidate_get_ip(candidate);
-	JingleRawUdpCandidate *rawudp_candidate =
-			jingle_rawudp_candidate_new(id, generation,
-			purple_media_candidate_get_component_id(candidate),
-			ip, purple_media_candidate_get_port(candidate));
-	g_free(ip);
-	g_free(id);
-	return rawudp_candidate;
-}
-
-static JingleIceUdpCandidate *
-jingle_rtp_candidate_to_iceudp(JingleSession *session, guint generation,
-		PurpleMediaCandidate *candidate)
-{
-	gchar *id = jabber_get_next_id(jingle_session_get_js(session));
-	gchar *ip = purple_media_candidate_get_ip(candidate);
-	gchar *username = purple_media_candidate_get_username(candidate);
-	gchar *password = purple_media_candidate_get_password(candidate);
-	PurpleMediaCandidateType type =
-			purple_media_candidate_get_candidate_type(candidate);
-
-	JingleIceUdpCandidate *iceudp_candidate = jingle_iceudp_candidate_new(
-			purple_media_candidate_get_component_id(candidate),
-			purple_media_candidate_get_foundation(candidate),
-			generation, id, ip, 0,
-			purple_media_candidate_get_port(candidate),
-			purple_media_candidate_get_priority(candidate), "udp",
-			type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" :
-			type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" :
-			type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" :
-			type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" :
-			"", username, password);
-	iceudp_candidate->reladdr =
-			purple_media_candidate_get_base_ip(candidate);
-	iceudp_candidate->relport =
-			purple_media_candidate_get_base_port(candidate);
-	g_free(password);
-	g_free(username);
-	g_free(ip);
-	g_free(id);
-	return iceudp_candidate;
-}
-
 static JingleTransport *
-jingle_rtp_candidates_to_transport(JingleSession *session, GType type, guint generation, GList *candidates)
+jingle_rtp_candidates_to_transport(JingleSession *session, const gchar *type, guint generation, GList *candidates)
 {
-	if (type == JINGLE_TYPE_RAWUDP) {
-		JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_RAWUDP);
-		JingleRawUdpCandidate *rawudp_candidate;
-		for (; candidates; candidates = g_list_next(candidates)) {
-			PurpleMediaCandidate *candidate = candidates->data;
-			rawudp_candidate = jingle_rtp_candidate_to_rawudp(
-					session, generation, candidate);
-			jingle_rawudp_add_local_candidate(
-					JINGLE_RAWUDP(transport),
-					rawudp_candidate);
-		}
-		return transport;
-	} else if (type == JINGLE_TYPE_ICEUDP) {
-		JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_ICEUDP);
-		JingleIceUdpCandidate *iceudp_candidate;
-		for (; candidates; candidates = g_list_next(candidates)) {
-			PurpleMediaCandidate *candidate = candidates->data;
-			iceudp_candidate = jingle_rtp_candidate_to_iceudp(
-					session, generation, candidate);
-			jingle_iceudp_add_local_candidate(
-					JINGLE_ICEUDP(transport),
-					iceudp_candidate);
-		}
-		return transport;
-	} else {
+	JingleTransport *transport;
+
+	transport = jingle_transport_create(type);
+	if (!transport)
 		return NULL;
-	}
-}
-
-static GList *
-jingle_rtp_transport_to_candidates(JingleTransport *transport)
-{
-	const gchar *type = jingle_transport_get_transport_type(transport);
-	GList *ret = NULL;
-	if (!strcmp(type, JINGLE_TRANSPORT_RAWUDP)) {
-		GList *candidates = jingle_rawudp_get_remote_candidates(JINGLE_RAWUDP(transport));
 
-		for (; candidates; candidates = g_list_delete_link(candidates, candidates)) {
-			JingleRawUdpCandidate *candidate = candidates->data;
-			ret = g_list_append(ret, purple_media_candidate_new(
-					"", candidate->component,
-					PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
-					PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
-					candidate->ip, candidate->port));
-		}
-
-		return ret;
-	} else if (!strcmp(type, JINGLE_TRANSPORT_ICEUDP)) {
-		GList *candidates = jingle_iceudp_get_remote_candidates(JINGLE_ICEUDP(transport));
+	for (; candidates; candidates = g_list_next(candidates)) {
+		PurpleMediaCandidate *candidate = candidates->data;
+		gchar *id = jabber_get_next_id(jingle_session_get_js(session));
+		jingle_transport_add_local_candidate(transport, id, generation, candidate);
+		g_free(id);
+	}
 
-		for (; candidates; candidates = g_list_delete_link(candidates, candidates)) {
-			JingleIceUdpCandidate *candidate = candidates->data;
-			PurpleMediaCandidate *new_candidate = purple_media_candidate_new(
-					candidate->foundation, candidate->component,
-					!strcmp(candidate->type, "host") ?
-					PURPLE_MEDIA_CANDIDATE_TYPE_HOST :
-					!strcmp(candidate->type, "srflx") ?
-					PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX :
-					!strcmp(candidate->type, "prflx") ?
-					PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX :
-					!strcmp(candidate->type, "relay") ?
-					PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0,
-					PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
-					candidate->ip, candidate->port);
-			g_object_set(new_candidate,
-					"base-ip", candidate->reladdr,
-					"base-port", candidate->relport,
-					"username", candidate->username,
-					"password", candidate->password,
-					"priority", candidate->priority, NULL);
-			ret = g_list_append(ret, new_candidate);
-		}
-
-		return ret;
-	} else {
-		return NULL;
-	}
+	return transport;
 }
 
 static void jingle_rtp_ready(JingleSession *session);
@@ -378,10 +268,9 @@
 
 	oldtransport = jingle_content_get_transport(content);
 	candidates = purple_media_get_local_candidates(media, sid, name);
-	transport = JINGLE_TRANSPORT(jingle_rtp_candidates_to_transport(
-			session, JINGLE_IS_RAWUDP(oldtransport) ?
-				JINGLE_TYPE_RAWUDP : JINGLE_TYPE_ICEUDP,
-			0, candidates));
+	transport = jingle_rtp_candidates_to_transport(
+			session, jingle_transport_get_transport_type(oldtransport),
+			0, candidates);
 
 	g_list_free(candidates);
 	g_object_unref(oldtransport);
@@ -404,9 +293,9 @@
 static void
 jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, PurpleMediaCandidate *candidate, JingleSession *session)
 {
-	JingleContent *content = jingle_session_find_content(
-			session, sid, NULL);
+	JingleContent *content = jingle_session_find_content(session, sid, NULL);
 	JingleTransport *transport;
+	gchar *id;
 
 	purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n");
 
@@ -419,19 +308,13 @@
 
 	transport = jingle_content_get_transport(content);
 
-	if (JINGLE_IS_ICEUDP(transport))
-		jingle_iceudp_add_local_candidate(JINGLE_ICEUDP(transport),
-				jingle_rtp_candidate_to_iceudp(
-				session, 1, candidate));
-	else if (JINGLE_IS_RAWUDP(transport))
-		jingle_rawudp_add_local_candidate(JINGLE_RAWUDP(transport),
-				jingle_rtp_candidate_to_rawudp(
-				session, 1, candidate));
+	id = jabber_get_next_id(jingle_session_get_js(session));
+	jingle_transport_add_local_candidate(transport, id, 1, candidate);
+	g_free(id);
 
 	g_object_unref(transport);
 
-	jabber_iq_send(jingle_session_to_packet(session,
-			JINGLE_TRANSPORT_INFO));
+	jabber_iq_send(jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO));
 }
 
 static void
@@ -604,6 +487,8 @@
 		transmitter = "rawudp";
 	else if (JINGLE_IS_ICEUDP(transport))
 		transmitter = "nice";
+	else if (JINGLE_IS_GOOGLE_P2P(transport))
+		transmitter = "nice";
 	else
 		transmitter = "notransmitter";
 	g_object_unref(transport);
@@ -827,7 +712,7 @@
 			transport = jingle_transport_parse(
 					xmlnode_get_child(xmlcontent, "transport"));
 			description = xmlnode_get_child(xmlcontent, "description");
-			candidates = jingle_rtp_transport_to_candidates(transport);
+			candidates = jingle_transport_get_remote_candidates(transport);
 			codecs = jingle_rtp_parse_codecs(description);
 			name = jingle_content_get_name(content);
 			remote_jid = jingle_session_get_remote_jid(session);
@@ -863,7 +748,7 @@
 			JingleSession *session = jingle_content_get_session(content);
 			JingleTransport *transport = jingle_transport_parse(
 					xmlnode_get_child(xmlcontent, "transport"));
-			GList *candidates = jingle_rtp_transport_to_candidates(transport);
+			GList *candidates = jingle_transport_get_remote_candidates(transport);
 			gchar *name = jingle_content_get_name(content);
 			gchar *remote_jid =
 					jingle_session_get_remote_jid(session);
@@ -973,6 +858,8 @@
 		transport_type = JINGLE_TRANSPORT_ICEUDP;
 	} else if (jabber_resource_has_capability(jbr, JINGLE_TRANSPORT_RAWUDP)) {
 		transport_type = JINGLE_TRANSPORT_RAWUDP;
+	} else if (jabber_resource_has_capability(jbr, NS_GOOGLE_TRANSPORT_P2P)) {
+		transport_type = NS_GOOGLE_TRANSPORT_P2P;
 	} else {
 		purple_debug_error("jingle-rtp", "Resource doesn't support "
 				"the same transport types\n");
--- a/libpurple/protocols/jabber/jingle/rtp.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/rtp.h	Fri Jan 25 02:22:38 2013 -0500
@@ -65,10 +65,6 @@
 	JingleRtpPrivate *priv;      /**< The private data of this object. */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Gets the rtp class's GType
  *
@@ -84,10 +80,6 @@
 				   PurpleMediaSessionType type);
 void jingle_rtp_terminate_session(JabberStream *js, const gchar *who);
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif /* USE_VV */
--- a/libpurple/protocols/jabber/jingle/session.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/session.h	Fri Jan 25 02:22:38 2013 -0500
@@ -62,10 +62,6 @@
 
 struct _JingleContent;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Gets the session class's GType
  *
@@ -109,10 +105,6 @@
 JabberIq *jingle_session_terminate_packet(JingleSession *session, const gchar *reason);
 JabberIq *jingle_session_redirect_packet(JingleSession *session, const gchar *sid);
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif /* PURPLE_JABBER_JINGLE_SESSION_H */
--- a/libpurple/protocols/jabber/jingle/transport.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/transport.c	Fri Jan 25 02:22:38 2013 -0500
@@ -44,6 +44,8 @@
 static void jingle_transport_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
 JingleTransport *jingle_transport_parse_internal(xmlnode *transport);
 xmlnode *jingle_transport_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action);
+static void jingle_transport_add_local_candidate_internal(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+static GList *jingle_transport_get_remote_candidates_internal(JingleTransport *transport);
 
 static GObjectClass *parent_class = NULL;
 
@@ -85,6 +87,8 @@
 	gobject_class->get_property = jingle_transport_get_property;
 	klass->to_xml = jingle_transport_to_xml_internal;
 	klass->parse = jingle_transport_parse_internal;
+	klass->add_local_candidate = jingle_transport_add_local_candidate_internal;
+	klass->get_remote_candidates = jingle_transport_get_remote_candidates_internal;
 
 	g_type_class_add_private(klass, sizeof(JingleTransportPrivate));
 }
@@ -143,6 +147,32 @@
 	return JINGLE_TRANSPORT_GET_CLASS(transport)->transport_type;
 }
 
+void
+jingle_transport_add_local_candidate(JingleTransport *transport, const gchar *id,
+                                     guint generation, PurpleMediaCandidate *candidate)
+{
+	JINGLE_TRANSPORT_GET_CLASS(transport)->add_local_candidate(transport, id,
+	                                                           generation, candidate);
+}
+
+void
+jingle_transport_add_local_candidate_internal(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate)
+{
+	/* Nothing to do */
+}
+
+GList *
+jingle_transport_get_remote_candidates(JingleTransport *transport)
+{
+	return JINGLE_TRANSPORT_GET_CLASS(transport)->get_remote_candidates(transport);
+}
+
+static GList *
+jingle_transport_get_remote_candidates_internal(JingleTransport *transport)
+{
+	return NULL;
+}
+
 JingleTransport *
 jingle_transport_parse_internal(xmlnode *transport)
 {
--- a/libpurple/protocols/jabber/jingle/transport.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/jingle/transport.h	Fri Jan 25 02:22:38 2013 -0500
@@ -55,6 +55,8 @@
 	const gchar *transport_type;
 	xmlnode *(*to_xml) (JingleTransport *transport, xmlnode *content, JingleActionType action);
 	JingleTransport *(*parse) (xmlnode *transport);
+	void (*add_local_candidate) (JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+	GList *(*get_remote_candidates) (JingleTransport *transport);
 };
 
 /** The transport class's private data */
@@ -64,10 +66,6 @@
 	JingleTransportPrivate *priv;      /**< The private data of this object. */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Gets the transport class's GType
  *
@@ -77,15 +75,13 @@
 
 JingleTransport *jingle_transport_create(const gchar *type);
 const gchar *jingle_transport_get_transport_type(JingleTransport *transport);
-void jingle_transport_add_candidate();
+
+void jingle_transport_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate);
+GList *jingle_transport_get_remote_candidates(JingleTransport *transport);
 
 JingleTransport *jingle_transport_parse(xmlnode *transport);
 xmlnode *jingle_transport_to_xml(JingleTransport *transport, xmlnode *content, JingleActionType action);
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif /* PURPLE_JABBER_JINGLE_TRANSPORT_H */
--- a/libpurple/protocols/jabber/namespaces.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/namespaces.h	Fri Jan 25 02:22:38 2013 -0500
@@ -109,4 +109,9 @@
 #define NS_GOOGLE_SESSION_PHONE "http://www.google.com/session/phone"
 #define NS_GOOGLE_SESSION_VIDEO "http://www.google.com/session/video"
 
+#define NS_GOOGLE_TRANSPORT_P2P "http://www.google.com/transport/p2p"
+
+/* Apple extension(s) */
+#define NS_APPLE_IDLE "http://www.apple.com/xmpp/idle"
+
 #endif /* PURPLE_JABBER_NAMESPACES_H_ */
--- a/libpurple/protocols/jabber/presence.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/presence.c	Fri Jan 25 02:22:38 2013 -0500
@@ -1017,7 +1017,7 @@
 			pih(js, &presence, child);
 	}
 
-	if (presence.delayed && presence.idle) {
+	if (presence.delayed && presence.idle && presence.adjust_idle_for_delay) {
 		/* Delayed and idle, so update idle time */
 		presence.idle = presence.idle + (time(NULL) - presence.sent);
 	}
@@ -1172,11 +1172,33 @@
 }
 
 static void
+parse_apple_idle(JabberStream *js, JabberPresence *presence, xmlnode *x)
+{
+	xmlnode *since = xmlnode_get_child(x, "idle-since");
+	if (since) {
+		char *stamp = xmlnode_get_data_unescaped(since);
+		if (stamp) {
+			time_t tstamp = purple_str_to_time(stamp, TRUE, NULL, NULL, NULL);
+			if (tstamp != 0) {
+				presence->idle = time(NULL) - tstamp;
+				presence->adjust_idle_for_delay = FALSE;
+				if(presence->idle < 0) {
+					purple_debug_warning("jabber", "Received bogus idle timestamp %s\n", stamp);
+					presence->idle = 0;
+				}
+			}
+		}
+		g_free(stamp);
+	}
+}
+
+static void
 parse_idle(JabberStream *js, JabberPresence *presence, xmlnode *query)
 {
 	const gchar *seconds = xmlnode_get_attrib(query, "seconds");
 	if (seconds) {
 		presence->idle = atoi(seconds);
+		presence->adjust_idle_for_delay = TRUE;
 		if (presence->idle < 0) {
 			purple_debug_warning("jabber", "Received bogus idle time %s\n", seconds);
 			presence->idle = 0;
@@ -1276,6 +1298,9 @@
 	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);
+
+	/* Apple idle */
+	jabber_presence_register_handler("x", NS_APPLE_IDLE, parse_apple_idle);
 }
 
 void jabber_presence_uninit(void)
--- a/libpurple/protocols/jabber/presence.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/presence.h	Fri Jan 25 02:22:38 2013 -0500
@@ -71,6 +71,7 @@
 	gboolean delayed;
 	time_t sent;
 	int idle;
+	gboolean adjust_idle_for_delay;
 };
 
 typedef void (JabberPresenceHandler)(JabberStream *js, JabberPresence *presence,
--- a/libpurple/protocols/jabber/useravatar.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/jabber/useravatar.c	Fri Jan 25 02:22:38 2013 -0500
@@ -22,6 +22,7 @@
  */
 
 #include "internal.h"
+#include "obsolete.h"
 
 #include "useravatar.h"
 #include "pep.h"
--- a/libpurple/protocols/msn/msn.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/msn/msn.c	Fri Jan 25 02:22:38 2013 -0500
@@ -27,6 +27,7 @@
 
 #include "debug.h"
 #include "request.h"
+#include "obsolete.h"
 
 #include "accountopt.h"
 #include "contact.h"
@@ -2782,7 +2783,7 @@
 		{
 			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);
+			id = purple_imgstore_new_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_html(user_info, NULL, buf);
 		}
--- a/libpurple/protocols/msn/notification.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/msn/notification.c	Fri Jan 25 02:22:38 2013 -0500
@@ -1592,7 +1592,7 @@
 		/* Disconnect others, if MPOP is disabled */
 		if (is_me
 		 && !session->enable_mpop
-		 && strncasecmp(id + 1, session->guid, 36) != 0) {
+		 && g_ascii_strncasecmp(id + 1, session->guid, 36) != 0) {
 			purple_debug_info("msn", "Disconnecting Endpoint %s\n", id);
 
 			tmp = g_strdup_printf("%s;%s", user->passport, id);
--- a/libpurple/protocols/msn/session.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/msn/session.c	Fri Jan 25 02:22:38 2013 -0500
@@ -24,6 +24,7 @@
 
 #include "internal.h"
 #include "debug.h"
+#include "obsolete.h"
 
 #include "error.h"
 #include "msnutils.h"
--- a/libpurple/protocols/msn/slp.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/msn/slp.c	Fri Jan 25 02:22:38 2013 -0500
@@ -24,6 +24,7 @@
 
 #include "internal.h"
 #include "debug.h"
+#include "obsolete.h"
 
 #include "slp.h"
 #include "slpcall.h"
--- a/libpurple/protocols/msn/switchboard.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/msn/switchboard.c	Fri Jan 25 02:22:38 2013 -0500
@@ -827,7 +827,7 @@
 		return;
 	}
 
-	imgid = purple_imgstore_add_with_id(image_data, image_len, NULL);
+	imgid = purple_imgstore_new_with_id(image_data, image_len, NULL);
 	image_msg = g_strdup_printf("<IMG SRC='" PURPLE_STORED_IMAGE_PROTOCOL "%d'>",
 	                            imgid);
 
--- a/libpurple/protocols/msn/user.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/msn/user.c	Fri Jan 25 02:22:38 2013 -0500
@@ -761,17 +761,7 @@
 	str = purple_normalize_nocase(NULL, msn_user_get_passport(user));
 	pass = g_strdup(str);
 
-#if GLIB_CHECK_VERSION(2,16,0)
 	result = g_strcmp0(pass, purple_normalize_nocase(NULL, passport));
-#else
-	str = purple_normalize_nocase(NULL, passport);
-	if (!pass)
-		result = -(pass != str);
-	else if (!str)
-		result = pass != str;
-	else
-		result = strcmp(pass, str);
-#endif /* GLIB < 2.16.0 */
 
 	g_free(pass);
 
--- a/libpurple/protocols/mxit/actions.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/actions.c	Fri Jan 25 02:22:38 2013 -0500
@@ -43,11 +43,12 @@
  */
 static void mxit_profile_cb( PurpleConnection* gc, PurpleRequestFields* fields )
 {
-	struct MXitSession*		session	= purple_connection_get_protocol_data( gc ) ;
+	struct MXitSession*		session	= purple_connection_get_protocol_data( gc );
 	PurpleRequestField*		field	= NULL;
 	const char*				name	= NULL;
 	const char*				bday	= NULL;
 	const char*				err		= NULL;
+	GList*					entry	= NULL;
 
 	purple_debug_info( MXIT_PLUGIN_ID, "mxit_profile_cb\n" );
 
@@ -166,6 +167,14 @@
 		g_string_append( attributes, attrib );
 		acount++;
 
+		/* relationship status */
+		field = purple_request_fields_get_field( fields, "relationship" );
+		entry = g_list_first( purple_request_field_list_get_selected( field ) );
+		profile->relationship = atoi( purple_request_field_list_get_data( field, entry->data ) );
+		g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%i", CP_PROFILE_RELATIONSHIP, CP_PROFILE_TYPE_SHORT, profile->relationship );
+		g_string_append( attributes, attrib );
+		acount++;
+
 		/* update flags */
 		field = purple_request_fields_get_field( fields, "searchable" );
 		if ( purple_request_field_bool_get_value( field ) )		/* is searchable -> clear not-searchable flag */
@@ -253,6 +262,22 @@
 		field = purple_request_field_string_new( "whereami", _( "Where I Live" ), profile->whereami, FALSE);
 		purple_request_field_group_add_field( public_group, field );
 
+		/* relationship status */
+		field = purple_request_field_list_new( "relationship", _( "Relationship Status" ) );
+		purple_request_field_list_set_multi_select( field, FALSE );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_UNKNOWN ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_UNKNOWN ) );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_DONTSAY ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_DONTSAY ) );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_SINGLE ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_SINGLE ) );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_INVOLVED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_INVOLVED ) );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_ENGAGED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_ENGAGED ) );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_MARRIED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_MARRIED ) );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_COMPLICATED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_COMPLICATED ) );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_WIDOWED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_WIDOWED ) );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_SEPARATED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_SEPARATED ) );
+		purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_DIVORCED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_DIVORCED ) );
+		purple_request_field_list_add_selected( field, mxit_relationship_to_name( profile->relationship ) );
+		purple_request_field_group_add_field( public_group, field );
+
 		purple_request_fields_add_group( fields, public_group );
 	}
 
--- a/libpurple/protocols/mxit/cipher.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/cipher.c	Fri Jan 25 02:22:38 2013 -0500
@@ -163,6 +163,10 @@
 	/* base64 decode the message */
 	raw_message = purple_base64_decode( message, &raw_len );
 
+	/* AES-encrypted data is always blocks of 16 bytes */
+	if ( ( raw_len == 0 ) || ( raw_len % 16 != 0 ) )
+		return NULL;
+
 	/* build the AES key */
 	ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey );
 
--- a/libpurple/protocols/mxit/formcmds.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/formcmds.c	Fri Jan 25 02:22:38 2013 -0500
@@ -28,6 +28,7 @@
 #include <glib.h>
 
 #include "purple.h"
+#include "obsolete.h"
 
 #include "protocol.h"
 #include "mxit.h"
@@ -106,7 +107,7 @@
 	}
 
 	/* we now have the inline image, store a copy in the imagestore */
-	id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL);
+	id = purple_imgstore_new_with_id(g_memdup(url_text, len), len, NULL);
 
 	/* map the inline image id to purple image id */
 	intptr = g_malloc(sizeof(int));
@@ -334,7 +335,7 @@
 	if (img) {
 		rawimg = purple_base64_decode(img, &rawimglen);
 		//purple_util_write_data_to_file_absolute("/tmp/mxitinline.png", (char*) rawimg, rawimglen);
-		imgid = purple_imgstore_add_with_id(rawimg, rawimglen, NULL);
+		imgid = purple_imgstore_new_with_id(rawimg, rawimglen, NULL);
 		g_string_append_printf(msg,
 		                       "<img src=\"" PURPLE_STORED_IMAGE_PROTOCOL "%i\">",
 		                       imgid);
@@ -372,7 +373,7 @@
 	reply = g_hash_table_lookup(hash, "replymsg");
 	if (reply) {
 		g_string_append_printf(msg, "\n");
-		mxit_add_html_link(mx, reply, FALSE, _( "click here" ));
+		mxit_add_html_link(mx, purple_url_decode(reply), FALSE, _( "click here" ));
 	}
 }
 
--- a/libpurple/protocols/mxit/login.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/login.c	Fri Jan 25 02:22:38 2013 -0500
@@ -25,6 +25,7 @@
 
 #include    "internal.h"
 #include	"purple.h"
+#include	"obsolete.h"
 
 #include	"protocol.h"
 #include	"mxit.h"
--- a/libpurple/protocols/mxit/markup.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/markup.c	Fri Jan 25 02:22:38 2013 -0500
@@ -25,6 +25,7 @@
 
 #include    "internal.h"
 #include	"purple.h"
+#include	"obsolete.h"
 
 #include	"protocol.h"
 #include	"mxit.h"
@@ -59,7 +60,9 @@
 };
 
 
-#define		MXIT_VIBE_MSG_COLOR		"#9933FF"
+#define		MXIT_VIBE_MSG_COLOR			"#9933FF"
+#define		MXIT_FAREWELL_MSG_COLOR		"#949494"
+
 
 /* vibes */
 static const char*	vibes[] = {
@@ -593,7 +596,7 @@
 	}
 
 	/* we now have the emoticon, store it in the imagestore */
-	id = purple_imgstore_add_with_id( em_data, em_size, NULL );
+	id = purple_imgstore_new_with_id( em_data, em_size, NULL );
 
 	/* map the mxit emoticon id to purple image id */
 	intptr = g_malloc( sizeof( int ) );
@@ -1003,6 +1006,12 @@
 					break;
 		}
 	}
+
+	if ( msgflags & CP_MSG_FAREWELL ) {
+		/* this is a farewell message */
+		g_string_prepend( mx->msg, "<font color=\""MXIT_FAREWELL_MSG_COLOR"\"><i>" );
+		g_string_append( mx->msg, "</i></font>" );
+	}
 }
 
 
--- a/libpurple/protocols/mxit/mxit.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/mxit.c	Fri Jan 25 02:22:38 2013 -0500
@@ -427,7 +427,7 @@
 	char*					statusmsg2;
 
 	/* Handle mood changes */
-	if ( purple_status_type_get_primitive(purple_status_get_type( status ) ) == PURPLE_STATUS_MOOD ) {
+	if ( purple_status_type_get_primitive( purple_status_get_type( status ) ) == PURPLE_STATUS_MOOD ) {
 		const char* moodid = purple_status_get_attr_string( status, PURPLE_MOOD_NAME );
 		int mood;
 
--- a/libpurple/protocols/mxit/profile.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/profile.c	Fri Jan 25 02:22:38 2013 -0500
@@ -35,6 +35,40 @@
 
 
 /*------------------------------------------------------------------------
+ * Return the MXit Relationship status as a string.
+ *
+ * @param id		The Relationship status value (see profile.h)
+ * @return			The relationship status as a text string.
+ */
+const char* mxit_relationship_to_name( short id )
+{
+	switch ( id ) {
+		case MXIT_RELATIONSHIP_UNKNOWN :
+			return _( "Unknown" );
+		case MXIT_RELATIONSHIP_DONTSAY :
+			return _( "Don't want to say" );
+		case MXIT_RELATIONSHIP_SINGLE :
+			return _( "Single" );
+		case MXIT_RELATIONSHIP_INVOLVED :
+			return _( "In a relationship" );
+		case MXIT_RELATIONSHIP_ENGAGED :
+			return _( "Engaged" );
+		case MXIT_RELATIONSHIP_MARRIED :
+			return _( "Married" );
+		case MXIT_RELATIONSHIP_COMPLICATED :
+			return _( "It's complicated" );
+		case MXIT_RELATIONSHIP_WIDOWED :
+			return _( "Widowed" );
+		case MXIT_RELATIONSHIP_SEPARATED :
+			return _( "Separated" );
+		case MXIT_RELATIONSHIP_DIVORCED :
+			return _( "Divorced" );
+		default :
+			return "";
+	}
+}
+
+/*------------------------------------------------------------------------
  * Returns true if it is a valid date.
  *
  * @param bday		Date-of-Birth string (YYYY-MM-DD)
@@ -130,7 +164,7 @@
 	age = now.tm_year - bdate.tm_year;
 	if ( now.tm_mon < bdate.tm_mon )		/* is before month of birth */
 		age--;
-	else if ( (now.tm_mon == bdate.tm_mon ) && ( now.tm_mday < bdate.tm_mday ) )	/* before birthday in current month */
+	else if ( ( now.tm_mon == bdate.tm_mon ) && ( now.tm_mday < bdate.tm_mday ) )	/* before birthday in current month */
 		age--;
 
 	return age;
@@ -195,6 +229,8 @@
 	if ( *profile->whereami )
 		purple_notify_user_info_add_pair_plaintext( info, _( "Where I Live" ), profile->whereami );
 
+	purple_notify_user_info_add_pair_plaintext( info, _( "Relationship Status" ), mxit_relationship_to_name( profile->relationship ) );
+
 	purple_notify_user_info_add_section_break( info );
 
 	if ( contact ) {
@@ -236,7 +272,7 @@
 				img_text = g_strdup_printf( "<img src='" PURPLE_STORED_IMAGE_PROTOCOL "%d'>",
 				                            contact->imgid );
 				purple_notify_user_info_add_pair_html( info, _( "Photo" ), img_text );
-				g_free(img_text);
+				g_free( img_text );
 			}
 
 			if ( contact->statusMsg ) {
--- a/libpurple/protocols/mxit/profile.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/profile.h	Fri Jan 25 02:22:38 2013 -0500
@@ -29,6 +29,18 @@
 #include	<glib.h>
 
 
+/* MXit relationship status types */
+#define MXIT_RELATIONSHIP_UNKNOWN		0
+#define MXIT_RELATIONSHIP_DONTSAY		1
+#define MXIT_RELATIONSHIP_SINGLE		2
+#define MXIT_RELATIONSHIP_INVOLVED		3
+#define MXIT_RELATIONSHIP_ENGAGED		4
+#define MXIT_RELATIONSHIP_MARRIED		5
+#define MXIT_RELATIONSHIP_COMPLICATED	6
+#define MXIT_RELATIONSHIP_WIDOWED		7
+#define MXIT_RELATIONSHIP_SEPARATED		8
+#define MXIT_RELATIONSHIP_DIVORCED		9
+
 struct MXitProfile {
 	/* required */
 	char		loginname[64];						/* name user uses to log into MXit with (aka 'mxitid') */
@@ -47,6 +59,7 @@
 	char		regcountry[3];						/* user's registered country code */
 	char		whereami[51];						/* where am I / where I live */
 	char		aboutme[513];						/* about me */
+	int			relationship;						/* relationship status */
 
 	int			flags;								/* user's profile flags */
 	gint64		lastonline;							/* user's last-online timestamp */
@@ -55,6 +68,7 @@
 struct MXitSession;
 void mxit_show_profile( struct MXitSession* session, const char* username, struct MXitProfile* profile );
 void mxit_show_search_results( struct MXitSession* session, int searchType, int maxResults, GList* entries );
+const char* mxit_relationship_to_name( short id );
 
 gboolean validateDate( const char* bday );
 
--- a/libpurple/protocols/mxit/protocol.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/protocol.c	Fri Jan 25 02:22:38 2013 -0500
@@ -25,6 +25,7 @@
 
 #include    "internal.h"
 #include	"purple.h"
+#include	"obsolete.h"
 
 #include	"protocol.h"
 #include	"mxit.h"
@@ -458,7 +459,7 @@
 	packet->headerlen = 0;
 
 	/* create generic packet header */
-	hlen = snprintf( header, sizeof( header ), "id=%s%c", purple_account_get_username( session->acc), CP_REC_TERM );			/* client msisdn */
+	hlen = snprintf( header, sizeof( header ), "id=%s%c", purple_account_get_username( session->acc ), CP_REC_TERM );			/* client msisdn */
 
 	if ( session->http ) {
 		/* http connection only */
@@ -1135,7 +1136,7 @@
  *  @param id			The identifier of the event (received in message)
  *  @param event		Identified the type of event
  */
-void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event)
+void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event )
 {
 	char		data[CP_MAX_PACKET];
 	int			datalen;
@@ -1450,7 +1451,7 @@
 	const char*		statusmsg;
 	const char*		profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME,
 									CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL,
-									CP_PROFILE_MOBILENR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME, CP_PROFILE_FLAGS };
+									CP_PROFILE_MOBILENR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME, CP_PROFILE_RELATIONSHIP, CP_PROFILE_FLAGS };
 
 	purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
 
@@ -1877,6 +1878,10 @@
 			/* about me */
 			g_strlcpy( profile->aboutme, fvalue, sizeof( profile->aboutme ) );
 		}
+		else if ( strcmp( CP_PROFILE_RELATIONSHIP, fname ) == 0) {
+			/* relatinship status */
+			profile->relationship = strtol( fvalue, NULL, 10 );
+		}
 		else {
 			/* invalid profile attribute */
 			purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile attribute received '%s' \n", fname );
@@ -2030,6 +2035,47 @@
 	g_list_foreach( entries, (GFunc)g_free, NULL );
 }
 
+/*------------------------------------------------------------------------
+ * Process a received message event packet.
+ *
+ *  @param session		The MXit session object
+ *  @param records		The packet's data records
+ *  @param rcount		The number of data records
+ */
+static void mxit_parse_cmd_msgevent( struct MXitSession* session, struct record** records, int rcount )
+{
+	int event;
+
+	/*
+	 * contactAddress \1 dateTime \1 id \1 event 
+	 */
+
+	/* strip off dummy domain */
+	mxit_strip_domain( records[0]->fields[0]->data );
+
+	event = atoi( records[0]->fields[3]->data );
+
+	switch ( event ) {
+		case CP_MSGEVENT_TYPING :							/* user is typing */
+		case CP_MSGEVENT_ANGRY :							/* user is typing angrily */
+			serv_got_typing( session->con, records[0]->fields[0]->data, 0, PURPLE_TYPING );
+			break;
+
+		case CP_MSGEVENT_STOPPED :							/* user has stopped typing */
+			serv_got_typing_stopped( session->con, records[0]->fields[0]->data );
+			break;
+
+		case CP_MSGEVENT_ERASING :							/* user is erasing text */
+		case CP_MSGEVENT_DELIVERED :						/* message was delivered */
+		case CP_MSGEVENT_DISPLAYED :						/* message was viewed */
+			/* these are currently not supported by libPurple */
+			break;
+
+		default:
+			purple_debug_error( MXIT_PLUGIN_ID, "Unknown message event received (%i)\n", event );
+	}
+}
+
 
 /*------------------------------------------------------------------------
  * Return the length of a multimedia chunk
@@ -2136,7 +2182,7 @@
 					contact = get_mxit_invite_contact( session, chunk.mxitid );
 					if ( contact ) {
 						/* this is an invite (add image to the internal image store) */
-						contact->imgid = purple_imgstore_add_with_id( g_memdup( chunk.data, chunk.length ), chunk.length, NULL );
+						contact->imgid = purple_imgstore_new_with_id( g_memdup( chunk.data, chunk.length ), chunk.length, NULL );
 						/* show the profile */
 						mxit_show_profile( session, chunk.mxitid, contact->profile );
 					}
@@ -2309,6 +2355,11 @@
 				mxit_parse_cmd_suggestcontacts( session, &packet->records[2], packet->rcount - 2 );
 				break;
 
+		case CP_CMD_GOT_MSGEVENT :
+				/* received message event */
+				mxit_parse_cmd_msgevent( session, &packet->records[2], packet->rcount - 2 );
+				break;
+
 		case CP_CMD_MOOD :
 				/* mood update */
 		case CP_CMD_UPDATE :
--- a/libpurple/protocols/mxit/protocol.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/protocol.h	Fri Jan 25 02:22:38 2013 -0500
@@ -77,9 +77,12 @@
 #define		MXIT_CF_VOICE			0x1000000
 #define		MXIT_CF_VIDEO			0x2000000
 #define		MXIT_CF_TOUCHSCREEN		0x4000000
+#define		MXIT_CF_SVC_CONNECTION	0x8000000
+#define		MXIT_CF_MXML			0x10000000
+#define		MXIT_CF_TYPING_NOTIFY	0x20000000
 
 /* Client features supported by this implementation */
-#define		MXIT_CP_FEATURES		( MXIT_CF_FILE_TRANSFER | MXIT_CF_FILE_ACCESS | MXIT_CF_AUDIO | MXIT_CF_MARKUP | MXIT_CF_EXT_MARKUP | MXIT_CF_NO_GATEWAYS | MXIT_CF_IMAGES | MXIT_CF_COMMANDS | MXIT_CF_VIBES | MXIT_CF_MIDP2 )
+#define		MXIT_CP_FEATURES		( MXIT_CF_FILE_TRANSFER | MXIT_CF_FILE_ACCESS | MXIT_CF_AUDIO | MXIT_CF_MARKUP | MXIT_CF_EXT_MARKUP | MXIT_CF_NO_GATEWAYS | MXIT_CF_IMAGES | MXIT_CF_COMMANDS | MXIT_CF_VIBES | MXIT_CF_MIDP2 | MXIT_CF_TYPING_NOTIFY )
 
 
 #define		MXIT_PING_INTERVAL		( 5 * 60 )				/* ping the server after X seconds of being idle (5 minutes) */
@@ -132,6 +135,7 @@
 #define		CP_CMD_SPLASHCLICK		0x001F					/* (31) splash-screen clickthrough */
 #define		CP_CMD_STATUS			0x0020					/* (32) set shown presence & status */
 #define		CP_CMD_MSGEVENT			0x0023					/* (35) Raise message event */
+#define		CP_CMD_GOT_MSGEVENT		0x0024					/* (36) Get message event */
 #define		CP_CMD_MOOD				0x0029					/* (41) set mood */
 #define		CP_CMD_KICK				0x002B					/* (43) login kick */
 #define		CP_CMD_GRPCHAT_CREATE	0x002C					/* (44) create new groupchat */
@@ -161,6 +165,7 @@
 #define		CP_MSG_RPLY_TL_ENCRYPT	0x0080					/* reply should be transport encrypted */
 #define		CP_MSG_MARKUP			0x0200					/* message may contain markup */
 #define		CP_MSG_EMOTICON			0x0400					/* message may contain custom emoticons */
+#define		CP_MSG_FAREWELL			0x0800					/* this is a farewell message */
 
 /* redirect types */
 #define		CP_REDIRECT_PERMANENT	1						/* permanent redirect */
@@ -178,6 +183,10 @@
 /* message event types */
 #define		CP_MSGEVENT_DELIVERED	0x02					/* message was delivered */
 #define		CP_MSGEVENT_DISPLAYED	0x04					/* message was viewed */
+#define		CP_MSGEVENT_TYPING		0x10					/* user is typing */
+#define		CP_MSGEVENT_STOPPED		0x20					/* user has stopped typing */
+#define		CP_MSGEVENT_ANGRY		0x40					/* user is typing angrily */
+#define		CP_MSGEVENT_ERASING		0x80					/* user is erasing text */
 
 /* extended profile attribute fields */
 #define		CP_PROFILE_BIRTHDATE	"birthdate"				/* Birthdate (String - ISO 8601 format) */
@@ -198,9 +207,11 @@
 #define		CP_PROFILE_LASTSEEN		"lastseen"				/* Last-Online timestamp */
 #define		CP_PROFILE_WHEREAMI		"whereami"				/* Where am I / Where I live */
 #define		CP_PROFILE_ABOUTME		"aboutme"				/* About me */
+#define		CP_PROFILE_RELATIONSHIP	"relationship"			/* Relationship Status */
 
 /* extended profile field types */
 #define		CP_PROFILE_TYPE_BOOL	0x02					/* boolean (0 or 1) */
+#define		CP_PROFILE_TYPE_SHORT	0x04					/* short (16-bit) */
 #define		CP_PROFILE_TYPE_INT		0x05					/* integer (32-bit) */
 #define		CP_PROFILE_TYPE_LONG	0x06					/* long (64-bit) */
 #define		CP_PROFILE_TYPE_UTF8	0x0A					/* UTF8 string */
--- a/libpurple/protocols/mxit/roster.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/roster.h	Fri Jan 25 02:22:38 2013 -0500
@@ -82,6 +82,7 @@
 /* MXit presence flags */
 #define		MXIT_PFLAG_VOICE			0x1
 #define		MXIT_PFLAG_VIDEO			0x2
+#define		MXIT_PFLAG_TYPING			0x4
 
 
 /* Subscription types */
--- a/libpurple/protocols/mxit/splashscreen.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/mxit/splashscreen.c	Fri Jan 25 02:22:38 2013 -0500
@@ -184,7 +184,7 @@
 		char buf[128];
 
 		/* Add splash-image to imagestore */
-		imgid = purple_imgstore_add_with_id(g_memdup(imgdata, imglen), imglen, NULL);
+		imgid = purple_imgstore_new_with_id(g_memdup(imgdata, imglen), imglen, NULL);
 
 		/* Generate and display message */
 		g_snprintf(buf, sizeof(buf),
--- a/libpurple/protocols/myspace/message.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/myspace/message.c	Fri Jan 25 02:22:38 2013 -0500
@@ -129,7 +129,7 @@
  *
  * @param first_key The first argument (a key), or NULL to take all arguments
  *    from argp.
- * @param argp A va_list of variadic arguments, already started with va_start(). Will be va_end()'d.
+ * @param argp A va_list of variadic arguments, already started with va_start().
  * @return New MsimMessage *, must be freed with msim_msg_free().
  *
  * For internal use - users probably want msim_msg_new() or msim_send().
@@ -211,7 +211,6 @@
 				break;
 		}
 	} while(key);
-	va_end(argp);
 
 	return msg;
 }
@@ -227,14 +226,16 @@
 MsimMessage *
 msim_msg_new(gchar *first_key, ...)
 {
+	MsimMessage *ret = NULL;
 	va_list argp;
 
 	if (first_key) {
-	va_start(argp, first_key);
-		return msim_msg_new_v(first_key, argp);
-	} else {
-		return NULL;
+		va_start(argp, first_key);
+		ret = msim_msg_new_v(first_key, argp);
+		va_end(argp);
 	}
+
+	return ret;
 }
 
 /**
@@ -960,6 +961,7 @@
 
 	va_start(argp, session);
 	msg = msim_msg_new_v(NULL, argp);
+	va_end(argp);
 
 	/* Actually send the message. */
 	success = msim_msg_send(session, msg);
--- a/libpurple/protocols/myspace/user.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/myspace/user.h	Fri Jan 25 02:22:38 2013 -0500
@@ -20,6 +20,8 @@
 #ifndef _MYSPACE_USER_H
 #define _MYSPACE_USER_H
 
+#include "obsolete.h"
+
 /* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */
 /* GHashTable? */
 typedef struct _MsimUser
--- a/libpurple/protocols/oscar/family_feedbag.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/oscar/family_feedbag.c	Fri Jan 25 02:22:38 2013 -0500
@@ -605,9 +605,10 @@
 				if (od->ssi.pending) {
 					for (cur=od->ssi.pending; cur->next; cur=cur->next);
 					cur->next = new;
-				} else
+				} else {
 					od->ssi.pending = new;
-					aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1);
+				}
+				aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1);
 			}
 		}
 	}
@@ -626,9 +627,10 @@
 				if (od->ssi.pending) {
 					for (cur=od->ssi.pending; cur->next; cur=cur->next);
 					cur->next = new;
-				} else
+				} else {
 					od->ssi.pending = new;
-					aim_ssi_item_debug_append(debugstr, "Adding item ", cur1);
+				}
+				aim_ssi_item_debug_append(debugstr, "Adding item ", cur1);
 			}
 		}
 	}
@@ -648,20 +650,23 @@
 				if (od->ssi.pending) {
 					for (cur=od->ssi.pending; cur->next; cur=cur->next);
 					cur->next = new;
-				} else
+				} else {
 					od->ssi.pending = new;
-					aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1);
+				}
+				aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1);
 			}
 		}
 	}
 	if (debugstr->len > 0) {
 		purple_debug_info("oscar", "%s", debugstr->str);
 		if (purple_debug_is_verbose()) {
+			PurpleAccount *account = purple_connection_get_account(od->gc);
 			g_string_truncate(debugstr, 0);
-			for (cur1 = od->ssi.local.data; cur1; cur1 = cur1->next)
+			for (cur1 = od->ssi.local.data; cur1; cur1 = cur1->next) {
 				aim_ssi_item_debug_append(debugstr, "\t", cur1);
+			}
 			purple_debug_misc("oscar", "Dumping item list of account %s:\n%s",
-				purple_account_get_username(purple_connection_get_account(od->gc)), debugstr->str);
+					purple_account_get_username(account), debugstr->str);
 		}
 	}
 	g_string_free(debugstr, TRUE);
--- a/libpurple/protocols/oscar/odc.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/oscar/odc.c	Fri Jan 25 02:22:38 2013 -0500
@@ -356,7 +356,7 @@
 
 			if ((embedded_data != NULL) && (embedded_data->size == size))
 			{
-				imgid = purple_imgstore_add_with_id(g_memdup(embedded_data->data, size), size, src);
+				imgid = purple_imgstore_new_with_id(g_memdup(embedded_data->data, size), size, src);
 
 				/* Record the image number */
 				images = g_slist_append(images, GINT_TO_POINTER(imgid));
--- a/libpurple/protocols/oscar/oscar.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/oscar/oscar.c	Fri Jan 25 02:22:38 2013 -0500
@@ -633,15 +633,6 @@
 	return subtype1 - subtype2;
 }
 
-#if !GLIB_CHECK_VERSION(2,14,0)
-static void hash_table_get_list_of_keys(gpointer key, gpointer value, gpointer user_data)
-{
-	GList **handlers = (GList **)user_data;
-
-	*handlers = g_list_prepend(*handlers, key);
-}
-#endif /* GLIB < 2.14.0 */
-
 void
 oscar_login(PurpleAccount *account)
 {
@@ -711,12 +702,7 @@
 	oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, purple_parse_searchreply, 0);
 
 	g_string_append(msg, "Registered handlers: ");
-#if GLIB_CHECK_VERSION(2,14,0)
 	handlers = g_hash_table_get_keys(od->handlerlist);
-#else
-	handlers = NULL;
-	g_hash_table_foreach(od->handlerlist, hash_table_get_list_of_keys, &handlers);
-#endif /* GLIB < 2.14.0 */
 	sorted_handlers = g_list_sort(g_list_copy(handlers), compare_handlers);
 	for (cur = sorted_handlers; cur; cur = cur->next) {
 		guint x = GPOINTER_TO_UINT(cur->data);
@@ -2317,6 +2303,7 @@
 	va_list ap;
 	guint16 chan, reason;
 	char *who;
+	int ret = 1;
 
 	va_start(ap, fr);
 	chan = (guint16)va_arg(ap, unsigned int);
@@ -2325,7 +2312,7 @@
 
 	if (chan == 0x0002) { /* File transfer declined */
 		guchar *cookie = va_arg(ap, guchar *);
-		return purple_parse_clientauto_ch2(od, who, reason, cookie);
+		ret = purple_parse_clientauto_ch2(od, who, reason, cookie);
 	} else if (chan == 0x0004) { /* ICQ message */
 		guint32 state = 0;
 		char *msg = NULL;
@@ -2333,12 +2320,12 @@
 			state = va_arg(ap, guint32);
 			msg = va_arg(ap, char *);
 		}
-		return purple_parse_clientauto_ch4(od, who, reason, state, msg);
+		ret = purple_parse_clientauto_ch4(od, who, reason, state, msg);
 	}
 
 	va_end(ap);
 
-	return 1;
+	return ret;
 }
 
 static int purple_parse_genericerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
--- a/libpurple/protocols/oscar/oscar.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/oscar/oscar.h	Fri Jan 25 02:22:38 2013 -0500
@@ -35,6 +35,7 @@
 #include "eventloop.h"
 #include "proxy.h"
 #include "sslconn.h"
+#include "obsolete.h"
 
 #include <stdio.h>
 #include <string.h>
--- a/libpurple/protocols/sametime/sametime.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/sametime/sametime.c	Fri Jan 25 02:22:38 2013 -0500
@@ -2780,7 +2780,7 @@
       cid = make_cid(cid);
 
       /* add image to the purple image store */
-      img = purple_imgstore_add_with_id(d_dat, d_len, cid);
+      img = purple_imgstore_new_with_id(d_dat, d_len, cid);
 
       /* map the cid to the image store identifier */
       g_hash_table_insert(img_by_cid, cid, GINT_TO_POINTER(img));
--- a/libpurple/protocols/silc/Makefile.mingw	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/silc/Makefile.mingw	Fri Jan 25 02:22:38 2013 -0500
@@ -11,7 +11,7 @@
 
 TARGET = libsilc
 NEEDED_DLLS =		$(SILC_TOOLKIT)/bin/libsilc-1-1-2.dll \
-			$(SILC_TOOLKIT)/bin/libsilcclient-1-1-2.dll
+			$(SILC_TOOLKIT)/bin/libsilcclient-1-1-3.dll
 TYPE = PLUGIN
 
 # Static or Plugin...
--- a/libpurple/protocols/silc/ops.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/silc/ops.c	Fri Jan 25 02:22:38 2013 -0500
@@ -207,7 +207,7 @@
 		if (channel && !convo)
 			goto out;
 
-		imgid = purple_imgstore_add_with_id(g_memdup(data, data_len), data_len, "");
+		imgid = purple_imgstore_new_with_id(g_memdup(data, data_len), data_len, "");
 		if (imgid) {
 			cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV;
 			g_snprintf(tmp, sizeof(tmp),
--- a/libpurple/protocols/yahoo/libymsg.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/yahoo/libymsg.c	Fri Jan 25 02:22:38 2013 -0500
@@ -22,6 +22,7 @@
  */
 
 #include "internal.h"
+#include "obsolete.h"
 
 #include "account.h"
 #include "accountopt.h"
--- a/libpurple/protocols/yahoo/libymsg.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/yahoo/libymsg.h	Fri Jan 25 02:22:38 2013 -0500
@@ -96,6 +96,7 @@
 #define YAHOOJP_CLIENT_VERSION "9.0.0.1727"
 
 #define YAHOO_CLIENT_USERAGENT "Mozilla/5.0"
+#define YAHOO_CLIENT_USERAGENT_ALIAS "Mozilla/4.0 (compatible; MSIE 5.5)"
 
 /* Index into attention types list. */
 #define YAHOO_BUZZ 0
--- a/libpurple/protocols/yahoo/yahoo_aliases.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/yahoo/yahoo_aliases.c	Fri Jan 25 02:22:38 2013 -0500
@@ -23,6 +23,7 @@
 
 
 #include "internal.h"
+#include "obsolete.h"
 
 #include "account.h"
 #include "accountopt.h"
--- a/libpurple/protocols/yahoo/yahoo_picture.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/yahoo/yahoo_picture.c	Fri Jan 25 02:22:38 2013 -0500
@@ -22,6 +22,7 @@
  */
 
 #include "internal.h"
+#include "obsolete.h"
 
 #include "account.h"
 #include "accountopt.h"
--- a/libpurple/protocols/yahoo/yahoo_profile.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/protocols/yahoo/yahoo_profile.c	Fri Jan 25 02:22:38 2013 -0500
@@ -24,6 +24,7 @@
 #define PHOTO_SUPPORT 1
 
 #include "internal.h"
+#include "obsolete.h"
 #include "debug.h"
 #include "notify.h"
 #include "util.h"
@@ -1045,7 +1046,7 @@
 		} else {
 			purple_debug_info("yahoo", "%s is %" G_GSIZE_FORMAT
 					" bytes\n", photo_url_text, len);
-			id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL);
+			id = purple_imgstore_new_with_id(g_memdup(url_text, len), len, NULL);
 
 			tmp = g_strdup_printf("<img id=\"" PURPLE_STORED_IMAGE_PROTOCOL "%d\"><br>",
 			                      id);
--- a/libpurple/prpl.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/prpl.h	Fri Jan 25 02:22:38 2013 -0500
@@ -475,7 +475,7 @@
 	 * On the other hand, both of these are invalid for protocols with
 	 * number-based usernames, so function should return NULL in such case.
 	 *
-	 * @param account  The account, that username is related with. Can
+	 * @param account  The account the username is related to. Can
 	 *                 be NULL.
 	 * @param who      The username to convert.
 	 * @return         Normalized username, or NULL, if it's invalid.
--- a/libpurple/smiley.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/smiley.c	Fri Jan 25 02:22:38 2013 -0500
@@ -608,7 +608,7 @@
 		return NULL;
 	}
 
-	stored_img = purple_imgstore_add(smiley_data, smiley_data_len, filename);
+	stored_img = purple_imgstore_new(smiley_data, smiley_data_len, filename);
 
 	g_free(filename);
 
--- a/libpurple/stringref.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/stringref.c	Fri Jan 25 02:22:38 2013 -0500
@@ -73,7 +73,9 @@
 	len = strlen(value);
 
 	newref = g_malloc(sizeof(PurpleStringref) + len);
-	g_strlcpy(newref->value, value, len);
+	/* g_strlcpy() takes the size of the buffer, including the NUL.
+	   strlen() returns the length of the string, without the NUL. */
+	g_strlcpy(newref->value, value, len + 1);
 	newref->ref = 1;
 
 	return newref;
--- a/libpurple/tests/check_libpurple.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/tests/check_libpurple.c	Fri Jan 25 02:22:38 2013 -0500
@@ -25,11 +25,7 @@
 	purple_check_input_add,
 	g_source_remove,
 	NULL, /* input_get_error */
-#if GLIB_CHECK_VERSION(2,14,0)
 	g_timeout_add_seconds,
-#else
-	NULL,
-#endif
 	NULL,
 	NULL,
 	NULL
--- a/libpurple/tests/test_util.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/tests/test_util.c	Fri Jan 25 02:22:38 2013 -0500
@@ -167,14 +167,179 @@
 {
 	gchar *xhtml = NULL;
 	gchar *plaintext = NULL;
+
 	purple_markup_html_to_xhtml("<a>", &xhtml, &plaintext);
 	assert_string_equal_free("<a href=\"\"></a>", xhtml);
 	assert_string_equal_free("", plaintext);
 
+	purple_markup_html_to_xhtml("<A href='URL'>ABOUT</a>", &xhtml, &plaintext);
+	assert_string_equal_free("<a href=\"URL\">ABOUT</a>", xhtml);
+	assert_string_equal_free("ABOUT <URL>", plaintext);
+
+	purple_markup_html_to_xhtml("<a href='URL'>URL</a>", &xhtml, &plaintext);
+	assert_string_equal_free("URL", plaintext);
+	assert_string_equal_free("<a href=\"URL\">URL</a>", xhtml);
+
+	purple_markup_html_to_xhtml("<a href='mailto:mail'>mail</a>", &xhtml, &plaintext);
+	assert_string_equal_free("mail", plaintext);
+	assert_string_equal_free("<a href=\"mailto:mail\">mail</a>", xhtml);
+
+	purple_markup_html_to_xhtml("<A href='\"U&apos;R&L'>ABOUT</a>", &xhtml, &plaintext);
+	assert_string_equal_free("<a href=\"&quot;U&apos;R&amp;L\">ABOUT</a>", xhtml);
+	assert_string_equal_free("ABOUT <\"U'R&L>", plaintext);
+
+	purple_markup_html_to_xhtml("<img src='SRC' alt='ALT'/>", &xhtml, &plaintext);
+	assert_string_equal_free("<img src='SRC' alt='ALT' />", xhtml);
+	assert_string_equal_free("ALT", plaintext);
+
+	purple_markup_html_to_xhtml("<img src=\"'S&apos;R&C\" alt=\"'A&apos;L&T\"/>", &xhtml, &plaintext);
+	assert_string_equal_free("<img src='&apos;S&apos;R&amp;C' alt='&apos;A&apos;L&amp;T' />", xhtml);
+	assert_string_equal_free("'A'L&T", plaintext);
+
+	purple_markup_html_to_xhtml("<unknown>", &xhtml, &plaintext);
+	assert_string_equal_free("&lt;unknown>", xhtml);
+	assert_string_equal_free("<unknown>", plaintext);
+
+	purple_markup_html_to_xhtml("&eacute;&amp;", &xhtml, &plaintext);
+	assert_string_equal_free("&eacute;&amp;", xhtml);
+	assert_string_equal_free("&eacute;&", plaintext);
+
+	purple_markup_html_to_xhtml("<h1>A<h2>B</h2>C</h1>", &xhtml, &plaintext);
+	assert_string_equal_free("<h1>A<h2>B</h2>C</h1>", xhtml);
+	assert_string_equal_free("ABC", plaintext);
+
+	purple_markup_html_to_xhtml("<h1><h2><h3><h4>", &xhtml, &plaintext);
+	assert_string_equal_free("<h1><h2><h3><h4></h4></h3></h2></h1>", xhtml);
+	assert_string_equal_free("", plaintext);
+
+	purple_markup_html_to_xhtml("<italic/>", &xhtml, &plaintext);
+	assert_string_equal_free("<em/>", xhtml);
+	assert_string_equal_free("", plaintext);
+
+	purple_markup_html_to_xhtml("</", &xhtml, &plaintext);
+	assert_string_equal_free("&lt;/", xhtml);
+	assert_string_equal_free("</", plaintext);
+
+	purple_markup_html_to_xhtml("</div>", &xhtml, &plaintext);
+	assert_string_equal_free("", xhtml);
+	assert_string_equal_free("", plaintext);
+
+	purple_markup_html_to_xhtml("<hr/>", &xhtml, &plaintext);
+	assert_string_equal_free("<br/>", xhtml);
+	assert_string_equal_free("\n", plaintext);
+
+	purple_markup_html_to_xhtml("<hr>", &xhtml, &plaintext);
+	assert_string_equal_free("<br/>", xhtml);
+	assert_string_equal_free("\n", plaintext);
+
+	purple_markup_html_to_xhtml("<br />", &xhtml, &plaintext);
+	assert_string_equal_free("<br/>", xhtml);
+	assert_string_equal_free("\n", plaintext);
+
+	purple_markup_html_to_xhtml("<br>INSIDE</br>", &xhtml, &plaintext);
+	assert_string_equal_free("<br/>INSIDE", xhtml);
+	assert_string_equal_free("\nINSIDE", plaintext);
+
+	purple_markup_html_to_xhtml("<div></div>", &xhtml, &plaintext);
+	assert_string_equal_free("<div></div>", xhtml);
+	assert_string_equal_free("", plaintext);
+
+	purple_markup_html_to_xhtml("<div/>", &xhtml, &plaintext);
+	assert_string_equal_free("<div/>", xhtml);
+	assert_string_equal_free("", plaintext);
+
+	purple_markup_html_to_xhtml("<div attr='\"&<>'/>", &xhtml, &plaintext);
+	assert_string_equal_free("<div attr='&quot;&amp;&lt;&gt;'/>", xhtml);
+	assert_string_equal_free("", plaintext);
+
+	purple_markup_html_to_xhtml("<div attr=\"'\"/>", &xhtml, &plaintext);
+	assert_string_equal_free("<div attr=\"&apos;\"/>", xhtml);
+	assert_string_equal_free("", plaintext);
+
+	purple_markup_html_to_xhtml("<div/> < <div/>", &xhtml, &plaintext);
+	assert_string_equal_free("<div/> &lt; <div/>", xhtml);
+	assert_string_equal_free(" < ", plaintext);
+
+	purple_markup_html_to_xhtml("<div>x</div>", &xhtml, &plaintext);
+	assert_string_equal_free("<div>x</div>", xhtml);
+	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<b>x</b>", &xhtml, &plaintext);
+	assert_string_equal_free("<span style='font-weight: bold;'>x</span>", xhtml);
+	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<bold>x</bold>", &xhtml, &plaintext);
+	assert_string_equal_free("<span style='font-weight: bold;'>x</span>", xhtml);
+	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<strong>x</strong>", &xhtml, &plaintext);
+	assert_string_equal_free("<span style='font-weight: bold;'>x</span>", xhtml);
+	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<u>x</u>", &xhtml, &plaintext);
+	assert_string_equal_free("<span style='text-decoration: underline;'>x</span>", xhtml);
+	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<underline>x</underline>", &xhtml, &plaintext);
+	assert_string_equal_free("<span style='text-decoration: underline;'>x</span>", xhtml);
+	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<s>x</s>", &xhtml, &plaintext);
+	assert_string_equal_free("<span style='text-decoration: line-through;'>x</span>", xhtml);
+	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<strike>x</strike>", &xhtml, &plaintext);
+	assert_string_equal_free("<span style='text-decoration: line-through;'>x</span>", xhtml);
+	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<sub>x</sub>", &xhtml, &plaintext);
+	assert_string_equal_free("<span style='vertical-align:sub;'>x</span>", xhtml);
+	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<sup>x</sup>", &xhtml, &plaintext);
+	assert_string_equal_free("<span style='vertical-align:super;'>x</span>", xhtml);
+	assert_string_equal_free("x", plaintext);
 
 	purple_markup_html_to_xhtml("<FONT>x</FONT>", &xhtml, &plaintext);
 	assert_string_equal_free("x", xhtml);
 	assert_string_equal_free("x", plaintext);
+
+	purple_markup_html_to_xhtml("<font face=\"'Times&gt;New & Roman'\">x</font>", &xhtml, &plaintext);
+	assert_string_equal_free("x", plaintext);
+	assert_string_equal_free("<span style='font-family: \"Times&gt;New &amp; Roman\";'>x</span>", xhtml);
+
+	purple_markup_html_to_xhtml("<font back=\"'color&gt;blue&red'\">x</font>", &xhtml, &plaintext);
+	assert_string_equal_free("x", plaintext);
+	assert_string_equal_free("<span style='background: \"color&gt;blue&amp;red\";'>x</span>", xhtml);
+
+	purple_markup_html_to_xhtml("<font color=\"'color&gt;blue&red'\">x</font>", &xhtml, &plaintext);
+	assert_string_equal_free("x", plaintext);
+	assert_string_equal_free("<span style='color: \"color&gt;blue&amp;red\";'>x</span>", xhtml);
+
+	purple_markup_html_to_xhtml("<font size=1>x</font>", &xhtml, &plaintext);
+	assert_string_equal_free("x", plaintext);
+	assert_string_equal_free("<span style='font-size: xx-small;'>x</span>", xhtml);
+
+	purple_markup_html_to_xhtml("<font size=432>x</font>", &xhtml, &plaintext);
+	assert_string_equal_free("x", plaintext);
+	assert_string_equal_free("<span style='font-size: medium;'>x</span>", xhtml);
+
+        /* The following tests document a behaviour that looks suspicious */
+
+        /* bug report http://developer.pidgin.im/ticket/13485 */
+        purple_markup_html_to_xhtml("<!--COMMENT-->", &xhtml, &plaintext);
+	assert_string_equal_free("<!--COMMENT-->", xhtml);
+	assert_string_equal_free("COMMENT-->", plaintext);
+
+        /* no bug report */
+	purple_markup_html_to_xhtml("<br  />", &xhtml, &plaintext);
+	assert_string_equal_free("&lt;br  />", xhtml);
+	assert_string_equal_free("<br  />", plaintext);
+
+        /* same code section as <br  /> */
+	purple_markup_html_to_xhtml("<hr  />", &xhtml, &plaintext);
+	assert_string_equal_free("&lt;hr  />", xhtml);
+	assert_string_equal_free("<hr  />", plaintext);
 }
 END_TEST
 
--- a/libpurple/upnp.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/upnp.c	Fri Jan 25 02:22:38 2013 -0500
@@ -34,6 +34,7 @@
 #include "signals.h"
 #include "util.h"
 #include "xmlnode.h"
+#include "obsolete.h"
 
 /***************************************************************
 ** General Defines                                             *
@@ -256,9 +257,12 @@
 	}
 
 	/* get the baseURL of the device */
+	baseURL = NULL;
 	if((baseURLNode = xmlnode_get_child(xmlRootNode, "URLBase")) != NULL) {
 		baseURL = xmlnode_get_data(baseURLNode);
-	} else {
+	}
+	/* fixes upnp-descriptions with empty urlbase-element */
+	if(baseURL == NULL){
 		baseURL = g_strdup(httpURL);
 	}
 
--- a/libpurple/util.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/util.c	Fri Jan 25 02:22:38 2013 -0500
@@ -28,50 +28,11 @@
 #include "core.h"
 #include "debug.h"
 #include "notify.h"
-#include "ntlm.h"
 #include "prpl.h"
 #include "prefs.h"
 #include "util.h"
 
-struct _PurpleUtilFetchUrlData
-{
-	PurpleUtilFetchUrlCallback callback;
-	void *user_data;
-
-	struct
-	{
-		char *user;
-		char *passwd;
-		char *address;
-		int port;
-		char *page;
-
-	} website;
-
-	char *url;
-	int num_times_redirected;
-	gboolean full;
-	char *user_agent;
-	gboolean http11;
-	char *request;
-	gsize request_written;
-	gboolean include_headers;
-
-	gboolean is_ssl;
-	PurpleSslConnection *ssl_connection;
-	PurpleProxyConnectData *connect_data;
-	int fd;
-	guint inpa;
-
-	gboolean got_headers;
-	gboolean has_explicit_data_len;
-	char *webdata;
-	gsize len;
-	unsigned long data_len;
-	gssize max_len;
-	gboolean chunked;
-	PurpleAccount *account;
-};
+#include <json-glib/json-glib.h>
 
 struct _PurpleMenuAction
 {
@@ -84,6 +45,8 @@
 static char *custom_user_dir = NULL;
 static char *user_dir = NULL;
 
+static JsonNode *escape_js_node = NULL;
+static JsonGenerator *escape_js_gen = NULL;
 
 PurpleMenuAction *
 purple_menu_action_new(const char *label, PurpleCallback callback, gpointer data,
@@ -165,8 +128,8 @@
 void
 purple_util_init(void)
 {
-	/* This does nothing right now.  It exists for symmetry with
-	 * purple_util_uninit() and forwards compatibility. */
+	escape_js_node = json_node_new(JSON_NODE_VALUE);
+	escape_js_gen = json_generator_new();
 }
 
 void
@@ -179,6 +142,12 @@
 
 	g_free(user_dir);
 	user_dir = NULL;
+
+	json_node_free(escape_js_node);
+	escape_js_node = NULL;
+
+	g_object_unref(escape_js_gen);
+	escape_js_gen = NULL;
 }
 
 /**************************************************************************
@@ -1891,7 +1860,7 @@
 							if (cdata && url &&
 									(!g_string_equal(cdata, url) && (g_ascii_strncasecmp(url->str, "mailto:", 7) != 0 ||
 									                                 g_utf8_collate(url->str + 7, cdata->str) != 0)))
-								g_string_append_printf(plain, " <%s>", g_strstrip(url->str));
+									g_string_append_printf(plain, " <%s>", g_strstrip(purple_unescape_html(url->str)));
 							if (cdata) {
 								g_string_free(cdata, TRUE);
 								cdata = NULL;
@@ -2022,33 +1991,39 @@
 				if (!g_ascii_strncasecmp(c, "<img", 4) && (*(c+4) == '>' || *(c+4) == ' ')) {
 					const char *p = c + 4;
 					GString *src = NULL, *alt = NULL;
+#define ESCAPE(from, to)        \
+		CHECK_QUOTE(from); \
+		while (VALID_CHAR(from)) { \
+			int len; \
+			if ((*from == '&') && (purple_markup_unescape_entity(from, &len) == NULL)) \
+				to = g_string_append(to, "&amp;"); \
+			else if (*from == '\'') \
+				to = g_string_append(to, "&apos;"); \
+			else \
+				to = g_string_append_c(to, *from); \
+			from++; \
+		}
+
 					while (*p && *p != '>') {
 						if (!g_ascii_strncasecmp(p, "src=", 4)) {
 							const char *q = p + 4;
 							if (src)
 								g_string_free(src, TRUE);
 							src = g_string_new("");
-							CHECK_QUOTE(q);
-							while (VALID_CHAR(q)) {
-								src = g_string_append_c(src, *q);
-								q++;
-							}
+							ESCAPE(q, src);
 							p = q;
 						} else if (!g_ascii_strncasecmp(p, "alt=", 4)) {
 							const char *q = p + 4;
 							if (alt)
 								g_string_free(alt, TRUE);
 							alt = g_string_new("");
-							CHECK_QUOTE(q);
-							while (VALID_CHAR(q)) {
-								alt = g_string_append_c(alt, *q);
-								q++;
-							}
+							ESCAPE(q, alt);
 							p = q;
 						} else {
 							p++;
 						}
 					}
+#undef ESCAPE
 					if ((c = strchr(p, '>')) != NULL)
 						c++;
 					else
@@ -2058,7 +2033,7 @@
 						g_string_append_printf(xhtml, "<img src='%s' alt='%s' />", g_strstrip(src->str), alt ? alt->str : "");
 					if(alt) {
 						if(plain)
-							plain = g_string_append(plain, alt->str);
+							plain = g_string_append(plain, purple_unescape_html(alt->str));
 						if(!src && xhtml)
 							xhtml = g_string_append(xhtml, alt->str);
 						g_string_free(alt, TRUE);
@@ -2083,6 +2058,8 @@
 								int len;
 								if ((*q == '&') && (purple_markup_unescape_entity(q, &len) == NULL))
 									url = g_string_append(url, "&amp;");
+								else if (*q == '"')
+									url = g_string_append(url, "&quot;");
 								else
 									url = g_string_append_c(url, *q);
 								q++;
@@ -2104,6 +2081,18 @@
 						g_string_append_printf(xhtml, "<a href=\"%s\">", url ? g_strstrip(url->str) : "");
 					continue;
 				}
+#define ESCAPE(from, to)        \
+		CHECK_QUOTE(from); \
+		while (VALID_CHAR(from)) { \
+			int len; \
+			if ((*from == '&') && (purple_markup_unescape_entity(from, &len) == NULL)) \
+				to = g_string_append(to, "&amp;"); \
+			else if (*from == '\'') \
+				to = g_string_append_c(to, '\"'); \
+			else \
+				to = g_string_append_c(to, *from); \
+			from++; \
+		}
 				if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) {
 					const char *p = c + 5;
 					GString *style = g_string_new("");
@@ -2112,33 +2101,21 @@
 						if (!g_ascii_strncasecmp(p, "back=", 5)) {
 							const char *q = p + 5;
 							GString *color = g_string_new("");
-							CHECK_QUOTE(q);
-							while (VALID_CHAR(q)) {
-								color = g_string_append_c(color, *q);
-								q++;
-							}
+							ESCAPE(q, color);
 							g_string_append_printf(style, "background: %s; ", color->str);
 							g_string_free(color, TRUE);
 							p = q;
 						} else if (!g_ascii_strncasecmp(p, "color=", 6)) {
 							const char *q = p + 6;
 							GString *color = g_string_new("");
-							CHECK_QUOTE(q);
-							while (VALID_CHAR(q)) {
-								color = g_string_append_c(color, *q);
-								q++;
-							}
+							ESCAPE(q, color);
 							g_string_append_printf(style, "color: %s; ", color->str);
 							g_string_free(color, TRUE);
 							p = q;
 						} else if (!g_ascii_strncasecmp(p, "face=", 5)) {
 							const char *q = p + 5;
 							GString *face = g_string_new("");
-							CHECK_QUOTE(q);
-							while (VALID_CHAR(q)) {
-								face = g_string_append_c(face, *q);
-								q++;
-							}
+							ESCAPE(q, face);
 							g_string_append_printf(style, "font-family: %s; ", g_strstrip(face->str));
 							g_string_free(face, TRUE);
 							p = q;
@@ -2193,6 +2170,7 @@
 					g_string_free(style, TRUE);
 					continue;
 				}
+#undef ESCAPE
 				if (!g_ascii_strncasecmp(c, "<body ", 6)) {
 					const char *p = c + 6;
 					gboolean did_something = FALSE;
@@ -3022,6 +3000,15 @@
 		return FALSE;
 	}
 
+#ifndef _WIN32
+	/* Set file permissions */
+	if (fchmod(fileno(file), S_IRUSR | S_IWUSR) == -1)
+	{
+		purple_debug_error("util", "Error setting permissions of %s: %s\n",
+				filename_temp, g_strerror(errno));
+	}
+#endif
+
 	/* Write to file */
 	real_size = (size == -1) ? strlen(data) : (size_t) size;
 	byteswritten = fwrite(data, 1, real_size, file);
@@ -3101,15 +3088,6 @@
 		return FALSE;
 	}
 
-#ifndef _WIN32
-	/* Set file permissions */
-	if (chmod(filename_temp, S_IRUSR | S_IWUSR) == -1)
-	{
-		purple_debug_error("util", "Error setting permissions of file %s: %s\n",
-						 filename_temp, g_strerror(errno));
-	}
-#endif
-
 	/* Rename to the REAL name */
 	if (g_rename(filename_temp, filename_full) == -1)
 	{
@@ -3421,12 +3399,7 @@
 gboolean
 purple_strequal(const gchar *left, const gchar *right)
 {
-#if GLIB_CHECK_VERSION(2,16,0)
 	return (g_strcmp0(left, right) == 0);
-#else
-	return ((left == NULL && right == NULL) ||
-	        (left != NULL && right != NULL && strcmp(left, right) == 0));
-#endif
 }
 
 const char *
@@ -3733,9 +3706,9 @@
 		}
 
 		if (size_index == 0) {
-			return g_strdup_printf("%" G_GSIZE_FORMAT " %s", size, size_str[size_index]);
+			return g_strdup_printf("%" G_GOFFSET_FORMAT " %s", size, _(size_str[size_index]));
 		} else {
-			return g_strdup_printf("%.2f %s", size_mag, size_str[size_index]);
+			return g_strdup_printf("%.2f %s", size_mag, _(size_str[size_index]));
 		}
 	}
 }
@@ -3997,646 +3970,6 @@
 #undef PASSWD_CTRL
 }
 
-/**
- * The arguments to this function are similar to printf.
- */
-static void
-purple_util_fetch_url_error(PurpleUtilFetchUrlData *gfud, const char *format, ...)
-{
-	gchar *error_message;
-	va_list args;
-
-	va_start(args, format);
-	error_message = g_strdup_vprintf(format, args);
-	va_end(args);
-
-	gfud->callback(gfud, gfud->user_data, NULL, 0, error_message);
-	g_free(error_message);
-	purple_util_fetch_url_cancel(gfud);
-}
-
-static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message);
-static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond);
-static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data);
-
-static gboolean
-parse_redirect(const char *data, size_t data_len,
-			   PurpleUtilFetchUrlData *gfud)
-{
-	gchar *s;
-	gchar *new_url, *temp_url, *end;
-	gboolean full;
-	int len;
-
-	if ((s = g_strstr_len(data, data_len, "\nLocation: ")) == NULL)
-		/* We're not being redirected */
-		return FALSE;
-
-	s += strlen("Location: ");
-	end = strchr(s, '\r');
-
-	/* Just in case :) */
-	if (end == NULL)
-		end = strchr(s, '\n');
-
-	if (end == NULL)
-		return FALSE;
-
-	len = end - s;
-
-	new_url = g_malloc(len + 1);
-	strncpy(new_url, s, len);
-	new_url[len] = '\0';
-
-	full = gfud->full;
-
-	if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL)
-	{
-		temp_url = new_url;
-
-		new_url = g_strdup_printf("%s:%d%s", gfud->website.address,
-								  gfud->website.port, temp_url);
-
-		g_free(temp_url);
-
-		full = FALSE;
-	}
-
-	purple_debug_info("util", "Redirecting to %s\n", new_url);
-
-	gfud->num_times_redirected++;
-	if (gfud->num_times_redirected >= 5)
-	{
-		purple_util_fetch_url_error(gfud,
-				_("Could not open %s: Redirected too many times"),
-				gfud->url);
-		return TRUE;
-	}
-
-	/*
-	 * Try again, with this new location.  This code is somewhat
-	 * ugly, but we need to reuse the gfud because whoever called
-	 * us is holding a reference to it.
-	 */
-	g_free(gfud->url);
-	gfud->url = new_url;
-	gfud->full = full;
-	g_free(gfud->request);
-	gfud->request = NULL;
-
-	if (gfud->is_ssl) {
-		gfud->is_ssl = FALSE;
-		purple_ssl_close(gfud->ssl_connection);
-		gfud->ssl_connection = NULL;
-	} else {
-		purple_input_remove(gfud->inpa);
-		gfud->inpa = 0;
-		close(gfud->fd);
-		gfud->fd = -1;
-	}
-	gfud->request_written = 0;
-	gfud->len = 0;
-	gfud->data_len = 0;
-
-	g_free(gfud->website.user);
-	g_free(gfud->website.passwd);
-	g_free(gfud->website.address);
-	g_free(gfud->website.page);
-	purple_url_parse(new_url, &gfud->website.address, &gfud->website.port,
-				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
-
-	if (purple_strcasestr(new_url, "https://") != NULL) {
-		gfud->is_ssl = TRUE;
-		gfud->ssl_connection = purple_ssl_connect(gfud->account,
-				gfud->website.address, gfud->website.port,
-				ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud);
-	} else {
-		gfud->connect_data = purple_proxy_connect(NULL, gfud->account,
-				gfud->website.address, gfud->website.port,
-				url_fetch_connect_cb, gfud);
-	}
-
-	if (gfud->ssl_connection == NULL && gfud->connect_data == NULL)
-	{
-		purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
-				gfud->website.address);
-	}
-
-	return TRUE;
-}
-
-static const char *
-find_header_content(const char *data, size_t data_len, const char *header, size_t header_len)
-{
-	const char *p = NULL;
-
-	if (header_len <= 0)
-		header_len = strlen(header);
-
-	/* Note: data is _not_ nul-terminated.  */
-	if (data_len > header_len) {
-		if (header[0] == '\n')
-			p = (g_ascii_strncasecmp(data, header + 1, header_len - 1) == 0) ? data : NULL;
-		if (!p)
-			p = purple_strcasestr(data, header);
-		if (p)
-			p += header_len;
-	}
-
-	/* If we can find the header at all, try to sscanf it.
-	 * Response headers should end with at least \r\n, so sscanf is safe,
-	 * if we make sure that there is indeed a \n in our header.
-	 */
-	if (p && g_strstr_len(p, data_len - (p - data), "\n")) {
-		return p;
-	}
-
-	return NULL;
-}
-
-static size_t
-parse_content_len(const char *data, size_t data_len)
-{
-	size_t content_len = 0;
-	const char *p = NULL;
-
-	p = find_header_content(data, data_len, "\nContent-Length: ", sizeof("\nContent-Length: ") - 1);
-	if (p) {
-		sscanf(p, "%" G_GSIZE_FORMAT, &content_len);
-		purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len);
-	}
-
-	return content_len;
-}
-
-static gboolean
-content_is_chunked(const char *data, size_t data_len)
-{
-	const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ", sizeof("\nTransfer-Encoding: ") - 1);
-	if (p && g_ascii_strncasecmp(p, "chunked", 7) == 0)
-		return TRUE;
-
-	return FALSE;
-}
-
-/* Process in-place */
-static void
-process_chunked_data(char *data, gsize *len)
-{
-	gsize sz;
-	gsize newlen = 0;
-	char *p = data;
-	char *s = data;
-
-	while (*s) {
-		/* Read the size of this chunk */
-		if (sscanf(s, "%" G_GSIZE_MODIFIER "x", &sz) != 1)
-		{
-			purple_debug_error("util", "Error processing chunked data: "
-					"Expected data length, found: %s\n", s);
-			break;
-		}
-		if (sz == 0) {
-			/* We've reached the last chunk */
-			/*
-			 * TODO: The spec allows "footers" to follow the last chunk.
-			 *       If there is more data after this line then we should
-			 *       treat it like a header.
-			 */
-			break;
-		}
-
-		/* Advance to the start of the data */
-		s = strstr(s, "\r\n");
-		if (s == NULL)
-			break;
-		s += 2;
-
-		if (s + sz > data + *len) {
-			purple_debug_error("util", "Error processing chunked data: "
-					"Chunk size %" G_GSIZE_FORMAT " bytes was longer "
-					"than the data remaining in the buffer (%"
-					G_GSIZE_FORMAT " bytes)\n", sz, data + *len - s);
-		}
-
-		/* Move all data overtop of the chunk length that we read in earlier */
-		g_memmove(p, s, sz);
-		p += sz;
-		s += sz;
-		newlen += sz;
-		if (*s != '\r' && *(s + 1) != '\n') {
-			purple_debug_error("util", "Error processing chunked data: "
-					"Expected \\r\\n, found: %s\n", s);
-			break;
-		}
-		s += 2;
-	}
-
-	/* NULL terminate the data */
-	*p = 0;
-
-	*len = newlen;
-}
-
-static void
-url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond)
-{
-	PurpleUtilFetchUrlData *gfud = url_data;
-	int len;
-	char buf[4096];
-	char *data_cursor;
-	gboolean got_eof = FALSE;
-
-	/*
-	 * Read data in a loop until we can't read any more!  This is a
-	 * little confusing because we read using a different function
-	 * depending on whether the socket is ssl or cleartext.
-	 */
-	while ((gfud->is_ssl && ((len = purple_ssl_read(gfud->ssl_connection, buf, sizeof(buf))) > 0)) ||
-			(!gfud->is_ssl && (len = read(source, buf, sizeof(buf))) > 0))
-	{
-		if(gfud->max_len != -1 && (gfud->len + len) > gfud->max_len) {
-			purple_util_fetch_url_error(gfud, _("Error reading from %s: response too long (%d bytes limit)"),
-						    gfud->website.address, gfud->max_len);
-			return;
-		}
-
-		/* If we've filled up our buffer, make it bigger */
-		if((gfud->len + len) >= gfud->data_len) {
-			while((gfud->len + len) >= gfud->data_len)
-				gfud->data_len += sizeof(buf);
-
-			gfud->webdata = g_realloc(gfud->webdata, gfud->data_len);
-		}
-
-		data_cursor = gfud->webdata + gfud->len;
-
-		gfud->len += len;
-
-		memcpy(data_cursor, buf, len);
-
-		gfud->webdata[gfud->len] = '\0';
-
-		if(!gfud->got_headers) {
-			char *end_of_headers;
-
-			/* See if we've reached the end of the headers yet */
-			end_of_headers = strstr(gfud->webdata, "\r\n\r\n");
-			if (end_of_headers) {
-				char *new_data;
-				guint header_len = (end_of_headers + 4 - gfud->webdata);
-				size_t content_len;
-
-				purple_debug_misc("util", "Response headers: '%.*s'\n",
-					header_len, gfud->webdata);
-
-				/* See if we can find a redirect. */
-				if(parse_redirect(gfud->webdata, header_len, gfud))
-					return;
-
-				gfud->got_headers = TRUE;
-
-				/* No redirect. See if we can find a content length. */
-				content_len = parse_content_len(gfud->webdata, header_len);
-				gfud->chunked = content_is_chunked(gfud->webdata, header_len);
-
-				if (content_len == 0) {
-					/* We'll stick with an initial 8192 */
-					content_len = 8192;
-				} else {
-					gfud->has_explicit_data_len = TRUE;
-				}
-
-
-				/* If we're returning the headers too, we don't need to clean them out */
-				if (gfud->include_headers) {
-					gfud->data_len = content_len + header_len;
-					gfud->webdata = g_realloc(gfud->webdata, gfud->data_len);
-				} else {
-					size_t body_len = gfud->len - header_len;
-
-					content_len = MAX(content_len, body_len);
-
-					new_data = g_try_malloc(content_len);
-					if (new_data == NULL) {
-						purple_debug_error("util",
-								"Failed to allocate %" G_GSIZE_FORMAT " bytes: %s\n",
-								content_len, g_strerror(errno));
-						purple_util_fetch_url_error(gfud,
-								_("Unable to allocate enough memory to hold "
-								  "the contents from %s.  The web server may "
-								  "be trying something malicious."),
-								gfud->website.address);
-
-						return;
-					}
-
-					/* We may have read part of the body when reading the headers, don't lose it */
-					if (body_len > 0) {
-						memcpy(new_data, end_of_headers + 4, body_len);
-					}
-
-					/* Out with the old... */
-					g_free(gfud->webdata);
-
-					/* In with the new. */
-					gfud->len = body_len;
-					gfud->data_len = content_len;
-					gfud->webdata = new_data;
-				}
-			}
-		}
-
-		if(gfud->has_explicit_data_len && gfud->len >= gfud->data_len) {
-			got_eof = TRUE;
-			break;
-		}
-	}
-
-	if(len < 0) {
-		if(errno == EAGAIN) {
-			return;
-		} else {
-			purple_util_fetch_url_error(gfud, _("Error reading from %s: %s"),
-					gfud->website.address, g_strerror(errno));
-			return;
-		}
-	}
-
-	if((len == 0) || got_eof) {
-		gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1);
-		gfud->webdata[gfud->len] = '\0';
-
-		if (!gfud->include_headers && gfud->chunked) {
-			/* Process only if we don't want the headers. */
-			process_chunked_data(gfud->webdata, &gfud->len);
-		}
-
-		gfud->callback(gfud, gfud->user_data, gfud->webdata, gfud->len, NULL);
-		purple_util_fetch_url_cancel(gfud);
-	}
-}
-
-static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
-{
-	url_fetch_recv_cb(data, -1, cond);
-}
-
-/**
- * This function is called when the socket is available to be written
- * to.
- *
- * @param source The file descriptor that can be written to.  This can
- *        be an http connection or it can be the SSL connection of an
- *        https request.  So be careful what you use it for!  If it's
- *        an https request then use purple_ssl_write() instead of
- *        writing to it directly.
- */
-static void
-url_fetch_send_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	PurpleUtilFetchUrlData *gfud;
-	int len, total_len;
-
-	gfud = data;
-
-	if (gfud->request == NULL) {
-
-		PurpleProxyInfo *gpi = purple_proxy_get_setup(gfud->account);
-		GString *request_str = g_string_new(NULL);
-
-		g_string_append_printf(request_str, "GET %s%s HTTP/%s\r\n"
-						    "Connection: close\r\n",
-			(gfud->full ? "" : "/"),
-			(gfud->full ? (gfud->url ? gfud->url : "") : (gfud->website.page ? gfud->website.page : "")),
-			(gfud->http11 ? "1.1" : "1.0"));
-
-		if (gfud->user_agent)
-			g_string_append_printf(request_str, "User-Agent: %s\r\n", gfud->user_agent);
-
-		/* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1
-		 * clients must know how to handle the "chunked" transfer encoding.
-		 * Purple doesn't know how to handle "chunked", so should always send
-		 * the Host header regardless, to get around some observed problems
-		 */
-		g_string_append_printf(request_str, "Accept: */*\r\n"
-						    "Host: %s\r\n",
-			(gfud->website.address ? gfud->website.address : ""));
-
-		if (purple_proxy_info_get_username(gpi) != NULL
-				&& (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR
-					|| purple_proxy_info_get_type(gpi) == PURPLE_PROXY_HTTP)) {
-			/* This chunk of code was copied from proxy.c http_start_connect_tunneling()
-			 * This is really a temporary hack - we need a more complete proxy handling solution,
-			 * so I didn't think it was worthwhile to refactor for reuse
-			 */
-			char *t1, *t2, *ntlm_type1;
-			char hostname[256];
-			int ret;
-
-			ret = gethostname(hostname, sizeof(hostname));
-			hostname[sizeof(hostname) - 1] = '\0';
-			if (ret < 0 || hostname[0] == '\0') {
-				purple_debug_warning("util", "proxy - gethostname() failed -- is your hostname set?");
-				strcpy(hostname, "localhost");
-			}
-
-			t1 = g_strdup_printf("%s:%s",
-				purple_proxy_info_get_username(gpi),
-				purple_proxy_info_get_password(gpi) ?
-					purple_proxy_info_get_password(gpi) : "");
-			t2 = purple_base64_encode((const guchar *)t1, strlen(t1));
-			g_free(t1);
-
-			ntlm_type1 = purple_ntlm_gen_type1(hostname, "");
-
-			g_string_append_printf(request_str,
-				"Proxy-Authorization: Basic %s\r\n"
-				"Proxy-Authorization: NTLM %s\r\n"
-				"Proxy-Connection: Keep-Alive\r\n",
-				t2, ntlm_type1);
-			g_free(ntlm_type1);
-			g_free(t2);
-		}
-
-		g_string_append(request_str, "\r\n");
-
-		gfud->request = g_string_free(request_str, FALSE);
-	}
-
-	if(purple_debug_is_unsafe())
-		purple_debug_misc("util", "Request: '%s'\n", gfud->request);
-	else
-		purple_debug_misc("util", "request constructed\n");
-
-	total_len = strlen(gfud->request);
-
-	if (gfud->is_ssl)
-		len = purple_ssl_write(gfud->ssl_connection, gfud->request + gfud->request_written,
-				total_len - gfud->request_written);
-	else
-		len = write(gfud->fd, gfud->request + gfud->request_written,
-				total_len - gfud->request_written);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len < 0) {
-		purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"),
-				gfud->website.address, g_strerror(errno));
-		return;
-	}
-	gfud->request_written += len;
-
-	if (gfud->request_written < total_len)
-		return;
-
-	/* We're done writing our request, now start reading the response */
-	if (gfud->is_ssl) {
-		purple_input_remove(gfud->inpa);
-		gfud->inpa = 0;
-		purple_ssl_input_add(gfud->ssl_connection, ssl_url_fetch_recv_cb, gfud);
-	} else {
-		purple_input_remove(gfud->inpa);
-		gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb,
-			gfud);
-	}
-}
-
-static void
-url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message)
-{
-	PurpleUtilFetchUrlData *gfud;
-
-	gfud = url_data;
-	gfud->connect_data = NULL;
-
-	if (source == -1)
-	{
-		purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"),
-				(gfud->website.address ? gfud->website.address : ""), error_message);
-		return;
-	}
-
-	gfud->fd = source;
-
-	gfud->inpa = purple_input_add(source, PURPLE_INPUT_WRITE,
-								url_fetch_send_cb, gfud);
-	url_fetch_send_cb(gfud, source, PURPLE_INPUT_WRITE);
-}
-
-static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond)
-{
-	PurpleUtilFetchUrlData *gfud;
-
-	gfud = data;
-
-	gfud->inpa = purple_input_add(ssl_connection->fd, PURPLE_INPUT_WRITE,
-			url_fetch_send_cb, gfud);
-	url_fetch_send_cb(gfud, ssl_connection->fd, PURPLE_INPUT_WRITE);
-}
-
-static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data)
-{
-	PurpleUtilFetchUrlData *gfud;
-
-	gfud = data;
-	gfud->ssl_connection = NULL;
-
-	purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"),
-			(gfud->website.address ? gfud->website.address : ""),
-	purple_ssl_strerror(error));
-}
-
-PurpleUtilFetchUrlData *
-purple_util_fetch_url_request(PurpleAccount *account,
-		const char *url, gboolean full,	const char *user_agent, gboolean http11,
-		const char *request, gboolean include_headers, gssize max_len,
-		PurpleUtilFetchUrlCallback callback, void *user_data)
-{
-	PurpleUtilFetchUrlData *gfud;
-
-	g_return_val_if_fail(url      != NULL, NULL);
-	g_return_val_if_fail(callback != NULL, NULL);
-
-	if(purple_debug_is_unsafe())
-		purple_debug_info("util",
-				 "requested to fetch (%s), full=%d, user_agent=(%s), http11=%d\n",
-				 url, full, user_agent?user_agent:"(null)", http11);
-	else
-		purple_debug_info("util", "requesting to fetch a URL\n");
-
-	gfud = g_new0(PurpleUtilFetchUrlData, 1);
-
-	gfud->callback = callback;
-	gfud->user_data  = user_data;
-	gfud->url = g_strdup(url);
-	gfud->user_agent = g_strdup(user_agent);
-	gfud->http11 = http11;
-	gfud->full = full;
-	gfud->request = g_strdup(request);
-	gfud->include_headers = include_headers;
-	gfud->fd = -1;
-	gfud->max_len = max_len;
-	gfud->account = account;
-
-	purple_url_parse(url, &gfud->website.address, &gfud->website.port,
-				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
-
-	if (purple_strcasestr(url, "https://") != NULL) {
-		if (!purple_ssl_is_supported()) {
-			purple_util_fetch_url_error(gfud,
-					_("Unable to connect to %s: %s"),
-					gfud->website.address,
-					_("Server requires TLS/SSL, but no TLS/SSL support was found."));
-			return NULL;
-		}
-
-		gfud->is_ssl = TRUE;
-		gfud->ssl_connection = purple_ssl_connect(account,
-				gfud->website.address, gfud->website.port,
-				ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud);
-	} else {
-		gfud->connect_data = purple_proxy_connect(NULL, account,
-				gfud->website.address, gfud->website.port,
-				url_fetch_connect_cb, gfud);
-	}
-
-	if (gfud->ssl_connection == NULL && gfud->connect_data == NULL)
-	{
-		purple_util_fetch_url_error(gfud, _("Unable to connect to %s"),
-				gfud->website.address);
-		return NULL;
-	}
-
-	return gfud;
-}
-
-void
-purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *gfud)
-{
-	if (gfud->ssl_connection != NULL)
-		purple_ssl_close(gfud->ssl_connection);
-
-	if (gfud->connect_data != NULL)
-		purple_proxy_connect_cancel(gfud->connect_data);
-
-	if (gfud->inpa > 0)
-		purple_input_remove(gfud->inpa);
-
-	if (gfud->fd >= 0)
-		close(gfud->fd);
-
-	g_free(gfud->website.user);
-	g_free(gfud->website.passwd);
-	g_free(gfud->website.address);
-	g_free(gfud->website.page);
-	g_free(gfud->url);
-	g_free(gfud->user_agent);
-	g_free(gfud->request);
-	g_free(gfud->webdata);
-
-	g_free(gfud);
-}
 
 const char *
 purple_url_decode(const char *str)
@@ -5364,6 +4697,18 @@
 	return buf;
 }
 
+gchar * purple_escape_js(const gchar *str)
+{
+	gchar *escaped;
+
+	json_node_set_string(escape_js_node, str);
+	json_generator_set_root(escape_js_gen, escape_js_node);
+	escaped = json_generator_to_data(escape_js_gen, NULL);
+	json_node_set_boolean(escape_js_node, FALSE);
+
+	return escaped;
+}
+
 void purple_restore_default_signal_handlers(void)
 {
 #ifndef _WIN32
--- a/libpurple/util.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/util.h	Fri Jan 25 02:22:38 2013 -0500
@@ -32,12 +32,6 @@
 #include <stdio.h>
 
 /**
-  * An opaque structure representing a URL request. Can be used to cancel
-  * the request.
-  */
-typedef struct _PurpleUtilFetchUrlData PurpleUtilFetchUrlData;
-
-/**
  * A generic structure that contains information about an "action."  One
  * place this is is used is by PRPLs to tell the core the list of available
  * right-click actions for a buddy list row.
@@ -1165,70 +1159,6 @@
 						char **ret_path, char **ret_user, char **ret_passwd);
 
 /**
- * This is the signature used for functions that act as the callback
- * to purple_util_fetch_url() or purple_util_fetch_url_request().
- *
- * @param url_data      The same value that was returned when you called
- *                      purple_fetch_url() or purple_fetch_url_request().
- * @param user_data     The user data that your code passed into either
- *                      purple_util_fetch_url() or purple_util_fetch_url_request().
- * @param url_text      This will be NULL on error.  Otherwise this
- *                      will contain the contents of the URL.
- * @param len           0 on error, otherwise this is the length of buf.
- * @param error_message If something went wrong then this will contain
- *                      a descriptive error message, and buf will be
- *                      NULL and len will be 0.
- */
-typedef void (*PurpleUtilFetchUrlCallback)(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message);
-
-/**
- * Fetches the data from a URL, and passes it to a callback function.
- *
- * @param url        The URL.
- * @param full       TRUE if this is the full URL, or FALSE if it's a
- *                   partial URL.
- * @param user_agent The user agent field to use, or NULL.
- * @param http11     TRUE if HTTP/1.1 should be used to download the file.
- * @param max_len    The maximum number of bytes to retrieve (-1 for unlimited)
- * @param cb         The callback function.
- * @param data       The user data to pass to the callback function.
- */
-#define purple_util_fetch_url(url, full, user_agent, http11, max_len, cb, data) \
-	purple_util_fetch_url_request(NULL, url, full, user_agent, http11, NULL, \
-		FALSE, max_len, cb, data);
-
-/**
- * Fetches the data from a URL, and passes it to a callback function.
- *
- * @param account    The account for which the request is needed, or NULL.
- * @param url        The URL.
- * @param full       TRUE if this is the full URL, or FALSE if it's a
- *                   partial URL.
- * @param user_agent The user agent field to use, or NULL.
- * @param http11     TRUE if HTTP/1.1 should be used to download the file.
- * @param request    A HTTP request to send to the server instead of the
- *                   standard GET
- * @param include_headers
- *                   If TRUE, include the HTTP headers in the response.
- * @param max_len    The maximum number of bytes to retrieve (-1 for unlimited)
- * @param callback   The callback function.
- * @param data       The user data to pass to the callback function.
- */
-PurpleUtilFetchUrlData *purple_util_fetch_url_request(
-		PurpleAccount *account, const gchar *url,
-		gboolean full, const gchar *user_agent, gboolean http11,
-		const gchar *request, gboolean include_headers, gssize max_len,
-		PurpleUtilFetchUrlCallback callback, gpointer data);
-
-/**
- * Cancel a pending URL request started with either
- * purple_util_fetch_url_request() or purple_util_fetch_url().
- *
- * @param url_data The data returned when you initiated the URL fetch.
- */
-void purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *url_data);
-
-/**
  * Decodes a URL into a plain string.
  *
  * This will change hex codes and such to their ascii equivalents.
@@ -1460,6 +1390,15 @@
 const char *purple_escape_filename(const char *str);
 
 /**
+ * Escapes javascript-unfriendly substrings from a string.
+ *
+ * @param str The string to escape.
+ *
+ * @return The javascript-safe string (must be g_free'd after use).
+ */
+gchar * purple_escape_js(const gchar *str);
+
+/**
  * Restore default signal handlers for signals which might reasonably have
  * handlers. This should be called by a fork()'d child process, since child processes
  * inherit the handlers of the parent.
--- a/libpurple/win32/global.mak	Thu Aug 23 01:27:48 2012 -0400
+++ b/libpurple/win32/global.mak	Fri Jan 25 02:22:38 2013 -0500
@@ -15,13 +15,15 @@
 GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0-2.14
 GTK_BIN ?= $(GTK_TOP)/bin
 BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK
-LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.7.4
-MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa2
-NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.12.5-nspr-4.8.2
+LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.9.0
+MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa3
+NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.13.6-nspr-4.9.2
 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10.0
-SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.8
+SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.10
 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5
 GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10.13
+GCC_SSP_TOP ?= $(WIN32_DEV_TOP)/gcc-core-4.4.0-mingw32-dll
+CYRUS_SASL_TOP ?= $(WIN32_DEV_TOP)/cyrus-sasl-2.1.25
 
 # Where we installing this stuff to?
 PIDGIN_INSTALL_DIR := $(PIDGIN_TREE_TOP)/win32-install-dir
@@ -57,6 +59,10 @@
 
 GCCWARNINGS ?= -Waggregate-return -Wcast-align -Wdeclaration-after-statement -Werror-implicit-function-declaration -Wextra -Wno-sign-compare -Wno-unused-parameter -Winit-self -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wundef
 
+CC_HARDENING_OPTIONS ?= -Wstack-protector -fwrapv -fno-strict-overflow -Wno-missing-field-initializers -Wformat-security -fstack-protector-all --param ssp-buffer-size=1
+LD_HARDENING_OPTIONS ?= -Wl,--dynamicbase -Wl,--nxcompat
+
+
 # parse the version number from the configure.ac file if it is newer
 #m4_define([purple_major_version], [2])
 #m4_define([purple_minor_version], [0])
@@ -84,17 +90,14 @@
 
 DEFINES += -DHAVE_CONFIG_H -DWIN32_LEAN_AND_MEAN
 
-# Use -g flag when building debug version of Pidgin (including plugins).
-# Use -fnative-struct instead of -mms-bitfields when using mingw 1.1
-# (gcc 2.95)
-CFLAGS += -O2 -Wall $(GCCWARNINGS) -pipe -mno-cygwin -mms-bitfields -g
+CFLAGS += -O2 -Wall $(GCCWARNINGS) $(CC_HARDENING_OPTIONS) -pipe -mms-bitfields -g
 
 # If not specified, dlls are built with the default base address of 0x10000000.
 # When loaded into a process address space a dll will be rebased if its base
 # address colides with the base address of an existing dll.  To avoid rebasing 
 # we do the following.  Rebasing can slow down the load time of dlls and it
 # also renders debug info useless.
-DLL_LD_FLAGS += -Wl,--enable-auto-image-base
+DLL_LD_FLAGS += -Wl,--enable-auto-image-base -Wl,--enable-auto-import $(LD_HARDENING_OPTIONS) -lssp
 
 # Build programs
 ifeq "$(origin CC)" "default"
@@ -106,6 +109,8 @@
 WINDRES ?= windres
 STRIP ?= strip
 INTLTOOL_MERGE ?= $(WIN32_DEV_TOP)/intltool_0.40.4-1_win32/bin/intltool-merge
+MONO_SIGNCODE ?= signcode
+GPG_SIGN ?= gpg
 
 PIDGIN_COMMON_RULES := $(PURPLE_TOP)/win32/rules.mak
 PIDGIN_COMMON_TARGETS := $(PURPLE_TOP)/win32/targets.mak
--- a/pidgin.spec.in	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin.spec.in	Fri Jan 25 02:22:38 2013 -0500
@@ -368,7 +368,7 @@
 %doc ChangeLog
 %doc NEWS
 %doc README
-%doc README.MTN
+%doc README.hg
 %doc doc/the_penguin.txt
 %doc %{_mandir}/man1/pidgin.*
 %doc %{_mandir}/man3*/*
--- a/pidgin/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -168,6 +168,7 @@
 	$(LIBXML_LIBS) \
 	$(WEBKIT_LIBS) \
 	$(GTK_LIBS) \
+	$(X11_LIBS) \
 	$(top_builddir)/libpurple/libpurple.la
 
 if USE_INTERNAL_LIBGADU
@@ -188,6 +189,7 @@
 	$(GSTREAMER_CFLAGS) \
 	$(DEBUG_CFLAGS) \
 	$(GTK_CFLAGS) \
+	$(X11_CFLAGS) \
 	$(DBUS_CFLAGS) \
 	$(GTKSPELL_CFLAGS) \
 	$(LIBXML_CFLAGS) \
--- a/pidgin/Makefile.mingw	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/Makefile.mingw	Fri Jan 25 02:22:38 2013 -0500
@@ -18,7 +18,7 @@
 
 WINAPP := -mwindows
 
-LDFLAGS := $(WINAPP)
+LDFLAGS := $(WINAPP) $(LD_HARDENING_OPTIONS) -Wl,--enable-auto-import -lssp
 
 ##
 ## INCLUDE PATHS
@@ -163,7 +163,7 @@
 	$(CC) -shared $(PIDGIN_OBJECTS) $(LIB_PATHS) $(PIDGIN_LIBS) $(DLL_LD_FLAGS) -Wl,--output-def,$(PIDGIN_TARGET).def,--out-implib,$(PIDGIN_TARGET).dll.a -o $(PIDGIN_TARGET).dll
 
 $(EXE_TARGET).exe: $(PIDGIN_CONFIG_H) $(PIDGIN_DLL).a $(EXE_OBJECTS) $(PIDGIN_TARGET).dll
-	$(CC) $(LDFLAGS) $(EXE_OBJECTS) -o $(EXE_TARGET).exe
+	$(CC) $(EXE_OBJECTS) $(LDFLAGS) -o $(EXE_TARGET).exe
 
 ##
 ## CLEAN RULES
--- a/pidgin/gtk3compat.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtk3compat.h	Fri Jan 25 02:22:38 2013 -0500
@@ -37,6 +37,16 @@
 #define gdk_x11_window_get_xid GDK_WINDOW_XWINDOW
 #define gtk_widget_get_preferred_size(x,y,z) gtk_widget_size_request(x,z)
 
+#ifdef GDK_WINDOWING_X11
+#define GDK_IS_X11_WINDOW(window) TRUE
+#endif
+#ifdef GDK_WINDOWING_WIN32
+#define GDK_IS_WIN32_WINDOW(window) TRUE
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
+#define GDK_IS_QUARTZ_WINDOW(window) TRUE
+#endif
+
 #if !GTK_CHECK_VERSION(2,24,0)
 
 #define gdk_x11_set_sm_client_id gdk_set_sm_client_id
--- a/pidgin/gtkaccount.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkaccount.c	Fri Jan 25 02:22:38 2013 -0500
@@ -185,7 +185,7 @@
 	if (data != NULL)
 	{
 		if (len > 0)
-			dialog->icon_img = purple_imgstore_add(data, len, new_icon_path);
+			dialog->icon_img = purple_imgstore_new(data, len, new_icon_path);
 		else
 			g_free(data);
 	}
--- a/pidgin/gtkblist.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkblist.c	Fri Jan 25 02:22:38 2013 -0500
@@ -1147,6 +1147,10 @@
 chat_select_account_cb(GObject *w, PurpleAccount *account,
                        PidginChatData *data)
 {
+	g_return_if_fail(w != NULL);
+	g_return_if_fail(data != NULL);
+	g_return_if_fail(account != NULL);
+
 	if (strcmp(purple_account_get_protocol_id(data->rq_data.account),
 	           purple_account_get_protocol_id(account)) == 0)
 	{
@@ -3884,8 +3888,7 @@
 		{
 			pce = cur->data;
 
-			if (!pce->secret && (!pce->required &&
-				g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier) == NULL))
+			if (!pce->secret)
 			{
 				tmp = purple_text_strip_mnemonic(pce->label);
 				name = g_markup_escape_text(tmp, -1);
@@ -5142,6 +5145,10 @@
 				gtkblist->headline_destroy(gtkblist->headline_data);
 			reset_headline(gtkblist);
 		}
+	} else {
+		if (gtkblist->headline_destroy)
+			gtkblist->headline_destroy(gtkblist->headline_data);
+		reset_headline(gtkblist);
 	}
 
 	return FALSE;
@@ -5152,7 +5159,7 @@
 {
 	GdkCursor *hand_cursor = gdk_cursor_new(GDK_HAND2);
 	gdk_window_set_cursor(gtk_widget_get_window(widget), hand_cursor);
-	gdk_cursor_unref(hand_cursor);
+	g_object_unref(hand_cursor);
 }
 
 static gboolean
--- a/pidgin/gtkconv-theme.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkconv-theme.c	Fri Jan 25 02:22:38 2013 -0500
@@ -66,6 +66,8 @@
 	char    *incoming_next_context_html;
 	char    *outgoing_next_context_html;
 	char    *basestyle_css;
+
+	GArray  *nick_colors;
 } PidginConvThemePrivate;
 
 /******************************************************************************
@@ -498,6 +500,9 @@
 	}
 	g_free(priv->variant);
 
+	if (priv->nick_colors)
+		g_array_unref(priv->nick_colors);
+
 	parent_class->finalize(obj);
 }
 
@@ -749,3 +754,44 @@
 	}
 }
 
+GArray *
+pidgin_conversation_theme_get_nick_colors(PidginConvTheme *theme)
+{
+	PidginConvThemePrivate *priv;
+	const char *dir;
+
+	g_return_val_if_fail(theme != NULL, NULL);
+
+	priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
+
+	dir = purple_theme_get_dir(PURPLE_THEME(theme));
+	if (NULL == priv->nick_colors)
+	{
+		char *file = g_build_filename(dir, "Contents", "Resources", "Incoming", "SenderColors.txt", NULL);
+		char *contents;
+		priv->nick_colors = g_array_new(FALSE, FALSE, sizeof(GdkColor));
+		if (g_file_get_contents(file, &contents, NULL, NULL)) {
+			int i;
+			gchar ** color_strings = g_strsplit_set(contents, "\r\n:", -1);
+
+			for(i=0; color_strings[i]; i++)
+			{
+				GdkColor color;
+				if(gdk_color_parse(color_strings[i], &color))
+				{
+					g_array_append_val(priv->nick_colors, color);
+				}
+			}
+
+			g_strfreev(color_strings);
+			g_free(contents);
+		}
+		g_free(file);
+	}
+
+	if(priv->nick_colors->len)
+		return g_array_ref(priv->nick_colors);
+	else
+		return NULL;
+}
+
--- a/pidgin/gtkconv-theme.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkconv-theme.h	Fri Jan 25 02:22:38 2013 -0500
@@ -191,6 +191,15 @@
  */
 char *pidgin_conversation_theme_get_css_path(PidginConvTheme *theme);
 
+/**
+ * Get (and reference) the array of nick colors
+ *
+ * @param theme The conversation theme
+ *
+ * @return Pointer to GArray of nick colors, or NULL if no colors in theme
+ */
+GArray *pidgin_conversation_theme_get_nick_colors(PidginConvTheme *theme);
+
 G_END_DECLS
 #endif /* PIDGIN_CONV_THEME_H */
 
--- a/pidgin/gtkconv.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkconv.c	Fri Jan 25 02:22:38 2013 -0500
@@ -33,13 +33,6 @@
 # include <X11/Xlib.h>
 #endif
 
-#ifdef USE_GTKSPELL
-# include <gtkspell/gtkspell.h>
-# ifdef _WIN32
-#  include "wspell.h"
-# endif
-#endif
-
 #include <gdk/gdkkeysyms.h>
 
 #include "account.h"
@@ -157,9 +150,8 @@
 #define MIN_BRIGHTNESS_CONTRAST 75
 #define MIN_COLOR_CONTRAST 200
 
-#define NUM_NICK_COLORS 220
-static GdkColor *nick_colors = NULL;
-static guint nbr_nick_colors;
+#define NICK_COLOR_GENERATE_COUNT 220
+static GArray *generated_nick_colors = NULL;
 
 /* These probably won't conflict with any WebKit values. */
 #define PIDGIN_DRAG_BLIST_NODE (1337)
@@ -215,7 +207,7 @@
 static void update_typing_icon(PidginConversation *gtkconv);
 static void update_typing_message(PidginConversation *gtkconv, const char *message);
 gboolean pidgin_conv_has_focus(PurpleConversation *conv);
-static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background);
+static GArray* generate_nick_colors(guint numcolors, GdkColor background);
 static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast);
 static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, PurpleMessageFlags flag, gboolean create);
 static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields);
@@ -234,7 +226,7 @@
 	GtkStyle *style = gtk_widget_get_style(gtkconv->webview);
 	float scale;
 
-	col = nick_colors[g_str_hash(name) % nbr_nick_colors];
+	col = g_array_index(gtkconv->nick_colors, GdkColor, g_str_hash(name) % gtkconv->nick_colors->len);
 	scale = ((1-(LUMINANCE(style->base[GTK_STATE_NORMAL]) / LUMINANCE(style->white))) *
 		       (LUMINANCE(style->white)/MAX(MAX(col.red, col.blue), col.green)));
 
@@ -1652,6 +1644,8 @@
 			node = webkit_dom_node_list_item(nodes, len - 1);
 	}
 
+	g_object_unref(nodes);
+
 	return node;
 }
 
@@ -3321,15 +3315,10 @@
 			return FALSE;
 
 		buddy = purple_find_buddy(account, purple_conversation_get_name(conv));
-
-		/* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle
-		 * removing "isolated" buddy nodes well */
-		if (purple_version_check(2, 0, 2) == NULL) {
-			if ((buddy == NULL) && (gtkconv->webview != NULL)) {
-				buddy = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy");
-			}
-
-			if ((buddy == NULL) && (gtkconv->webview != NULL)) {
+		if (!buddy && gtkconv->webview) {
+			buddy = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy");
+
+			if (!buddy) {
 				buddy = purple_buddy_new(account, purple_conversation_get_name(conv), NULL);
 				purple_blist_node_set_flags((PurpleBlistNode *)buddy,
 						PURPLE_BLIST_NODE_FLAG_NO_SAVE);
@@ -5795,6 +5784,7 @@
 	gtkconv->theme = PIDGIN_CONV_THEME(g_object_ref(theme));
 	gtkconv->last_flags = 0;
 
+
 	if (conv_type == PURPLE_CONV_TYPE_IM) {
 		gtkconv->u.im = g_malloc0(sizeof(PidginImPane));
 	} else if (conv_type == PURPLE_CONV_TYPE_CHAT) {
@@ -5892,9 +5882,13 @@
 	else
 		pidgin_conv_placement_place(gtkconv);
 
-	if (nick_colors == NULL) {
-		nbr_nick_colors = NUM_NICK_COLORS;
-		nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->webview)->base[GTK_STATE_NORMAL]);
+	if (generated_nick_colors == NULL) {
+		generated_nick_colors = generate_nick_colors(NICK_COLOR_GENERATE_COUNT, gtk_widget_get_style(gtkconv->webview)->base[GTK_STATE_NORMAL]);
+	}
+
+	if(NULL == (gtkconv->nick_colors = pidgin_conversation_theme_get_nick_colors(gtkconv->theme)))
+	{
+		gtkconv->nick_colors = g_array_ref(generated_nick_colors);
 	}
 
 	if (purple_conversation_get_features(conv) & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY)
@@ -6010,6 +6004,8 @@
 		g_source_remove(gtkconv->attach.timer);
 	}
 
+	g_array_unref(gtkconv->nick_colors);
+
 	g_object_disconnect(G_OBJECT(gtkconv->theme), "any_signal::notify",
 	                    conv_variant_changed_cb, gtkconv, NULL);
 	g_object_unref(gtkconv->theme);
@@ -6320,6 +6316,10 @@
 		} else if (g_str_has_prefix(cur, "%sender%")) {
 			replace = alias;
 
+		} else if (g_str_has_prefix(cur, "%senderColor%")) {
+			const GdkColor *color = get_nick_color(PIDGIN_CONVERSATION(conv), name);
+			replace = freeval = g_strdup_printf("#%02x%02x%02x", (color->red >> 8), (color->green >> 8), (color->blue >> 8));
+
 		} else if (g_str_has_prefix(cur, "%service%")) {
 			replace = purple_account_get_protocol_name(purple_conversation_get_account(conv));
 
@@ -7409,15 +7409,28 @@
 			const char *topic = gtkconv->u.chat->topic_text
 				? gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text))
 				: NULL;
-			char *esc = NULL, *tmp;
-			esc = topic ? g_markup_escape_text(topic, -1) : NULL;
-			tmp = g_markup_escape_text(purple_conversation_get_title(conv), -1);
-			markup = g_strdup_printf("%s%s<span color='%s' size='smaller'>%s</span>",
-						tmp, esc  && *esc ? "\n" : "",
+			const char *title = purple_conversation_get_title(conv);
+			const char *name = purple_conversation_get_name(conv);
+
+			char *topic_esc, *unaliased, *unaliased_esc, *title_esc;
+
+			topic_esc = topic ? g_markup_escape_text(topic, -1) : NULL;
+			unaliased = g_utf8_collate(title, name) ? g_strdup_printf("(%s)", name) : NULL;
+			unaliased_esc = unaliased ? g_markup_escape_text(unaliased, -1) : NULL;
+			title_esc = g_markup_escape_text(title, -1);
+
+			markup = g_strdup_printf("%s%s<span size='smaller'>%s</span>%s<span color='%s' size='smaller'>%s</span>",
+						title_esc,
+						unaliased_esc ? " " : "",
+						unaliased_esc ? unaliased_esc : "",
+						topic_esc  && *topic_esc ? "\n" : "",
 						pidgin_get_dim_grey_string(gtkconv->infopane),
-						esc ? esc : "");
-			g_free(tmp);
-			g_free(esc);
+						topic_esc ? topic_esc : "");
+
+			g_free(title_esc);
+			g_free(topic_esc);
+			g_free(unaliased);
+			g_free(unaliased_esc);
 		}
 		gtk_list_store_set(gtkconv->infopane_model, &(gtkconv->infopane_iter),
 				CONV_TEXT_COLUMN, markup, -1);
@@ -7887,7 +7900,6 @@
 spellcheck_pref_cb(const char *name, PurplePrefType type,
 				   gconstpointer value, gpointer data)
 {
-#ifdef USE_GTKSPELL
 	GList *cl;
 	PurpleConversation *conv;
 	PidginConversation *gtkconv;
@@ -7901,9 +7913,9 @@
 
 		gtkconv = PIDGIN_CONVERSATION(conv);
 
-		pidgin_webview_set_spellcheck(GTK_WEBVIEW(gtkconv->entry), value);
-	}
-#endif
+		pidgin_webview_set_spellcheck(GTK_WEBVIEW(gtkconv->entry),
+		                              (gboolean)GPOINTER_TO_INT(value));
+	}
 }
 
 static void
@@ -8775,6 +8787,7 @@
 	default_conv_theme = purple_theme_manager_load_theme(theme_dir, "conversation");
 	g_free(theme_dir);
 
+#if !GTK_CHECK_VERSION(3,0,0)
 	{
 		/* Set default tab colors */
 		GString *str = g_string_new(NULL);
@@ -8810,6 +8823,7 @@
 		g_string_free(str, TRUE);
 		gtk_rc_reset_styles(settings);
 	}
+#endif
 }
 
 void
@@ -10099,12 +10113,12 @@
 void
 pidgin_conv_window_destroy(PidginWindow *win)
 {
-	PidginConversation *gtkconv;
-	GList *iter;
-
 	if (win->gtkconvs) {
-		for (iter = win->gtkconvs; iter != NULL; iter = iter->next) {
-			gtkconv = iter->data;
+		GList *iter = win->gtkconvs;
+		while (iter)
+		{
+			PidginConversation *gtkconv = iter->data;
+			iter = iter->next;
 			close_conv_cb(NULL, gtkconv);
 		}
 		return;
@@ -10170,6 +10184,52 @@
 	return FALSE;
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+static void
+set_default_tab_colors(GtkWidget *widget)
+{
+	GString *str;
+	GtkCssProvider *provider;
+	GError *error = NULL;
+	int iter;
+
+	struct {
+		const char *labelname;
+		const char *color;
+	} styles[] = {
+		{"tab-label-typing", "#4e9a06"},
+		{"tab-label-typed", "#c4a000"},
+		{"tab-label-attention", "#006aff"},
+		{"tab-label-unreadchat", "#cc0000"},
+		{"tab-label-event", "#888a85"},
+		{NULL, NULL}
+	};
+
+	str = g_string_new(NULL);
+
+	for (iter = 0; styles[iter].labelname; iter++) {
+		g_string_append_printf(str,
+		                       "#%s {\n"
+		                       "	color: %s;\n"
+		                       "}\n",
+		                       styles[iter].labelname,
+		                       styles[iter].color);
+	}
+
+	provider = gtk_css_provider_new();
+
+	gtk_css_provider_load_from_data(provider, str->str, str->len, &error);
+
+	gtk_style_context_add_provider(gtk_widget_get_style_context(widget),
+	                               GTK_STYLE_PROVIDER(provider),
+	                               GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+	if (error)
+		g_error_free(error);
+	g_string_free(str, TRUE);
+}
+#endif
+
 void
 pidgin_conv_window_add_gtkconv(PidginWindow *win, PidginConversation *gtkconv)
 {
@@ -10208,6 +10268,9 @@
 
 	/* Tab label. */
 	gtkconv->tab_label = gtk_label_new(tmp_lab = purple_conversation_get_title(conv));
+#if GTK_CHECK_VERSION(3,0,0)
+	set_default_tab_colors(gtkconv->tab_label);
+#endif
 	gtk_widget_set_name(gtkconv->tab_label, "tab-label");
 
 	gtkconv->menu_tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
@@ -10944,12 +11007,11 @@
 }
 
 
-static GdkColor*
-generate_nick_colors(guint *color_count, GdkColor background)
-{
-	guint numcolors = *color_count;
+static GArray*
+generate_nick_colors(guint numcolors, GdkColor background)
+{
 	guint i = 0, j = 0;
-	GdkColor *colors = g_new(GdkColor, numcolors);
+	GArray *colors = g_array_new(FALSE, FALSE, sizeof(GdkColor));
 	GdkColor nick_highlight;
 	GdkColor send_color;
 	time_t breakout_time;
@@ -10973,7 +11035,7 @@
 			color_is_visible(color, nick_highlight, MIN_COLOR_CONTRAST / 2, 0) &&
 			color_is_visible(color, send_color,     MIN_COLOR_CONTRAST / 4, 0))
 		{
-			colors[i] = color;
+			g_array_append_val(colors, color);
 			i++;
 		}
 		j++;
@@ -10992,17 +11054,13 @@
 			color_is_visible(color, nick_highlight, MIN_COLOR_CONTRAST / 2, 0) &&
 			color_is_visible(color, send_color,     MIN_COLOR_CONTRAST / 4, 0))
 		{
-			colors[i] = color;
+			g_array_append_val(colors, color);
 			i++;
 		}
 	}
 
 	if (i < numcolors) {
-		GdkColor *c = colors;
 		purple_debug_warning("gtkconv", "Unable to generate enough random colors before timeout. %u colors found.\n", i);
-		colors = g_memdup(c, i * sizeof(GdkColor));
-		g_free(c);
-		*color_count = i;
 	}
 
 	return colors;
--- a/pidgin/gtkconv.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkconv.h	Fri Jan 25 02:22:38 2013 -0500
@@ -101,6 +101,7 @@
 	GtkWidget *menu_tabby;
 
 	PidginConvTheme *theme;
+	GArray *nick_colors;
 	PurpleMessageFlags last_flags;
 	GtkWidget *webview;
 	GtkWidget *entry;
--- a/pidgin/gtkdebug.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkdebug.c	Fri Jan 25 02:22:38 2013 -0500
@@ -76,7 +76,22 @@
 	/* Regex */ \
 	"div.hide{display:none;}" \
 	"span.regex{background-color:#ffafaf;font-weight:bold;}" \
-	"</style></head><body class=l%d></body></html>"
+	"</style><script>" \
+	"function append(level, time, cat, msg) {" \
+		"var div = document.createElement('div');" \
+		"div.className = 'l' + level;" \
+		"div.appendChild(document.createTextNode('(' + time + ') '));" \
+		"if (cat) {" \
+			"var cat_n = document.createElement('b');" \
+			"cat_n.appendChild(document.createTextNode(cat + ':'));" \
+			"div.appendChild(cat_n);" \
+			"div.appendChild(document.createTextNode(' '));" \
+		"}" \
+		"div.appendChild(document.createTextNode(msg));" \
+		"document.body.appendChild(div);" \
+		"alert('appended');" \
+	"}" \
+	"</script></head><body class=l%d></body></html>"
 
 static DebugWindow *debug_win = NULL;
 static guint debug_enabled_timer = 0;
@@ -94,7 +109,8 @@
 		text = gtk_entry_get_text(GTK_ENTRY(debug_win->expression));
 		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text);
 	}
-	g_regex_unref(debug_win->regex);
+	if (debug_win->regex != NULL)
+		g_regex_unref(debug_win->regex);
 
 	/* If the "Save Log" dialog is open then close it */
 	purple_request_close_with_handle(debug_win);
@@ -207,8 +223,16 @@
 	if (!WEBKIT_DOM_IS_HTML_ELEMENT(div))
 		return;
 
+#if (WEBKIT_MAJOR_VERSION == 1 && \
+     WEBKIT_MINOR_VERSION == 9 && \
+     WEBKIT_MICRO_VERSION == 90)
+	/* Workaround WebKit API bug. */
+	classes = webkit_dom_element_get_class_list(WEBKIT_DOM_ELEMENT(div));
+#else
 	classes = webkit_dom_html_element_get_class_list(WEBKIT_DOM_HTML_ELEMENT(div));
+#endif
 	webkit_dom_dom_token_list_toggle(classes, "hide", NULL);
+	g_object_unref(classes);
 }
 
 static void
@@ -235,6 +259,8 @@
 
 		webkit_dom_node_replace_child(parent, WEBKIT_DOM_NODE(text), span, &err);
 	}
+
+	g_object_unref(nodes);
 }
 
 static void
@@ -395,11 +421,14 @@
 	/* Re-show debug lines that didn't match regex */
 	list = webkit_dom_document_get_elements_by_class_name(dom, "hide");
 	i = webkit_dom_node_list_get_length(list);
+
 	while (i--) {
 		WebKitDOMNode *div = webkit_dom_node_list_item(list, i);
 		regex_toggle_div(div);
 	}
 
+	g_object_unref(list);
+
 	if (filter) {
 		list = webkit_dom_document_get_elements_by_tag_name(dom, "div");
 
@@ -407,6 +436,8 @@
 			WebKitDOMNode *div = webkit_dom_node_list_item(list, i);
 			regex_match(win, dom, div);
 		}
+
+		g_object_unref(list);
 	}
 }
 
@@ -624,25 +655,39 @@
 }
 
 static void
-regex_html_appended_cb(GtkWebView *webview, WebKitDOMRange *range, DebugWindow *win)
+debug_window_appended(DebugWindow *win)
 {
-	if (!win || !win->window)
+	WebKitDOMDocument *dom;
+	WebKitDOMHTMLElement *body;
+	WebKitDOMNode *div;
+
+	if (!gtk_toggle_tool_button_get_active(
+		GTK_TOGGLE_TOOL_BUTTON(win->filter)))
 		return;
 
-	if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) {
-		WebKitDOMDocument *dom;
-		WebKitDOMHTMLElement *body;
-		WebKitDOMNode *div;
+	dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text));
+	body = webkit_dom_document_get_body(dom);
+	div = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body));
+
+	if (webkit_dom_element_webkit_matches_selector(
+		WEBKIT_DOM_ELEMENT(div), "body>div:not(#pause)", NULL))
+		regex_match(win, dom, div);
+}
 
-		dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text));
-		body = webkit_dom_document_get_body(dom);
-		div = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body));
+static gboolean debug_window_alert_cb(WebKitWebView *webview,
+	WebKitWebFrame *frame, gchar *message, gpointer _win)
+{
+	DebugWindow *win = _win;
 
-		if (webkit_dom_element_webkit_matches_selector(WEBKIT_DOM_ELEMENT(div),
-		                                               "body>div:not(#pause)",
-		                                               NULL))
-			regex_match(win, dom, div);
+	if (!win || !win->window)
+		return FALSE;
+
+	if (g_strcmp0(message, "appended") == 0) {
+		debug_window_appended(win);
+		return TRUE;
 	}
+
+	return FALSE;
 }
 
 static DebugWindow *
@@ -816,8 +861,8 @@
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 	gtk_widget_show(frame);
 
-	g_signal_connect(G_OBJECT(win->text), "html-appended",
-	                 G_CALLBACK(regex_html_appended_cb), win);
+	g_signal_connect(G_OBJECT(win->text), "script-alert",
+		G_CALLBACK(debug_window_alert_cb), win);
 
 	clear_cb(NULL, win);
 
@@ -943,6 +988,7 @@
 	REGISTER_G_LOG_HANDLER("GModule");
 	REGISTER_G_LOG_HANDLER("GLib-GObject");
 	REGISTER_G_LOG_HANDLER("GThread");
+	REGISTER_G_LOG_HANDLER("Json");
 #ifdef USE_GSTREAMER
 	REGISTER_G_LOG_HANDLER("GStreamer");
 #endif
@@ -984,41 +1030,29 @@
 
 static void
 pidgin_debug_print(PurpleDebugLevel level, const char *category,
-					 const char *arg_s)
+	const char *arg_s)
 {
-	gchar *ts_s;
-	gchar *esc_s, *cat_s, *tmp, *s;
+	gchar *esc_s;
 	const char *mdate;
 	time_t mtime;
+	gchar *js;
 
-	if (debug_win == NULL ||
-		!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
-	{
+	if (debug_win == NULL)
 		return;
-	}
+	if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
+		return;
 
 	mtime = time(NULL);
 	mdate = purple_utf8_strftime("%H:%M:%S", localtime(&mtime));
-	ts_s = g_strdup_printf("(%s) ", mdate);
-	if (category == NULL)
-		cat_s = g_strdup("");
-	else
-		cat_s = g_strdup_printf("<b>%s:</b> ", category);
 
-	tmp = purple_utf8_try_convert(arg_s);
-	esc_s = g_markup_escape_text(tmp, -1);
+	esc_s = purple_escape_js(arg_s);
 
-	s = g_strdup_printf("<div class=\"l%d\">%s%s%s</div>",
-	                    level, ts_s, cat_s, esc_s);
-
-	g_free(ts_s);
-	g_free(cat_s);
+	js = g_strdup_printf("append(%d, '%s', '%s', %s);",
+		level, mdate, category ? category : "", esc_s);
 	g_free(esc_s);
-	g_free(tmp);
 
-	gtk_webview_append_html(GTK_WEBVIEW(debug_win->text), s);
-
-	g_free(s);
+	gtk_webview_safe_execute_script(GTK_WEBVIEW(debug_win->text), js);
+	g_free(js);
 }
 
 static gboolean
--- a/pidgin/gtkdialogs.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkdialogs.c	Fri Jan 25 02:22:38 2013 -0500
@@ -207,6 +207,7 @@
 	{NULL,                      NULL, "Erdal Ronahi", "erdal.ronahi@gmail.com"},
 	{NULL,                      NULL, "Rizoyê Xerzî", "rizoxerzi@hotmail.com"},
 	{N_("Lao"),                 "lo", "Anousak Souphavah", "anousak@gmail.com"},
+	{N_("Lithuanian"),          "lt", "Algimantas Margevičius", "margevicius.algimantas@gmail.com"},
 	{N_("Maithili"),            "mai", "Sangeeta Kumari", "sangeeta_0975@yahoo.com"},
 	{NULL,                      NULL, "Rajesh Ranjan", "rajeshkajha@yahoo.com"},
 	{N_("Meadow Mari"),         "mhr", "David Preece", "davidpreece1@gmail.com"},
@@ -219,7 +220,7 @@
 	{N_("Malay"),               "ms_MY", "Muhammad Najmi bin Ahmad Zabidi", "najmi.zabidi@gmail.com"},
 	{N_("Burmese"),             "my_MM", "Thura Hlaing", "trhura@gmail.com"},
 	{N_("Bokmål Norwegian"),    "nb", "Hans Fredrik Nordhaug", "hans@nordhaug.priv.no"},
-	{N_("Nepali"),              "ne", "Shyam Krishna Bal", "shyamkrishna_bal@yahoo.com"},
+	{N_("Nepali"),              "ne", "Shyam Krishna Bal", NULL},
 	{N_("Dutch, Flemish"),      "nl", "Gideon van Melle", "translations@gvmelle.com"},
 	{N_("Norwegian Nynorsk"),   "nn", "Yngve Spjeld Landro", "l10n@landro.net"},
 	{N_("Occitan"),             "oc", "Yannig Marchegay", "yannig@marchegay.org"},
@@ -246,7 +247,6 @@
 	{NULL,                      NULL, "Viveka Nathan K", "vivekanathan@users.sourceforge.net"},
 	{N_("Telugu"),              "te", "Krishnababu Krottapalli", "krottapalli@ymail.com"},
 	{N_("Thai"),                "th", "Isriya Paireepairit", "markpeak@gmail.com"},
-	{N_("Turkish"),             "tr", "Serdar Soytetir", "tulliana@gmail.com"},
 	{N_("Ukranian"),            "uk", "Oleksandr Kovalenko", "alx.kovalenko@gmail.com"},
 	{N_("Urdu"),                "ur", "RKVS Raman", "rkvsraman@gmail.com"},
 	{N_("Vietnamese"),          "vi", "Nguyễn Vũ Hưng", "vuhung16plus@gmail.com"},
@@ -324,7 +324,8 @@
 	{N_("Swedish"),             "sv", "Tore Lundqvist", NULL},
 	{NULL,                      NULL, "Christian Rose", NULL},
 	{N_("Telugu"),              "te", "Mr. Subbaramaih", "info.gist@cdac.in"},
-	{N_("Turkish"),             "tr", "Ahmet Alp Balkan", NULL},
+	{N_("Turkish"),             "tr", "Serdar Soytetir", "tulliana@gmail.com"},
+	{NULL,                      "tr", "Ahmet Alp Balkan", NULL},
 	{N_("Vietnamese"),          "vi", N_("T.M.Thanh and the Gnome-Vi Team"), "gnomevi-list@lists.sf.net"},
 	{N_("Simplified Chinese"),  "zh_CN", "Hashao", NULL},
 	{NULL,                      NULL, "Rocky S. Lee", NULL},
--- a/pidgin/gtkdocklet.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkdocklet.c	Fri Jan 25 02:22:38 2013 -0500
@@ -44,6 +44,7 @@
 #include "pidginstock.h"
 #include "gtkdocklet.h"
 #include "gtkdialogs.h"
+#include "gtknotify.h"
 
 #include "gtk3compat.h"
 
@@ -58,8 +59,7 @@
 static GtkStatusIcon *docklet = NULL;
 static guint embed_timeout = 0;
 static PurpleStatusPrimitive status = PURPLE_STATUS_OFFLINE;
-static gboolean pending = FALSE;
-static gboolean connecting = FALSE;
+static PidginDockletFlag flags = 0;
 static gboolean enable_join_chat = FALSE;
 static gboolean visible = FALSE;
 static gboolean visibility_manager = FALSE;
@@ -71,8 +71,14 @@
 /**************************************************************************
  * docklet status and utility functions
  **************************************************************************/
+static inline gboolean
+docklet_is_blinking()
+{
+	return flags && !(flags & PIDGIN_DOCKLET_CONNECTING);
+}
+
 static void
-docklet_gtk_status_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending)
+docklet_gtk_status_update_icon(PurpleStatusPrimitive status, PidginDockletFlag newflag)
 {
 	const gchar *icon_name = NULL;
 
@@ -97,9 +103,11 @@
 			break;
 	}
 
-	if (pending)
+	if (newflag & PIDGIN_DOCKLET_EMAIL_PENDING)
+		icon_name = PIDGIN_STOCK_TRAY_EMAIL;
+	if (newflag & PIDGIN_DOCKLET_CONV_PENDING)
 		icon_name = PIDGIN_STOCK_TRAY_PENDING;
-	if (connecting)
+	if (newflag & PIDGIN_DOCKLET_CONNECTING)
 		icon_name = PIDGIN_STOCK_TRAY_CONNECT;
 
 	if (icon_name) {
@@ -108,7 +116,11 @@
 
 #if !GTK_CHECK_VERSION(3,0,0)
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")) {
-		gtk_status_icon_set_blinking(docklet, (pending && !connecting));
+		gboolean pending = FALSE;
+		pending |= (newflag & PIDGIN_DOCKLET_EMAIL_PENDING);
+		pending |= (newflag & PIDGIN_DOCKLET_CONV_PENDING);
+		gtk_status_icon_set_blinking(docklet, pending &&
+			!(newflag & PIDGIN_DOCKLET_CONNECTING));
 	} else if (gtk_status_icon_get_blinking(docklet)) {
 		gtk_status_icon_set_blinking(docklet, FALSE);
 	}
@@ -129,7 +141,7 @@
 		return l_im;
 
 	l_chat = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT,
-		 					 PIDGIN_UNSEEN_NICK,
+	    purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/notification_chat"),
 							 FALSE, max);
 
 	if (l_im != NULL && l_chat != NULL)
@@ -147,7 +159,7 @@
 	int count;
 	PurpleSavedStatus *saved_status;
 	PurpleStatusPrimitive newstatus = PURPLE_STATUS_OFFLINE;
-	gboolean newpending = FALSE, newconnecting = FALSE;
+	PidginDockletFlag newflags = 0;
 
 	/* get the current savedstatus */
 	saved_status = purple_savedstatus_get_current();
@@ -174,7 +186,7 @@
 	if (convs != NULL) {
 		/* set tooltip if messages are pending */
 		GString *tooltip_text = g_string_new("");
-		newpending = TRUE;
+		newflags |= PIDGIN_DOCKLET_CONV_PENDING;
 
 		for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) {
 			PurpleConversation *conv = (PurpleConversation *)l->data;
@@ -223,18 +235,20 @@
 			continue;
 
 		if (purple_account_is_connecting(account))
-			newconnecting = TRUE;
+			newflags |= PIDGIN_DOCKLET_CONNECTING;
+
+		if (pidgin_notify_emails_pending())
+			newflags |= PIDGIN_DOCKLET_EMAIL_PENDING;
 	}
 
 	newstatus = purple_savedstatus_get_type(saved_status);
 
 	/* update the icon if we changed status */
-	if (status != newstatus || pending!=newpending || connecting!=newconnecting) {
+	if (status != newstatus || flags != newflags) {
 		status = newstatus;
-		pending = newpending;
-		connecting = newconnecting;
+		flags = newflags;
 
-		docklet_gtk_status_update_icon(status, connecting, pending);
+		docklet_gtk_status_update_icon(status, flags);
 	}
 
 	return FALSE; /* for when we're called by the glib idle handler */
@@ -709,7 +723,7 @@
 
 	menuitem = gtk_menu_item_new_with_mnemonic(_("_Unread Messages"));
 
-	if (pending) {
+	if (flags & PIDGIN_DOCKLET_CONV_PENDING) {
 		GtkWidget *submenu = gtk_menu_new();
 		GList *l = get_pending_list(0);
 		if (l == NULL) {
@@ -785,7 +799,9 @@
 {
 	switch (button_type) {
 		case 1:
-			if (pending) {
+			if (flags & PIDGIN_DOCKLET_EMAIL_PENDING) {
+				pidgin_notify_emails_present(NULL);
+			} else if (flags & PIDGIN_DOCKLET_CONV_PENDING) {
 				GList *l = get_pending_list(1);
 				if (l != NULL) {
 					pidgin_conv_present_conversation((PurpleConversation *)l->data);
@@ -811,7 +827,7 @@
 	}
 	visible = TRUE;
 	docklet_update_status();
-	docklet_gtk_status_update_icon(status, connecting, pending);
+	docklet_gtk_status_update_icon(status, flags);
 }
 
 static void
@@ -827,14 +843,6 @@
 	}
 }
 
-static gboolean
-docklet_gtk_recreate_cb(gpointer data)
-{
-	docklet_gtk_status_create(TRUE);
-
-	return FALSE;
-}
-
 #ifndef _WIN32
 static gboolean
 docklet_gtk_embed_timeout_cb(gpointer data)
@@ -896,19 +904,6 @@
 #endif
 
 static void
-docklet_gtk_destroyed_cb(GtkWidget *widget, gpointer data)
-{
-	purple_debug_info("docklet", "destroyed\n");
-
-	pidgin_docklet_remove();
-
-	g_object_unref(G_OBJECT(docklet));
-	docklet = NULL;
-
-	g_idle_add(docklet_gtk_recreate_cb, NULL);
-}
-
-static void
 docklet_gtk_status_activated_cb(GtkStatusIcon *status_icon, gpointer user_data)
 {
 	pidgin_docklet_clicked(1);
@@ -939,7 +934,6 @@
 	}
 
 	gtk_status_icon_set_visible(docklet, FALSE);
-	g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
 	g_object_unref(G_OBJECT(docklet));
 	docklet = NULL;
 
@@ -965,7 +959,6 @@
 #if GTK_CHECK_VERSION(2,12,0)
 	g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL);
 #endif
-	g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
 
 	gtk_status_icon_set_visible(docklet, TRUE);
 
@@ -1025,6 +1018,7 @@
 	void *accounts_handle = purple_accounts_get_handle();
 	void *status_handle = purple_savedstatuses_get_handle();
 	void *docklet_handle = pidgin_docklet_get_handle();
+	void *notify_handle = purple_notify_get_handle();
 	gchar *tmp;
 
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet");
@@ -1032,6 +1026,7 @@
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/docklet/show", "always");
 	purple_prefs_connect_callback(docklet_handle, PIDGIN_PREFS_ROOT "/docklet/show",
 				    docklet_show_pref_changed_cb, NULL);
+	purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/notification_chat", PIDGIN_UNSEEN_TEXT);
 
 	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/gtk");
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) {
@@ -1064,6 +1059,12 @@
 			    docklet_handle, PURPLE_CALLBACK(docklet_conv_updated_cb), NULL);
 	purple_signal_connect(status_handle, "savedstatus-changed",
 			    docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL);
+	purple_signal_connect(notify_handle, "displaying-email-notification",
+			    docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL);
+	purple_signal_connect(notify_handle, "displaying-emails-notification",
+			    docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL);
+	purple_signal_connect(notify_handle, "displaying-emails-clear",
+			    docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL);
 #if 0
 	purple_signal_connect(purple_get_core(), "quitting",
 			    docklet_handle, PURPLE_CALLBACK(purple_quit_cb), NULL);
--- a/pidgin/gtkdocklet.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkdocklet.h	Fri Jan 25 02:22:38 2013 -0500
@@ -27,6 +27,13 @@
 
 G_BEGIN_DECLS
 
+typedef enum
+{
+	PIDGIN_DOCKLET_CONNECTING 	= 0x1,
+	PIDGIN_DOCKLET_CONV_PENDING 	= 0x2,
+	PIDGIN_DOCKLET_EMAIL_PENDING 	= 0x4
+} PidginDockletFlag;
+
 /**
  * Returns the GtkStatusIcon used for the docklet.
  */
--- a/pidgin/gtkeventloop.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkeventloop.c	Fri Jan 25 02:22:38 2013 -0500
@@ -124,11 +124,7 @@
 	pidgin_input_add,
 	g_source_remove,
 	NULL, /* input_get_error */
-#if GLIB_CHECK_VERSION(2,14,0)
 	g_timeout_add_seconds,
-#else
-	NULL,
-#endif
 	NULL,
 	NULL,
 	NULL
--- a/pidgin/gtkimhtml.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkimhtml.c	Fri Jan 25 02:22:38 2013 -0500
@@ -2523,7 +2523,7 @@
 [19:58] <Robot101> marv: images go into the imgstore, a refcounted... well.. hash. :)
 [19:59] <KingAnt> marv: I think the image tag used by the core is something like <img id="#"/>
 [19:59] Ro0tSiEgE robert42 RobFlynn Robot101 ross22 roz
-[20:00] <KingAnt> marv: Where the ID is the what is returned when you add the image to the imgstore using purple_imgstore_add
+[20:00] <KingAnt> marv: Where the ID is the what is returned when you add the image to the imgstore using purple_imgstore_new
 [20:00] <marv> Robot101: so how does the image get passed to serv_got_im() and serv_send_im()? just as the <img id="#" and then the prpl looks it up from the store?
 [20:00] <KingAnt> marv: Right
 [20:00] <marv> alright
@@ -3963,8 +3963,7 @@
 
 	gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(image->image));
 
-	if(!gtk_check_version(2, 4, 0))
-		g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL);
+	g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL);
 
 	gtk_widget_show(GTK_WIDGET(image->image));
 	gtk_widget_show(box);
--- a/pidgin/gtkimhtmltoolbar.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkimhtmltoolbar.c	Fri Jan 25 02:22:38 2013 -0500
@@ -529,7 +529,7 @@
 
 	name = strrchr(filename, G_DIR_SEPARATOR) + 1;
 
-	id = purple_imgstore_add_with_id(filedata, size, name);
+	id = purple_imgstore_new_with_id(filedata, size, name);
 
 	if (id == 0) {
 		buf = g_strdup_printf(_("Failed to store image: %s\n"), filename);
--- a/pidgin/gtkmain.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkmain.c	Fri Jan 25 02:22:38 2013 -0500
@@ -88,10 +88,6 @@
 	SIGINT,
 	SIGTERM,
 	SIGQUIT,
-	SIGCHLD,
-#if defined(USE_GSTREAMER) && !defined(GST_CAN_DISABLE_FORKING)
-	SIGALRM,
-#endif
 	-1
 };
 
@@ -136,29 +132,6 @@
 
 static void sighandler(int sig);
 
-/*
- * This child process reaping stuff is currently only used for processes that
- * were forked to play sounds.  It's not needed for forked DNS child, which
- * have their own waitpid() call.  It might be wise to move this code into
- * gtksound.c.
- */
-static void
-clean_pid(void)
-{
-	int status;
-	pid_t pid;
-
-	do {
-		pid = waitpid(-1, &status, WNOHANG);
-	} while (pid != 0 && pid != (pid_t)-1);
-
-	if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
-		char errmsg[BUFSIZ];
-		snprintf(errmsg, sizeof(errmsg), "Warning: waitpid() returned %d", pid);
-		perror(errmsg);
-	}
-}
-
 static void sighandler(int sig)
 {
 	ssize_t written;
@@ -209,33 +182,8 @@
 		return FALSE;
 	}
 
-	switch (sig) {
-#if defined(USE_GSTREAMER) && !defined(GST_CAN_DISABLE_FORKING)
-/* By default, gstreamer forks when you initialize it, and waitpids for the
- * child.  But if libpurple reaps the child rather than leaving it to
- * gstreamer, gstreamer's initialization fails.  So, we wait a second before
- * reaping child processes, to give gst a chance to reap it if it wants to.
- *
- * This is not needed in later gstreamers, which let us disable the forking.
- * And, it breaks the world on some Real Unices.
- */
-	case SIGCHLD:
-		/* Restore signal catching */
-		signal(SIGCHLD, sighandler);
-		alarm(1);
-		break;
-	case SIGALRM:
-#else
-	case SIGCHLD:
-#endif
-		clean_pid();
-		/* Restore signal catching */
-		signal(SIGCHLD, sighandler);
-		break;
-	default:
-		purple_debug_warning("sighandler", "Caught signal %d\n", sig);
-		purple_core_quit();
-	}
+	purple_debug_warning("sighandler", "Caught signal %d\n", sig);
+	purple_core_quit();
 
 	return TRUE;
 }
@@ -465,8 +413,8 @@
 /* FUCKING GET ME A TOWEL! */
 #ifdef _WIN32
 /* suppress gcc "no previous prototype" warning */
-int pidgin_main(HINSTANCE hint, int argc, char *argv[]);
-int pidgin_main(HINSTANCE hint, int argc, char *argv[])
+int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[]);
+int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[])
 #else
 int main(int argc, char *argv[])
 #endif
@@ -806,16 +754,9 @@
 		return 0;
 	}
 
-	/* TODO: Move blist loading into purple_blist_init() */
-	purple_set_blist(purple_blist_new());
-	purple_blist_load();
-
 	/* load plugins we had when we quit */
 	purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded");
 
-	/* TODO: Move pounces loading into purple_pounces_init() */
-	purple_pounces_load();
-
 	ui_main();
 
 #ifdef USE_SM
--- a/pidgin/gtkmedia.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkmedia.c	Fri Jan 25 02:22:38 2013 -0500
@@ -38,14 +38,16 @@
 #ifdef USE_VV
 #include "media-gst.h"
 
-#ifdef _WIN32
+#ifdef GDK_WINDOWING_WIN32
 #include <gdk/gdkwin32.h>
-#elif defined(GDK_WINDOWING_QUARTZ)
+#endif
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
 #include <gdk/gdkquartz.h>
 #endif
 
-#include <gst/interfaces/xoverlay.h>
-
 #include "gtk3compat.h"
 
 #define PIDGIN_TYPE_MEDIA            (pidgin_media_get_type())
@@ -551,15 +553,27 @@
 	}
 
 	if (window) {
-		gulong window_id;
-#ifdef _WIN32
-		window_id = GDK_WINDOW_HWND(window);
-#elif defined(HAVE_X11)
-		window_id = gdk_x11_window_get_xid(window);
-#elif defined(GDK_WINDOWING_QUARTZ)
-		window_id = (gulong) gdk_quartz_window_get_nsview(window);
-#else
-#		error "Unsupported windowing system"
+		gulong window_id = 0;
+#ifdef GDK_WINDOWING_WIN32
+		if (GDK_IS_WIN32_WINDOW(window))
+			window_id = GDK_WINDOW_HWND(window);
+		else
+#endif
+#ifdef GDK_WINDOWING_X11
+		if (GDK_IS_X11_WINDOW(window))
+			window_id = gdk_x11_window_get_xid(window);
+		else
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
+		if (GDK_IS_QUARTZ_WINDOW(window))
+			window_id = (gulong)gdk_quartz_window_get_nsview(window);
+		else
+#endif
+			g_warning("Unsupported GDK backend");
+#if !(defined(GDK_WINDOWING_WIN32) \
+   || defined(GDK_WINDOWING_X11) \
+   || defined(GDK_WINDOWING_QUARTZ))
+#		error "Unsupported GDK windowing system"
 #endif
 
 		purple_media_set_output_window(priv->media, data->session_id,
--- a/pidgin/gtknotify.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtknotify.c	Fri Jan 25 02:22:38 2013 -0500
@@ -375,11 +375,37 @@
 static void
 reset_mail_dialog(GtkDialog *unused)
 {
+	g_return_if_fail(mail_dialog != NULL);
+
 	if (mail_dialog->in_use)
 		return;
 	gtk_widget_destroy(mail_dialog->dialog);
 	g_free(mail_dialog);
 	mail_dialog = NULL;
+	purple_signal_emit(purple_notify_get_handle(), "displaying-emails-clear");
+}
+
+gboolean
+pidgin_notify_emails_pending()
+{
+#if GTK_CHECK_VERSION(2,18,0)
+	return mail_dialog != NULL
+		&& !gtk_widget_get_visible(mail_dialog->dialog);
+#else
+	return mail_dialog != NULL
+		&& !GTK_WIDGET_VISIBLE(mail_dialog->dialog);
+#endif
+}
+
+void pidgin_notify_emails_present(void *data)
+{
+    if (pidgin_notify_emails_pending()) {
+		gtk_widget_show_all(mail_dialog->dialog);
+		mail_dialog->in_use = TRUE;
+		pidgin_blist_set_headline(NULL, NULL, NULL, NULL, NULL);
+		mail_dialog->in_use = FALSE;
+	}
+	purple_signal_emit(purple_notify_get_handle(), "displaying-emails-clear");
 }
 
 static void
@@ -796,7 +822,7 @@
 										   remove the notifications when replacing an
 										   old notification. */
 		pidgin_blist_set_headline(label_text,
-					    pixbuf, G_CALLBACK(gtk_widget_show_all), mail_dialog->dialog,
+					    pixbuf, G_CALLBACK(pidgin_notify_emails_present), mail_dialog->dialog,
 					    (GDestroyNotify)reset_mail_dialog);
 		mail_dialog->in_use = FALSE;
 		g_free(label_text);
--- a/pidgin/gtknotify.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtknotify.h	Fri Jan 25 02:22:38 2013 -0500
@@ -63,4 +63,18 @@
 
 G_END_DECLS
 
+/**
+ * Returns TRUE if there are unseen emails, FALSE otherwise.
+ *
+ * @return TRUE if there are unseen emails, FALSE otherwise.
+ */
+gboolean pidgin_notify_emails_pending(void);
+
+/**
+ * Presents mail dialog to the user.
+ *
+ * @return void.
+ */
+void pidgin_notify_emails_present(void *data);
+
 #endif /* _PIDGINNOTIFY_H_ */
--- a/pidgin/gtkpounce.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkpounce.c	Fri Jan 25 02:22:38 2013 -0500
@@ -1510,31 +1510,13 @@
 
 		if (command != NULL)
 		{
-#ifndef _WIN32
-			char *localecmd = g_locale_from_utf8(command, -1, NULL,
-					NULL, NULL);
-
-			if (localecmd != NULL)
-			{
-				int pid = fork();
-
-				if (pid == 0) {
-					char *args[4];
-
-					args[0] = "sh";
-					args[1] = "-c";
-					args[2] = (char *)localecmd;
-					args[3] = NULL;
-
-					execvp(args[0], args);
-
-					_exit(0);
-				}
-				g_free(localecmd);
+			GError *error = NULL;
+			if (!g_spawn_command_line_async(command, &error)) {
+				purple_debug_error("gtkpounce",
+				                   "pounce command could not be launched: %s\n",
+				                   error->message);
+				g_error_free(error);
 			}
-#else /* !_WIN32 */
-			winpidgin_shell_execute(command, "open", NULL);
-#endif /* !_WIN32 */
 		}
 	}
 
--- a/pidgin/gtkprefs.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkprefs.c	Fri Jan 25 02:22:38 2013 -0500
@@ -26,6 +26,7 @@
  */
 #include "internal.h"
 #include "pidgin.h"
+#include "obsolete.h"
 
 #include "debug.h"
 #include "nat-pmp.h"
@@ -58,6 +59,24 @@
 #include "gtkwebview.h"
 #include "gtkwebviewtoolbar.h"
 #include "pidginstock.h"
+#ifdef USE_VV
+#include "media-gst.h"
+#if GST_CHECK_VERSION(1,0,0)
+#include <gst/video/videooverlay.h>
+#else
+#include <gst/interfaces/xoverlay.h>
+#include <gst/interfaces/propertyprobe.h>
+#endif
+#ifdef GDK_WINDOWING_WIN32
+#include <gdk/gdkwin32.h>
+#endif
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
+#include <gdk/gdkquartz.h>
+#endif
+#endif
 
 #include "gtk3compat.h"
 
@@ -105,6 +124,60 @@
 static GtkListStore *prefs_status_icon_themes;
 static GtkListStore *prefs_smiley_themes;
 
+#ifdef USE_VV
+
+static const gchar *AUDIO_SRC_PLUGINS[] = {
+	"alsasrc",	"ALSA",
+	/* "esdmon",	"ESD", ? */
+	"osssrc",	"OSS",
+	"pulsesrc",	"PulseAudio",
+	"sndiosrc",	"sndio",
+	/* "audiotestsrc wave=silence", "Silence", */
+	"audiotestsrc",	"Test Sound",
+	NULL
+};
+
+static const gchar *AUDIO_SINK_PLUGINS[] = {
+	"alsasink",	"ALSA",
+	"artsdsink",	"aRts",
+	"esdsink",	"ESD",
+	"osssink",	"OSS",
+	"pulsesink",	"PulseAudio",
+	"sndiosink",	"sndio",
+	NULL
+};
+
+static const gchar *VIDEO_SRC_PLUGINS[] = {
+	"videotestsrc",	"Test Input",
+	"dshowvideosrc","DirectDraw",
+	"ksvideosrc",	"KS Video",
+	"qcamsrc",	"Quickcam",
+	"v4lsrc",	"Video4Linux",
+	"v4l2src",	"Video4Linux2",
+	"v4lmjpegsrc",	"Video4Linux MJPEG",
+	NULL
+};
+
+static const gchar *VIDEO_SINK_PLUGINS[] = {
+	/* "aasink",	"AALib", Didn't work for me */
+	"directdrawsink","DirectDraw",
+	"glimagesink",	"OpenGL",
+	"ximagesink",	"X Window System",
+	"xvimagesink",	"X Window System (Xv)",
+	NULL
+};
+
+typedef struct {
+	GtkWidget *level;
+	GtkWidget *threshold;
+	GtkWidget *volume;
+} BusCbCtx;
+
+static GstElement *voice_pipeline;
+static GstElement *video_pipeline;
+
+#endif
+
 /*
  * PROTOTYPES
  */
@@ -661,6 +734,19 @@
 	prefs_smiley_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
 }
 
+/**
+ * Attempt to load the given directory as a theme.  If we are unable to
+ * open the path as a theme then we recurse into path and attempt to
+ * load each subdirectory that we encounter.
+ *
+ * @param path A directory containing a theme.  The theme could be at the
+ *        top level of this directory or in any subdirectory thereof.
+ * @param type The type of theme to load.  The loader for this theme type
+ *        will be used and this loader will determine what constitutes a
+ *        "theme."
+ *
+ * @return A new reference to a PurpleTheme.
+ */
 static PurpleTheme *
 prefs_theme_find_theme(const gchar *path, const gchar *type)
 {
@@ -804,7 +890,11 @@
 						 "purple", info->type, NULL);
 
 			/* move the entire directory to new location */
-			g_rename(purple_theme_get_dir(theme), theme_dest);
+			if (g_rename(purple_theme_get_dir(theme), theme_dest)) {
+				purple_debug_error("gtkprefs", "Error renaming %s to %s: "
+						"%s\n", purple_theme_get_dir(theme), theme_dest,
+						g_strerror(errno));
+			}
 
 			g_free(theme_dest);
 			g_remove(destdir);
@@ -848,14 +938,21 @@
 				if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
 					purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
 
-				g_rename(purple_theme_get_dir(theme), theme_dest);
+				if (g_rename(purple_theme_get_dir(theme), theme_dest)) {
+					purple_debug_error("gtkprefs", "Error renaming %s to %s: "
+							"%s\n", purple_theme_get_dir(theme), theme_dest,
+							g_strerror(errno));
+				}
 
 				g_free(theme_dest);
 				g_object_unref(theme);
 
 				prefs_themes_refresh();
 			} else {
-				g_remove(temp_path);
+				if (g_remove(temp_path)) {
+					purple_debug_error("gtkprefs", "Error removing %s: %s\n",
+							temp_path, g_strerror(errno));
+				}
 				purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL);
 			}
 		} else {
@@ -1648,6 +1745,13 @@
 
 	vbox = pidgin_make_frame(ret, _("Conversations"));
 
+	pidgin_prefs_dropdown(vbox, _("Chat notification:"),
+		PURPLE_PREF_INT, PIDGIN_PREFS_ROOT "/conversations/notification_chat",
+		_("On unseen events"), PIDGIN_UNSEEN_EVENT,
+		_("On unseen text"), PIDGIN_UNSEEN_TEXT,
+		_("On unseen text and the nick was said"), PIDGIN_UNSEEN_NICK,
+		NULL);
+
 	pidgin_prefs_checkbox(_("Show _formatting on incoming messages"),
 				PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox);
 	pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"),
@@ -1664,10 +1768,8 @@
 
 	pidgin_prefs_checkbox(_("_Notify buddies that you are typing to them"),
 			"/purple/conversations/im/send_typing", vbox);
-#ifdef USE_GTKSPELL
 	pidgin_prefs_checkbox(_("Highlight _misspelled words"),
 			PIDGIN_PREFS_ROOT "/conversations/spellcheck", vbox);
-#endif
 
 	pidgin_prefs_checkbox(_("Use smooth-scrolling"), PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", vbox);
 
@@ -3015,6 +3117,616 @@
 	return ret;
 }
 
+#ifdef USE_VV
+static GList *
+get_vv_element_devices(const gchar *element_name)
+{
+	GList *ret = NULL;
+	GstElement *element;
+	GObjectClass *klass;
+#if !GST_CHECK_VERSION(1,0,0)
+	GstPropertyProbe *probe;
+	const GParamSpec *pspec;
+#endif
+
+	ret = g_list_prepend(ret, (gpointer)_("Default"));
+	ret = g_list_prepend(ret, "");
+
+	if (!strcmp(element_name, "<custom>") || (*element_name == '\0')) {
+		return g_list_reverse(ret);
+	}
+
+	element = gst_element_factory_make(element_name, "test");
+	if (!element) {
+		purple_debug_info("vvconfig", "'%s' - unable to find element\n",
+		                  element_name);
+		return g_list_reverse(ret);
+	}
+
+	klass = G_OBJECT_GET_CLASS (element);
+	if (!klass) {
+		purple_debug_info("vvconfig", "'%s' - unable to find GObject Class\n",
+		                  element_name);
+		return g_list_reverse(ret);
+	}
+
+#if GST_CHECK_VERSION(1,0,0)
+	purple_debug_info("vvconfig", "'%s' - no device\n", element_name);
+#else
+	if (!g_object_class_find_property(klass, "device") ||
+			!GST_IS_PROPERTY_PROBE(element) ||
+			!(probe = GST_PROPERTY_PROBE(element)) ||
+			!(pspec = gst_property_probe_get_property(probe, "device"))) {
+		purple_debug_info("vvconfig", "'%s' - no device\n", element_name);
+	} else {
+		gint n;
+		GValueArray *array;
+
+		/* Set autoprobe[-fps] to FALSE to avoid delays when probing. */
+		if (g_object_class_find_property(klass, "autoprobe")) {
+			g_object_set(G_OBJECT(element), "autoprobe", FALSE, NULL);
+			if (g_object_class_find_property(klass, "autoprobe-fps")) {
+				g_object_set(G_OBJECT(element), "autoprobe-fps", FALSE, NULL);
+			}
+		}
+
+		array = gst_property_probe_probe_and_get_values(probe, pspec);
+		if (array == NULL) {
+			purple_debug_info("vvconfig", "'%s' has no devices\n", element_name);
+			return g_list_reverse(ret);
+		}
+
+		for (n = 0; n < array->n_values; ++n) {
+			GValue *device;
+			const gchar *name;
+			const gchar *device_name;
+
+			device = g_value_array_get_nth(array, n);
+			g_object_set_property(G_OBJECT(element), "device", device);
+			if (gst_element_set_state(element, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) {
+				purple_debug_warning("vvconfig", "Error changing state of %s\n",
+				                     element_name);
+				continue;
+			}
+
+			g_object_get(G_OBJECT(element), "device-name", &name, NULL);
+			device_name = g_value_get_string(device);
+			if (name == NULL)
+				name = _("Unknown");
+			purple_debug_info("vvconfig", "Found device %s : %s for %s\n",
+			                  device_name, name, element_name);
+			ret = g_list_prepend(ret, (gpointer)name);
+			ret = g_list_prepend(ret, (gpointer)device_name);
+			gst_element_set_state(element, GST_STATE_NULL);
+		}
+	}
+#endif
+	gst_object_unref(element);
+
+	return g_list_reverse(ret);
+}
+
+static GList *
+get_vv_element_plugins(const gchar **plugins)
+{
+	GList *ret = NULL;
+
+	ret = g_list_prepend(ret, (gpointer)_("Default"));
+	ret = g_list_prepend(ret, "");
+	for (; plugins[0] && plugins[1]; plugins += 2) {
+#if GST_CHECK_VERSION(1,0,0)
+		if (gst_registry_check_feature_version(gst_registry_get(),
+		                                       plugins[0], 0, 0, 0)) {
+#else
+		if (gst_default_registry_check_feature_version(plugins[0], 0, 0, 0)) {
+#endif
+			ret = g_list_prepend(ret, (gpointer)plugins[1]);
+			ret = g_list_prepend(ret, (gpointer)plugins[0]);
+		}
+	}
+
+	return g_list_reverse(ret);
+}
+
+static void
+vv_plugin_changed_cb(const gchar *name, PurplePrefType type,
+                     gconstpointer value, gpointer data)
+{
+	GtkWidget *vbox = data;
+	GtkSizeGroup *sg;
+	GtkWidget *widget;
+	gchar *pref;
+	GList *devices;
+
+	sg = g_object_get_data(G_OBJECT(vbox), "size-group");
+	widget = g_object_get_data(G_OBJECT(vbox), "device-hbox");
+	gtk_widget_destroy(widget);
+
+	pref = g_strdup(name);
+	strcpy(pref + strlen(pref) - strlen("plugin"), "device");
+	devices = get_vv_element_devices(value);
+	if (g_list_find_custom(devices, purple_prefs_get_string(pref),
+	                       (GCompareFunc)strcmp) == NULL)
+		purple_prefs_set_string(pref, g_list_next(devices)->data);
+	widget = pidgin_prefs_dropdown_from_list(vbox, _("_Device"),
+	                                         PURPLE_PREF_STRING, pref, devices);
+	g_list_free(devices);
+	gtk_size_group_add_widget(sg, widget);
+	gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5);
+
+	g_object_set_data(G_OBJECT(vbox), "device-hbox",
+	                  gtk_widget_get_parent(widget));
+	g_signal_connect_swapped(widget, "destroy", G_CALLBACK(g_free), pref);
+}
+
+static void
+make_vv_frame(GtkWidget *parent, GtkSizeGroup *sg,
+              const gchar *name, const gchar **plugin_strs,
+              const gchar *plugin_pref, const gchar *device_pref)
+{
+	GtkWidget *vbox, *widget;
+	GList *plugins, *devices;
+
+	vbox = pidgin_make_frame(parent, name);
+
+	/* Setup plugin preference */
+	plugins = get_vv_element_plugins(plugin_strs);
+	widget = pidgin_prefs_dropdown_from_list(vbox, _("_Plugin"),
+	                                         PURPLE_PREF_STRING, plugin_pref,
+	                                         plugins);
+	g_list_free(plugins);
+	gtk_size_group_add_widget(sg, widget);
+	gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5);
+
+	/* Setup device preference */
+	devices = get_vv_element_devices(purple_prefs_get_string(plugin_pref));
+	if (g_list_find_custom(devices, purple_prefs_get_string(device_pref),
+	                       (GCompareFunc)strcmp) == NULL)
+		purple_prefs_set_string(device_pref, g_list_next(devices)->data);
+	widget = pidgin_prefs_dropdown_from_list(vbox, _("_Device"),
+	                                         PURPLE_PREF_STRING, device_pref,
+	                                         devices);
+	g_list_free(devices);
+	gtk_size_group_add_widget(sg, widget);
+	gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5);
+
+	widget = gtk_widget_get_parent(widget);
+	g_object_set_data(G_OBJECT(vbox), "size-group", sg);
+	g_object_set_data(G_OBJECT(vbox), "device-hbox", widget);
+	purple_prefs_connect_callback(vbox, plugin_pref, vv_plugin_changed_cb,
+	                              vbox);
+	g_signal_connect_swapped(vbox, "destroy",
+	                         G_CALLBACK(purple_prefs_disconnect_by_handle), vbox);
+}
+
+static GstElement *
+create_test_element(const char *type, const char *dir, PurpleMediaElementInfo *info)
+{
+	char *tmp;
+	const gchar *plugin;
+	const gchar *device;
+	GstElement *ret;
+
+	tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/plugin", type, dir);
+	plugin = purple_prefs_get_string(tmp);
+	g_free(tmp);
+
+	tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/device", type, dir);
+	device = purple_prefs_get_string(tmp);
+	g_free(tmp);
+
+	if (plugin[0] == '\0')
+		return purple_media_element_info_call_create(info, NULL, NULL, NULL);
+
+	ret = gst_element_factory_make(plugin, NULL);
+	if (device[0] != '\0')
+		g_object_set(G_OBJECT(ret), "device", device, NULL);
+	if (!strcmp(plugin, "videotestsrc"))
+		g_object_set(G_OBJECT(ret), "is-live", 1, NULL);
+
+	return ret;
+}
+
+static void
+vv_test_switch_page_cb(GtkNotebook *notebook, GtkWidget *page, guint num, gpointer data)
+{
+	GtkWidget *test = data;
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(test), FALSE);
+}
+
+static GstElement *
+create_voice_pipeline(void)
+{
+	PurpleMediaManager *manager;
+	PurpleMediaElementInfo *audio_src, *audio_sink;
+	GstElement *pipeline;
+	GstElement *src, *sink;
+	GstElement *volume;
+	GstElement *level;
+	GstElement *valve;
+
+	manager = purple_media_manager_get();
+	audio_src = purple_media_manager_get_active_element(manager,
+			PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC);
+	audio_sink = purple_media_manager_get_active_element(manager,
+			PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK);
+
+	pipeline = gst_pipeline_new("voicetest");
+	src = create_test_element("audio", "src", audio_src);
+	sink = create_test_element("audio", "sink", audio_sink);
+	volume = gst_element_factory_make("volume", "volume");
+	level = gst_element_factory_make("level", "level");
+	valve = gst_element_factory_make("valve", "valve");
+
+	gst_bin_add_many(GST_BIN(pipeline), src, volume, level, valve, sink, NULL);
+	gst_element_link_many(src, volume, level, valve, sink, NULL);
+
+	gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
+
+	return pipeline;
+}
+
+static void
+on_volume_change_cb(GtkWidget *w, GstBin *pipeline)
+{
+	GstElement *volume;
+
+	g_return_if_fail(pipeline != NULL);
+
+	volume = gst_bin_get_by_name(pipeline, "volume");
+	g_object_set(volume, "volume",
+	             gtk_scale_button_get_value(GTK_SCALE_BUTTON(w)) * 10.0, NULL);
+}
+
+static gdouble
+gst_msg_db_to_percent(GstMessage *msg, gchar *value_name)
+{
+	const GValue *list;
+	const GValue *value;
+	gdouble value_db;
+	gdouble percent;
+
+	list = gst_structure_get_value(gst_message_get_structure(msg), value_name);
+#if GST_CHECK_VERSION(1,0,0)
+	value = g_value_array_get_nth(g_value_get_boxed(list), 0);
+#else
+	value = gst_value_list_get_value(list, 0);
+#endif
+	value_db = g_value_get_double(value);
+	percent = pow(10, value_db / 20);
+	return (percent > 1.0) ? 1.0 : percent;
+}
+
+static gboolean
+gst_bus_cb(GstBus *bus, GstMessage *msg, BusCbCtx *ctx)
+{
+	if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
+		gst_structure_has_name(gst_message_get_structure(msg), "level")) {
+
+		GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
+		gchar *name = gst_element_get_name(src);
+
+		if (!strcmp(name, "level")) {
+			gdouble percent;
+			gdouble threshold;
+			GstElement *valve;
+
+			percent = gst_msg_db_to_percent(msg, "rms");
+			gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ctx->level), percent);
+
+			percent = gst_msg_db_to_percent(msg, "decay");
+			threshold = gtk_range_get_value(GTK_RANGE(ctx->threshold)) / 100.0;
+			valve = gst_bin_get_by_name(GST_BIN(GST_ELEMENT_PARENT(src)), "valve");
+			g_object_set(valve, "drop", (percent < threshold), NULL);
+			g_object_set(ctx->level, "text",
+			             (percent < threshold) ? _("DROP") : " ", NULL);
+		}
+
+		g_free(name);
+	}
+
+	return TRUE;
+}
+
+static void
+voice_test_destroy_cb(GtkWidget *w, gpointer data)
+{
+	if (!voice_pipeline)
+		return;
+
+	gst_element_set_state(voice_pipeline, GST_STATE_NULL);
+	gst_object_unref(voice_pipeline);
+	voice_pipeline = NULL;
+}
+
+static void
+toggle_voice_test_cb(GtkToggleButton *test, gpointer data)
+{
+	BusCbCtx *ctx = data;
+	GstBus *bus;
+
+	if (gtk_toggle_button_get_active(test)) {
+		gtk_widget_set_sensitive(ctx->level, TRUE);
+		voice_pipeline = create_voice_pipeline();
+		bus = gst_pipeline_get_bus(GST_PIPELINE(voice_pipeline));
+		gst_bus_add_signal_watch(bus);
+		g_signal_connect(bus, "message", G_CALLBACK(gst_bus_cb), ctx);
+		gst_object_unref(bus);
+
+		g_signal_connect(ctx->volume, "value-changed",
+		                 G_CALLBACK(on_volume_change_cb), voice_pipeline);
+		g_signal_connect(test, "destroy",
+		                 G_CALLBACK(voice_test_destroy_cb), NULL);
+		g_signal_connect(prefsnotebook, "switch-page",
+		                 G_CALLBACK(vv_test_switch_page_cb), test);
+	} else {
+		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ctx->level), 0.0);
+		gtk_widget_set_sensitive(ctx->level, FALSE);
+		g_object_disconnect(ctx->volume, "any-signal::value-changed",
+		                    G_CALLBACK(on_volume_change_cb), voice_pipeline,
+		                    NULL);
+		g_object_disconnect(test, "any-signal::destroy",
+		                    G_CALLBACK(voice_test_destroy_cb), NULL,
+		                    NULL);
+		g_object_disconnect(prefsnotebook, "any-signal::switch-page",
+		                    G_CALLBACK(vv_test_switch_page_cb), test,
+		                    NULL);
+		voice_test_destroy_cb(NULL, NULL);
+	}
+}
+
+static void
+volume_changed_cb(GtkScaleButton *button, gdouble value, gpointer data)
+{
+	purple_prefs_set_int("/purple/media/audio/volume/input", value * 100);
+}
+
+static void
+threshold_value_changed_cb(GtkScale *scale, GtkWidget *label)
+{
+	int value;
+	char *tmp;
+
+	value = (int)gtk_range_get_value(GTK_RANGE(scale));
+	tmp = g_strdup_printf(_("Silence threshold: %d%%"), value);
+	gtk_label_set_label(GTK_LABEL(label), tmp);
+	g_free(tmp);
+
+	purple_prefs_set_int("/purple/media/audio/silence_threshold", value);
+}
+
+static void
+make_voice_test(GtkWidget *vbox)
+{
+	GtkWidget *test;
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *level;
+	GtkWidget *volume;
+	GtkWidget *threshold;
+	BusCbCtx *ctx;
+	char *tmp;
+
+	label = gtk_label_new(NULL);
+	gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
+
+	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+	label = gtk_label_new(_("Volume:"));
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	volume = gtk_volume_button_new();
+	gtk_box_pack_start(GTK_BOX(hbox), volume, TRUE, TRUE, 0);
+	gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume),
+			purple_prefs_get_int("/purple/media/audio/volume/input") / 100.0);
+	g_signal_connect(volume, "value-changed",
+	                 G_CALLBACK(volume_changed_cb), NULL);
+
+	tmp = g_strdup_printf(_("Silence threshold: %d%%"),
+	                      purple_prefs_get_int("/purple/media/audio/silence_threshold"));
+	label = gtk_label_new(tmp);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	g_free(tmp);
+	threshold = gtk_hscale_new_with_range(0, 100, 1);
+	gtk_box_pack_start(GTK_BOX(vbox), threshold, FALSE, FALSE, 0);
+	gtk_range_set_value(GTK_RANGE(threshold),
+			purple_prefs_get_int("/purple/media/audio/silence_threshold"));
+	gtk_scale_set_draw_value(GTK_SCALE(threshold), FALSE);
+	g_signal_connect(threshold, "value-changed",
+	                 G_CALLBACK(threshold_value_changed_cb), label);
+
+	test = gtk_toggle_button_new_with_label(_("Test Audio"));
+	gtk_box_pack_start(GTK_BOX(vbox), test, FALSE, FALSE, 0);
+
+	level = gtk_progress_bar_new();
+	gtk_box_pack_start(GTK_BOX(vbox), level, FALSE, FALSE, 0);
+	gtk_widget_set_sensitive(level, FALSE);
+
+	ctx = g_new(BusCbCtx, 1);
+	ctx->volume = volume;
+	ctx->level = level;
+	ctx->threshold = threshold;
+	g_signal_connect(test, "toggled",
+	                 G_CALLBACK(toggle_voice_test_cb), ctx);
+}
+
+static GstElement *
+create_video_pipeline(void)
+{
+	PurpleMediaManager *manager;
+	PurpleMediaElementInfo *video_src, *video_sink;
+	GstElement *pipeline;
+	GstElement *src, *sink;
+
+	manager = purple_media_manager_get();
+	video_src = purple_media_manager_get_active_element(manager,
+			PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC);
+	video_sink = purple_media_manager_get_active_element(manager,
+			PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK);
+
+	pipeline = gst_pipeline_new("videotest");
+	src = create_test_element("video", "src", video_src);
+	sink = create_test_element("video", "sink", video_sink);
+
+	gst_bin_add_many(GST_BIN(pipeline), src, sink, NULL);
+	gst_element_link_many(src, sink, NULL);
+
+	gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);
+
+	return pipeline;
+}
+
+static void
+video_test_destroy_cb(GtkWidget *w, gpointer data)
+{
+	if (!video_pipeline)
+		return;
+
+	gst_element_set_state(video_pipeline, GST_STATE_NULL);
+	gst_object_unref(video_pipeline);
+	video_pipeline = NULL;
+}
+
+static void
+window_id_cb(GstBus *bus, GstMessage *msg, gulong window_id)
+{
+	if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT
+#if GST_CHECK_VERSION(1,0,0)
+	 || !gst_is_video_overlay_prepare_window_handle_message(msg))
+#else
+	 || !gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
+#endif
+		return;
+
+	g_signal_handlers_disconnect_matched(bus,
+	                                     G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
+	                                     0, 0, NULL, window_id_cb,
+	                                     (gpointer)window_id);
+
+#if GST_CHECK_VERSION(1,0,0)
+	gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg)),
+	                                    window_id);
+#elif GST_CHECK_VERSION(0,10,31)
+	gst_x_overlay_set_window_handle(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)),
+	                                window_id);
+#else
+	gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)),
+	                             window_id);
+#endif
+}
+
+static void
+toggle_video_test_cb(GtkToggleButton *test, gpointer data)
+{
+	GtkWidget *video = data;
+	GstBus *bus;
+
+	if (gtk_toggle_button_get_active(test)) {
+		GdkWindow *window = gtk_widget_get_window(video);
+		gulong window_id = 0;
+#ifdef GDK_WINDOWING_WIN32
+		if (GDK_IS_WIN32_WINDOW(window))
+			window_id = GDK_WINDOW_HWND(window);
+		else
+#endif
+#ifdef GDK_WINDOWING_X11
+		if (GDK_IS_X11_WINDOW(window))
+			window_id = gdk_x11_window_get_xid(window);
+		else
+#endif
+#ifdef GDK_WINDOWING_QUARTZ
+		if (GDK_IS_QUARTZ_WINDOW(window))
+			window_id = (gulong)gdk_quartz_window_get_nsview(window);
+		else
+#endif
+			g_warning("Unsupported GDK backend");
+#if !(defined(GDK_WINDOWING_WIN32) \
+   || defined(GDK_WINDOWING_X11) \
+   || defined(GDK_WINDOWING_QUARTZ))
+#		error "Unsupported GDK windowing system"
+#endif
+
+		video_pipeline = create_video_pipeline();
+		bus = gst_pipeline_get_bus(GST_PIPELINE(video_pipeline));
+#if GST_CHECK_VERSION(1,0,0)
+		gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL, NULL);
+#else
+		gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL);
+#endif
+		g_signal_connect(bus, "sync-message::element",
+		                 G_CALLBACK(window_id_cb), (gpointer)window_id);
+		gst_object_unref(bus);
+
+		g_signal_connect(test, "destroy",
+		                 G_CALLBACK(video_test_destroy_cb), NULL);
+		g_signal_connect(prefsnotebook, "switch-page",
+		                 G_CALLBACK(vv_test_switch_page_cb), test);
+	} else {
+		g_object_disconnect(test, "any-signal::destroy",
+		                    G_CALLBACK(video_test_destroy_cb), NULL,
+		                    NULL);
+		g_object_disconnect(prefsnotebook, "any-signal::switch-page",
+		                    G_CALLBACK(vv_test_switch_page_cb), test,
+		                    NULL);
+		video_test_destroy_cb(NULL, NULL);
+	}
+}
+
+static void
+make_video_test(GtkWidget *vbox)
+{
+	GtkWidget *test;
+	GtkWidget *video;
+	GdkColor color = {0, 0, 0, 0};
+
+	video = gtk_drawing_area_new();
+	gtk_box_pack_start(GTK_BOX(vbox), video, TRUE, TRUE, 0);
+	gtk_widget_modify_bg(video, GTK_STATE_NORMAL, &color);
+	gtk_widget_set_size_request(GTK_WIDGET(video), 240, 180);
+
+	test = gtk_toggle_button_new_with_label(_("Test Video"));
+	gtk_box_pack_start(GTK_BOX(vbox), test, FALSE, FALSE, 0);
+
+	g_signal_connect(test, "toggled",
+	                 G_CALLBACK(toggle_video_test_cb), video);
+}
+
+static GtkWidget *
+vv_page(void)
+{
+	GtkWidget *ret;
+	GtkWidget *vbox;
+	GtkSizeGroup *sg;
+
+	ret = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+	gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
+
+	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+
+	vbox = pidgin_make_frame(ret, _("Audio"));
+	make_vv_frame(vbox, sg, _("Input"), AUDIO_SRC_PLUGINS,
+	              PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin",
+	              PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device");
+	make_vv_frame(vbox, sg, _("Output"), AUDIO_SINK_PLUGINS,
+	              PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin",
+	              PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device");
+	make_voice_test(vbox);
+
+	vbox = pidgin_make_frame(ret, _("Video"));
+	make_vv_frame(vbox, sg, _("Input"), VIDEO_SRC_PLUGINS,
+	              PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin",
+	              PIDGIN_PREFS_ROOT "/vvconfig/video/src/device");
+	make_vv_frame(vbox, sg, _("Output"), VIDEO_SINK_PLUGINS,
+	              PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin",
+	              PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device");
+	make_video_test(vbox);
+
+	gtk_widget_show_all(ret);
+
+	return ret;
+}
+#endif
+
 static int
 prefs_notebook_add_page(const char *text, GtkWidget *page, int ind)
 {
@@ -3042,6 +3754,9 @@
 	prefs_notebook_add_page(_("Sounds"), sound_page(), notebook_page++);
 	prefs_notebook_add_page(_("Status / Idle"), away_page(), notebook_page++);
 	prefs_notebook_add_page(_("Themes"), theme_page(), notebook_page++);
+#ifdef USE_VV
+	prefs_notebook_add_page(_("Voice/Video"), vv_page(), notebook_page++);
+#endif
 }
 
 void
@@ -3173,6 +3888,26 @@
 	purple_prefs_connect_callback(&prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
 								smiley_theme_pref_cb, NULL);
 
+#ifdef USE_VV
+	/* Voice/Video */
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig");
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio");
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/src");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin", "");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device", "");
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin", "");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device", "");
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video");
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/src");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin", "");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device", "");
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video");
+	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/sink");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin", "");
+	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", "");
+#endif
+
 	pidgin_prefs_update_old();
 }
 
@@ -3261,4 +3996,42 @@
 			PIDGIN_PREFS_ROOT "/conversations/im/x");
 	purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/y",
 			PIDGIN_PREFS_ROOT "/conversations/im/y");
+
+	/* Fixup vvconfig plugin prefs */
+	if (purple_prefs_exists("/plugins/core/vvconfig/audio/src/plugin")) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin",
+				purple_prefs_get_string("/plugins/core/vvconfig/audio/src/plugin"));
+	}
+	if (purple_prefs_exists("/plugins/core/vvconfig/audio/src/device")) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device",
+				purple_prefs_get_string("/plugins/core/vvconfig/audio/src/device"));
+	}
+	if (purple_prefs_exists("/plugins/core/vvconfig/audio/sink/plugin")) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin",
+				purple_prefs_get_string("/plugins/core/vvconfig/audio/sink/plugin"));
+	}
+	if (purple_prefs_exists("/plugins/core/vvconfig/audio/sink/device")) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device",
+				purple_prefs_get_string("/plugins/core/vvconfig/audio/sink/device"));
+	}
+	if (purple_prefs_exists("/plugins/core/vvconfig/video/src/plugin")) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin",
+				purple_prefs_get_string("/plugins/core/vvconfig/video/src/plugin"));
+	}
+	if (purple_prefs_exists("/plugins/core/vvconfig/video/src/device")) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device",
+				purple_prefs_get_string("/plugins/core/vvconfig/video/src/device"));
+	}
+	if (purple_prefs_exists("/plugins/gtk/vvconfig/video/sink/plugin")) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin",
+				purple_prefs_get_string("/plugins/gtk/vvconfig/video/sink/plugin"));
+	}
+	if (purple_prefs_exists("/plugins/gtk/vvconfig/video/sink/device")) {
+		purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device",
+				purple_prefs_get_string("/plugins/gtk/vvconfig/video/sink/device"));
+	}
+
+	purple_prefs_remove("/plugins/core/vvconfig");
+	purple_prefs_remove("/plugins/gtk/vvconfig");
 }
+
--- a/pidgin/gtksmiley.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtksmiley.c	Fri Jan 25 02:22:38 2013 -0500
@@ -27,6 +27,7 @@
 
 #include "internal.h"
 #include "pidgin.h"
+#include "obsolete.h"
 
 #include "debug.h"
 #include "notify.h"
@@ -417,21 +418,40 @@
 	g_signal_connect(window, "response", G_CALLBACK(do_add_select_cb), s);
 
 	/* The vbox */
+#if GTK_CHECK_VERSION(3,0,0)
+	vbox = gtk_grid_new();
+	gtk_grid_set_row_spacing(GTK_GRID(vbox), PIDGIN_HIG_BORDER);
+#else
 	vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
+#endif
 	gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(window))),
                     vbox);
 	gtk_widget_show(vbox);
 
 	/* The hbox */
+#if GTK_CHECK_VERSION(3,0,0)
+	hbox = gtk_grid_new();
+	gtk_grid_set_column_spacing(GTK_GRID(hbox), PIDGIN_HIG_BORDER);
+	gtk_grid_attach(GTK_GRID(vbox), hbox, 0, 0, 1, 1);
+#else
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)), hbox);
+#endif
 
 	label = gtk_label_new_with_mnemonic(_("_Image:"));
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_grid_attach(GTK_GRID(hbox), label, 0, 0, 1, 1);
+#else
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+#endif
 	gtk_widget_show(label);
 
 	filech = gtk_button_new();
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_grid_attach_next_to(GTK_GRID(hbox), filech, NULL, GTK_POS_RIGHT, 1, 1);
+#else
 	gtk_box_pack_end(GTK_BOX(hbox), filech, FALSE, FALSE, 0);
+#endif
 	pidgin_set_accessible_label(filech, label);
 
 	s->smiley_image = gtk_image_new();
@@ -453,12 +473,23 @@
 	gtk_widget_show_all(hbox);
 
 	/* info */
+#if GTK_CHECK_VERSION(3,0,0)
+	hbox = gtk_grid_new();
+	gtk_grid_set_column_spacing(GTK_GRID(hbox), PIDGIN_HIG_BORDER);
+
+	gtk_grid_attach_next_to(GTK_GRID(vbox), hbox, NULL, GTK_POS_BOTTOM, 1, 1);
+#else
 	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
 	gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)),hbox);
+#endif
 
 	/* Shortcut text */
 	label = gtk_label_new_with_mnemonic(_("S_hortcut text:"));
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_grid_attach(GTK_GRID(hbox), label, 0, 0, 1, 1);
+#else
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+#endif
 	gtk_widget_show(label);
 
 	s->smile = gtk_entry_new();
@@ -476,7 +507,11 @@
 	g_signal_connect(G_OBJECT(s->smile), "insert-text", G_CALLBACK(smiley_name_insert_cb), s);
 	g_signal_connect(G_OBJECT(s->smile), "delete-text", G_CALLBACK(smiley_name_delete_cb), s);
 
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_grid_attach_next_to(GTK_GRID(hbox), s->smile, NULL, GTK_POS_RIGHT, 1, 1);
+#else
 	gtk_box_pack_end(GTK_BOX(hbox), s->smile, FALSE, FALSE, 0);
+#endif
 	gtk_widget_show(s->smile);
 
 	gtk_widget_show(hbox);
--- a/pidgin/gtksound.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtksound.c	Fri Jan 25 02:22:38 2013 -0500
@@ -319,9 +319,6 @@
 
 #ifdef USE_GSTREAMER
 	purple_debug_info("sound", "Initializing sound output drivers.\n");
-#ifdef GST_CAN_DISABLE_FORKING
-	gst_registry_fork_set_enabled (FALSE);
-#endif
 	if ((gst_init_failed = !gst_init_check(NULL, NULL, &error))) {
 		purple_notify_error(NULL, _("GStreamer Failure"),
 					_("GStreamer failed to initialize."),
@@ -530,7 +527,11 @@
 		return;
 	}
 
+#if GST_CHECK_VERSION(1,0,0)
 	play = gst_element_factory_make("playbin", "play");
+#else
+	play = gst_element_factory_make("playbin2", "play");
+#endif
 
 	if (play == NULL) {
 		return;
--- a/pidgin/gtkstatusbox.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkstatusbox.c	Fri Jan 25 02:22:38 2013 -0500
@@ -43,6 +43,7 @@
 #include <gdk/gdkkeysyms.h>
 
 #include "internal.h"
+#include "obsolete.h"
 
 #include "account.h"
 #include "buddyicon.h"
@@ -60,13 +61,6 @@
 #include "gtkstatusbox.h"
 #include "gtkutils.h"
 
-#ifdef USE_GTKSPELL
-#  include <gtkspell/gtkspell.h>
-#  ifdef _WIN32
-#    include "wspell.h"
-#  endif
-#endif
-
 #include "gtk3compat.h"
 
 /* Timeout for typing notifications in seconds */
--- a/pidgin/gtkutils.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkutils.c	Fri Jan 25 02:22:38 2013 -0500
@@ -28,9 +28,7 @@
 #include "internal.h"
 #include "pidgin.h"
 
-#ifndef _WIN32
-# include <X11/Xlib.h>
-#else
+#ifdef _WIN32
 # ifdef small
 #  undef small
 # endif
@@ -120,6 +118,7 @@
 		             NULL);
 
 		webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webview), settings);
+		g_object_unref(settings);
 	}
 #endif
 }
@@ -1411,7 +1410,7 @@
 		}
 		shortname = strrchr(data->filename, G_DIR_SEPARATOR);
 		shortname = shortname ? shortname + 1 : data->filename;
-		id = purple_imgstore_add_with_id(filedata, size, shortname);
+		id = purple_imgstore_new_with_id(filedata, size, shortname);
 
 		gtk_webview_insert_image(GTK_WEBVIEW(gtkconv->entry), id);
 		purple_imgstore_unref_by_id(id);
@@ -2162,7 +2161,7 @@
 static void
 icon_preview_change_cb(GtkFileChooser *widget, struct _icon_chooser *dialog)
 {
-	GdkPixbuf *pixbuf, *scale;
+	GdkPixbuf *pixbuf;
 	int height, width;
 	char *basename, *markup, *size;
 	struct stat st;
@@ -2171,7 +2170,7 @@
 	filename = gtk_file_chooser_get_preview_filename(
 					GTK_FILE_CHOOSER(dialog->icon_filesel));
 
-	if (!filename || g_stat(filename, &st) || !(pixbuf = pidgin_pixbuf_new_from_file(filename)))
+	if (!filename || g_stat(filename, &st) || !(pixbuf = pidgin_pixbuf_new_from_file_at_size(filename, 128, 128)))
 	{
 		gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL);
 		gtk_label_set_markup(GTK_LABEL(dialog->icon_text), "");
@@ -2179,8 +2178,7 @@
 		return;
 	}
 
-	width = gdk_pixbuf_get_width(pixbuf);
-	height = gdk_pixbuf_get_height(pixbuf);
+	gdk_pixbuf_get_file_info(filename, &width, &height);
 	basename = g_path_get_basename(filename);
 	size = purple_str_size_to_units(st.st_size);
 	markup = g_strdup_printf(_("<b>File:</b> %s\n"
@@ -2188,13 +2186,10 @@
 							   "<b>Image size:</b> %dx%d"),
 							 basename, size, width, height);
 
-	scale = gdk_pixbuf_scale_simple(pixbuf, width * 50 / height,
-									50, GDK_INTERP_BILINEAR);
-	gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), scale);
+	gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), pixbuf);
 	gtk_label_set_markup(GTK_LABEL(dialog->icon_text), markup);
 
 	g_object_unref(G_OBJECT(pixbuf));
-	g_object_unref(G_OBJECT(scale));
 	g_free(filename);
 	g_free(basename);
 	g_free(size);
--- a/pidgin/gtkwebview.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkwebview.c	Fri Jan 25 02:22:38 2013 -0500
@@ -38,6 +38,7 @@
 #define MAX_FONT_SIZE 7
 #define MAX_SCROLL_TIME 0.4 /* seconds */
 #define SCROLL_DELAY 33 /* milliseconds */
+#define GTK_WEBVIEW_MAX_PROCESS_TIME 100000 /* microseconds */
 
 #define GTK_WEBVIEW_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE((obj), GTK_TYPE_WEBVIEW, GtkWebViewPriv))
@@ -705,8 +706,8 @@
 	}
 }
 
-static gboolean
-process_load_queue(GtkWebView *webview)
+static void
+process_load_queue_element(GtkWebView *webview)
 {
 	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
 	int type;
@@ -717,15 +718,6 @@
 	WebKitDOMRange *range;
 	gboolean require_scroll = FALSE;
 
-	if (priv->is_loading) {
-		priv->loader = 0;
-		return FALSE;
-	}
-	if (!priv->load_queue || g_queue_is_empty(priv->load_queue)) {
-		priv->loader = 0;
-		return FALSE;
-	}
-
 	type = GPOINTER_TO_INT(g_queue_pop_head(priv->load_queue));
 	str = g_queue_pop_head(priv->load_queue);
 
@@ -783,7 +775,35 @@
 	}
 
 	g_free(str);
+}
 
+static gboolean
+process_load_queue(GtkWebView *webview)
+{
+	GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview);
+	gint64 start_time;
+
+	if (priv->is_loading) {
+		priv->loader = 0;
+		return FALSE;
+	}
+	if (!priv->load_queue || g_queue_is_empty(priv->load_queue)) {
+		priv->loader = 0;
+		return FALSE;
+	}
+
+	start_time = g_get_monotonic_time();
+	while (!g_queue_is_empty(priv->load_queue)) {
+		process_load_queue_element(webview);
+		if (g_get_monotonic_time() - start_time >
+			GTK_WEBVIEW_MAX_PROCESS_TIME)
+			break;
+	}
+
+	if (g_queue_is_empty(priv->load_queue)) {
+		priv->loader = 0;
+		return FALSE;
+	}
 	return TRUE;
 }
 
@@ -1143,6 +1163,7 @@
 		              node, uri);
 
 		g_free(uri);
+		g_object_unref(hit);
 
 		return TRUE;
 	}
--- a/pidgin/gtkwebviewtoolbar.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/gtkwebviewtoolbar.c	Fri Jan 25 02:22:38 2013 -0500
@@ -579,7 +579,7 @@
 
 	name = strrchr(filename, G_DIR_SEPARATOR) + 1;
 
-	id = purple_imgstore_add_with_id(filedata, size, name);
+	id = purple_imgstore_new_with_id(filedata, size, name);
 
 	if (id == 0) {
 		buf = g_strdup_printf(_("Failed to store image: %s\n"), filename);
@@ -594,7 +594,9 @@
 	g_free(filename);
 
 	gtk_webview_insert_image(GTK_WEBVIEW(toolbar->webview), id);
-	purple_imgstore_unref_by_id(id);
+	/* TODO: do it after passing an image to prpl, not before
+	 * purple_imgstore_unref_by_id(id);
+	 */
 }
 
 static void
--- a/pidgin/minidialog.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/minidialog.c	Fri Jan 25 02:22:38 2013 -0500
@@ -455,6 +455,7 @@
 	g_object_class_install_property (object_class, PROP_ENABLE_DESCRIPTION_MARKUP, param_spec);
 }
 
+#if !GTK_CHECK_VERSION(3,0,0)
 /* 16 is the width of the icon, due to PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL */
 #define BLIST_WIDTH_OTHER_THAN_LABEL \
 	((PIDGIN_HIG_BOX_SPACE * 3) + 16)
@@ -476,13 +477,16 @@
 	gtk_widget_set_size_request(GTK_WIDGET(priv->title), label_width, -1);
 	gtk_widget_set_size_request(GTK_WIDGET(priv->desc), label_width, -1);
 }
+#endif
 
 static void
 pidgin_mini_dialog_init(PidginMiniDialog *self)
 {
 	GtkBox *self_box = GTK_BOX(self);
+#if !GTK_CHECK_VERSION(3,0,0)
 	guint blist_width = purple_prefs_get_int(BLIST_WIDTH_PREF);
 	guint label_width = blist_width - BLIST_WIDTH_OTHER_THAN_LABEL;
+#endif
 
 	PidginMiniDialogPrivate *priv = g_new0(PidginMiniDialogPrivate, 1);
 	self->priv = priv;
@@ -495,7 +499,9 @@
 	gtk_misc_set_alignment(GTK_MISC(priv->icon), 0, 0);
 
 	priv->title = GTK_LABEL(gtk_label_new(NULL));
+#if !GTK_CHECK_VERSION(3,0,0)
 	gtk_widget_set_size_request(GTK_WIDGET(priv->title), label_width, -1);
+#endif
 	gtk_label_set_line_wrap(priv->title, TRUE);
 	gtk_label_set_selectable(priv->title, TRUE);
 	gtk_misc_set_alignment(GTK_MISC(priv->title), 0, 0);
@@ -504,7 +510,9 @@
 	gtk_box_pack_start(priv->title_box, GTK_WIDGET(priv->title), TRUE, TRUE, 0);
 
 	priv->desc = GTK_LABEL(gtk_label_new(NULL));
+#if !GTK_CHECK_VERSION(3,0,0)
 	gtk_widget_set_size_request(GTK_WIDGET(priv->desc), label_width, -1);
+#endif
 	gtk_label_set_line_wrap(priv->desc, TRUE);
 	gtk_misc_set_alignment(GTK_MISC(priv->desc), 0, 0);
 	gtk_label_set_selectable(priv->desc, TRUE);
@@ -513,8 +521,10 @@
 	 */
 	g_object_set(G_OBJECT(priv->desc), "no-show-all", TRUE, NULL);
 
+#if !GTK_CHECK_VERSION(3,0,0)
 	purple_prefs_connect_callback(self, BLIST_WIDTH_PREF,
 		blist_width_changed_cb, self);
+#endif
 
 	self->contents = GTK_BOX(gtk_vbox_new(FALSE, 0));
 
--- a/pidgin/pidgin-3.pc.in	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/pidgin-3.pc.in	Fri Jan 25 02:22:38 2013 -0500
@@ -11,5 +11,5 @@
 Name: Pidgin
 Description: Pidgin is a GTK2-based instant messenger application.
 Version: @VERSION@
-Requires: gtk+-2.0 purple-3
+Requires: @GTK_PC_MODULE@ purple-3
 Cflags: -I${includedir}
--- a/pidgin/pidgin.h	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/pidgin.h	Fri Jan 25 02:22:38 2013 -0500
@@ -29,10 +29,6 @@
 
 #include <gtk/gtk.h>
 
-#ifdef GDK_WINDOWING_X11
-# include <gdk/gdkx.h>
-#endif
-
 #ifdef _WIN32
 # include "gtkwin32dep.h"
 #endif
--- a/pidgin/pidginstock.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/pidginstock.c	Fri Jan 25 02:22:38 2013 -0500
@@ -67,7 +67,7 @@
 	{ PIDGIN_STOCK_IGNORE,          NULL,      GTK_STOCK_DIALOG_ERROR     },
 	{ PIDGIN_STOCK_INVITE,          NULL,      GTK_STOCK_JUMP_TO          },
 	{ PIDGIN_STOCK_MODIFY,          NULL,      GTK_STOCK_PREFERENCES      },
-	{ PIDGIN_STOCK_ADD,             NULL,	   GTK_STOCK_ADD			  },
+	{ PIDGIN_STOCK_ADD,             NULL,	   GTK_STOCK_ADD              },
 	{ PIDGIN_STOCK_PAUSE,           NULL,      GTK_STOCK_MEDIA_PAUSE      },
 	{ PIDGIN_STOCK_POUNCE,          NULL,      GTK_STOCK_REDO             },
 	{ PIDGIN_STOCK_OPEN_MAIL,       NULL,      GTK_STOCK_JUMP_TO          },
@@ -80,17 +80,17 @@
 
 static const GtkStockItem stock_items[] =
 {
-	{ PIDGIN_STOCK_ALIAS,               N_("_Alias"),      0, 0, NULL },
-	{ PIDGIN_STOCK_CHAT,                N_("_Join"),       0, 0, NULL },
-	{ PIDGIN_STOCK_CLOSE_TABS,          N_("Close _tabs"), 0, 0, NULL },
-	{ PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, N_("I_M"),         0, 0, NULL },
-	{ PIDGIN_STOCK_TOOLBAR_USER_INFO,   N_("_Get Info"),   0, 0, NULL },
-	{ PIDGIN_STOCK_INVITE,              N_("_Invite"),     0, 0, NULL },
-	{ PIDGIN_STOCK_MODIFY,              N_("_Modify..."),  0, 0, NULL },
-	{ PIDGIN_STOCK_ADD,                 N_("_Add..."),     0, 0, NULL },
-	{ PIDGIN_STOCK_OPEN_MAIL,           N_("_Open Mail"),  0, 0, NULL },
-	{ PIDGIN_STOCK_PAUSE,               N_("_Pause"),      0, 0, NULL },
-	{ PIDGIN_STOCK_EDIT,                N_("_Edit"),       0, 0, NULL }
+	{ PIDGIN_STOCK_ALIAS,               N_("_Alias"),      0, 0, PACKAGE },
+	{ PIDGIN_STOCK_CHAT,                N_("_Join"),       0, 0, PACKAGE },
+	{ PIDGIN_STOCK_CLOSE_TABS,          N_("Close _tabs"), 0, 0, PACKAGE },
+	{ PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, N_("I_M"),         0, 0, PACKAGE },
+	{ PIDGIN_STOCK_TOOLBAR_USER_INFO,   N_("_Get Info"),   0, 0, PACKAGE },
+	{ PIDGIN_STOCK_INVITE,              N_("_Invite"),     0, 0, PACKAGE },
+	{ PIDGIN_STOCK_MODIFY,              N_("_Modify..."),  0, 0, PACKAGE },
+	{ PIDGIN_STOCK_ADD,                 N_("_Add..."),     0, 0, PACKAGE },
+	{ PIDGIN_STOCK_OPEN_MAIL,           N_("_Open Mail"),  0, 0, PACKAGE },
+	{ PIDGIN_STOCK_PAUSE,               N_("_Pause"),      0, 0, PACKAGE },
+	{ PIDGIN_STOCK_EDIT,                N_("_Edit"),       0, 0, PACKAGE }
 };
 
 typedef struct {
--- a/pidgin/pixmaps/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/pixmaps/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -297,6 +297,7 @@
 		protocols/48/bonjour.png \
 		protocols/48/facebook.png \
 		protocols/48/gadu-gadu.png \
+		protocols/48/google-talk.png \
 		protocols/48/novell.png \
 		protocols/48/icq.png \
 		protocols/48/irc.png \
--- a/pidgin/pixmaps/emotes/default/24/default.theme.in	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/pixmaps/emotes/default/24/default.theme.in	Fri Jan 25 02:22:38 2013 -0500
@@ -189,106 +189,6 @@
 fingers-crossed.png	(yn)    (YN)
 
 
-# Following QQ 2006
-[QQ]
-shocked.png         /:O      /jy       /surprised
-curl-lip.png        /:~      /pz       /curl_lip
-desire.png          /:*      /se       /desire
-dazed.png           /:|      /dazed
-party.png           /8-)     /dy       /revel
-crying.png          /:<      /ll       /cry
-bashful.png         /:$      /hx       /bashful
-shut-mouth.png      /:X      /bz       /shut_mouth
-sleeping.png        /:Z      /shui     /sleep
-weep.png            /:'(     /dk       /weep
-embarrassed.png     /:-|     /gg       /embarassed
-pissed-off.png      /:@      /fn       /pissed_off
-act-up.png          /:P      /tp       /act_up
-excited.png         /:D      /cy       /toothy_smile
-happy.png           /:)      /wx       /small_smile
-sad.png             /:(      /ng       /sad
-glasses-cool.png    /:+      /kuk      /cool
-doctor.png          /:#      /feid     /SARS
-silly.png           /:Q      /zk       /crazy
-sick.png            /:T      /tu       /vomit
-snicker.png         /;p      /tx       /titter
-cute.png            /;-D     /ka       /cute
-disdain.png         /;d      /by       /disdain
-arrogant.png        /;o      /am       /arrogant
-starving.png        /:g      /jie      /starving
-sleepy.png          /|-)     /kun      /sleepy
-terror.png          /:!      /jk       /terror
-hot.png             /:L      /sweat
-smirk.png           /:>      /hanx     /smirk
-soldier.png         /:;      /db       /soldier
-struggle.png        /;f      /fendou   /struggle
-curse.png           /:-S     /zhm      /curse
-question.png        /?       /yiw      /question
-quiet.png           /;x      /xu       /shh
-hypnotized.png      /;@      /yun      /dizzy
-excruciating.png    /:8      /zhem     /excrutiating
-freaked-out.png     /;!      /shuai    /freaked_out
-skeleton.png        /!!!     /kl       /skeleton
-hammer.png          /xx      /qiao     /hammer
-bye.png             /bye     /zj       /bye
-go-away.png         /go      /shan     /go
-afraid.png          /shake   /fad      /shake
-amorous.png         /love    /aiq      /love
-jump.png            /jump    /tiao     /jump
-search.png          /find    /zhao     /search
-lashes.png          /&       /mm       /beautiful_eyebrows
-pig.png             /pig     /zt       /pig
-cat.png             /cat     /mm       /cat
-dog.png             /dog     /xg       /dog
-hug-left.png        /hug     /yb       /hug
-coins.png           /$       /qianc    /money
-lamp.png            /!       /dp       /lightbulb
-bowl.png            /cup     /bei      /cup
-cake.png            /cake    /dg       /cake
-thunder.png         /li      /shd      /lightning
-bomb.png            /bome    /zhd      /bomb
-knife.png           /kn      /dao      /knife
-soccerball.png      /footb   /zq       /soccer
-musical-note.png    /music   /yy       /music
-poop.png            /shit    /bb       /shit
-coffee.png          /coffee  /kf       /coffee
-hungry.png          /eat     /fan      /eat
-pill.png            /pill    /yw       /pill
-rose.png            /rose    /mg       /rose
-wilt.png            /fade    /dx       /wilt
-kiss.png            /kiss    /wen      /kiss
-in_love.png         /heart   /xin      /heart
-love-over.png       /break   /xs       /broken_heart
-meeting.png         /meeting /hy       /meeting
-present.png         /gift    /lw       /gift
-phone.png           /phone   /dh       /phone
-clock.png           /time    /sj       /time
-mail.png            /email   /yj       /email
-tv.png              /TV      /ds       /TV
-sun.png             /sun     /ty       /sun
-moon.png            /moon    /yl       /moon
-good.png            /strong  /qiang    /thumbs_up
-bad.png             /weak    /ruo      /thumbs_down
-handshake.png       /share   /ws       /handshake
-victory.png         /v       /shl      /victory
-beauty.png          /<J>     /mn       /beauty
-qq.png              /<QQ>    /qz       /qq
-blowkiss.png        /<L>     /fw       /blow_kiss
-angry.png           /<O>     /oh       /angry
-liquor.png          /<B>     /bj       /baijiu
-can.png             /<U>     /qsh      /soda
-watermelon.png      /<W>     /xigua    /watermelon
-rain.png            /<!!>    /xy       /rain
-cloudy.png          /<~>     /duoy     /cloudy
-snowman.png         /<Z>     /xr       /snowman
-star.png            /<*>     /xixing   /star
-girl.png            /<00>    /nv       /woman
-boy.png             /<11>    /nan      /man
-! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)   :(|)    8-|)
-! cyclops.png       O-)     o-)
-
-
 # Following ICQ 6.0
 [ICQ]
 happy.png           :-)     :)
--- a/pidgin/pixmaps/emotes/small/16/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/pixmaps/emotes/small/16/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -43,6 +43,7 @@
     excruciating.png \
     eyeroll.png \
     girl.png \
+    glasses-cool.png \
     happy.png \
     hug-left.png \
     hug-right.png \
@@ -65,6 +66,7 @@
     question.png \
     rose.png \
     sad.png \
+    sarcastic.png \
     shame.png \
     shocked.png \
     shut-mouth.png  \
--- a/pidgin/pixmaps/emotes/small/16/small.theme.in	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/pixmaps/emotes/small/16/small.theme.in	Fri Jan 25 02:22:38 2013 -0500
@@ -118,44 +118,6 @@
 console.png			(xx)    (XX)
 
 
-# Following QQ 2006
-[QQ]
-shocked.png         /:O      /jy       /surprised
-party.png           /8-)     /dy       /revel
-crying.png          /:<      /ll       /cry
-shut-mouth.png      /:X      /bz       /shut_mouth
-sleeping.png        /:Z      /shui     /sleep
-embarrassed.png     /:-|     /gg       /embarassed
-pissed-off.png      /:@      /fn       /pissed_off
-excited.png         /:D      /cy       /toothy_smile
-happy.png           /:)      /wx       /small_smile
-sad.png             /:(      /ng       /sad
-glasses-cool.png    /:+      /kuk      /cool
-sick.png            /:T      /tu       /vomit
-sleepy.png          /|-)     /kun      /sleepy
-hot.png             /:L      /sweat
-question.png        /?       /yiw      /question
-excruciating.png    /:8      /zhem     /excrutiating
-afraid.png          /shake   /fad      /shake
-amorous.png         /love    /aiq      /love
-search.png          /find    /zhao     /search
-hug-left.png        /hug     /yb       /hug
-lamp.png            /!       /dp       /lightbulb
-thunder.png         /li      /shd      /lightning
-musical-note.png    /music   /yy       /music
-coffee.png          /coffee  /kf       /coffee
-hungry.png          /eat     /fan      /eat
-rose.png            /rose    /mg       /rose
-kiss.png            /kiss    /wen      /kiss
-in_love.png         /heart   /xin      /heart
-meeting.png         /meeting /hy       /meeting
-phone.png           /phone   /dh       /phone
-tv.png              /TV      /ds       /TV
-angry.png           /<O>     /oh       /angry
-girl.png            /<00>    /nv       /woman
-boy.png             /<11>    /nan      /man
-
-
 # Following ICQ 6.0
 [ICQ]
 happy.png           :-)     :)
Binary file pidgin/pixmaps/protocols/48/google-talk.png has changed
--- a/pidgin/plugins/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/plugins/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -45,7 +45,6 @@
 sendbutton_la_LDFLAGS       = -module -avoid-version
 spellchk_la_LDFLAGS         = -module -avoid-version
 themeedit_la_LDFLAGS        = -module -avoid-version
-vvconfig_la_LDFLAGS         = -module -avoid-version
 webkit_la_LDFLAGS           = -module -avoid-version
 xmppconsole_la_LDFLAGS      = -module -avoid-version
 
@@ -67,10 +66,6 @@
 	webkit.la           \
 	xmppconsole.la
 
-if USE_VV
-plugin_LTLIBRARIES +=	vvconfig.la
-endif
-
 noinst_LTLIBRARIES = \
 	contact_priority.la \
 	gtk_signals_test.la
--- a/pidgin/plugins/cap/cap.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/plugins/cap/cap.c	Fri Jan 25 02:22:38 2013 -0500
@@ -43,7 +43,7 @@
 	int threshold = purple_prefs_get_int("/plugins/gtk/cap/threshold");
 	int min_minute = (current_minute - threshold) % 1440;
 	int max_minute = (current_minute + threshold) % 1440;
-	char *sql;
+	char *sql, sta_id = NULL;
 	sqlite3_stmt *stmt = NULL;
 	const char *tail = NULL;
 	int rc;
@@ -94,7 +94,9 @@
 	sqlite3_free(sql);
 
 
-	if(strcmp(purple_status_get_id(get_status_for(buddy)), "offline") == 0) {
+	sta_id = purple_status_get_id(get_status_for(buddy));
+
+	if(sta_id && !strcmp(sta_id, "offline")) {
 		/* This is kind of stupid, change it. */
 		if(prediction == 1.0f)
 			prediction = 0.0f;
--- a/pidgin/plugins/disco/xmppdisco.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/plugins/disco/xmppdisco.c	Fri Jan 25 02:22:38 2013 -0500
@@ -265,7 +265,7 @@
 	g_return_val_if_fail(str != NULL, "");
 
 	for ( ; disco_type_mappings[i].from; ++i) {
-		if (!strcasecmp(str, disco_type_mappings[i].from))
+		if (!g_ascii_strcasecmp(str, disco_type_mappings[i].from))
 			return disco_type_mappings[i].to;
 	}
 
--- a/pidgin/plugins/notify.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/plugins/notify.c	Fri Jan 25 02:22:38 2013 -0500
@@ -96,16 +96,10 @@
 #include "gtkplugin.h"
 #include "gtkutils.h"
 
-#ifndef _WIN32
-#include <X11/Xatom.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#endif
-
 #define NOTIFY_PLUGIN_ID "gtk-x11-notify"
 
 static PurplePlugin *my_plugin = NULL;
-#ifndef _WIN32
+#ifdef HAVE_X11
 static GdkAtom _Cardinal = GDK_NONE;
 static GdkAtom _PurpleUnseenCount = GDK_NONE;
 #endif
@@ -525,7 +519,7 @@
 static void
 handle_count_xprop(PidginWindow *purplewin)
 {
-#ifndef _WIN32
+#ifdef HAVE_X11
 	guint count;
 	GtkWidget *window;
 	GdkWindow *gdkwin;
@@ -757,7 +751,7 @@
 	g_signal_connect(G_OBJECT(toggle), "toggled",
 	                 G_CALLBACK(method_toggle_cb), "method_count");
 
-#ifndef _WIN32
+#ifdef HAVE_X11
 	/* Count xprop method button */
 	toggle = gtk_check_button_new_with_mnemonic(_("Insert count of new message into _X property"));
 	gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
--- a/pidgin/plugins/perl/common/Makefile.mingw	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/plugins/perl/common/Makefile.mingw	Fri Jan 25 02:22:38 2013 -0500
@@ -102,7 +102,7 @@
 $(C_FILES): $(PIDGIN_CONFIG_H)
 
 $(TARGET).dll: $(PIDGIN_DLL).a $(PURPLE_PERL_DLL).a $(OBJECTS)
-	$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) -o $(TARGET).dll
+	$(CC) -shared $(OBJECTS) $(LIB_PATHS) $(DLL_LD_FLAGS) $(LIBS) -o $(TARGET).dll
 
 ##
 ## CLEAN
--- a/pidgin/plugins/relnot.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/plugins/relnot.c	Fri Jan 25 02:22:38 2013 -0500
@@ -36,6 +36,7 @@
 #include "debug.h"
 #include "gtkblist.h"
 #include "gtkutils.h"
+#include "http.h"
 #include "notify.h"
 #include "pidginstock.h"
 #include "prefs.h"
@@ -60,42 +61,20 @@
 	purple_notify_uri(NULL, PURPLE_WEBSITE);
 }
 
-static void
-version_fetch_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,
-		const gchar *response, size_t len, const gchar *error_message)
+static void version_fetch_cb(PurpleHttpConnection *hc,
+	PurpleHttpResponse *response, gpointer user_data)
 {
 	gchar *cur_ver;
-	const char *tmp, *changelog;
-	char response_code[4];
+	const char *changelog;
 	GtkWidget *release_dialog;
 
 	GString *message;
 	int i = 0;
 
-	if(error_message || !response || !len)
+	if(!purple_http_response_is_successfull(response))
 		return;
 
-	memset(response_code, '\0', sizeof(response_code));
-	/* Parse the status code - the response should be in the form of "HTTP/?.? 200 ..." */
-	if ((tmp = strstr(response, " ")) != NULL) {
-		tmp++;
-		/* Read the 3 digit status code */
-		if (len - (tmp - response) > 3) {
-			memcpy(response_code, tmp, 3);
-		}
-	}
-
-	if (strcmp(response_code, "200") != 0) {
-		purple_debug_error("relnot", "Didn't recieve a HTTP status code of 200.\n");
-		return;
-	}
-
-	/* Go to the start of the data */
-	if((changelog = strstr(response, "\r\n\r\n")) == NULL) {
-		purple_debug_error("relnot", "Unable to find start of HTTP response data.\n");
-		return;
-	}
-	changelog += 4;
+	changelog = purple_http_response_get_data(response, NULL);
 
 	while(changelog[i] && changelog[i] != '\n') i++;
 
@@ -131,10 +110,10 @@
 {
 	int last_check = purple_prefs_get_int("/plugins/gtk/relnot/last_check");
 	if(!last_check || time(NULL) - last_check > MIN_CHECK_INTERVAL) {
-		gchar *url, *request;
+		gchar *url;
 		const char *host = "pidgin.im";
 
-		url = g_strdup_printf("http://%s/version.php?version=%s&build=%s",
+		url = g_strdup_printf("https://%s/version.php?version=%s&build=%s",
 				host,
 				purple_core_get_version(),
 #ifdef _WIN32
@@ -144,18 +123,8 @@
 #endif
 		);
 
-		request = g_strdup_printf(
-				"GET %s HTTP/1.0\r\n"
-				"Connection: close\r\n"
-				"Accept: */*\r\n"
-				"Host: %s\r\n\r\n",
-				url,
-				host);
+		purple_http_get(NULL, url, version_fetch_cb, NULL);
 
-		purple_util_fetch_url_request(NULL, url, TRUE, NULL, FALSE,
-			request, TRUE, -1, version_fetch_cb, NULL);
-
-		g_free(request);
 		g_free(url);
 
 		purple_prefs_set_int("/plugins/gtk/relnot/last_check", time(NULL));
--- a/pidgin/smileyparser.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/smileyparser.c	Fri Jan 25 02:22:38 2013 -0500
@@ -151,7 +151,7 @@
 	}
 
 	/* now for each theme smiley, observe that this does look nasty */
-	if (!current_smiley_theme || !(current_smiley_theme->list)) {
+	if (!current_smiley_theme) {
 		purple_debug_warning("smiley", "theme does not exist\n");
 		return temp;
 	}
--- a/pidgin/themes/Contents/Resources/Content.html	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/themes/Contents/Resources/Content.html	Fri Jan 25 02:22:38 2013 -0500
@@ -1,6 +1,6 @@
 <div class="x-container %messageClasses% %messageDirection%">
 	<abbr class="x-time" title="%time{yyyy-MM-ddTHH:mm:ssZZ}%">(%time%)</abbr>
-	<div class="x-sender">%sender%:</div>
-	<div class="x-message">%message%</div>
+	<span class="x-sender">%sender%:</span>
+	<span class="x-message">%message%</span>
 </div>
 <div id="insert"></div>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/themes/Contents/Resources/Incoming/Content.html	Fri Jan 25 02:22:38 2013 -0500
@@ -0,0 +1,8 @@
+<div class="x-container %messageClasses% %messageDirection%">
+	<span class="x-header" style="color: %senderColor%;">
+		<abbr class="x-time" title="%time{yyyy-MM-ddTHH:mm:ssZZ}%">(%time%)</abbr>
+		<span class="x-sender">%sender%:</span>
+	</span>
+	<span class="x-message">%message%</span>
+</div>
+<div id="insert"></div>
--- a/pidgin/themes/Contents/Resources/main.css	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/themes/Contents/Resources/main.css	Fri Jan 25 02:22:38 2013 -0500
@@ -10,8 +10,11 @@
 .x-container,
 .x-status_container
 {
+	clear: left;
 	line-height: 1.3em;
 	margin-bottom: 4px;
+	height: 100%;
+	overflow: hidden;
 }
 
 .x-container .x-time,
@@ -23,22 +26,19 @@
 
 .x-container .x-sender
 {
-	display: inline;
 	font-weight: bold;
 }
 
-.x-container .x-message,
-.x-status_container .x-message
+.x-message p
 {
-	display: inline;
+	margin: 0;
 }
 
 /* Colour-ify things */
 
-.x-container.incoming .x-time,
-.x-container.incoming .x-sender
+#Chat:not(.groupchat) .x-container.incoming .x-header
 {
-	color: #cc0000;
+	color: #cc0000 ! important;
 }
 
 .x-container.outgoing .x-time,
--- a/pidgin/themes/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/themes/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -7,6 +7,8 @@
 themeresources_DATA = Contents/Resources/Content.html \
                       Contents/Resources/Status.html \
                       Contents/Resources/main.css
+themeresourcesincomingdir = $(themeresourcesdir)/Incoming
+themeresourcesincoming_DATA = Contents/Resources/Incoming/Content.html
 themevariantsdir = $(themeresourcesdir)/Variants
 themevariants_DATA = Contents/Resources/Variants/Default.css \
                      Contents/Resources/Variants/No-Timestamps.css
@@ -15,5 +17,6 @@
 	$(themetemplate_DATA) \
 	$(themecontents_DATA) \
 	$(themeresources_DATA) \
+	$(themeresourcesincoming_DATA) \
 	$(themevariants_DATA)
 
--- a/pidgin/win32/gtkdocklet-win32.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/win32/gtkdocklet-win32.c	Fri Jan 25 02:22:38 2013 -0500
@@ -46,7 +46,7 @@
  */
 static HWND systray_hwnd = NULL;
 /* additional two cached_icons entries for pending and connecting icons */
-static HICON cached_icons[PURPLE_STATUS_NUM_PRIMITIVES + 2];
+static HICON cached_icons[PURPLE_STATUS_NUM_PRIMITIVES + 3];
 static GtkWidget *image = NULL;
 /* 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;
@@ -497,14 +497,16 @@
 }
 
 static void winpidgin_tray_update_icon(PurpleStatusPrimitive status,
-		gboolean connecting, gboolean pending) {
+		PidginDockletFlag flags) {
 
 	int icon_index;
 	g_return_if_fail(image != NULL);
 
-	if(connecting)
+	if(flags & PIDGIN_DOCKLET_CONNECTING)
 		icon_index = PURPLE_STATUS_NUM_PRIMITIVES;
-	else if(pending)
+	else if(flags & PIDGIN_DOCKLET_EMAIL_PENDING)
+		icon_index = PURPLE_STATUS_NUM_PRIMITIVES+2;
+	else if(flags & PIDGIN_DOCKLET_CONV_PENDING)
 		icon_index = PURPLE_STATUS_NUM_PRIMITIVES+1;
 	else
 		icon_index = status;
@@ -535,9 +537,11 @@
 				break;
 		}
 
-		if (pending)
+		if (flags & PIDGIN_DOCKLET_EMAIL_PENDING)
+			icon_name = PIDGDIN_STOCK_TRAY_EMAIL;
+		else if (flags & PIDGIN_DOCKLET_CONV_PENDING)
 			icon_name = PIDGIN_STOCK_TRAY_PENDING;
-		if (connecting)
+		else if (flags & PIDGIN_DOCKLET_CONNECTING)
 			icon_name = PIDGIN_STOCK_TRAY_CONNECT;
 
 		g_return_if_fail(icon_name != NULL);
--- a/pidgin/win32/gtkwin32dep.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/win32/gtkwin32dep.c	Fri Jan 25 02:22:38 2013 -0500
@@ -171,10 +171,19 @@
 }
 
 void winpidgin_notify_uri(const char *uri) {
-	/* We'll allow whatever URI schemes are supported by the
-	 * default http browser.
+	/* Allow a few commonly used and "safe" schemes to go to the specific
+	 * class handlers and send everything else to the default http browser.
+	 * This isn't optimal, but should cover the most common cases. I didn't
+	 * see any better secure solutions when I did some research.
 	 */
-	winpidgin_shell_execute(uri, "open", "http");
+	gchar *scheme = g_uri_parse_scheme(uri);
+	if (scheme && (g_ascii_strcasecmp(scheme, "https") == 0
+			|| g_ascii_strcasecmp(scheme, "ftp") == 0
+			|| g_ascii_strcasecmp(scheme, "mailto") == 0))
+		winpidgin_shell_execute(uri, "open", scheme);
+	else
+		winpidgin_shell_execute(uri, "open", "http");
+	g_free(scheme);
 }
 
 #define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13)
@@ -377,7 +386,8 @@
 }
 
 void winpidgin_init(HINSTANCE hint) {
-	FARPROC proc;
+	typedef void (__cdecl* LPFNSETLOGFILE)(const LPCSTR);
+	LPFNSETLOGFILE MySetLogFile;
 	gchar *exchndl_dll_path;
 
 	purple_debug_info("winpidgin", "winpidgin_init start\n");
@@ -385,10 +395,10 @@
 	exe_hInstance = hint;
 
 	exchndl_dll_path = g_build_filename(wpurple_install_dir(), "exchndl.dll", NULL);
-	proc = wpurple_find_and_loadproc(exchndl_dll_path, "SetLogFile");
+	MySetLogFile = (LPFNSETLOGFILE) wpurple_find_and_loadproc(exchndl_dll_path, "SetLogFile");
 	g_free(exchndl_dll_path);
 	exchndl_dll_path = NULL;
-	if (proc) {
+	if (MySetLogFile) {
 		gchar *debug_dir, *locale_debug_dir;
 
 		debug_dir = g_build_filename(purple_user_dir(), "pidgin.RPT", NULL);
@@ -396,7 +406,7 @@
 
 		purple_debug_info("winpidgin", "Setting exchndl.dll LogFile to %s\n", debug_dir);
 
-		(proc)(locale_debug_dir);
+		MySetLogFile(locale_debug_dir);
 
 		g_free(debug_dir);
 		g_free(locale_debug_dir);
--- a/pidgin/win32/nsis/generate_gtk_zip.sh	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/win32/nsis/generate_gtk_zip.sh	Fri Jan 25 02:22:38 2013 -0500
@@ -2,31 +2,57 @@
 # Script to generate zip file for GTK+ runtime to be included in Pidgin installer
 
 PIDGIN_BASE=$1
+GPG_SIGN=$2
 
 if [ ! -e $PIDGIN_BASE/ChangeLog ]; then
 	echo $(basename $0) must must have the pidgin base dir specified as a parameter.
 	exit 1
 fi
 
-STAGE_DIR=$PIDGIN_BASE/pidgin/win32/nsis/gtk_runtime_stage
+STAGE_DIR=`readlink -f $PIDGIN_BASE/pidgin/win32/nsis/gtk_runtime_stage`
 #Subdirectory of $STAGE_DIR
 INSTALL_DIR=Gtk
 CONTENTS_FILE=$INSTALL_DIR/CONTENTS
+PIDGIN_VERSION=$( < $PIDGIN_BASE/VERSION )
 
 #This needs to be changed every time there is any sort of change.
-BUNDLE_VERSION=2.16.6.0
+BUNDLE_VERSION=2.16.6.1
+BUNDLE_SHA1SUM=5e16b7efb11943e8c80bc390f6c38df904fd36ed
+ZIP_FILE="$PIDGIN_BASE/pidgin/win32/nsis/gtk-runtime-$BUNDLE_VERSION.zip"
 
-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.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"
+#Download the existing file (so that we distribute the exact same file for all releases with the same bundle version)
+FILE="$ZIP_FILE"
+if [ ! -e "$FILE" ]; then
+	wget "https://pidgin.im/win32/download_redir.php?version=$PIDGIN_VERSION&gtk_version=$BUNDLE_VERSION&dl_pkg=gtk" -O "$FILE"
+fi
+CHECK_SHA1SUM=`sha1sum $FILE`
+CHECK_SHA1SUM=${CHECK_SHA1SUM%%\ *}
+if [ "$CHECK_SHA1SUM" != "$BUNDLE_SHA1SUM" ]; then
+	echo "sha1sum ($CHECK_SHA1SUM) for $FILE doesn't match expected value of $BUNDLE_SHA1SUM"
+	# Allow "devel" versions to build their own bundles if the download doesn't succeed
+	if [[ "$PIDGIN_VERSION" == *"devel" ]]; then
+		echo "Continuing GTK+ Bundle creation for development version of Pidgin"
+	else
+		exit 1
+	fi
+else
+	exit 0
+fi
+
+
+ATK="http://ftp.gnome.org/pub/gnome/binaries/win32/atk/1.32/atk_1.32.0-2_win32.zip ATK 1.32.0-2 sha1sum:3c31c9d6b19af840e2bd8ccbfef4072a6548dc4e"
+#Cairo 1.10.2 has a bug that can be seen when selecting text
+#CAIRO="http://ftp.gnome.org/pub/GNOME/binaries/win32/dependencies/cairo_1.10.2-2_win32.zip Cairo 1.10.2-2 sha1sum:d44cd66a9f4d7d29a8f2c28d1c1c5f9b0525ba44"
+CAIRO="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/cairo_1.8.10-1_win32.zip Cairo 1.8.10-1 sha1sum:a08476cccd807943958610977a138c4d6097c7b8"
+EXPAT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/expat_2.1.0-1_win32.zip Expat 2.1.0-1 gpg:0x71D4DDE53F188CBE"
+FONTCONFIG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/fontconfig_2.8.0-2_win32.zip Fontconfig 2.8.0-2 sha1sum:37a3117ea6cc50c8a88fba9b6018f35a04fa71ce"
+FREETYPE="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/freetype_2.4.10-1_win32.zip Freetype 2.4.10-1 gpg:0x71D4DDE53F188CBE"
+GETTEXT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-runtime_0.18.1.1-2_win32.zip Gettext 0.18.1.1-2 sha1sum:a7cc1ce2b99b408d1bbea9a3b4520fcaf26783b3"
+GLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.28/glib_2.28.8-1_win32.zip Glib 2.28.8-1 sha1sum:5d158f4c77ca0b5508e1042955be573dd940b574"
+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 sha1sum:012853e6de814ebda0cc4459f9eed8ae680e6d17"
+LIBPNG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.4.12-1_win32.zip libpng 1.4.12-1 gpg:0x71D4DDE53F188CBE"
+PANGO="http://ftp.gnome.org/pub/gnome/binaries/win32/pango/1.29/pango_1.29.4-1_win32.zip Pango 1.29.4-1 sha1sum:3959319bd04fbce513458857f334ada279b8cdd4"
+ZLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/zlib_1.2.5-2_win32.zip zlib 1.2.5-2 sha1sum:568907188761df2d9309196e447d91bbc5555d2b"
 
 ALL="ATK CAIRO EXPAT FONTCONFIG FREETYPE GETTEXT GLIB GTK LIBPNG PANGO ZLIB"
 
@@ -41,11 +67,40 @@
 
 function download_and_extract {
 	URL=${1%%\ *}
-	NAME=${1#*\ }
+	VALIDATION=${1##*\ }
+	NAME=${1%\ *}
+	NAME=${NAME#*\ }
 	FILE=$(basename $URL)
 	if [ ! -e $FILE ]; then
 		echo Downloading $NAME
-		wget $URL || return 1
+		wget $URL || exit 1
+	fi
+	VALIDATION_TYPE=${VALIDATION%%:*}
+	VALIDATION_VALUE=${VALIDATION##*:}
+	if [ $VALIDATION_TYPE == 'sha1sum' ]; then
+		CHECK_SHA1SUM=`sha1sum $FILE`
+		CHECK_SHA1SUM=${CHECK_SHA1SUM%%\ *}
+		if [ "$CHECK_SHA1SUM" != "$VALIDATION_VALUE" ]; then
+			echo "sha1sum ($CHECK_SHA1SUM) for $FILE doesn't match expected value of $VALIDATION_VALUE"
+			exit 1
+		fi
+	elif [ $VALIDATION_TYPE == 'gpg' ]; then
+		if [ ! -e "$FILE.asc" ]; then
+			echo Downloading GPG key for $NAME
+			wget "$URL.asc" || exit 1
+		fi
+		#Use our own keyring to avoid adding stuff to the main keyring
+		#This doesn't use $GPG_SIGN because we don't this validation to be bypassed when people are skipping signing output
+		GPG_BASE="gpg -q --keyring $STAGE_DIR/$VALIDATION_VALUE-keyring.gpg" 
+		if [[ ! -e $STAGE_DIR/$VALIDATION_VALUE-keyring.gpg \
+				|| `$GPG_BASE --list-keys "$VALIDATION_VALUE" > /dev/null && echo -n "0"` -ne 0 ]]; then
+			touch $STAGE_DIR/$VALIDATION_VALUE-keyring.gpg
+		       	$GPG_BASE --no-default-keyring --keyserver pgp.mit.edu --recv-key "$VALIDATION_VALUE" || exit 1
+		fi
+		$GPG_BASE --verify "$FILE.asc" || (echo "$FILE failed signature verification"; exit 1) || exit 1
+	else
+		echo "Unrecognized validation type of $VALIDATION_TYPE"
+		exit 1
 	fi
 	EXTENSION=${FILE##*.}
 	#This is an OpenSuSE build service RPM
@@ -77,7 +132,9 @@
 done
 
 #Generate zip file to be included in installer
-zip -9 -r ../gtk-runtime-$BUNDLE_VERSION.zip Gtk
+rm -f $ZIP_FILE
+zip -9 -r $ZIP_FILE Gtk
+($GPG_SIGN -ab $ZIP_FILE && $GPG_SIGN --verify $ZIP_FILE.asc) || exit 1
 
 exit 0
 
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Fri Jan 25 02:22:38 2013 -0500
@@ -34,7 +34,6 @@
 
 !include "MUI.nsh"
 !include "Sections.nsh"
-!include "WinVer.nsh"
 !include "LogicLib.nsh"
 !include "Memento.nsh"
 
@@ -71,7 +70,7 @@
 !define PERL_REG_KEY				"SOFTWARE\Perl"
 !define PERL_DLL				"perl510.dll"
 
-!define DOWNLOADER_URL				"http://pidgin.im/win32/download_redir.php?version=${PIDGIN_VERSION}"
+!define DOWNLOADER_URL				"https://pidgin.im/win32/download_redir.php?version=${PIDGIN_VERSION}"
 
 !define MEMENTO_REGISTRY_ROOT			HKLM
 !define MEMENTO_REGISTRY_KEY			"${PIDGIN_UNINSTALL_KEY}"
@@ -93,6 +92,7 @@
 ;Reserve files used in .onInit
 ;for faster start-up
 ReserveFile "${NSISDIR}\Plugins\System.dll"
+ReserveFile "${NSISDIR}\Plugins\UserInfo.dll"
 !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
 !insertmacro MUI_RESERVEFILE_LANGDLL
 
@@ -118,8 +118,8 @@
 
   ;Finish Page config
   !define MUI_FINISHPAGE_NOAUTOCLOSE
-  !define MUI_FINISHPAGE_RUN			"$INSTDIR\pidgin.exe"
-  !define MUI_FINISHPAGE_RUN_NOTCHECKED
+  ;!define MUI_FINISHPAGE_RUN			"$INSTDIR\pidgin.exe"
+  ;!define MUI_FINISHPAGE_RUN_NOTCHECKED
   !define MUI_FINISHPAGE_LINK			$(PIDGINFINISHVISITWEBSITE)
   !define MUI_FINISHPAGE_LINK_LOCATION		"http://pidgin.im"
 
@@ -148,13 +148,6 @@
   !include "${PIDGIN_NSIS_INCLUDE_PATH}\langmacros.nsh"
 
 ;--------------------------------
-;Reserve Files
-  ; Only need this if using bzip2 compression
-
-  !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
-  !insertmacro MUI_RESERVEFILE_LANGDLL
-  ReserveFile "${NSISDIR}\Plugins\UserInfo.dll"
-
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Start Install Sections ;;
@@ -268,9 +261,18 @@
   NSISdl::download /TIMEOUT=10000 $R2 $R1
   Pop $R0
   ;StrCmp $R0 "cancel" done
-  StrCmp $R0 "success" +2
+  StrCmp $R0 "success" 0 prompt_retry
+
+  Push "${GTK_SHA1SUM}"
+  Push "$R1" ; Filename
+  Call CheckSHA1Sum
+  Pop $R0
+
+  StrCmp "$R0" "0" extract
+    prompt_retry:
     MessageBox MB_RETRYCANCEL "$(PIDGINGTKDOWNLOADERROR)" /SD IDCANCEL IDRETRY retry IDCANCEL done
 
+  extract:
 !endif
 
   ;Delete the old Gtk directory
@@ -282,7 +284,9 @@
   StrCmp $R0 "success" +2
     DetailPrint "$R0" ;print error message to log
 
+!ifndef OFFLINE_INSTALLER
   done:
+!endif
 SectionEnd ; end of GTK+ section
 
 ;--------------------------------
@@ -335,7 +339,7 @@
     Delete "$INSTDIR\plugins\liboscar.dll"
     Delete "$INSTDIR\plugins\libjabber.dll"
 
-    File /r /x locale ..\..\..\${PIDGIN_INSTALL_DIR}\*.*
+    File /r /x locale /x Gtk ..\..\..\${PIDGIN_INSTALL_DIR}\*.*
 
     ; Check if Perl is installed, if so add it to the AppPaths
     ReadRegStr $R2 HKLM ${PERL_REG_KEY} ""
@@ -351,21 +355,6 @@
 
     perl_done:
 
-    ; If this is under NT4, delete the SILC support stuff
-    ; there is a bug that will prevent any account from connecting
-    ; See https://lists.silcnet.org/pipermail/silc-devel/2005-January/001588.html
-    ; Also, remove the GSSAPI SASL plugin and associated files as they aren't
-    ; compatible with NT4.
-    ${If} ${IsNT}
-    ${AndIf} ${IsWinNT4}
-      ;SILC
-      Delete "$INSTDIR\plugins\libsilc.dll"
-      Delete "$INSTDIR\libsilcclient-1-1-2.dll"
-      Delete "$INSTDIR\libsilc-1-1-2.dll"
-      ;GSSAPI
-      Delete "$INSTDIR\sasl2\saslGSSAPI.dll"
-    ${EndIf}
-
     SetOutPath "$INSTDIR"
 
     ; If we don't have install rights we're done
@@ -467,9 +456,18 @@
   NSISdl::download /TIMEOUT=10000 $R2 $R1
   Pop $R0
   StrCmp $R0 "cancel" done
-  StrCmp $R0 "success" +2
+  StrCmp $R0 "success" 0 prompt_retry
+
+  Push "${DEBUG_SYMBOLS_SHA1SUM}"
+  Push "$R1" ; Filename
+  Call CheckSHA1Sum
+  Pop $R0
+
+  StrCmp "$R0" "0" extract
+    prompt_retry:
     MessageBox MB_RETRYCANCEL "$(PIDGINDEBUGSYMBOLSERROR)" /SD IDCANCEL IDRETRY retry IDCANCEL done
 
+  extract:
 !endif
 
   SetOutPath "$INSTDIR"
@@ -478,7 +476,9 @@
   StrCmp $R0 "success" +2
     DetailPrint "$R0" ;print error message to log
 
+!ifndef OFFLINE_INSTALLER
   done:
+!endif
 SectionEnd
 
 ;--------------------------------
@@ -637,7 +637,8 @@
     Delete "$INSTDIR\libpurple.dll"
     Delete "$INSTDIR\libsasl.dll"
     Delete "$INSTDIR\libsilc-1-1-2.dll"
-    Delete "$INSTDIR\libsilcclient-1-1-2.dll"
+    Delete "$INSTDIR\libsilcclient-1-1-3.dll"
+    Delete "$INSTDIR\libssp-0.dll"
     Delete "$INSTDIR\libxml2-2.dll"
     Delete "$INSTDIR\libymsg.dll"
     Delete "$INSTDIR\nss3.dll"
@@ -1300,3 +1301,40 @@
   Pop $R0
   Exch $R1
 FunctionEnd
+
+!ifndef OFFLINE_INSTALLER
+; Input Stack: Filename, SHA1sum
+; Output Return Code: 0=Match; 1=FileSum error; 2=Mismatch
+Function CheckSHA1Sum
+  Push $R0
+  Exch
+  Pop $R0 ;Filename
+  Push $R2
+  Exch 2
+  Pop $R2 ;SHA1sum
+  Push $R1
+
+  SHA1Plugin::FileSum "$R0"
+  Pop $R1
+  Pop $R0
+
+  StrCmp "$R1" "0" +4
+    DetailPrint "SHA1Sum calculation error: $R0"
+    IntOp $R1 0 + 1
+    Goto done
+
+  ; Compare the SHA1Sums
+  StrCmp $R2 $R0 +4
+    DetailPrint "SHA1Sum mismatch... Expected $R2; got $R0"
+    IntOp $R1 0 + 2
+    Goto done
+
+  IntOp $R1 0 + 0
+
+  done:
+  Pop $R2
+  Pop $R0
+  Exch $R1 ;$R1 has the return code
+FunctionEnd
+!endif
+
--- a/pidgin/win32/winpidgin.c	Thu Aug 23 01:27:48 2012 -0400
+++ b/pidgin/win32/winpidgin.c	Fri Jan 25 02:22:38 2013 -0500
@@ -38,9 +38,10 @@
 #include <sys/stat.h>
 #include "config.h"
 
-typedef int (CALLBACK* LPFNPIDGINMAIN)(HINSTANCE, int, char**);
-typedef void (CALLBACK* LPFNSETDLLDIRECTORY)(LPCWSTR);
-typedef BOOL (CALLBACK* LPFNATTACHCONSOLE)(DWORD);
+typedef int (__cdecl* LPFNPIDGINMAIN)(HINSTANCE, int, char**);
+typedef void (WINAPI* LPFNSETDLLDIRECTORY)(LPCWSTR);
+typedef BOOL (WINAPI* LPFNATTACHCONSOLE)(DWORD);
+typedef BOOL (WINAPI* LPFNSETPROCESSDEPPOLICY)(DWORD);
 
 static BOOL portable_mode = FALSE;
 
@@ -556,7 +557,7 @@
 	len = WideCharToMultiByte(CP_UTF8, 0, tmp1,
 			wlen, NULL, 0, NULL, NULL);
 	if (len) {
-		utf8msg = malloc(len * sizeof(char));
+		utf8msg = malloc(len);
 		len = WideCharToMultiByte(CP_UTF8, 0, tmp1,
 			wlen, utf8msg, len, NULL, NULL);
 	}
@@ -642,16 +643,24 @@
 		}
 	}
 
+	/* Permanently enable DEP if the OS supports it */
+	if ((hmod = GetModuleHandleW(L"kernel32.dll"))) {
+		LPFNSETPROCESSDEPPOLICY MySetProcessDEPPolicy =
+			(LPFNSETPROCESSDEPPOLICY)
+			GetProcAddress(hmod, "SetProcessDEPPolicy");
+		if (MySetProcessDEPPolicy)
+			MySetProcessDEPPolicy(1); //PROCESS_DEP_ENABLE
+	}
+
 	if (debug || help || version) {
 		/* If stdout hasn't been redirected to a file, alloc a console
 		 *  (_istty() doesn't work for stuff using the GUI subsystem) */
 		if (_fileno(stdout) == -1 || _fileno(stdout) == -2) {
 			LPFNATTACHCONSOLE MyAttachConsole = NULL;
-			if ((hmod = GetModuleHandleW(L"kernel32.dll"))) {
+			if (hmod)
 				MyAttachConsole =
 					(LPFNATTACHCONSOLE)
 					GetProcAddress(hmod, "AttachConsole");
-			}
 			if ((MyAttachConsole && MyAttachConsole(ATTACH_PARENT_PROCESS))
 					|| AllocConsole()) {
 				freopen("CONOUT$", "w", stdout);
@@ -683,31 +692,34 @@
 
 			wcscat(pidgin_dir, L"\\exchndl.dll");
 			if ((hmod = LoadLibraryW(pidgin_dir))) {
-				FARPROC proc;
+				typedef void (__cdecl* LPFNSETLOGFILE)(const LPCSTR);
+				LPFNSETLOGFILE MySetLogFile;
 				/* exchndl.dll is built without UNICODE */
 				char debug_dir[MAX_PATH];
 				printf("Loaded exchndl.dll\n");
 				/* Temporarily override exchndl.dll's logfile
 				 * to something sane (Pidgin will override it
 				 * again when it initializes) */
-				proc = GetProcAddress(hmod, "SetLogFile");
-				if (proc) {
-					if (GetTempPathA(sizeof(debug_dir) * sizeof(char), debug_dir) != 0) {
+				MySetLogFile = (LPFNSETLOGFILE) GetProcAddress(hmod, "SetLogFile");
+				if (MySetLogFile) {
+					if (GetTempPathA(sizeof(debug_dir), debug_dir) != 0) {
 						strcat(debug_dir, "pidgin.RPT");
 						printf(" Setting exchndl.dll LogFile to %s\n",
 							debug_dir);
-						(proc)(debug_dir);
+						MySetLogFile(debug_dir);
 					}
 				}
-				proc = GetProcAddress(hmod, "SetDebugInfoDir");
-				if (proc) {
+				/* The function signature for SetDebugInfoDir is the same as SetLogFile,
+				 * so we can reuse the variable */
+				MySetLogFile = (LPFNSETLOGFILE) GetProcAddress(hmod, "SetDebugInfoDir");
+				if (MySetLogFile) {
 					char *pidgin_dir_ansi = NULL;
 					/* Restore pidgin_dir to point to where the executable is */
 					pidgin_dir_start[0] = L'\0';
 					i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir,
 						-1, NULL, 0, NULL, NULL);
 					if (i != 0) {
-						pidgin_dir_ansi = malloc(i * sizeof(char));
+						pidgin_dir_ansi = malloc(i);
 						i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir,
 							-1, pidgin_dir_ansi, i, NULL, NULL);
 						if (i == 0) {
@@ -716,13 +728,13 @@
 						}
 					}
 					if (pidgin_dir_ansi != NULL) {
-						_snprintf(debug_dir, sizeof(debug_dir) / sizeof(char),
+						_snprintf(debug_dir, sizeof(debug_dir),
 							"%s\\pidgin-%s-dbgsym",
 							pidgin_dir_ansi,  VERSION);
-						debug_dir[sizeof(debug_dir) / sizeof(char) - 1] = '\0';
+						debug_dir[sizeof(debug_dir) - 1] = '\0';
 						printf(" Setting exchndl.dll DebugInfoDir to %s\n",
 							debug_dir);
-						(proc)(debug_dir);
+						MySetLogFile(debug_dir);
 						free(pidgin_dir_ansi);
 					}
 				}
@@ -800,7 +812,7 @@
 			int len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i],
 				-1, NULL, 0, NULL, NULL);
 			if (len != 0) {
-				char *arg = malloc(len * sizeof(char));
+				char *arg = malloc(len);
 				len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i],
 					-1, arg, len, NULL, NULL);
 				if (len != 0) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/ca-certs/Baltimore_CyberTrust_Root.pem	Fri Jan 25 02:22:38 2013 -0500
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
+DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
+ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
+VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
+mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
+IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
+mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
+XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
+dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
+jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
+BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
+DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
+9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
+jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
+Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
+ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
+R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
--- a/share/ca-certs/Makefile.am	Thu Aug 23 01:27:48 2012 -0400
+++ b/share/ca-certs/Makefile.am	Fri Jan 25 02:22:38 2013 -0500
@@ -1,6 +1,7 @@
 CERTIFICATES = \
 		AddTrust_External_Root.pem \
 		America_Online_Root_Certification_Authority_1.pem \
+		Baltimore_CyberTrust_Root.pem \
 		CAcert_Root.pem \
 		CAcert_Class3.pem \
 		Deutsche_Telekom_Root_CA_2.pem \
@@ -14,7 +15,6 @@
 		Thawte_Premium_Server_CA.pem \
 		Thawte_Primary_Root_CA.pem \
 		ValiCert_Class_2_VA.pem \
-		Verisign_RSA_Secure_Server_CA.pem \
 		Verisign_Class3_Primary_CA.pem \
 		VeriSign_Class_3_Public_Primary_Certification_Authority_-_G2.pem \
 		VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.pem \
@@ -23,9 +23,7 @@
 EXTRA_CERTS = \
 		AOL_Member_CA.pem \
 		DigiCertHighAssuranceCA-3.pem \
-		Microsoft_Internet_Authority.pem \
 		Microsoft_Internet_Authority_2010.pem \
-		Microsoft_Secure_Server_Authority.pem \
 		Microsoft_Secure_Server_Authority_2010.pem \
 		VeriSign_Class3_Extended_Validation_CA.pem \
 		VeriSign_International_Server_Class_3_CA.pem
--- a/share/ca-certs/Microsoft_Internet_Authority.pem	Thu Aug 23 01:27:48 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIFCjCCBHOgAwIBAgIEBycWdTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
-UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
-cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
-b2JhbCBSb290MB4XDTA4MDIxOTE4MjcwMloXDTExMDIxOTE4MjQ1M1owJzElMCMG
-A1UEAxMcTWljcm9zb2Z0IEludGVybmV0IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcN
-AQEBBQADggIPADCCAgoCggIBAKiloatvDehDG/rQriel2AC9qmSJdvjKb2fmJf30
-K7SaC3zQu8kGQxENUEFsHsH0jmBejJ9vvn9tHZ8hGL+kORvWUVBskdMzP65rC0V0
-VeVgUYzPZ7MvrLfhh9+v3yJ7qRuf1aNgmzJg5t1AA91X+aRrFT4lRzl9BFWhQ1VS
-XaD7l6qoiyhD8FbrdLRAe61swsRmzWeXoy6NJpOBsGXaCSG1Jooylro+zkWxt97c
-NkNf/wYqoYcIXo02YpFbwreveejW9a0Lh/1z9+e9aiMtC5QnPT57GTqNINt5R0rp
-Iz4g3GJhmjXVoVF/tev5DMJuhRgPoz0W0aA3UnSmTWh2RFvgqawLqSRrKUhVjySi
-/m5s62uG5xxIftO7/6ljzS061CFoV/RBl/I3WghYp04sr4cSXWa/rL449YhBT8BJ
-jltefWCYAOcT1nA4oFXwXbl1qCUIkZ0bqwju2FGW5vl2qh6vmzcQjc3XxD0m2UqC
-yJNFa9SUgVXtUCqeOI+KqgLW01tpqZteG10byWKmppTVAvdPwHoGE0bl6wBwXldE
-f7Pn4lqu6Yp4bedP3Qv1sfp2H7D98cxSwYRt3UcXN2kjTPv+pby2fEHm9GWf3iE/
-7OtzUI7LYhP7+rGyuCYTtZKH1jDWHrTZBpnAPmrAQQHCI8/4TjF9ZQ1mqRi6x9I9
-9XGfAgMBAAGjggFvMIIBazASBgNVHRMBAf8ECDAGAQH/AgEBMFMGA1UdIARMMEow
-SAYJKwYBBAGxPgEAMDswOQYIKwYBBQUHAgEWLWh0dHA6Ly9jeWJlcnRydXN0Lm9t
-bmlyb290LmNvbS9yZXBvc2l0b3J5LmNmbTAOBgNVHQ8BAf8EBAMCAYYwgYkGA1Ud
-IwSBgTB/oXmkdzB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0
-aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAh
-BgNVBAMTGkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290ggIBpTBFBgNVHR8EPjA8
-MDqgOKA2hjRodHRwOi8vd3d3LnB1YmxpYy10cnVzdC5jb20vY2dpLWJpbi9DUkwv
-MjAxOC9jZHAuY3JsMB0GA1UdDgQWBBTG27vA2CAZkvFg/IjxWH+8G06PGjANBgkq
-hkiG9w0BAQUFAAOBgQBnSDXCyiqGmHTAEJOtZYVm/IbzGtzCY423NF6/yuccYZkm
-spJnDoh8nq3nx3P2KBEyPAqoQ1MEFC+ByQjV4AAQ9dMQALUGNRfHhFUhBeeIybYd
-bzvKOxSlIYSwO2T56+oXyDlkbQWKmHec5qzCE0lwGHslsRU/CHwhuFu2nH+CxQ==
------END CERTIFICATE-----
--- a/share/ca-certs/Microsoft_Secure_Server_Authority.pem	Thu Aug 23 01:27:48 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGEzCCA/ugAwIBAgIKYRZtLwAEAAAAIDANBgkqhkiG9w0BAQUFADAnMSUwIwYD
-VQQDExxNaWNyb3NvZnQgSW50ZXJuZXQgQXV0aG9yaXR5MB4XDTA4MDQwOTIxMzc1
-NFoXDTExMDIxOTE4MjQ1M1owgYsxEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJ
-kiaJk/IsZAEZFgltaWNyb3NvZnQxFDASBgoJkiaJk/IsZAEZFgRjb3JwMRcwFQYK
-CZImiZPyLGQBGRYHcmVkbW9uZDEqMCgGA1UEAxMhTWljcm9zb2Z0IFNlY3VyZSBT
-ZXJ2ZXIgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-kYTz6fKXvrdfIr5o3Ue4CRIzhTE+8JE4hrLTQki3emjYn/CfHRPb7hmMiOZmWBdE
-DUEymyXOyZ7Sy2tC6WaBC4onVYotPoSsaOZJv6EJeHPk64RiWTfX+XqufRndYOEC
-DUmotYQNPV/8InioIBf9+gOSsAMdmyGZF6C1PkJqvPZTRxNv6hxuMMb6uOQIPoFX
-/ceQvAOZcJx2qGsAVKsJHylYkC0GgVyFVhOI0vcZZBcP5T+NtOmyjVBWdxS413HL
-D+8w+3wG0bOP8EyOeRnuf0KLXGBangte0ZFIRd28GXpo5UrcA/r5000e2RTHmhC4
-8YPMIoi+q9XZoF5R0Z069QIDAQABo4IB2jCCAdYwEgYDVR0TAQH/BAgwBgEB/wIB
-ADAdBgNVHQ4EFgQUFFXEOeA9LtFVLkiWsNh+FCIGk7wwCwYDVR0PBAQDAgGGMBIG
-CSsGAQQBgjcVAQQFAgMFAAUwIwYJKwYBBAGCNxUCBBYEFM7FoL4P/nlmdZEP8PeS
-WzWYqBWzMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMB8GA1UdIwQYMBaAFMbb
-u8DYIBmS8WD8iPFYf7wbTo8aMIGjBgNVHR8EgZswgZgwgZWggZKggY+GNmh0dHA6
-Ly9tc2NybC5taWNyb3NvZnQuY29tL3BraS9tc2NvcnAvY3JsL21zd3d3KDQpLmNy
-bIY0aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9tc2NvcnAvY3JsL21zd3d3
-KDQpLmNybIYfaHR0cDovL2NvcnBwa2kvY3JsL21zd3d3KDQpLmNybDB5BggrBgEF
-BQcBAQRtMGswPAYIKwYBBQUHMAKGMGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
-a2kvbXNjb3JwL21zd3d3KDQpLmNydDArBggrBgEFBQcwAoYfaHR0cDovL2NvcnBw
-a2kvYWlhL21zd3d3KDQpLmNydDANBgkqhkiG9w0BAQUFAAOCAgEAempuzk/VLM4N
-H9TAbFtCjKc95iGmyx2bHbEk9m2cbGxXjBre+N4cJoIYYmhLrZ6L712ov1NjM73b
-m8fb2Fy8Yw8Cmwc8VtarPZT2yzGr8MhNUDVuZswaKfjCY3H7RYv/XKc7AOMd25WP
-/M0WTT4Bna6hl9dUaDGwv5SZFFIJ17FLo4FR2H7IkOOI/WcUPAHeDXUewp4qRPE/
-560xZrLSeNH2lKnOAwwXxwnXSo5WOF5AQXh1nRdbBV9Nu7yI6jH1QV6fKf6oFU2Y
-IOjpnJ0FihVB6XoZ0wNOUMzPEEQcTfIoVoc+t0iK02wcmTLgBgbYU703dHvvPTcn
-IfdI2mscx8l9MjUOdklIIve0FhCxRPqHpEeKjM95gllbXmWgQxAXiog+A62fEo5d
-M7nfeEyiweSlhj1cv+2dyhzyS5saKYkk3ocCnOMCyD0M+4gJx4n4b/zT3rcujyN+
-7m20PbBTjcdTT1+AxOs75rON2hhKUqqrk2MDCpnEJsNK4TuRyDUtm9r+AxaZ4XRK
-MT8InY1Xl9hzrIK6MVERYH46kxg6odwpzJ8Urn4dREBiMy6Gzq8mtyXvpYEcmeGL
-zz1aT7qNNbQ0qqbPb6RpOMHlUWOIhVWJC71T5WK1pynAc3P9zOm8BkUYvIyJvCbR
-bufCGVng4FAtVZ1advxSVRoa4GyuFZ8=
------END CERTIFICATE-----
--- a/share/ca-certs/Verisign_RSA_Secure_Server_CA.pem	Thu Aug 23 01:27:48 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzEL
-MAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMu
-MS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9y
-aXR5MB4XDTk0MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UE
-BhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD
-VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGb
-MA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6O
-LDfO6zV4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZs
-iAeP94FZbYQHZXATcXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmC
-sZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZIhvcNAQECBQADfgBl3X7hsuyw
-4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3YQO2WxZpO8ZECAyIUwxr
-l0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc1/p3yjkWWW8O6tO1
-g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==
------END CERTIFICATE-----
--- a/valgrind-suppressions	Thu Aug 23 01:27:48 2012 -0400
+++ b/valgrind-suppressions	Fri Jan 25 02:22:38 2013 -0500
@@ -151,4 +151,16 @@
    fun:purple_plugins_load_saved
    fun:main
 }
-
+{
+   libfontconfig leaks
+   Memcheck:Leak
+   fun:*alloc
+   obj:/usr/lib/libfontconfig.so.*
+   ...
+}
+{
+   webkitgtk uninitialized values
+   Memcheck:Cond
+   obj:/usr/lib/libwebkitgtk-*
+   ...
+}

mercurial