Merge 5b5516d9a5dbbd6e534eba231284bbd1c4c16f57 to fix package_version.h mxit

Wed, 13 Jun 2012 19:30:27 -0400

author
Hg Conversion <devel@pidgin.im>
date
Wed, 13 Jun 2012 19:30:27 -0400
branch
mxit
changeset 33038
854cd00e1ab4
parent 33003
f08d44ececa1 (diff)
parent 33035
5b5516d9a5db (current diff)
child 33044
a2f78d09fc27

Merge 5b5516d9a5dbbd6e534eba231284bbd1c4c16f57 to fix package_version.h

Makefile.am file | annotate | diff | comparison | revisions
libpurple/win32/targets.mak file | annotate | diff | comparison | revisions
--- a/.mtn-ignore	Wed Jun 13 19:28:57 2012 -0400
+++ b/.mtn-ignore	Wed Jun 13 19:30:27 2012 -0400
@@ -1,8 +1,8 @@
+(.*/)?TAGS$
 (.*/)?\.svn
+.*/?.*\.pc$
 .*/?Makefile(\.in)?$
 .*/?Makefile\.am\.mingw$
-(.*/)?TAGS$
-.*/?.*\.pc$
 .*/perl/common/[^/]+\.c$
 .*/perl/common/blib.*
 .*/perl/common/pm_to_blib$
@@ -11,8 +11,8 @@
 .*\.dll$
 .*\.exe$
 .*\.loT$
-intltool-.*
 Doxyfile(\.mingw)?$
+VERSION$
 aclocal.m4
 autogen.args
 compile
@@ -24,31 +24,15 @@
 config.status
 config.sub
 configure$
+depcomp
+doc/finch.1$
+doc/html
+doc/pidgin.1$
 finch/finch$
 finch/libgnt/gntmarshal.c
 finch/libgnt/gntmarshal.h
-depcomp
-doc/finch.1$
-doc/pidgin.1$
-doc/html
-package_revision.h
-package_revision_raw.txt
-pidgin.apspec$
-pidgin.desktop$
-pidgin.spec$
-pidgin-.*.tar.gz
-pidgin-.*.tar.bz2
-pidgin-*.*.*-win32bin$
-pidgin/pidgin$
-pidgin/pixmaps/emotes/default/24/theme
-pidgin/pixmaps/emotes/none/theme
-pidgin/pixmaps/emotes/small/16/theme
-pidgin/plugins/musicmessaging/music-messaging-bindings.c
-pidgin/plugins/perl/common/Makefile.PL$
-pidgin/plugins/perl/common/Makefile.old
-pidgin/win32/pidgin_dll_rc.rc$
-pidgin/win32/pidgin_exe_rc.rc$
 install-sh
+intltool-.*
 libpurple/dbus-bindings.c
 libpurple/dbus-signals.c
 libpurple/dbus-types.c
@@ -62,10 +46,10 @@
 libpurple/plugins/perl/common/const-c.inc
 libpurple/plugins/perl/common/const-xs.inc
 libpurple/plugins/perl/common/lib
-libpurple/purple.h$
 libpurple/purple-client-bindings.c
 libpurple/purple-client-bindings.h
 libpurple/purple-client-example
+libpurple/purple.h$
 libpurple/tests/check_libpurple
 libpurple/tests/libpurple..
 libpurple/version.h$
@@ -75,6 +59,34 @@
 ltmain.sh
 missing
 mkinstalldirs
+package_revision.h
+package_revision_raw.txt
+pidgin-*.*.*-dbgsym$
+pidgin-*.*.*-dbgsym.zip$
+pidgin-*.*.*-win32-bin.zip$
+pidgin-*.*.*-win32bin$
+pidgin-.*.tar.bz2
+pidgin-.*.tar.gz
+pidgin.apspec$
+pidgin.desktop$
+pidgin.spec$
+pidgin/pidgin$
+pidgin/pixmaps/emotes/default/24/theme
+pidgin/pixmaps/emotes/none/theme
+pidgin/pixmaps/emotes/small/16/theme
+pidgin/plugins/musicmessaging/music-messaging-bindings.c
+pidgin/plugins/perl/common/Makefile.PL$
+pidgin/plugins/perl/common/Makefile.old
+pidgin/win32/nsis/gtk-runtime-*.*.*.*.zip
+pidgin/win32/nsis/gtk_runtime_stage$
+pidgin/win32/nsis/langmacros.nsh
+pidgin/win32/nsis/nsis_translations.desktop
+pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh
+pidgin/win32/nsis/pidgin-spellcheck.nsh
+pidgin/win32/nsis/pidgin-translations.nsh$
+pidgin/win32/nsis/translations
+pidgin/win32/pidgin_dll_rc.rc$
+pidgin/win32/pidgin_exe_rc.rc$
 po/Makefile.in.in
 po/POTFILES$
 po/missing
@@ -83,4 +95,3 @@
 po/stamp-it
 stamp-h1
 win32-install-dir(\.release)?
-VERSION$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.tx/config	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,8 @@
+[main]
+host = https://www.transifex.net
+
+[pidgin.pidgin]
+file_filter = po/<lang>.po
+source_file = po/pidgin.pot
+source_lang = en
+
--- a/AUTHORS	Wed Jun 13 19:28:57 2012 -0400
+++ b/AUTHORS	Wed Jun 13 19:30:27 2012 -0400
@@ -12,53 +12,58 @@
 Paul 'darkrain42' Aurich - Developer
 John 'rekkanoryo' Bailey - Developer
 Ethan 'Paco-Paco' Blanton - Developer
-Thomas Butter - Developer
-Ka-Hing Cheung - Developer
 Sadrul Habib Chowdhury - Developer
 Mark 'KingAnt' Doliner - Developer
-Sean Egan - Developer
 Casey Harkins - Developer
+Ivan Komarov - Developer
 Gary 'grim' Kramlich - Developer
 Richard 'rlaager' Laager - Developer
+Marcus 'malu' Lundblad - Developer
+Sulabh 'sulabh_m' Mahajan - Developer
 Richard 'wabz' Nelson - Developer
-Christopher 'siege' O'Brien - Developer
-Bartosz Oler - Developer
 Etan 'deryni' Reisner - Developer
-Tim 'marv' Ringenbach - Developer
 Michael 'Maiku' Ruprecht - Developer, voice and video
 Elliott 'QuLogic' Sales de Andrade - Developer
 Luke 'LSchiere' Schierer - Support
-Megan 'Cae' Schneider - support/QA
 Evan Schoenberg - Developer
 Kevin 'SimGuy' Stange - Developer & Webmaster
 Will 'resiak' Thompson - Developer
 Stu 'nosnilmot' Tomlinson - Developer
-Nathan 'faceprint' Walp - Developer
+Jorge 'Masca' Villaseñor - Developer
 
 Crazy Patch Writers:
 -------------------
-Marcus 'malu' Lundblad
-Dennis 'EvilDennisR' Ristuccia
+Jakub 'haakon' Adam
+Krzysztof Klinikowski
 Peter 'Fmoo' Ruibal
 Gabriel 'Nix' Schulhof
-Jorge 'Masca' Villaseñor
+Tomasz Wasilczyk
 
 Retired Developers:
 ------------------
 Herman Bloggs - Win32 Port
+Thomas Butter - Developer
+Ka-Hing Cheung - Developer
 Jim Duchek <jim@linuxpimps.com> - maintainer
+Sean Egan - Developer
 Rob Flynn <gaim@robflynn.com> - maintainer
 Adam Fritzler - libfaim maintainer
 Christian 'ChipX86' Hammond - Developer & Webmaster
 Syd Logan - hacker and designated driver [lazy bum]
+Christopher 'siege' O'Brien - Developer
+Bartosz Oler - Developer
+Tim 'marv' Ringenbach - Developer
+Megan 'Cae' Schneider - support/QA
 Jim Seymour - XMPP developer
 Mark Spencer <markster@marko.net> - original author
+Nathan 'faceprint' Walp - Developer
 Eric Warmenhoven <eric@warmenhoven.org> - lead developer
 
 Retired Crazy Patch Writers:
 ---------------------------
 Felipe 'shx' Contreras
 Decklin Foster
+Dennis 'EvilDennisR' Ristuccia - Senior Contributor/QA
 Peter 'Bleeter' Lawler
 Robert 'Robot101' McQueen
 Benjamin Miller
@@ -136,4 +141,4 @@
 Thanks to Jeroen van der Vegt for the initial smiley plugin and images.
 
 The OpenQ Team
-	Wrote the QQ plugin (see AUTHORS in the qq directory)
+	Wrote the QQ plugin dropped in 2.8.0 (see libpurple/qq/AUTHORS in 2.7.11)
--- a/COPYRIGHT	Wed Jun 13 19:28:57 2012 -0400
+++ b/COPYRIGHT	Wed Jun 13 19:30:27 2012 -0400
@@ -1,11 +1,26 @@
 Pidgin, Finch, and libpurple
-Copyright (C) 1998-2009 by the following:
+
+This file is intended to be a comprehensive list of contributors to
+this project.  If you have contributed to this project then you deserve
+to be on this list.  Contact us (see: AUTHORS) and we'll add you.
 
-If you have contributed to this project then you deserve to be on this
-list.  Contact us (see: AUTHORS) and we'll add you.
+Many open source projects list contributor names at the top of each
+source file containing their contribution.  However, we've found
+that it is difficult to keep this list accurate, especially when old
+code is removed or existing code is moved to a different file.  So
+instead we chose to list a generic message at the top of each source
+file that points here.
+
+If concerns are raised as to the copyright holder of a particular
+piece of code, then that code should be traced through our version
+control system to see from where it came and who has modified it.
+
+Copyright (C) 1998-2011 by the following:
 
 Saleem Abdulrasool
+Jakub Adam
 Dave Ahlswede
+Thijs Alkemade
 Manuel Amador
 Matt Amato
 Josef Andrysek
@@ -33,9 +48,12 @@
 Stefan Becker
 Carlos Bederian
 Dave Bell
+Matthew W.S. Bell
 Igor Belyi
+David Benjamin
 Brian Bernas
 Paul Betts
+Runa Bhattacharjee
 Jonas Birmé
 George-Cristian Bîrzan
 Eric Blade
@@ -50,7 +68,10 @@
 Paolo Borelli
 Julien Bossart
 Craig Boston
+Éric Boumaour
 Chris Boyle
+Stanislav Brabec
+Quentin Brandon
 Derrick J Brashear
 Mauro Sérgio Ferreira Brasil
 Luke Bratch
@@ -60,14 +81,17 @@
 Jeffery Brown
 Philip Brown
 Dan Bruce
+Guillaume Brunerie
 Norbert Buchmuller
 Johannes Buchner
 Sean Burke
+Gabriel Burt
 Thomas Butter
 Trevor Caira
 Andrea Canciani
 Damien Carbery
 Michael Carlson
+Rodrigo Tobar Carrizo
 Keegan Carruthers-Smith
 Ludovico Cavedon
 Steve Cavilia
@@ -75,11 +99,15 @@
 Matěj Cepl
 Cerulean Studios, LLC
 Jonathan Champ
+Markos Chandras
+Matthew Chapman
 Christophe Chapuis
+Tirtha Chatterjee
 Patrick Cheung
 Ka-Hing Cheung
 Sadrul Habib Chowdhury
 Brian Chu
+Howard Chu
 Arturo Cisneros, Jr.
 Vincas Ciziunas
 Jonathan Clark
@@ -103,11 +131,14 @@
 Jeramey Crawford
 Michael Culbertson
 Steven Danna
+Simon Danner
 Chris Davies
 Josh Davis
 Martijn Dekker
 Florian Delizy
+Jiri Denemark
 Vinicius Depizzol
+Marc Dequènes
 Philip Derrin
 Taso N. Devetzis
 Balwinder Singh Dheeman
@@ -128,18 +159,23 @@
 Ignacio J. Elia
 Brian Enigma
 Mattias Eriksson
+Pat Erley
 Stefan Esser
 Steffen Eschenbacher
 Marc Etcheverry
 David Everly
 Larry Ewing
+Facebook, Inc.
+Fartash Faghri
 Gábor Farkas
 Jesse Farmer
 Gavan Fantom (gavan)
 Leonardo Fernandes
 David Fiander
+Ryan Flegel
 Rob Flynn <gaim@robflynn.com>
 Rob Foehl (rwf)
+Chris Foote
 Alan Ford
 Nathan Fredrickson
 Chris J. Friesen
@@ -156,11 +192,13 @@
 Ignacy Gawedzki
 Georgi Georgiev
 Brian Geppert
+Emanuele Giaquinta
 Thomas Gibson-Robinson
 Ike Gingerich
 Gustavo Giráldez
 Richard Gobeille
 Ian Goldberg
+Jon Goldberg
 Matthew Goldstein
 Michael Golden
 Charlie Gordon
@@ -197,6 +235,7 @@
 Andrew Hoffman
 Iain Holmes
 Joshua Honeycutt
+Jeffrey Honig
 Nigel Horne
 Jensen Hornick
 Juanjo Molinero Horno
@@ -212,6 +251,7 @@
 Instant Messaging Freedom, Inc.
 Vitaliy Ischenko
 Intel Corporation
+Andrew Ivanov
 Scott Jackson
 Hans Petter Jansson
 David Jedelsky
@@ -221,22 +261,26 @@
 Yuriy Kaminskiy
 Anders Kaseorg
 Praveen Karadakal
-Jaromír Karmazín
+Tomáš Kebert
 John Kelm
 Jochen Kemnade
 Yann Kerherve
 Gordian Klein
+Marten Klencke
 Krzysztof Klinikowski
 Akuke Kok
 Kir Kolyshkin
+Ivan Komarov
 F.W. Kong
 Konstantin Korikov
 Cole Kowalski
+Nikita Kozlov
 Matt Kramer
 Gary Kramlich
 Jan Kratochvil
 Andrej Krivulčík
 Patrik Kullman
+Sangeeta Kumari
 Tuomas Kuosmanen
 Tero Kuusela
 Richard Laager
@@ -246,6 +290,7 @@
 Joe LaPenna
 Steve Láposi
 Daniel Larsson
+Julia Lawall
 Peter Lawler
 Vadim Lebedev
 Ho-seok Lee
@@ -254,6 +299,7 @@
 Ambrose C. Li
 Nicolas Lichtmaier
 Wesley Lin
+Shaun Lindsay
 Artem Litvinovich
 Josh Littlefield
 Daniel Ljungborg
@@ -261,6 +307,8 @@
 Lokheed
 Norberto Lopes
 Shlomi Loubaton
+Pieter Loubser
+Brian Lu
 Uli Luckas
 Matthew Luckie
 Marcus Lundblad
@@ -272,17 +320,22 @@
 Paolo Maggi
 Sulabh Mahajan
 Willian T. Mahan
+Jonathan Maltz
+Rok Mandeljc
 Tobias Markmann
 Kris Marsh
 Fidel Martinez
 Lalo Martins
 John Matthews
 Simo Mattila
+Robert Matusewicz
 Michal Matyska
 Ryan McCabe
 Peter McCurdy
 Kurt McKee
+James McLaughlin
 Torrey McMahon
+Greg McNew
 Robert McQueen
 Mihály Mészáros
 Robert Mibus
@@ -292,7 +345,9 @@
 Paul Miller
 Arkadiusz Miskiewicz
 David Mohr
+Kartik Mohta
 Andrew Molloy
+Tomasz Mon
 Michael Monreal
 Laurent Montaron
 Marco Monteiro
@@ -300,8 +355,12 @@
 John Moody
 Tim Mooney
 Sergio Moretto
+Nader Morshed
+Keith Moyer
 Andrei Mozzhuhin
 Christian Muise
+MXit Lifestyle (Pty) Ltd.
+Alexander Nartov
 Richard Nelson
 Dennis Nezic
 Matthew A. Nicholson
@@ -319,6 +378,8 @@
 Gudmundur Bjarni Olafsson
 Bartosz Oler
 Oliver
+The openSUSE Project
+Jürgen Orschiedt
 Stefan Ott
 Shawn Outman
 Nathan Owens (pianocomp81)
@@ -330,14 +391,18 @@
 Riley Patterson
 Havoc Pennington
 Ted Percival
+Hugo Pereira Da Costa
 Eduardo Pérez
 Matt Perry
+Ani Peter
 Luke Petre
 Diego Petten
 Nathan Peterson
 Dmitry Petroff
 Sebastián E. Peyrott
+Amitakhya Phukan
 Andrea Piccinelli
+Mateusz Piękos
 Celso Pinto
 Joao Luís Marques Pinto
 Aleksander Piotrowski
@@ -346,8 +411,10 @@
 Eric Polino <aluink@gmail.com>
 Ari Pollak
 Stephen Pope
+Cristi Posoiu
 Nathan Poznick
 Jory A. Pratt
+David Preece
 Brent Priddy
 Justin Pryzby
 Florian Quèze
@@ -356,8 +423,14 @@
 Yosef Radchenko
 David Raeman
 R. Ramkumar
+Rajesh Ranjan
 Mart Raudsepp
 Etan Reisner
+David Reiss
+Luoh Ren-Shan
+Noa Resare
+Tim Retout
+Daniele Ricci
 Kristian Rietveld
 Pekka Riikonen
 Tim Ringenbach
@@ -369,6 +442,7 @@
 Luciano Miguel Ferreira Rocha
 Andrew Rodland
 Miguel Rodríguez (migrax)
+Adi Roiban
 Martin Rosinski
 Bob Rossi
 Jason Roth
@@ -390,11 +464,10 @@
 Carsten Schaar
 Toby Schaffer
 Jonathan Schleifer <js-pidgin@webkeks.org>
-Matteo Settenvini
-Colin Seymour
 Luke Schierer
 Ralph Schmieder
 David Schmitt
+Heiko Schmitt
 Mark Schneider
 Evan Schoenberg
 Gabriel Schulhof
@@ -404,6 +477,8 @@
 Peter Seebach
 Don Seiler
 Leonardo Serra
+Matteo Settenvini
+Colin Seymour
 Jim Seymour
 Javeed Shaikh
 Joe Shaw
@@ -441,6 +516,8 @@
 Marcus Sundberg
 Mårten Svantesson (fursten)
 Amir Szekely (kichik)
+Gábor Szuromi (kukkerman)
+Jakub Szypulka
 Robert T.
 Greg Taeger
 Rob Taft
@@ -470,17 +547,23 @@
 Kyle Turman
 Jon Turney
 Junichi Uekawa
+Max Ulidtko
+Dmitry Utkin
 Igor Vlasenko
 István Váradi
 Martijn van Beers
+Gideon van Melle
 Arjan van de Ven
 Philip Van Hoof
 Kristof Vansant
 James Vega
 David Vermeille
 Sid Vicious
+Andrew Victor
 Jorge Villaseñor (Masca)
 Bjoern Voigt
+Peter Volkov
+Marius Wachtler
 Wan Hing Wah
 Philip Walford
 Nathan Walp
@@ -488,6 +571,7 @@
 Eric Warmenhoven
 Adam J. Warrington
 Denis Washington
+Tomasz Wasilczyk
 Zsombor Welker
 Andrew Wellington
 Adam Wendt
@@ -496,8 +580,10 @@
 Zac West
 Daniel Westermann-Clark
 Andrew Whewell
+Stephen Whitmore
 Simon Wilkinson
 Dan Willemsen
+Dan Williams
 Justin Williams (Jaywalker)
 Jason Willis
 Alex Willmer
@@ -512,9 +598,12 @@
 Justin Wood
 Ximian
 Ma Xuan
+Yonas Yanfa
 Jared Yanovich
 Timmy Yee
 Li Yuan
+Yuriy Yevgrafov
+Jan Zachorowski
 Nickolai Zeldovich
 Tom Zickel
 Marco Ziech
--- a/ChangeLog	Wed Jun 13 19:28:57 2012 -0400
+++ b/ChangeLog	Wed Jun 13 19:30:27 2012 -0400
@@ -1,6 +1,1292 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.6.0 (??/??/2009):
+version 3.0.0 (??/??/????):
+	Finch:
+	* Support the conversation-extended signal for extending the
+	  conversation menu. (Howard Chu) (#14818)
+
+	AIM and ICQ:
+	* Make buddy list management code more efficient. (Oliver) (#4816)
+
+	Bonjour:
+	* Support file transfers up to ~9 EiB.
+
+	Gadu-Gadu:
+	* Possibility to require encryption. Also, using encryption when
+	  available is default option now. (Tomasz Wasilczyk)
+	* Show local time for incoming messages. (Tomasz Wasilczyk) (#4579)
+	* Fixed password change dialog and problems with connecting to accounts
+	  with non-ASCII passwords. (Tomasz Wasilczyk) (#14652)
+	* Option to show links from strangers. (Tomasz Wasilczyk) (#10591)
+	* Better handling of "invisible" and "chatty" statuses. (Tomasz
+	  Wasilczyk) (#13836)
+
+	MSN:
+	* Fix file transfer with older Mac MSN clients.
+	* Support file transfers up to ~9 EiB.
+	* Support new protocol version MSNP18. (#14753)
+	* Fix messages to offline contacts. (#14302)
+
+	MXit:
+	* Remove all reference to Hidden Number.
+	* Fix decoding of font-size changes in the markup of received messages.
+	* 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.
+
+	XMPP:
+	* Strip element prefixes from XHTML-IM messages as they're presented
+	  to the core (and UIs) as incoming messages (Thijs Alkemade).
+	  (#14529)
+	* Support file transfers up to ~9 EiB.
+
+	Plugins:
+	* The Voice/Video Settings plugin supports using the sndio GStreamer
+	 backends. (Brad Smith) (#14414)
+
+version 2.10.2 (02/22/2012):
+	General:
+	* Fix compilation when using binutils 2.22 and new GDK pixbuf. (#14799)
+	* Fix compilation of the MXit protocol plugin with GLib 2.31. (#14773)
+
+	Pidgin:
+	* Add support for the GNOME3 Network dialog. (#13882)
+	* Fix rare crash. (#14392)
+
+	libpurple:
+	* Support new connection states and signals for NetworkManager 0.9+.
+	  (Dan Williams) (#13859)
+
+	AIM and ICQ:
+	* Allow signing on with usernames containing periods and
+	  underscores. (#13500)
+	* Allow adding buddies containing periods and underscores. (#13500)
+	* Don't try to format ICQ usernames entered as email addresses.
+	  Gets rid of an "Unable to format username" error at login. (#13883)
+
+	MSN:
+	* Fix possible crashes caused by not validating incoming messages as
+	  UTF-8. (Thijs Alkemade) (#14884)
+
+	Windows-Specific Changes:
+	* Fix compilation of the Bonjour protocol plugin. (#14802)
+
+version 2.10.1 (12/06/2011):
+	Finch:
+	* Fix compilation on OpenBSD.
+
+	AIM and ICQ:
+	* Fix remotely-triggerable crashes by validating strings in a few
+	  messages related to buddy list management.  Thanks to Evgeny Boger
+	  for reporting this!  (#14682)
+
+	Bonjour:
+	* IPv6 fixes (Linus Lüssing)
+
+	Gadu-Gadu:
+	* Fix problems linking against GnuTLS. (#14544)
+
+	IRC:
+	* Fix a memory leak when admitting UTF-8 text with a non-UTF-8 primary
+	  encoding.  (#14700)
+
+	Jabber:
+	* Fix crashes and memory leaks when receiving malformed voice
+	  and video requests.  Thanks to Thijs Alkemade for reporting this!
+
+	Sametime:
+	* Separate "username" and "server" when adding new Sametime accounts.
+	  (#14608)
+	* Fix compilation in Visual C++. (#14608)
+
+	SILC:
+	* Fix CVE-2011-3594, by UTF-8 validating incoming messages before
+	  passing them to glib or libpurple.  Identified by Diego Bauche
+	  Madero from IOActive.  (#14636)
+
+	Yahoo!:
+	* Fetch buddy icons in some cases where we previously weren't. (#13050)
+
+	Windows-Specific Changes:
+	* Fix compilation
+
+version 2.10.0 (08/18/2011):
+	Pidgin:
+	* Make the max size of incoming smileys a pref instead of hardcoding it.
+	  (Quentin Brandon) (#5231)
+	* Added a plugin information dialog to show information for plugins
+	  that aren't otherwise visible in the plugins dialog.
+	* Fix building with GTK+ earlier than 2.14.0 (GTK+ 2.10 is still the
+	  minimum supported) (#14261)
+
+	libpurple:
+	* Fix a potential crash in the Log Reader plugin when reading QIP logs.
+	* Fix a large number of strcpy() and strcat() invocations to use
+	  strlcpy() and strlcat(), etc., forestalling an entire class of
+	  string buffer overrun bugs.
+	  (The Electronic Frontier Foundation, Dan Auerbach, Chris Palmer,
+	  Jacob Appelbaum)
+	* Change some filename manipulations in filectl.c to use MAXPATHLEN
+	  instead of arbitrary length constants.  (The Electronic Frontier
+	  Foundation, Dan Auerbach, Chris Palmer, Jacob Appelbaum)
+	* Fix endianness-related crash in NTLM authentication (Jon Goldberg)
+	  (#14163)
+
+	Gadu-Gadu:
+	* Fixed searching for buddies in public directory. (Tomasz Wasilczyk)
+	  (#5242)
+	* Better status message handling. (Tomasz Wasilczyk) (#14314)
+	* Merged two buddy blocking methods. (Tomasz Wasilczyk) (#5303)
+	* Fix building of the bundled libgadu library with older versions
+	  of GnuTLS. (patch plucked from upstream) (#14365)
+
+	ICQ:
+	* Fix crash selecting Tools->Set Mood when you're online with an
+	  ICQ account that is configured as an AIM account. (#14437)
+
+	IRC:
+	* Fix a crash when remote users have certain characters in their
+	  nicknames. (Discovered by Djego Ibanez) (#14341)
+	* Fix the handling of formatting following mIRC ^O (#14436)
+	* Fix crash when NAMES is empty. (James McLaughlin) (#14518)
+
+	MSN:
+	* Fix incorrect handling of HTTP 100 responses when using the HTTP
+	  connection method.  This can lead to a crash. (Discovered by Marius
+	  Wachtler)
+	* Fix seemingly random crashing. (#14307)
+	* Fix a crash when the account is disconnected at the time we are doing a
+	  SB request. (Hanzz, ported by shlomif) (#12431)
+
+	XMPP:
+	* Do not generate malformed XML ("</>") when setting an empty mood.
+	  (#14342)
+	* Fix the /join <room> behavior.  (Broken when adding support for
+	  <room>@<server>)  (#14205)
+
+	Yahoo!/Yahoo! JAPAN:
+	* Fix coming out of idle while in an unavailable state
+	* Fix logging into Yahoo! JAPAN.  (#14259)
+
+	Windows-Specific Changes:
+	* Open an explorer.exe window at the location of the file when clicking
+	  on a file link instead of executing the file, because executing a file
+	  can be potentially dangerous.  (Discovered by James Burton of
+	  Insomnia Security) (Fixed by Eion Robb)
+
+version 2.9.0 (06/23/2011):
+	Pidgin:
+	* Fix a potential remote denial-of-service bug related to displaying
+	  buddy icons.
+	* Significantly improved performance of larger IRC channels (regression
+	  introduced in 2.8.0).
+	* Fix Conversation->Add on AIM and MSN.
+	* Entries in the chat user list are sorted properly again.  This was
+	  inadvertenly broken in 2.8.0.
+
+	Finch:
+	* Fix logging in to ICQ.
+
+	libpurple:
+	* media: Actually use the specified TCP port from the TURN configuration to
+	  create a TCP relay candidate.
+
+	AIM and ICQ:
+	* Fix crashes on some non-mainstream OSes when attempting to
+	  printf("%s", NULL).  (Clemens Huebner) (#14297)
+
+	Plugins:
+	* The Evolution Integration plugin compiles again.
+
+version 2.8.0 (06/07/2011):
+	General:
+	* Implement simple silence suppression for voice calls, preventing
+	  wasted bandwidth for silent periods during a call. (Jakub Adam)
+	  (half of #13180)
+	* Added the DigiCert High Assurance CA-3 intermediate CA, needed for
+	  validation of the Facebook XMPP interface's certificate.
+	* Removed the QQ protocol plugin.  It hasn't worked in a long time and
+	  isn't being maintained, therefore we no longer want it.
+
+	Pidgin:
+	* Duplicate code cleanup.  (Gabriel Schulhof) (#10599)
+	* Voice/Video call window adapts correctly to adding or removing
+	  streams on the fly. (Jakub Adam) (half of #13535)
+	* Don't cancel an ongoing call when rejecting the addition of a
+	  stream to the existing call. (Jakub Adam) (#13537)
+	* Pidgin plugins can now override tab completion and detect clicks on
+	  usernames in the chat userlist. (kawaii.neko) (#12599)
+	* Fix the tooltip being destroyed when it is full of information and
+	  cover the mouse (dliang) (#10510)
+
+	libpurple:
+	* media: Allow obtaining active local and remote candidates. (Jakub
+	  Adam) (#11830)
+	* media: Allow getting/setting video capabilities. (Jakub Adam) (half
+	  of #13095)
+	* Simple Silence Suppression is optional per-account. (Jakub Adam)
+	  (half of #13180)
+	* Fix purple-url-handler being unable to find an account.
+	* media: Allow adding/removing streams on the fly. (Jakub Adam)
+	  (half of #13535)
+	* Support new connection states in NetworkManager 0.9. (Dan Williams)
+	  (#13505)
+	* When removing a buddy, delete the pounces associated with it.
+	  (Kartik Mohta) (#1131)
+	* media: Allow libpurple and plugins to set SDES properties for RTP
+	  conferences. (Jakub Adam) (#12981)
+	* proxy: Add new "Tor/Privacy" proxy type that can be used to
+	  restrict operations that could leak potentially sensitive data
+	  (e.g. DNS queries).  (#11110, #13928)
+	* media: Add support for using TCP relaying with TURN (will only work with
+	  libnice 0.1.0 and later).
+
+	AIM:
+	* Fix setting icons with dimensions greater than 64x64 pixels by scaling
+	  them down to at most 64x64. (#12874, #13165)
+
+	AIM:
+	* Fix setting icons with dimensions greater than 64x64 pixels by scaling
+	  them down to at most 64x64. (#12874, #13165)
+
+	Gadu-Gadu:
+	* Allow showing your status only to buddies. (Mateusz Piękos) (#13358)
+	* Updated internal libgadu to version 1.10.1. (Robert Matusewicz,
+	  Krzysztof Klinikowski) (#13525)
+	* Updated internal libgadu to version 1.11.0. (Tomasz Wasilczyk)
+	  (#14248)
+	* Suppress blank messages that happen when receiving inline
+	  images. (Tomasz Wasilczyk) (#13554)
+	* Fix sending inline images to remote users, don't crash when
+	  trying to send large (> 256kB) images. (Tomasz Wasilczyk) (#13580)
+	* Support typing notifications. (Jan Zachorowski, Tomasz Wasilczyk,
+	  Krzysztof Klinikowski) (#13362, #13590)
+	* Require libgadu 1.11.0 to avoid using internal libgadu.
+	* Optional SSL connection support for GNUTLS users (not on Windows
+	  yet!). (Tomasz Wasilczyk) (#13613, #13894)
+	* Don't count received messages or statuses when determining whether
+	  to send a keepalive packet. (Jan Zachorowski) (#13699)
+	* Fix a crash when receiving images on Windows or an incorrect
+	  timestamp in the log when receiving images on Linux. (Tomasz
+	  Wasilczyk) (#10268)
+	* Support XML events, resulting in immediate update of other users'
+	  buddy icons. (Tomasz Wasilczyk) (#13739)
+	* Accept poorly formatted URLs from other third-party clients in
+	  the same manner as the official client.  (Tomasz Wasilczyk)
+	  (#13886)
+
+	ICQ:
+	* Fix setting icons with dimensions greater than 64x64 pixels by scaling
+	  them down to at most 64x64. (#12874, #13165)
+	* Fix unsetting your mood when "None" is selected.  (Dustin Gathmann)
+	  (#11895)
+	* Ignore Daylight Saving Time when performing calculations related to
+	  birthdays. (Dustin Gathmann) (#13533)
+	* It is now possible to specify multiple encodings on the Advanced
+	  tab of an ICQ account's settings by using a comma-delimited list.
+	  (Dmitry Utkin) (#13496)
+
+	IRC:
+	* Add "authserv" service command.  (tomos) (#13337)
+
+	MSN:
+	* Fix a hard-to-exploit crash in the MSN protocol when using the
+	  HTTP connection method (Reported by Marius Wachtler).
+
+	MXit:
+	* Support for an Invite Message when adding a buddy.
+	* Fixed bug in splitting-up of messages that contain a lot of links.
+	* Fixed crash caused by timer not being disabled on disconnect.
+	  (introduced in 2.7.11)
+	* Clearing of the conversation window now works.
+	* When receiving an invite you can display the sender's profile
+	  information, avatar image, invite message.
+	* The Change PIN option was moved into separate action.
+	* New profile attributes added and shown.
+	* Update to protocol v6.3.
+	* Added the ability to view and invite your Suggested Friends,
+	  and to search for contacts.
+	* Also display the Status Message of offline contacts in their
+	  profile information.
+
+	XMPP:
+	* Remember the previously entered user directory when searching.
+	  (Keith Moyer) (#12451)
+	* Correctly handle a buddy's unsetting his/her vCard-based avatar.
+	  (Matthew W.S. Bell) (#13370)
+	* Squash one more situation that resulted in duplicate entries in
+	  the roster (this one where the server reports the buddy as being
+	  in the same (empty) group.  (Reported by Danny Mayer)
+
+	Plugins:
+	* The Voice/Video Settings plugin now includes the ability to test
+	  microphone settings. (Jakub Adam) (#13182)
+	* Fix a crash when handling some saved settings in the Voice/Video
+	  Settings plugin. (Pat Erley) (13290, #13774)
+
+	Windows-Specific Changes:
+	* Fix building libpurple with Visual C++ .NET 2005. This was
+	  accidentally broken in 2.7.11. (Florian Quèze)
+	* Build internal libgadu using packed structs, fixing several
+	  long-standing Gadu-Gadu issues. (#11958, #6297)
+
+version 2.7.11 (03/10/2011):
+	General:
+	* Our bundled libgadu should now build on HP-UX.
+	* Fix some instances of file transfers never completing. (Cristi Posoiu)
+	  (#12472)
+
+	Pidgin:
+	* Sort by Status no longer causes buddies to move around when you
+	  click them.
+	* Fix embedding in the system tray on older GTK+ releases (such as on
+	  CentOS 5.5 and older Fedora).
+	* No longer require libstartup-notification for startup notification
+	  support.  GTK+ has included support for years, so use it instead. (David
+	  Benjamin) (#13245)
+
+	AIM:
+	* Fix a bug where some buddies from your buddy list might not show up.
+	  Affected non-English ICQ users the most. (#13386)
+	* Send keepalives for all types of network connections.  Will hopefully
+	  make chat rooms more reliable. (#1449)
+
+	MSN:
+	* Fix bug that prevented added buddies to your buddy list in certain
+	  circumstances.  (#13298)
+
+	MXit:
+	* MXit plugin and reported client version now follow the libpurple
+	  version.
+	* Don't try to request profile information for non-user contacts.
+	* Allow Re-Invite for contacts in Deleted or Rejected state.
+	* Ensure we don't send packets too fast to the MXit server and trigger
+	  its flood-detection mechanism.  Also increased the internal packet queue
+	  to 32 packets.
+
+	XMPP:
+	* Fix building on platforms with an older glib (inadvertantly broken in
+	  2.7.10).  (#13329)
+	* Don't treat the on-join status storms as 'new arrivals'.  (Thijs
+	  Alkemade) (#a14527)
+	* Extend the /join command to support room JIDs, enabling you to join
+	  a room on any server.  (Solarius, Matěj Cepl, Tirtha 'wyuka'
+	  Chatterjee) (#4526)
+	* Add support for receiving a limited amount of history when joining a
+	  room (not currently supported by Pidgin and Finch).  (Thijs Alkemade)
+	  (#10986, #a14219)
+
+	Yahoo!/Yahoo! JAPAN:
+	* Fix CVE-2011-1091, denials of service caused by NULL pointer
+	  dereferences due to improper handling of malformed YMSG packets.  Thanks
+	  to Marius Wachtler for reporting this and reviewing the fix!
+
+version 2.7.10 (02/06/2011):
+	General:
+	* Force video sources to all have the same capabilities.  This reduces the
+	  number of times video must be scaled down, saving CPU time. (Jakub Adam)
+	  (half of #13095)
+	* Starting multiple video calls and ending one no longer causes the other
+	  calls to stop sending audio and video. (Jakub Adam) (#12758, #13237)
+	* Perl bindings now respect LDFLAGS. (Peter Volkov, Markos Chandras)
+	  (#12638)
+	* Added AddTrust External Root CA.  (#11554)
+	* Resolve some issues validating X.509 certificates signed off the CAcert
+	  Class 3 intermediate cert when using the GnuTLS SSL/TLS plugin.
+
+	Gadu-Gadu:
+	* Don't drop whole messages when text is colored. (Jan Zachorowski)
+	  (#13259)
+
+	Groupwise:
+	* Don't show two windows when using "Get Info" on a buddy. (Gabriel Burt;
+	  Novell, Inc.) (#13108)
+
+	IRC:
+	* Don't send ISON messages longer than 512 bytes. (Jeffrey Honig) (#9692)
+
+	libpurple:
+	* Stop sending audio when placing a call on hold. (Jakub Adam) (#13032)
+	* Stop translating gpointers to ints in the dbus API.  This removes
+	  functions from the dbus API.  (The openSUSE Project) (#12507)
+	* Fix D-Bus introspection calls that omit the interface parameter.  (Tom
+	  Samstag) (#13073)
+	* Fixed bugs in purple_str_to_time() that caused the most recent 'make
+	  check' failures.  (Nader Morshed) (#13131)
+	* Correct an issue that caused some UIs other than Pidgin or Finch to
+	  leave a buddy in the "is typing" state.  (Jan Kaluza)
+	* Fix potential information disclosure issues in the Cipher code.  (Julia
+	  Lawall)
+
+	Pidgin:
+	* Support using the Page Up and Page Down keys on the numeric keypad in
+	  the conversation window.  (Ryan Flegel) (#13127)
+	* Fix a few memory leaks. (Nader Morshed) (#13162)
+	* Support rendering strikethrough when received as in-line CSS. (#13168)
+	* Editable comboboxes are now more friendly to some GTK+ themes. (Hugo
+	  Pereira Da Costa) (#13164).
+
+	Plugins:
+	* The Voice/Video Settings plugin no longer resets selected devices to
+	  defaults. (Jakub Adam) (#13044)
+	* The Voice/Video Settings plugin no longer crashes when a stored device
+	  name is not found in the list of available devices. (Jakub Adam)
+	  (#13238)
+	* The Autoaccept plugin now allows disabling filename escaping. (Rok
+	  Mandeljc) (half of #11459)
+	* The Autoaccept plugin now allows choosing Reject/Ask/Accept for
+	  non-buddies. (Rok Mandeljc) (half of #11459)
+
+	QQ:
+	* QQ2008 is now the default protocol version. (Michael Terry) (#11635)
+
+	XMPP:
+	* Don't crash when receiving an unexpected/invalid jingle transport type.
+	  (Nikita Kozlov) (#13136)
+	* Handle Connection: Close headers for BOSH, when the server does not
+	  terminate the connection itself. (#13008)
+	* Improved parsing for DIGEST-MD5, which should resolve issues
+	  connecting to some jabberd2 servers.  This corrects an issue parsing
+	  one-character or empty elements. (Noa Resare) (#a14514)
+
+	Yahoo!/Yahoo! JAPAN:
+	* Fix a crash when an account disconnects before a p2p session is
+	  completely set up. (Jan Kaluza) (#12432)
+
+version 2.7.9 (12/26/2010):
+	MSN:
+	* Fix CVE-2010-4528, a crash when receiving short packets related to
+	  P2Pv2 messages.
+
+version 2.7.8 (12/19/2010):
+	General:
+	* Fix the exceptions in purple-remote on Python 2.6+. (Ari Pollak)
+	  (#12151)
+
+	Pidgin:
+	* When a conversation has reached the maximum limit on the number
+	  of smileys, display the text representation of the smiley properly
+	  when it contains HTML-escapable characters (e.g. "<3" was previously
+	  displayed as "&lt;3").
+	* Drop dependency on GdkGC and use Cairo instead.
+	* New UI hack to assist in first-time setup of Facebook accounts with
+	  icon from Jakub Szypulka.
+	* Don't hide the buddy list if there is no notification area in which
+	  to put the icon. (#12129)
+
+	libpurple:
+	* Fix multipart parsing when '=' is included in the boundary for
+	  purple_mime_document_parse. (Jakub Adam) (#11598)
+
+	AIM and ICQ:
+	* Buddies who unset their status message will now be correctly shown
+	  without a message in your buddy list. (#12988)
+
+	Gadu-Gadu:
+	* Updated our bundled libgadu and minimum requirement for external
+	  libgadu to 1.9.0. (#12789)
+
+	MSN:
+	* Stop showing ourselves in the list of endpoints that can be
+	  disconnected.
+	* Allow full-size display names, by not escaping (most) non-English
+	  characters. (#8508)
+	* Fix receiving messages from users on Yahoo and other federated
+	  services. (#13022)
+	* Correctly remove old endpoints from the list when they sign out.
+	* Add option to disable connections from multiple locations. (#13017)
+	* Correctly update your own display name in the buddy list. (#13064) 
+	* Correctly show ourselves as offline in the buddy list when going
+	  invisible. (#12945)
+	* Correctly update your own icon in the buddy list. (#12973)
+	* Remove struct packing for better portability. (#12856)
+
+	XMPP:
+	* Terminate Jingle sessions with unsupported content types. (#13048)
+
+version 2.7.7 (11/23/2010):
+	General:
+	* Allow multiple CA certificates to share the same Distinguished Name
+	  (DN).  Partially fixes remaining MSN issues from #12906.
+	* The GNUTLS SSL plugin now discards any certificate (and all subsequent
+	  certificates) in a chain if it did not sign the previous certificate.
+	  Partially fixes remaining MSN issues from #12906.
+	* Open requests related to a file transfer are now closed when the request
+	  is cancelled locally. (#11666)
+
+	AIM and ICQ:
+	* AIM should now connect if "Use clientLogin" is turned off and the
+	  "Server" field is set to anything other than "login.oscar.aol.com" or
+	  "slogin.oscar.aol.com". (#12948)
+	* Fix a crash on connection loss. (#5927)
+
+version 2.7.6 (11/21/2010):
+	General:
+	* Included Microsoft Internet Authority 2010 and Microsoft Secure Server
+	  Authority 2010 intermediate CA certificates to our bundle.  This fixes
+	  the "Unable to validate certificate" error for omega.contacts.msn.com.
+	  (#12906)
+
+	Pidgin:
+	* Avoid a use-after-free race condition in the media code (when
+	  there's an error reported by GStreamer). (#12806, Jakub Adam)
+
+	AIM and ICQ:
+	* SSL option has been changed to a tri-state menu with choices for
+	  "Don't Use Encryption", "Use Encryption if Available", and "Require
+	  Encryption".
+	* Fix some possible clientLogin URL issues introduced in version 2.7.5.
+	* Don't show a "<URL>: Ok" connection error when using clientLogin.
+	* Cleaned up some debug output for improved readability.
+
+	MSN:
+	* Added support for MSNP16, including Multiple Points of Presence (MPOP)
+	  which allows multiple simultaneous sign-ins. (#8247)
+	* Added extended capabilities support (none implemented).
+	* Merged the work done on the Google SoC (major rewrite of SLP code)
+	* Reworked the data transfer architecture.
+	  (http://developer.pidgin.im/wiki/SlpArchitecture)
+	* Lots of little changes.
+	* Don't process zero-length DC messages. (#12660)
+	* Fixed a bunch of memory leaks.
+	* Prevent a use-after-free condition.
+
+	XMPP:
+	* Avoid a double-free in the Google Relay (V/V) code.
+	* Avoid double error message when failing a file transfer. (#12757)
+	* Password-related information is printed out for SASL authentication
+	  when the PURPLE_UNSAFE_DEBUG environment variable is set.
+	* Authentication mechanisms can now be added by UI's or other plugins
+	  with some work. This is outside the API/ABI rules! (#12715)
+	* Fixed a few printf("%s", NULL) crashes for broken OSes.
+
+	Windows-Specific Changes:
+	* Build the Pidgin Theme Editor plugin (finally).
+	* Untarring (for themes) now works for non-ASCII destination paths.
+
+version 2.7.5 (10/31/2010):
+	General:
+	* Added Verisign Class 3 Public CA - G2 root CA.
+
+	Pidgin:
+	* Properly differentiate between bn and bn_IN in the Translation
+	  Information dialog.
+
+	AIM and/or ICQ:
+	* Display the "Authorize buddy?" minidialog when the requestor has an
+	  empty nickname. (#12810)
+	* New ICQ accounts default to proper ICQ servers.  Old accounts using one
+	  of the old default servers will be silently migrated to use the proper
+	  servers.
+	* ICQ accounts using clientLogin now use the correct ICQ servers.  This is
+	  separate from the server settings mentioned above.
+	* '<' should no longer cause ICQ status messages to be truncated in some
+	  locations. (#11964, #12593)
+	* Fix sending messages to chat rooms. (#12768)
+
+	Bonjour:
+	* Don't crash when attempting to log into a Bonjour account and init
+	  failed.
+
+	Windows-Specific Changes:
+	* Quote the path stored in the registry when the "run at startup" option
+	  in the Windows Pidgin Options plugin is used. (#12781)
+
+version 2.7.4 (10/20/2010):
+	General:
+	* Fix search path for Tk when compiling on Debian Squeeze. (#12465)
+	* purple-remote now expects and produces UTF-8. (Guillaume Brunerie)
+	  (#12049)
+	* Add Deutsche Telekom, Thawte Primary, and Go Daddy Class 2 root CAs
+	  (#12667, #12668, and #12594)
+	* Fix CVE-2010-3711 by properly validating return values from the
+	  purple_base64_decode() function before using them.
+	* Fix two local crash bugs by properly validating return values from the
+	  purple_base16_decode() function before using them.
+
+	libpurple:
+	* Fall back to an ordinary request if a UI does not support showing a
+	  request with an icon.  Fixes receiving MSN file transfer requests
+	  including a thumbnail in Finch. (#12561)
+	* Fix an invalid memory access when removing UPnP mappings that could
+	  cause sporadic crashes, most notably when MSN Direct Connections are
+	  enabled. (#12387)
+	* Add a sentence to the certificate warning for expired certificates
+	  suggesting the user check their computer's date and time. (#12654)
+
+	Pidgin:
+	* Add support for the Gadu-Gadu protocol in the gevolution plugin to
+	  provide Evolution integration with contacts with GG IDs. (#10709)
+	* Remap the "Set User Mood" shortcut to Control-D, which does not
+	  conflict with the previous shortcut for Get Buddy Info on the
+	  selected buddy.
+	* Add a plugin action menu (under Tools) for the Voice and Video
+	  Settings plugin.
+	* Use GRegex for the debug window where available. This brings regex
+	  filtering to the debug window on Windows. (Eion Robb) (#12601)
+	* Add Google Chrome to the list of possible browsers on non-Windows
+	  systems.
+	* Add Chromium to the list of possible browsers on non-Windows systems.
+	* The "Manual" browser option is now stored as a string.  It is no
+	  longer necessary to specify a full path to the browser command.
+	  (Rodrigo Tobar Carrizo) (#12024)
+	* The Send To menu can now be used if the active account in the
+	  conversation becomes disabled or inactive. (Keith Moyer) (#12471)
+	* xdg-open is now the default browser for new users on non-Windows
+	  platforms. (Stanislav Brabec) (#12505)
+	* The "Authorize buddy?" mini-dialog now shows the nickname of
+	  the buddy requesting authorization as well as the icon of
+	  the IM protocol he is using. (#5038)
+
+	Finch:
+	* Add support for drop-down account options (like the SILC cipher
+	  and HMAC options or the QQ protocol version).
+
+	XMPP:
+	* Unify the connection security-related settings into one dropdown.
+	* Fix a crash when multiple accounts are simultaneously performing
+	  SASL authentication when built with Cyrus SASL support.  (thanks
+	  to Jan Kaluza) (#11560)
+	* Restore the ability to connect to XMPP servers that do not offer
+	  Stream ID. (#12331)
+	* Added support for using Google's relay servers when making voice and
+	  video calls to Google clients.
+	* Fix detecting file transfer proxies advertised by the server.
+	* Advertise support for Google Talk's JID Domain Discovery extension
+	  in all cases again (changed in 2.7.0), not just when the domain
+	  is "gmail.com" or "googlemail.com" (it's also needed for Google
+	  Talk used for accounts on arbitrary domains not using Google Apps
+	  for Your Domain). (#a14153)
+	* Improved handling of adding oneself to your buddy list when using
+	  Non-SASL (legacy) authentication. (#12499)
+	* Generate a connection error instead of just stalling when the
+	  _xmppconnect TXT record returns results, but none of them result
+	  in a valid BOSH URI. (#a14367, #12744)
+
+	AIM and ICQ:
+	* Add support for managing Visible/Invisible lists. (#10967)
+	* Fix a problem with receiving HTML messages from
+	  QIP/Miranda/Trillian. (#12044)
+	* Hopefully fixed all encoding-related problems, both
+	  for sending and receiving messages. (#10833 and the like)
+	* Fix a problem with receiving messages from pyicqt. (#12284)
+	* Don't set a custom status text when going Invisible to avoid
+	  being detected as Invisible. (#10633)
+
+	Yahoo/Yahoo JAPAN:
+	* Stop doing unnecessary lookups of certain alias information.  This
+	  solves deadlocks when a given Yahoo account has a ridiculously large
+	  (>500 buddies) list and may improve login speed for those on slow
+	  connections. (#12532)
+	* Fix sending SMS messages.  The lookup host changed on us.  (Thanks to
+	  todo) (#12688).
+	* Improvements for some file transfer scenarios, but not all.
+
+	Windows:
+	* Bonjour support now requires Apple Bonjour Print Services version
+	  2.0.0 or newer (http://support.apple.com/kb/dl999).
+
+	libpurple:
+	* Fall back to an ordinary request if a UI does not support showing a
+	  request with an icon.  Fixes receiving MSN file transfer requests
+	  including a thumbnail in Finch.
+
+	Pidgin:
+	* Add support for the Gadu-Gadu protocol in the gevolution plugin to
+	  provide Evolution integration with contacts with GG IDs. (#10709)
+	* Remap the "Set User Mood" shortcut to Control-D, which does not
+	  conflict with the previous shortcut for Get Buddy Info on the
+	  selected buddy.
+	* Add a plugin action menu (under Tools) for the Voice and Video
+	  Settings plugin.
+
+	Finch:
+	* Add support for drop-down account options (like the SILC cipher
+	  and HMAC options or the QQ protocol version).
+
+	XMPP:
+	* Unify the connection security-related settings into one dropdown.
+	* Fix a crash when multiple accounts are simultaneously performing
+	  SASL authentication when built with Cyrus SASL support.  (thanks
+	  to Jan Kaluza) (#11560)
+	* Restore the ability to connect to XMPP servers that do not offer
+	  Stream ID. (#12331)
+	* Added support for using Google's relay servers when making voice and
+	  video calls to Google clients.
+
+	Yahoo/Yahoo JAPAN:
+	* Stop doing unnecessary lookups of certain alias information.  This
+	  solves deadlocks when a given Yahoo account has a ridiculously large
+	  (>500 buddies) list and may improve login speed for those on slow
+	  connections. (#12532)
+
+version 2.7.3 (08/10/2010):
+	General:
+	* Use silent build rules for automake >1.11. You can enable verbose
+	  builds with the --disable-silent-rules configure option, or using
+	  make V=1.
+
+	libpurple:
+	* Fix the TURN server settings (broken in 2.7.0).
+
+	Pidgin:
+	* Re-focus the input area after clicking the attention toolbar button.
+	* Re-arrange media window to make it more netbook-friendly.
+
+	Finch:
+	* Rebindable 'suggest-next-page' and 'suggest-prev-page' actions for
+	  textboxes (GntEntry) to scroll through list of suggestions.
+	* Rebindable 'dropdown' action for comboboxes (GntComboBox) to show the
+	  dropdown list of options.
+
+	IRC:
+	* Fix non-ASCII arguments to /mode et al.  (thanks to Max Ulidtko)
+
+	MSN:
+	* Support for web-based buddy icons, used when a buddy logs in to the
+	  messenger on the Live website.
+	* Fix file transfers with some clients that don't support direct
+	  connections (e.g., papyon, telepathy-butterfly, etc.) (#12150)
+
+	MXit:
+	* Fix filename for the Shocked emoticon. (#12364)
+	* Implement the new naming conventions where possible. (MXitId, etc)
+	* Display a message in the Groupchat window when you invite somebody.
+	* Birthday field in profile cannot be edited when server says it is
+	  locked.
+	* If a buddy is offline, show in their profile when last they were online.
+	* Handle pushed profile update packets (ie, when changing your avatar via
+	  the Gallery bot).
+	* If a buddy is offline and we see from their profile that they have
+	  updated their avatar, request the new avatar image from the server.
+	* Fix a possible crash if a link is clicked while disconnected.
+	* Unescape any escaped characters in a chatroom nickname.
+	* Add the new MXit moods and emoticons.
+	* MXit emoticons added to the small emoticon theme.
+
+	XMPP:
+	* Allow connecting to servers that only advertise GSSAPI and expect
+	  a fallback to legacy IQ authentication (broken in 2.7.0).
+	* Fix a crash when receiving custom emoticons that don't adhere to
+	  the specification.
+	* When initiating a file transfer, don't show resources that are certain
+	  to not support file transfers in the resource selection dialog.
+	* Fix connecting to servers using BOSH and authenticating with
+	  DIGEST-MD5 when libpurple was built with Cyrus SASL support.
+
+	Yahoo/Yahoo JAPAN:
+	* Renamed "Use account proxy for SSL connections" to "Use account proxy
+	  for HTTP and HTTPS requests" and tied the option to HTTP requests too.
+	* Properly detect HTTP proxy server use when the HTTP proxy is the
+	  global proxy server, an account-level non-HTTP proxy server is
+	  configured, and the "Use account proxy for HTTP and HTTPS requests"
+	  account option is turned off.  This fixes connecting for some HTTP
+	  proxy servers.
+	* Fall back to connecting to scsa.msg.yahoo.com (not configurable) if
+	  the HTTP-based connect server lookup fails.  This does not work for
+	  Yahoo JAPAN accounts.
+	* Fix file transfers that get stuck with "Waiting for transfer to
+	  begin".
+
+version 2.7.2 (07/21/2010):
+	AIM and ICQ:
+	* Fix a crash bug related to X-Status messages that can be triggered by
+	  remote users.  This is CVE-2010-2528.
+	* Fix a rare crash bug caused by certain incoming SMS messages
+	  (discovered by Jan Kaluza--thanks Jan!).
+	* Change HTML sent from ICQ accounts so that official ICQ clients
+	  hopefully display it correctly.
+
+	MSN:
+	* Fix a crash related to fast buddy icon transfers.
+
+version 2.7.1 (05/29/2010):
+	General:
+	* Build fixes on OpenSolaris.  (Brian Lu)
+	* Add configure option --enable-trayicon-compat which installs tray
+	  icons into directories that are compatible with older versions of
+	  hicolor-icon-theme (0.9).
+
+	Pidgin:
+	* Restore the tray icon's blinking functionality.
+	* Fix a crash setting moods when an account is disconnected.
+
+	Bonjour:
+	* Fix a crash on disconnect.
+
+	ICQ:
+	* Fix bug that caused HTML to be displayed in incoming messages.
+
+	MSN:
+	* Fix unnecessary bandwidth consumption for buddy icon requests when
+	  buddies have capital letters in their passport addresses.
+	* Support for direct connections, enabling faster file transfers,
+	  smiley and buddy icon loading.  (Gábor Szuromi)
+
+	XMPP:
+	* Allow connecting to servers that advertise EXTERNAL (broken in
+	  2.7.0)
+
+	MXit:
+	* Replace the MXit-specific mood management with the new standard Moods
+	  API.
+	* Add the standard MXit emoticons.
+	* Improve the handling of users being kicked from MultiMX rooms.
+	* MXit doesn't allow you to see your buddy's Email Address or Title,
+	  so remove those two fields from the "Buddy Information" page.
+	* Show buddy's Registration Country in their profile.
+	* Increment protocol version to v6.0
+	* If an invite you sent was rejected with a reason, display that
+	  message in the buddy tooltip.
+	* CAPTCHA value is a required field during account activation.
+	  (Resolves issue on Maemo)
+	* When your avatar image is changed, don't forget the user's profile
+	  information.
+
+	Windows-Specific Changes:
+	* Fix a regression introduced in 2.7.0 that caused Window Flashing not
+	  to work.
+
+version 2.7.0 (05/12/2010):
+	General:
+	* Changed GTK+ minimum version requirement to 2.10.0.
+	* Changed GLib minimum version requirement to 2.12.0.
+	* Using the --disable-nls argument to configure now works properly.
+	  You will no longer be forced to have intltool to configure and build.
+	* Fix two related crashes in the GnuTLS and NSS plugins when they
+	  suffer internal errors immediately upon attempting to establish
+	  an SSL connection.
+	* Fix NSS to work when reinitialized after being used.  (Thanks to
+	  Ludovico Cavedon for the testcase)
+	* Added support for PURPLE_GNUTLS_PRIORITIES environment variable.
+	  This can be used to specify GnuTLS priorities on a per-host basis.
+	  The format is "host=priority;host2=priority;...".  The default
+	  priority can be overridden by using "*" as the host.  See the
+	  GnuTLS manual for documentation on the format of the priority
+	  strings.
+	* Fix autoconf detection of Python.  (Brad Smith)
+	* Fix a crash when a Windows proxy (from IE) does not have a port.
+	  (Marten Klencke)
+
+	Pidgin:
+	* Moved the "Debugging Information" section of the About box to a
+	  "Build Information" dialog accessible on the Help menu.
+	* Moved the Developer and Crazy Patch Writer information from the About
+	  box to a "Developer Information" dialog accessible on the Help menu.
+	* Moved the Translator information from the About box to a "Translator
+	  Information" dialog accessible on the Help menu.
+	* Use GtkStatusIcon for the docklet, providing better integration in
+	  notification area.
+	* Added UI for sending attentions (buzz, nudge) on supporting protocols.
+	* Make the search dialog unobtrusive in the conversation window (by
+	  making it look and behave like the search dialog in Firefox)
+	* The Recent Log Activity sort method for the Buddy List now
+	  distinguishes between no activity and a small amount of activity
+	  in the distant past.  (Greg McNew)
+	* Added a menu set mood globally for all mood-supporting accounts
+	  (currently XMPP and ICQ).
+	* Default binding of Ctrl+Shift+v to 'Paste as Plain Text' in
+	  conversation windows. This can be changed in .gtkrc-2.0. For example,
+	  Ctrl+v can be bound to 'Paste as Plain Text' by default.
+	* Plugins can now handle markup in buddy names by attaching to the
+	  "drawing-buddy" signal. (Daniele Ricci, Andrea Piccinelli)
+	* Be more accommodating when scaling down large images for use as
+	  buddy icons.
+	* The 'Message Timestamp Formats' plugin allows changing the timestamp
+	  format from the timestamps' context menu in conversation log.
+	* The 'Message Timestamp Formats' plugin allows forcing 12-hour
+	  timestamps.  (Jonathan Maltz)
+	* Fix pastes from Chrome (rich-text pastes and probably URLs
+	  having garbage appended to them).
+	* Show file transfer thumbnails for images on supporting protocols
+	  (currently only supported on MSN).
+
+	Bonjour:
+	* Added support for IPv6. (Thanks to T_X for testing)
+
+	Gadu-Gadu:
+	* Updated our bundled libgadu to 1.9.0-rc2 (many thanks to Krzysztof
+	  Klinikowski for the work and testing put in here!)
+	* Minimum requirement for external libgadu is now also 1.9.0-rc2.
+
+	AIM and ICQ:
+	* X-Status (Custom ICQ status icon) support.  Since most of the icons
+	  available reflect moods, this is labeled "Set Mood" on the
+	  Accounts->ICQ Account menu. (Andrew Ivanov, Tomáš Kebert,
+	  Yuriy Yevgrafov, and trac users bob007, salieff, and nops)
+	* Allow setting and displaying icons between 1x1 and 100x100 pixels for
+	  ICQ.  Previously only icons between 48x48 and 52x64 were allowed.
+	* When using the clientLogin authentication method, prompt for a
+	  password on reconnect when "Remember Password" is not checked and
+	  authentication fails due to an incorrect password.  (This is the same
+	  behavior as the legacy authentication method)
+	* Support sending and receiving HTML-formatted messages for ICQ.
+	* Use the proper URL for "View web profile" link for ICQ buddies.
+	  (Alexander Nartov)
+
+	MSN:
+	* Support for version 9 of the MSN protocol has been removed.  This
+	  version is no longer supported on the servers.
+	* Support file transfer thumbnails (previews) for images.
+	* Fix CVE-2010-1624 (custom emoticon remote crash).
+
+	XMPP:
+	* Direct messages to a specific resource only upon receipt of a message
+	  with content (as opposed to a typing notification, etc).  (Thanks to
+	  rjoly for testing)
+	* Present a better error message when authentication fails while trying
+	  to connect to Facebook.  (David Reiss, Facebook)
+	* When sending data using in-band-bytestreams, interpret the block-size
+	  attribute as the size of the BASE64-encoded representation of the
+	  data.
+	* Validate the hash on incoming BoB data objects (for custom smileys
+	  etc.), cache based per JID when the CID is not a valid hash (as
+	  specified by the BoB XEP).
+	* Send whitespace keepalives if we haven't sent data in a while (2
+	  minutes).  This fixes an issue with Openfire disconnecting a
+	  libpurple-baesd client that has just been quiet for about 6
+	  minutes.
+	* Only support Google Talk's JID Domain Discovery extension
+	  (allowing a user to log in with "@gmail.com" or "@googlemail.com"
+	  interchangeably) for those two domains.  This change was made
+	  due to interoperability issues with some BOSH Connection Managers
+	  and namespaced attributes.
+
+	Yahoo/Yahoo JAPAN:
+	* Attempt to better handle transparent proxies interfering with
+	  HTTP-based login.
+	* Fix handling of P2P packets, thus fixing the loss of some messages.
+	* Retrieve the pager server address from Yahoo!'s servers directly.
+	* Removed the "Pager server" account option, as it is no longer needed.
+	* The authentication code is now less order-sensitive with the
+	  components of the server's response.
+	* The authentication process now acts more like the official client.
+
+	Finch:
+	* New action 'history-search', with default binding ctrl+r, to search
+	  the entered string in the input history.
+
+	Windows-Specific Changes
+	* Updated GTK+ to 2.16.6
+	* Private GTK+ Runtime now used (GTK+ Installer no longer supported)
+	* Minimum required GTK+ version increased to 2.14.7
+	* Windows 95, Windows 98, Windows 98 Second Edition, Windows ME
+	  (Millennium Edition), and Windows NT 4.0 longer supported due to GTK+
+	  requirements changes.
+	* Crash Report files (pidgin.RPT) are now generated in the ~/.purple
+	  directory instead of the installation directory.
+	* NSS SSL Library upgraded to 3.12.5 (thanks to Berke Viktor)
+	* GtkSpell upgraded to 2.0.16, changing the spellchecking backend to
+	  enchant.  This means that myspell and hunspell (OpenOffice)
+	  dictionaries can be used (previous versions' aspell dictionaries
+	  will not work).
+
+version 2.6.6 (02/18/2010):
+	libpurple:
+	* Fix 'make check' on OS X. (David Fang)
+	* Fix a quirk in purple_markup_html_to_xhtml that caused some messages
+	  to be improperly converted to XHTML.
+	* Set "controlling-mode" correctly when initializing a media session.
+	  Fixes receiving voice calls from Psi.
+	* When looking up DNS records, use the type of record returned by the
+	  server (instead of the type we asked for) to determine how to process
+	  the record.
+	* Fix an issue with parsing XML attributes that contain "&lt;br&gt;".
+	  See ChangeLog.API for more details.
+
+	General:
+	* Correctly disable all missing dependencies when using the
+	  --disable-missing-dependencies option.  (Gabriel Schulhof)
+
+	Gadu-Gadu:
+	* Fix display of avatars after a server-side change. (Krzysztof
+	  Klinikowski)
+
+	AIM:
+	* Allow setting and displaying icons between 1x1 and 100x100 pixels.
+	  Previously only icons between 48x48 and 50x50 were allowed.
+
+	MSN:
+	* Fix CVE-2010-0277, a possible remote crash when parsing an incoming
+	  SLP message.  (Discovered by Fabian Yamaguchi)
+	* File transfer requests will no longer cause a crash if you delete the
+	  file before the other side accepts.
+	* Received files will no longer hold an extra lock after completion,
+	  meaning they can be moved or deleted without complaints from your OS.
+	* Buddies who sign in from a second location will no longer cause an
+	  unnecessary chat window to open.
+	* Support setting an animated GIF as a buddy icon.
+	* Numerous code cleanups and memory savings.
+
+	MySpace:
+	* Fix a leak and crash when retrieving buddy icons.
+
+	XMPP:
+	* Less likely to send messages to a contact's idle/inactive resource.
+	  Previously, if a message was received from a specific resource,
+	  responses would be sent to that resource until either it went offline
+	  or a message is received from another resource.  Now, messages are
+	  sent to the bare JID upon receipt of any presence change from the
+	  contact.
+	* Added support for the SCRAM-SHA-1 SASL mechanism.  This is only
+	  available when built without Cyrus SASL support.
+	* When getting info on a domain-only (server) JID, show uptime
+	  (when given by the result of the "last query") and don't show status
+	  as offline.
+	* Fix getting info on your own JID.
+	* Wrap XHTML messages in <p>, as described in XEP-0071, for
+	  compatibility with some clients.
+	* Don't do an SRV lookup for a STUN server associated with the account
+	  if one is already set globally in prefs.
+	* Don't send custom smileys larger than the recommended maximum object
+	  size specified in the BoB XEP.   This prevents a client from being
+	  disconnected by servers that dislike overly-large stanzas.
+	* Fix receiving messages without markup over an Openfire BOSH
+	  connection (forcibly put the stanzas in the jabber:client namespace).
+	* The default value for the file transfer proxies is automatically
+	  updated when an account connects, if it is still the old (broken)
+	  default (from 'proxy.jabber.org' to 'proxy.eu.jabber.org').
+	* Fix an issue where libpurple created duplicate buddies if the roster
+	  contains a buddy in two groups that differ only by case
+	  (e.g. "XMPP" and "xmpp") (or not at all).
+
+	Yahoo:
+	* Don't send <span> and </span> tags.  (Fartash Faghri)
+	* Support PingBox.  PingBoxes will appear as pbx/PingBoxName.  (Kartik
+	  Mohta)
+
+	Pidgin:
+	* Fix CVE-2010-0423, a denial of service attack due to the parsing
+	  of large numbers of smileys.  (Discovered by Antti Hayrynen)
+	* Correctly size conversation and status box entries when the
+	  interior-focus style property is diabled. (Gabriel Schulhof)
+	* Correctly handle a multiline text field being required in a
+	  request form.  (Thanks to Florian Zeitz for finding this problem)
+	* Search friends by email-addresses in the buddy list. (Luoh Ren-Shan)
+	* Allow dropping an image on Custom Smiley window to add a new one.
+	* Prompt for confirmation when clearing a whiteboard (doodle) session.
+	  (Kartik Mohta)
+	* Use the "hand" cursor when hovering over usernames in chat history to
+	  indicate that the username is an actionable item.
+	* Double-clicking usernames in chat history will open an IM with that
+	  user.
+	* Put an icon on the "Filter" button in the debug window.
+	* Don't treat "/messages/like/this " as commands.
+	* Explicitly mark user interaction when inserting smilies from the
+	  toolbar so "Undo" correctly removes these smilies.
+	* Clicking "New" or "Saved" in the status selector menu while typing a
+	  status message no longer keeps the status entry area stuck in "typing"
+	  mode forever.
+	* Show tooltips for ellipsized conversation tabs.  On older systems,
+	  tooltips will show for all tabs.
+	* The File Transfers and Debug Window windows are no longer created as
+	  dialogs.  These windows should now have minimize buttons in many
+	  environments in which they were previously missing
+	  (including Windows).
+	* Smiley themes with Windows line endings no longer cause theme
+	  descriptions not to be displayed in the theme selector.
+
+	Finch:
+	* Fix CVE-2010-0420, a possible remote crash when handling chat room
+	  buddy names.
+	* Rebindable 'move-first' and 'move-last' actions for tree widgets. So
+	  it is possible to jump to the first or last entry in the buddy list
+	  (and other such lists) by pressing home or end key (defaults)
+	  respectively.
+
+version 2.6.5 (01/08/2010):
+	libpurple:
+	* TLS certificates are actually stored to the local cache once again
+	  (accepting a name mismatch on a certificate should now be remembered)
+
+	General:
+	* Build-time fixes for Solaris.  (Paul Townsend)
+
+	AIM and ICQ:
+	* Messages from some mobile clients are no longer displayed as
+	  Chinese characters (broken in 2.6.4)
+
+	MSN:
+	* Fix an issue allowing a remote user to download arbitrary files from
+	  a libpurple client.  (CVE-2010-0013)
+
+	XMPP:
+	* Do not crash when attempting to register for a new account on Windows.
+	* Fix file transfer with clients that do not support Entity Capabilities
+	  (e.g. Spark)
+
+version 2.6.4 (11/29/2009):
+	libpurple:
+	* Actually emit the hold signal for media calls.
+	* Fix building the GnuTLS plugin with older versions of GnuTLS.
+	* Fix DNS TXT query resolution.
+	* Don't send Proxy-Authorization headers to HTTP proxy servers until
+	  we've received a "407 Proxy Authentication Required" response from
+	  the server.  (thecrux)
+	* Added "MXit" protocol plugin, supported and maintained by the MXit
+	  folks themselves (MXit Lifestyle (Pty) Ltd.)
+
+	General:
+	* New 'plugins' sub-command to 'debug' command (i.e. '/debug plugins')
+	  to announce the list of loaded plugins (in both Finch and Pidgin).
+	* Always rejoin open chats after an account reconnects.
+
+	AIM and ICQ:
+	* Better rate limit calculations and other improvements.  (Aman Gupta)
+	* More detailed error messages when messages fail to send.  (Aman Gupta)
+	* The simultaneous login account option is respected when using
+	  the clientLogin authentication method.
+	* Fix offline message retrieval (broken in 2.6.3)
+	* Fix handling of markup on some messages (broken in 2.6.2)
+	* Fix SSL when clientLogin is enabled.
+	* Fix sending and receiving Unicode characters in a Direct IM
+
+	MSN:
+	* Don't forget display names for buddies.
+	* Fix a random crash that might occur when idle.
+	* Fix more FQY 240 connection errors.
+	* Fix a crash that could occur when adding a buddy.
+	* Fix an occasional crash when sending message to an offline user.
+	* Fix a random crash that might occur when idle.
+	* Fix a crash when logging in with some long non-ASCII passwords.
+	  (Shaun Lindsay)
+	* Cache our own friendly name as the server no longer does that for
+	  us.  Users of older versions may need to re-set their friendly name
+	  as it has probably been reset.
+
+	XMPP:
+	* Users connecting to Google Talk now have an "Initiate Chat" context
+	  menu option for their buddies.  (Eion Robb)
+	* Fix a crash when attempting to validate an invalid JID.
+	* Resolve an issue when connecting to iChat Server when no resource
+	  is specified.
+	* Try to automatically find a STUN server by using an SRV lookup on the
+	  account's domain, and use that for voice and video if found and the
+	  user didn't set one manually in prefs.
+	* Fix a crash when adding a buddy without an '@'.
+	* Don't show the option to send a file to a buddy if we know for certain
+	  they don't support any file transfer method supported by libpurple.
+	* Keep the avatar on the server if one is not set locally.
+
+	Yahoo:
+	* Fix sending /buzz.
+	* Fix blocking behavior for federated (MSN/OCS/Sametime) service users.
+	  (Jason Cohen)
+	* Add support for adding OCS and Sametime buddies.  OCS users are added
+	  as "ocs/user@domain.tld" and Sametime users are added as
+	  "ibm/sametime_id".  (Jason Cohen)
+
+	Finch:
+	* The TinyURL plugin now creates shorter URLs for long non-conversation
+	  URLs, e.g. URLs to open Inbox in Yahoo/MSN protocols, or the Yahoo
+	  Captcha when joining chat rooms.
+	* Fix displaying umlauts etc. in non-utf8 locale (fix in libgnt).
+
+	Pidgin:
+	* The userlist in a multiuser chat can be styled via gtkrc by using the
+	  widget name "pidgin_conv_userlist". (Heiko Schmitt)
+	* Add a hold button to the media window.
+	* Fix a bug where the conversation backlog stops scrolling in a very
+	  busy chat room.
+	* In the Conversation "Send To" menu, offline buddies appear grayed
+	  out (but are still selectable).  Previously, only offline buddies on
+	  accounts that do not support offline messaging appeared grayed out.
+
+	Pidgin Preference and Preference Window Changes:
+	* Removed the "Use font from theme" and "Conversation Font" preferences
+	  for everyone except Windows users.  The font can be controlled from
+	  the Pidgin GTK+ Theme Control plugin.
+	* Tabs in the Preferences window are now on the left side.
+	* The Browser tab is now visible for GNOME users.
+	* Added a Proxy tab shown no matter what environment Pidgin runs in.
+	* The Browser and Proxy tabs show appropriate GNOME-specific messages
+	  and allow launching the correct applications to change the relevant
+	  GNOME preferences if found.  These were previously together on the
+	  Network tab.
+	* Moved the port range spin buttons on the Network tab to be beside the
+	  checkbox that enables/disables them.
+	* Reorganized preferences on the Status/Idle tab to have one less
+	  "section."
+	* Reorganized preferences on the Sounds tab to have one less "section."
+	* Renamed Smiley Themes tab to Themes.
+	* Moved Buddy List Theme and Status Icon Theme selectors from Interface
+	  tab to Themes tab.
+	* Moved Sound Theme selector from Sounds tab to Themes tab.
+	* Changed the Smiley Theme selector to be consistent with the other
+	  theme selectors.
+	* Rearranged tabs such that Interface is first and all remaining tabs
+	  are alphabetized in English.
+
+version 2.6.3 (10/16/2009):
+	General:
+	* Fix a crash when performing DNS queries on Unixes that use the
+	  blocking DNS lookups.  (Brian Lu)
+
+	AIM and ICQ:
+	* Fix a crash when some clients send contacts in a format we don't
+	  understand.
+	* Fix blocking and other privacy lists.  (Thanks to AOL)
+
+version 2.6.2 (09/05/2009):
+	libpurple:
+	* Fix --disable-avahi to actually disable it in configure, as opposed
+	  to just making the warning non-fatal.
+	* Fix using GNOME proxy settings properly.  (Erik van Pienbroek)
+
+	IRC:
+	* Fix parsing of invalid TOPIC messages.  (CVE-2009-2703)
+
+	MSN:
+	* Sending custom smileys in chats is now supported.
+	* Ink messages are now saved when using the HTML logger.
+	* Fix a crash when receiving some handwritten messages.
+	* Fix a crash when receiving certain SLP invite messages.
+	* Chats with multiple people should no longer spontaneously
+	  disconnect.
+
+	XMPP:
+	* Prompt the user before cancelling a presence subscription.
+	* Escape status messages that have HTML entities in the Get Info dialog.
+	* Fix connecting to XMPP domains with no SRV records from Pidgin on
+	  Windows.
+	* Fix typing notifications with Pidgin 2.5.9 or earlier.
+	* Fix connecting using BOSH and legacy authentication (XEP-0078).
+	* Adding buddies of the form "romeo@montague.net/Resource" are handled
+	  properly.  In addition, it is no longer possible to add buddies of
+	  the form "room@conference.example.net/User", where
+	  room@conference.example.net is a MUC.
+	* Don't crash when receiving "smileyfied" XHTML-IM from clients that
+	  don't support bits of binary (ie. when getting an empty <data/> in
+	  return)
+	* Fix bug where SSL/TLS was not required even though the
+	  "require SSL/TLS" preference checked when connecting to servers
+	  that use the older iq-based authentication.  (CVE-2009-3026)
+
+	Yahoo!/Yahoo! JAPAN:
+	* Accounts now have "Use account proxy for SSL connections" option.
+	  This option force-overrides the account specific proxy settings for
+	  SSL connections only and instead uses the global proxy configuration.
+
+	Finch:
+	* Properly detect libpanel on OpenBSD.  (Brad Smith)
+	* Remove IO watches in gnt_quit.  (Tomasz Mon)
+
+	Pidgin:
+	* Fix the auto-personize functionality in the Buddy List.
+	* Set the window icon for the media window to an icon corresponding to
+	  the type of call (headphone or webcam).
+	* Customized sound files are no longer reset whenever opening the
+	  Preferences dialog.
+	* The buddy list should now immediately refresh upon changing the icon
+	  theme.
+
+version 2.6.1 (08/18/2009):
+	* Fix a crash when some users send you a link in a Yahoo IM
+	* Fix compilation with GTK+ < 2.6.0
+	* Fix compilation on Windows
+
+version 2.6.0 (08/18/2009):
 	libpurple:
 	* Theme support in libpurple thanks to Justin Rodriguez's summer of code
 	  project, with some minor additions and cleanups from Paul Aurich.
@@ -10,45 +1296,58 @@
 	  in a group on the buddy list.
 	* Removed the unmaintained and unneeded toc protocol plugin.
 	* Fixed NTLM authentication on big-endian systems.
-	* Various memory cleanups when unloading libpurple. (Nick Hebner)
+	* Various memory cleanups when unloading libpurple. (Nick Hebner and
+	  Stefan Becker)
 	* Report idle time 'From last message sent' should work properly.
+	* Better handling of corrupt certificates in the TLS Peers cache.
+	* More efficient buddy list and conversation search functions.
+	  (Jan Kaluza and Aman Gupta)
+	* Install scalable versions of the main Pidgin icon, the protocol icons,
+	  the dialog icons, and the Buddy List emblems.
+	* Build properly on Hurd.  (Marc Dequènes)
+	* Various memory leaks fixed as reported by Josh Mueller.
+	* Properly handle an IRC buddy appearing in multiple groups.
+	* Escape HTML entities in usernames when written with the HTML logger.
+	* Do not display MySpace status changes as incoming IMs.  (Mark Doliner
+	  and Justin Williams)
+
+	DNS:
 	* DNS servers are re-read when DNS queries fail in case the system has
 	  moved to a new network and the old servers are not accessible.
 	* DNS SRV records with equal priority are sorted with respect to their
 	  weight as specified in RFC 2782.  (Vijay Raghunathan)
+	* Don't do IPv6 address lookups if the computer does not have an IPv6
+	  address configured.
+	* Fix a leak when the UI provides its own DNS resolving UI op.
+	  (Aman Gupta)
+	* Don't fork a DNS resolver process to resolve IP addresses.
+	  (Aman Gupta)
+	* Internationalized Domain Names are supported when libpurple is
+	  compiled against the GNU IDN library.
+
+	Environment Variables:
 	* GnuTLS logging (disabled by default) can be controlled through the
 	  PURPLE_GNUTLS_DEBUG environment variable, which is an integer between
 	  0 and 9 (higher is more verbose). Higher values may reveal sensitive
 	  information.
-	* PURPLE_VERBOSE_DEBUG environment variable.  Currently, this is an "on" or
-	  "off" variable.  Set it to any value to turn it on and unset it to turn
-	  it off.  This will optionally be used to only show less useful debug
-	  information on an as-needed basis.
-	* PURPLE_LEAKCHECK_HELP environment variable.  Currently, this is an "on"
+	* PURPLE_VERBOSE_DEBUG environment variable.  Currently, this is an "on"
 	  or "off" variable.  Set it to any value to turn it on and unset it to
-	  turn it off.  This will be used to perform various actions that are
-	  useful when running libpurple inside of Valgrind or similar programs.
-	  Currently, it keeps plugins in memory, allowing Valgrind to perform
-	  symbol resolution of leak traces at shutdown.
-	* Don't do IPv6 address lookups if the computer does not have an IPv6
-	  address configured.
-	* Fix a leak when the UI provides its own DNS resolving UI op.
-	  (Aman Gupta)
-	* Don't fork a DNS resolver process to resolve IP addresses.  (Aman Gupta)
-	* Better handling of corrupt certificates in the TLS Peers cache.
-	* More efficient buddy list and conversation search functions.
-	  (Jan Kaluza and Aman Gupta)
-	* Internationalized Domain Names are supported when libpurple is compiled
-	  against the GNU IDN library.
-	* Install scalable versions of the main Pidgin icon, the protocol icons,
-	  the dialog icons, and the Buddy List emblems.
+	  turn it off.  This will optionally be used to only show less useful
+	  debug information on an as-needed basis.
+	* PURPLE_LEAKCHECK_HELP environment variable.  Currently, this is an
+	  "on" or "off" variable.  Set it to any value to turn it on and unset
+	  it to turn it off.  This will be used to perform various actions
+	  that are useful when running libpurple inside of Valgrind or similar
+	  programs.  Currently, it keeps plugins in memory, allowing Valgrind
+	  to perform symbol resolution of leak traces at shutdown.
 
 	AIM and ICQ:
 	* Preliminary support for a new authentication scheme called
 	  "clientLogin."
 	* Fixed a bug where your away message sometimes would not get set when
 	  you first sign on.
-	* Make sure links in your away messages show up as links to other people.
+	* Make sure links in your away messages show up as links to other
+	  people.
 	* For ICQ, Never change the privacy setting specified by the user.
 
 	Gadu-Gadu:
@@ -60,11 +1359,23 @@
 	* Support connection progress steps in Gadu-Gadu.  (Krzysztof "kkszysiu"
 	  Klinikowski)
 
+	MSN:
+	* Add support for receiving handwritten (ink) messages on MSN.  (Chris
+	  Stafford, Gal Topper, and Elliott Sales de Andrade)
+	* Add support for receiving audio clips on MSN.  (Chris Stafford, Gal
+	  Topper, and Elliott Sales de Andrade)
+	* Show the invite message for buddies that requested authorization
+	  from you on MSN.
+	* Support sending an invite message to buddies when requesting
+	  authorization from them on MSN.
+	* Timeout switchboard connections after 60 seconds (msn-pecan devs).
+
 	XMPP:
-	* Voice & Video support with Jingle (XEP-0166, 0167, 0176, & 0177), voice
-	  support with GTalk and voice and video support with the GMail web
-	  client. (Mike "Maiku" Ruprecht)
-	* Added a Service Discovery Browser plugin for Pidgin. (Andrei Mozzhuhin)
+	* Voice & Video support with Jingle (XEP-0166, 0167, 0176, & 0177),
+	  voice support with GTalk and voice and video support with the GMail
+	  web client. (Mike "Maiku" Ruprecht)
+	* Added a Service Discovery Browser plugin for Pidgin.
+	  (Andrei Mozzhuhin)
 	* Support for in-band bytestreams for file transfers (XEP-0047). (Marcus
 	  Lundblad)
 	* Support for sending and receiving attentions (equivalent to "buzz"
@@ -81,28 +1392,30 @@
 	* Better support for receiving remote users' nicknames.
 	* /affiliate and /role will now list the room members with the specified
 	  affiliation/role if possible. (Andrei Mozzhuhin)
-	* Put section breaks between resources in "Get Info" to improve readability.
-	* Silently remove invalid XML 1.0 entities (e.g. ASCII control characters)
-	  from sent messages.
+	* Put section breaks between resources in "Get Info" to improve
+	  readability.
+	* Silently remove invalid XML 1.0 entities (e.g. ASCII control
+	  characters) from sent messages.
 	* XHTML markup is only included in outgoing messages when the message
 	  contains formatting.
-	* Show when the user was last logged in when doing "Get Info" on an offline
-	  buddy, provided the server supports it.
+	* Show when the user was last logged in when doing "Get Info" on an
+	  offline buddy, provided the server supports it.
 	* Support custom smileys in MUCs (only when all participants support the
-	  "Bits of Binary" extension, and a maximum of 10 participants are in the
-	  chat to avoid getting too many fetch requests).
+	  "Bits of Binary" extension, and a maximum of 10 participants are in
+	  the chat to avoid getting too many fetch requests).
 	* Fix an issue with Jabber (pre-XMPP) servers and the user's preference
 	  to require SSL not being respected.
 	* Fix an issue where Cyrus SASL DIGEST MD5 authentication might fail if
 	  the username, password, or realm (the JID domain) contain non-ASCII
 	  characters.
-	* Show emblem for mobile, handheld, and web clients and bots (if the other
-	  client supports it).
-	* Google Talk mail notifications should now work for people for whom they
-	  inexplicably did not.  (Thanks to yukam for determining the reason)
+	* Show emblem for mobile, handheld, and web clients and bots (if the
+	  other client supports it).
+	* Google Talk mail notifications should now work for people for whom
+	  they inexplicably did not.  (Thanks to yukam for determining the
+	  reason)
 	* New XMPP and Google Talk accounts require SSL by default.
-	* Display kicks (and the reasons given) in chat rooms when an occupant is
-	  kicked.
+	* Display kicks (and the reasons given) in chat rooms when an occupant
+	  is kicked.
 	* Fix issues with case-sensitivity of XMPP roster and case-insensitive
 	  Purple groups.
 	* For contacts who advertise Entity Capabilities, only send rich text
@@ -112,6 +1425,8 @@
 	* When the GNU IDN library (libidn) is available, it is used for
 	  normalization of Jabber IDs.  When unavailable, internal routines are
 	  used (as in previous versions).
+	* Topics that contain '<' followed by a non-whitespace character can now
+	  be set properly.
 
 	Yahoo!/Yahoo! JAPAN:
 	* P2P file transfers.  (Sulabh Mahajan)
@@ -127,15 +1442,6 @@
 	* Ability to set personal details for an account and for buddies in the
 	  buddylist.
 
-	MSN:
-	* Add support for receiving handwritten (ink) messages on MSN.
-	* Add support for receiving audio clips on MSN.
-	* Show the invite message for buddies that requested authorization
-	  from you on MSN.
-	* Support sending an invite message to buddies when requesting authorization
-	  from them on MSN.
-	* Timeout switchboard connections aggressively
-
 	Pidgin:
 	* Added -f command line option to tell Pidgin to ignore NetworkManager
 	  and assume it has a valid network connection.
@@ -172,11 +1478,19 @@
 	  conversation window is now linked to the file.
 	* Fix a crash when closing a conversation tab that has unread messages
 	  when the Message Notification plugin is loaded.
+	* Fix a crash when closing the New Mail dialog if an account with new
+	  mail was previously disconnected while the dialog was open.
+	* Fix incorrect unread message counts for the new mail notifications.
+	* Do not lose unread messages with a hidden conversation window when
+	  new IM conversations are hidden and "Close IMs immediately when the tab
+	  is closed" is unset.
 
 	Finch:
 	* The hardware cursor is updated correctly. This will be useful
 	  especially for users of braille terminals, screen readers etc.
 	* Added a TinyURL plugin, which aids copying longer URLs.
+	* Fixed UTF-8 compatibility problems which could cause exits or other
+	  unrequested behaviour.
 
 	Pidgin GTK+ Theme Control Plugin:
 	* Removed mouse cursor color preferences.
@@ -185,6 +1499,13 @@
 	* Preferences have been reorganized into three tabs for Colors, Fonts, and
 	  Miscellaneous categories.
 
+version 2.5.9 (08/18/2009):
+	* Fix a crash via a specially crafted MSN message (CVE-2009-2694,
+	  thanks to Core Security Technologies for discovering this and
+	  notifying us privately before announcing it).
+	* Fix a crash in Bonjour, MSN, and XMPP when trying to transfer files with
+	  NULL names.
+
 version 2.5.8 (06/27/2009):
 	ICQ:
 	* Fix misparsing a web message as an SMS message. (Yuriy Kaminskiy)
--- a/ChangeLog.API	Wed Jun 13 19:28:57 2012 -0400
+++ b/ChangeLog.API	Wed Jun 13 19:30:27 2012 -0400
@@ -1,6 +1,496 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
-version 2.6.0 (??/??/2009):
+version 3.0.0 (??/??/????):
+	libpurple:
+		Added:
+		* pidgin_create_webview
+		* purple_account_is_disconnecting
+		* purple_account_get_ui_data
+		* purple_account_set_ui_data
+		* purple_account_register_completed
+		* purple_conv_chat_cb_get_alias
+		* purple_conv_chat_cb_get_flags
+		* purple_conv_chat_cb_is_buddy
+		* purple_conv_chat_cb_get_ui_data
+		* purple_conv_chat_cb_set_ui_data
+		* purple_connection_get_flags
+		* purple_connection_set_flags
+		* purple_connection_update_last_received
+		* purple_conversation_get_ui_data
+		* purple_conversation_set_ui_data
+		* purple_conversation_message_get_alias
+		* purple_conversation_message_get_conv
+		* purple_contact_get_contact_size
+		* purple_notify_searchresult_column_get_title
+		* purple_notify_searchresult_column_is_visible
+		* purple_notify_searchresult_column_set_visible
+		* purple_notify_user_info_prepend_pair_plaintext
+		* purple_menu_action_get_callback
+		* purple_menu_action_get_children
+		* purple_menu_action_get_data
+		* purple_menu_action_set_label
+		* purple_menu_action_set_data
+		* purple_menu_action_set_callback
+		* purple_menu_action_set_children
+		* purple_request_field_get_tooltip
+		* purple_request_field_group_get_fields_list
+		* purple_request_field_set_tooltip
+		* purple_request_fields_get_ui_data
+		* purple_request_fields_set_ui_data
+		* purple_roomlist_get_account
+		* purple_roomlist_get_proto_data
+		* purple_roomlist_get_ui_data
+		* purple_roomlist_room_get_expanded_once
+		* purple_roomlist_room_set_expanded_once
+		* purple_roomlist_set_proto_data
+		* purple_roomlist_set_ui_data
+		* purple_whiteboard_get_account
+		* purple_whiteboard_get_draw_list
+		* purple_whiteboard_set_draw_list
+		* purple_whiteboard_get_protocol_data
+		* purple_whiteboard_set_protocol_data
+		* purple_whiteboard_get_state
+		* purple_whiteboard_set_state
+		* purple_whiteboard_get_ui_data
+		* purple_whiteboard_set_ui_data
+		* purple_whiteboard_get_who
+		* purple_xfer_get_fd
+		* purple_xfer_get_protocol_data
+		* purple_xfer_get_ui_data
+		* purple_xfer_get_watcher
+		* purple_xfer_set_fd
+		* purple_xfer_set_local_port
+		* purple_xfer_set_protocol_data
+		* purple_xfer_set_status
+		* purple_xfer_set_ui_data
+		* purple_xfer_set_watcher
+		* Various WebKit-related functions in gtkwebview.h
+		* xmlnode_get_default_namespace
+		* xmlnode_strip_prefixes
+
+		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_certificate_check_signature_chain now returns a list of failing
+		  PurpleCertificate*s as the second parameter
+		* purple_connection_error now takes a PurpleConnectionError
+		  as the second parameter
+		* purple_conversation_get_gc renamed to
+		  purple_conversation_get_connection
+		* purple_dnsquery_a now takes a PurpleAccount as the first parameter
+		* purple_network_listen now takes the protocol family as the second
+		  parameter
+		* purple_network_listen now takes a boolean indicating external port
+		  mapping as the fourth parameter
+		* purple_network_listen_range now takes a boolean indicating external
+		  port mapping as the fifth parameter
+		* purple_network_listen_range now takes the protocol family as the
+		  third parameter
+		* purple_notify_user_info_add_pair renamed to
+		  purple_notify_user_info_add_pair_html
+		* purple_notify_user_info_get_entries returns a GQueue instead of
+		  a GList
+		* purple_notify_user_info_prepend_pair renamed to
+		  purple_notify_user_info_prepend_pair_html
+		* purple_srv_resolve now takes a PurpleAccount as the first parameter
+		* purple_str_size_to_units now takes a goffset as the size parameter
+		* purple_txt_resolve now takes a PurpleAccount as the first parameter
+		* purple_util_fetch_url_request now takes a PurpleAccount as
+		  the first parameter
+		* purple_util_fetch_url_request now takes a length as the eighth
+		  parameter
+		* purple_util_fetch_url_len now takes a length as the fifth parameter
+		* purple_xfer_get_bytes_remaining now returns a goffset
+		* purple_xfer_get_bytes_sent now returns a goffset
+		* purple_xfer_get_size now returns a goffset
+		* purple_xfer_is_canceled renamed to purple_xfer_is_cancelled
+		* 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
+		* PurpleConnectionUiOps.report_disconnect now passes a
+		  PurpleConnectionError as the second parameter
+		* PurpleXfer.bytes_remaining is now a goffset
+		* PurpleXfer.bytes_sent is now a goffset
+		* PurpleXfer.size is now a goffset
+
+		Removed:
+		* _GntFileType
+		* _GntKeyPressMode
+		* _GntMouseEvent
+		* _GntParamFlags
+		* _GntProgressBarOrientation
+		* _GntTreeColumnFlag
+		* _GntWidgetFlags
+		* _PurpleCipherBatchMode
+		* _PurpleCipherCaps
+		* _PurpleCmdFlag
+		* _PurpleCmdPriority
+		* _PurpleCmdRet
+		* _PurpleCmdStatus
+		* _PurplePrefType
+		* _PurplePrivacyType
+		* _PurpleSoundEventID
+		* _XMLNodeType
+		* GtkIMHtml.clipboard_html_string
+		* GtkIMHtml.clipboard_text_string
+		* GtkIMHtmlFontDetail
+		* gtk_imhtml_animation_free
+		* gtk_imhtml_animation_new
+		* gtk_imhtml_image_add_to
+		* gtk_imhtml_image_free
+		* gtk_imhtml_image_new
+		* gtk_imhtml_image_scale
+		* pidgin_blist_update_account_error_state
+		* pidgin_check_if_dir
+		* PIDGIN_DIALOG
+		* pidgin_dialogs_alias_contact
+		* pidgin_set_custom_buddy_icon
+		* pidgin_setup_screenname_autocomplete
+		* PidginBuddyList.connection_errors
+		* PidginConversation.sg
+		* purple_account_add_buddies_with_invite
+		* purple_account_add_buddy_with_invite
+		* purple_blist_update_buddy_icon
+		* purple_buddy_get_local_alias
+		* purple_buddy_icons_has_custom_icon
+		* purple_buddy_icons_find_custom_icon
+		* purple_buddy_icons_set_custom_icon
+		* purple_certificate_check_signature_chain_with_failing. Use
+		  purple_certificate_check_signature_chain, instead
+		* purple_connection_error_reason
+		* purple_connection_new
+		* purple_connection_new_unregister
+		* purple_connection_destroy
+		* purple_contact_set_alias
+		* purple_conv_chat_set_users
+		* purple_core_migrate
+		* purple_dnsquery_a_account
+		* purple_network_listen_family. Use purple_network_listen, instead.
+		* purple_network_listen_map_external
+		* purple_network_listen_range_family. Use purple_network_listen,
+		  instead.
+		* purple_notify_searchresults_column_get_title
+		* purple_notify_searchresults_get_columns_count
+		* purple_notify_searchresults_get_rows_count
+		* purple_notify_searchresults_row_get
+		* purple_plugins_register_load_notify_cb
+		* purple_plugins_register_probe_notify_cb
+		* purple_plugins_register_unload_notify_cb
+		* purple_plugins_unregister_load_notify_cb
+		* purple_plugins_unregister_probe_notify_cb
+		* purple_plugins_unregister_unload_notify_cb
+		* purple_presence_add_status
+		* purple_presence_add_list
+		* purple_proxy_connect_socks5
+		* purple_request_field_list_add
+		* purple_srv_cancel
+		* purple_srv_resolve_account
+		* purple_ssl_connect_fd
+		* purple_status_set_attr_boolean
+		* purple_status_set_attr_int
+		* purple_status_set_attr_string
+		* purple_status_type_add_attr
+		* purple_status_type_add_attrs
+		* purple_status_type_add_attrs_vargs
+		* purple_status_type_get_primary_attr
+		* purple_status_type_set_primary_attr
+		* purple_strlcat
+		* purple_strlcpy
+		* purple_txt_cancel
+		* purple_txt_resolve_account
+		* purple_util_fetch_url_len. Use purple_util_fetch_url, instead.
+		* purple_util_fetch_url_request_len. Use
+		* purple_util_fetch_url_request, instead.
+		* purple_util_fetch_url_request_len_with_account.  Use
+		  purple_util_fetch_url_request, instead.
+		* PurpleConnectionUiOps.report_disconnect_reason
+		* PurplePluginProtocolInfo.add_buddy_with_invite
+		* PurplePluginProtocolInfo.add_buddies_with_invite
+		* PurplePluginProtocolInfo.get_cb_away
+		* serv_got_attention
+		* serv_send_attention
+		* struct _GtkIMHtmlAnimation
+		* struct _GtkIMHtmlFontDetail
+		* struct _GtkIMHtmlHr
+		* struct _GtkIMHtmlImage
+		* struct _GtkIMHtmlScalable
+		* struct _GtkSmileyTree
+		* struct _PidginChatPane
+		* struct _PidginImPane
+		* struct _PurpleAttentionType
+		* struct _PurpleConversation
+		* struct _PurpleConvChat
+		* struct _PurpleConvChatBuddy
+		* struct _PurpleConvIm
+		* struct _PurpleConvMessage
+		* struct _PurpleMenuAction
+		* struct _PurplePounce
+		* struct _PurpleProxyInfo
+		* struct _PurpleRequestField
+		* struct _PurpleRoomlist
+		* struct _PurpleRoomlistField
+		* struct _PurpleRoomlistRoom
+		* struct _PurpleWhiteboard
+		* struct PurpleAccountOption
+		* struct PurpleAccountUserSplit
+		* struct PurpleNotifySearchColumn
+		* wpurple_g_access
+		* xmlnode_set_attrib_with_namespace
+		* xmlnode_set_attrib_with_prefix
+
+version 2.10.1:
+	* No changes
+
+version 2.10.0:
+	libpurple:
+		Added:
+		* purple_srv_txt_query_destroy (accidentally left out of 2.8.0)
+
+	Pidgin:
+		Added:
+		* pidgin_dialogs_plugins_info (should not be used by anything but Pidgin)
+
+version 2.9.0:
+	libpurple:
+		Added:
+		* Hash table to PurpleConvChat struct, used to make
+		  purple_conv_chat_cb_find O(1).
+		* ui_data pointer to PurpleConvChatBuddy struct.
+		* deleting-chat-buddy signal (conversation signals)
+		* pidgin_pixbuf_from_data
+		* pidgin_pixbuf_anim_from_data
+		* pidgin_pixbuf_new_from_file
+		* pidgin_pixbuf_new_from_file_at_size
+		* pidgin_pixbuf_new_from_file_at_scale
+
+		Deprecated:
+		* purple_conv_chat_set_users
+		* PurpleConvChat in_room list
+
+version 2.8.0 (06/07/2011):
+	libpurple:
+		Added:
+		* account-authorization-requested-with-message signal (Stefan Ott)
+		  (#8690)
+		* cleared-message-history signal (conversation signals)
+		* purple_account_add_buddy_with_invite
+		* purple_account_add_buddies_with_invite
+		* purple_dnsquery_a_account
+		* purple_notify_user_info_add_pair_plaintext
+		* purple_media_get_active_local_candidates
+		* purple_media_get_active_remote_candidates
+		* purple_media_manager_get_video_caps (Jakub Adam) (#13095)
+		* purple_media_manager_set_video_caps (Jakub Adam) (#13095)
+		* purple_pounce_destroy_all_by_buddy (Kartik Mohta) (#1131)
+		* purple_proxy_connect_socks5_account
+		* purple_srv_resolve_account
+		* purple_txt_resolve_account
+		* Added add_buddy_with_invite to PurplePluginProtocolInfo
+		* Added add_buddies_with_invite to PurplePluginProtocolInfo
+		* Added PurpleSrvTxtQueryUiOps which allow UIs to specify their
+		  own mechanisms to resolve SRV and/or TXT queries. It works
+		  similar to PurpleDnsQueryUiOps
+		* purple_marshal_BOOLEAN__POINTER_BOOLEAN (kawaii.neko) (#12599)
+
+		Deprecated:
+		* purple_account_add_buddy
+		* purple_account_add_buddies_with_invite
+		* purple_dnsquery_a
+		* purple_proxy_connect_socks5
+		* purple_srv_resolve
+		* purple_txt_resolve
+		* add_buddy from PurplePluginProtocolInfo struct
+		* add_buddies from PurplePluginProtocolInfo struct
+
+	Pidgin:
+		Added:
+		* pidgin_make_scrollable (Gabriel Schulhof) (#10599)
+		* chat-nick-clicked signal (kawaii.neko) (#12599)
+		* chat-nick-autocomplete signal (kawaii.neko) (#12599)
+
+version 2.7.11 (03/10/2011):
+	* libpurple:
+		Added:
+		* Four entries in the GHashTable passed when joining
+		  an XMPP chat room which allow the UI to request a limited
+		  amount of history.  See XEP-0045 7.1.16 for details; the
+		  entries are named history_maxchars, history_maxstanzas,
+		  history_seconds, and history_since.  history_since must be
+		  interpretable by purple_str_to_time, and the prpl takes care
+		  of formatting the time properly.
+	* Perl:
+		Added:
+		* Purple::find_conversation_with_account
+		* Purple::Conversation::Chat::send_with_flags
+		* Purple::Conversation::IM::send_with_flags
+
+version 2.7.10 (02/06/2011):
+	* No changes
+
+version 2.7.9 (12/26/2010):
+	* No changes
+
+version 2.7.8 (12/19/2010):
+	* No changes
+
+version 2.7.7 (11/23/2010):
+	* No changes
+
+version 2.7.6 (11/21/2010):
+	* No changes
+
+version 2.7.5 (10/31/2010):
+	* No changes
+
+version 2.7.4 (10/20/2010):
+	Perl:
+		Added:
+		* Purple::BuddyList::Chat::get_components
+
+		Changed:
+		* Purple::BuddyList::Chat::new now works properly.  Thanks
+		  to Rafael in devel@conference.pidgin.im for reporting and
+		  testing.
+
+version 2.7.3 (08/10/2010):
+	libpurple:
+		Fixed:
+		* purple_account_[gs]et_public_alias no longer crash when
+		  called for a protocol that doesn't support the underlying
+		  calls and the caller does not specify a failure callback.
+
+	Perl:
+		Added:
+		* Exposed log-subsystem signals.
+
+	Pidgin:
+		Changed:
+		* Changing the visibility (gtk_widget_hide/show) of
+		  the widgets in the GtkIMHtmlToolbar should now affect
+		  the visibility of the entries in the 'lean' view
+		  (the default toolbar view).
+
+		Deprecated:
+		* pidgin_check_if_dir
+
+	libgnt:
+		Added:
+		* gnt_tree_row_get_key, gnt_tree_row_get_next,
+		  gnt_tree_row_get_prev, gnt_tree_row_get_child and
+		  gnt_tree_row_get_parent.
+
+version 2.7.2 (07/21/2010):
+	* No changes
+
+version 2.7.1 (05/29/2010):
+	* No changes
+
+version 2.7.0 (05/12/2010):
+	libpurple:
+		Added:
+		* Account signals (see account-signals.dox); useful for D-Bus
+		   * account-signed-on
+		   * account-signed-off
+		   * account-connection-error
+		* purple_account_get_name_for_display
+		* purple_account_get_privacy_type
+		* purple_account_get_public_alias
+		* purple_account_set_privacy_type
+		* purple_account_set_public_alias
+		* buddy-caps-changed blist signal
+		* Added media_caps to the PurpleBuddy struct
+		* purple_buddy_get_media_caps
+		* purple_buddy_set_media_caps
+		* purple_certificates_import for importing multiple
+		  certificates from a single file (and corresponding
+		  import_certificates member of PurpleCertificateScheme struct)
+		* autojoin connection signal
+		* purple_contact_get_group
+		* sent-attention conversation signal
+		* got-attention conversation signal
+		* ui-caps-changed media manager signal
+		* purple_media_candidate_copy
+		* purple_media_codec_copy
+		* purple_media_manager_get_backend_type
+		* purple_media_manager_set_backend_type
+		* PurpleMood struct in status.h
+		* purple_network_get_all_local_system_ips, which returns all
+		  local IPs on the system.  On systems with the getifaddrs()
+		  function, this will return both IPv4 and IPv6 addresses
+		  (excluding link-local and loopback addresses).  On others,
+		  it returns just IPv4 addresses.
+		* purple_network_listen_family and
+		  purple_network_listen_range_family.  These will replace the
+		  versions without _family in 3.0.0 and allow the caller to
+		  specifically request either an IPv4 or IPv6 socket.  IPv6 is
+		  only supported if the getaddrinfo() function is available
+		  at build-time (not the case on Windows, currently).
+		* purple_prpl_got_media_caps
+		* purple_request_action_with_icon
+		* purple_request_action_with_icon_varg
+		* purple_socket_get_family
+		* purple_socket_speaks_ipv4
+		* purple_unescape_text
+		* purple_uuid_random
+		* purple_xfer_get_thumbnail
+		* purple_xfer_get_thumbnail_mimetype
+		* purple_xfer_set_thumbnail
+		* purple_xfer_prepare_thumbnail
+
+	Pidgin:
+		Added:
+		* pidgin_dialogs_buildinfo (should not be used by anything but Pidgin)
+		* pidgin_dialogs_developers (should not be used by anything but Pidgin)
+		* pidgin_dialogs_translators (should not be used by anything but Pidgin)
+		* gtk_imhtmltoolbar_switch_active_conversation
+		* 'paste' signal for GtkIMHtml (more in gtkimhtml-signals.dox)
+		* 'drawing-buddy' signal for gtkblist (more in gtkblist-signals.dox)
+
+version 2.6.6 (02/18/2010):
+	libpurple:
+		Changed:
+		* purple_xfer_cancel_local is now called instead of
+		  purple_xfer_request_denied if an error is found when selecting
+		  a file to send. Request denied is still used when a receive
+		  request is not allowed.
+		* xmlnode_from_str now properly handles parsing an attribute which
+		  contain "&lt;br&gt;", which were previously transformed into a
+		  newline character (libxml2 unescapes all entities except
+		  representations of '&', and libpurple's purple_unescape_html
+		  converts "<br>" to a newline).
+
+	Perl:
+		Changed:
+		* Corrected the package names for the PurpleProxyType and
+		  PurpleLogReadFlags enums to have the correct number of colons
+		  (from Purple::ProxyType::::<type> to Purple::ProxyType::<type>
+		  and Purple::Log:ReadFlags::::<type> to
+		  Purple::Log::ReadFlags::<type>)  (Chris Foote)
+
+version 2.6.5 (01/08/2010):
+	No changes
+
+version 2.6.4 (11/29/2009):
+	No changes
+
+version 2.6.3 (10/16/2009):
+	No changes
+
+version 2.6.2 (09/05/2009):
+	Perl:
+		Added:
+		* Purple::XMLNode::get_next(), which returns the next neighbor tag of
+		  the current node.
+		Changed:
+		* Purple::XMLNode::get_child() will return the first child node if
+		  passed "" or undef as the name of the node.
+
+version 2.6.1 (08/18/2009):
+	No changes
+
+version 2.6.0 (08/18/2009):
 	libpurple:
 		Added:
 		* PurpleMedia and PurpleMediaManager API
@@ -18,6 +508,9 @@
 		* Three Blist UI ops used to overload libpurple's built-in saving
 		  of the buddy list to blist.xml. If a UI implements these, it probably
 		  wants to add the buddies itself and not call purple_blist_load.
+		* Three File Transfer UI ops used to overload libpurple's use of fread
+		  and fwrite for saving a file locally. These allow a UI to stream a
+		  file through a socket without buffering the file on the local disk.
 		* Jabber plugin signals (see jabber-signals.dox)
 		* purple_account_remove_setting
 		* purple_buddy_destroy
@@ -65,9 +558,12 @@
 		* purple_strequal
 		* purple_utf8_strip_unprintables
 		* purple_util_fetch_url_request_len_with_account
+		* purple_xfer_prpl_ready
+		* purple_xfer_ui_ready
 		* xmlnode_from_file
 		* xmlnode_get_parent
 		* xmlnode_set_attrib_full
+		* PURPLE_STATUS_MOOD as a new PurpleStatusPrimitive
 
 		Changed:
 		* xmlnode_remove_attrib now removes all attributes with the
@@ -95,6 +591,10 @@
 		* status is set before emitting signals in purple_xfer_set_status.
 		* Creating multiple distinct chats with the same name (i.e. "MSN Chat")
 		  is deprecated and will be removed in libpurple 3.0.0.
+		* purple_xfer_start now accepts -1 as the fd parameter if the protocol
+		  plugin will administer the transfer itself. 0 is still accepted for
+		  backward compatibility since older versions of libpurple will not
+		  accept -1.
 
 		Deprecated:
 		* buddy-added and buddy-removed blist signals
@@ -145,6 +645,9 @@
 		* GntProgressBar and functions (Saleem Abdulrasool)
 
 	perl:
+		Added:
+		* Purple::XMLNode::get_name()
+
 		Changed:
 		* Made a bunch of functions act more perl-like. Call the new()
 		  functions as Class->new(...) instead of Class::new(...):
@@ -158,6 +661,14 @@
 			* Purple::Request::Field::list_new
 			* Purple::Request::Field::string_new
 			* Purple::Request::Field::group_new
+		* Make the XMLNode API more perl-like. Don't pass len
+		  parameters and call them like:
+			* $xmlnode->copy()
+			* $xmlnode->to_str()
+			* $xmlnode->to_formatted_str()
+			* Purple::XMLNode::from_str(...)
+version 2.5.9 (08/18/2009):
+	No changes
 
 version 2.5.8 (06/27/2009):
 	No changes
@@ -462,7 +973,7 @@
 		* Added gnt_window_set_maximize and gnt_window_get_maximize, and
 		  GntWindowFlags enum.
 
-version 2.2.2 (??/??/????):
+version 2.2.2 (10/23/2007):
 	libpurple:
 		Changed:
 		* The size parameter of purple_util_write_data_to_file_absolute
--- a/ChangeLog.win32	Wed Jun 13 19:28:57 2012 -0400
+++ b/ChangeLog.win32	Wed Jun 13 19:30:27 2012 -0400
@@ -1,4 +1,48 @@
-version 2.6.0 (??/??/2009):
+Starting with Pidgin version 2.7.1, this ChangeLog file will no longer be
+updated.  It will be kept in the source tree for historical reasons only.
+
+version 2.7.1 (05/29/2010):
+	* No changes
+
+version 2.7.0 (05/12/2010):
+	* Updated GTK+ to 2.16.6
+	* Private GTK+ Runtime now used (GTK+ Installer no longer supported)
+	* Minimum required GTK+ version increased to 2.14.7
+	* Windows 95, Windows 98, Windows 98 Second Edition, Windows ME
+	  (Millennium Edition), and Windows NT 4.0 longer supported due to GTK+
+	  requirements changes.
+	* Crash Report files (pidgin.RPT) are now generated in the ~/.purple
+	  directory instead of the installation directory.
+	* NSS SSL Library upgraded to 3.12.5 (thanks to Berke Viktor)
+	* GtkSpell upgraded to 2.0.16, changing the spellchecking backend to
+	  enchant.  This means that myspell and hunspell (OpenOffice)
+	  dictionaries can be used (previous versions' aspell dictionaries
+	  will not work).
+
+version 2.6.6 (02/18/2010):
+	* Installer translations for: Norwegian nynorsk
+
+version 2.6.5 (01/08/2010):
+	* No changes
+
+version 2.6.4 (11/29/2009):
+	* Register URL handlers for everything that Windows knows about.  Still
+	  use the HTTP "open" handler for security reasons.
+
+version 2.6.3 (10/16/2009):
+	* No changes
+
+version 2.6.2 (09/05/2009):
+	* No changes
+
+version 2.6.1 (08/18/2009):
+	* No changes
+
+version 2.6.0 (08/18/2009):
+	* Added XMPP URI support.
+
+version 2.5.9 (08/18/2009):
+	* No changes and no win32 packages built.
 
 version 2.5.8 (06/27/2009):
 	* No changes
--- a/Doxyfile.in	Wed Jun 13 19:28:57 2012 -0400
+++ b/Doxyfile.in	Wed Jun 13 19:30:27 2012 -0400
@@ -1,4 +1,4 @@
-# Doxyfile 1.5.3
+# Doxyfile 1.7.1
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project
@@ -14,175 +14,169 @@
 # Project related configuration options
 #---------------------------------------------------------------------------
 
-# This tag specifies the encoding used for all characters in the config file that 
-# follow. The default is UTF-8 which is also the encoding used for all text before 
-# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into 
-# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of 
-# possible encodings.
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
 
 DOXYFILE_ENCODING      = UTF-8
 
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
 # by quotes) that should identify the project.
 
 PROJECT_NAME           = @PACKAGE@
 
-# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
-# This could be handy for archiving the generated documentation or 
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
 PROJECT_NUMBER         = @VERSION@
 
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
-# base path where the generated documentation will be put. 
-# If a relative path is entered, it will be relative to the location 
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
 # where doxygen was started. If left blank the current directory will be used.
 
 OUTPUT_DIRECTORY       = doc
 
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
-# 4096 sub-directories (in 2 levels) under the output directory of each output 
-# format and will distribute the generated files over these directories. 
-# Enabling this option can be useful when feeding doxygen a huge amount of 
-# source files, where putting all generated files in the same directory would 
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
 # otherwise cause performance problems for the file system.
 
 CREATE_SUBDIRS         = NO
 
-# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
-# documentation generated by doxygen is written. Doxygen will use this 
-# information to generate all constant output in the proper language. 
-# The default language is English, other supported languages are: 
-# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
-# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, 
-# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, 
-# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, 
-# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
 
 OUTPUT_LANGUAGE        = English
 
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
-# include brief member descriptions after the members that are listed in 
-# the file and class documentation (similar to JavaDoc). 
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
 # Set to NO to disable this.
 
 BRIEF_MEMBER_DESC      = YES
 
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
-# the brief description of a member or function before the detailed description. 
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
 # brief descriptions will be completely suppressed.
 
 REPEAT_BRIEF           = YES
 
-# This tag implements a quasi-intelligent brief description abbreviator 
-# that is used to form the text in various listings. Each string 
-# in this list, if found as the leading text of the brief description, will be 
-# stripped from the text and the result after processing the whole list, is 
-# used as the annotated text. Otherwise, the brief description is used as-is. 
-# If left blank, the following values are used ("$name" is automatically 
-# replaced with the name of the entity): "The $name class" "The $name widget" 
-# "The $name file" "is" "provides" "specifies" "contains" 
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
 # "represents" "a" "an" "the"
 
-ABBREVIATE_BRIEF       = 
+ABBREVIATE_BRIEF       =
 
-# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
-# Doxygen will generate a detailed section even if there is only a brief 
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
 # description.
 
 ALWAYS_DETAILED_SEC    = NO
 
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
-# inherited members of a class in the documentation of that class as if those 
-# members were ordinary class members. Constructors, destructors and assignment 
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
 # operators of the base classes will not be shown.
 
 INLINE_INHERITED_MEMB  = NO
 
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
-# path before files name in the file list and in the header files. If set 
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
 # to NO the shortest path that makes the file name unique will be used.
 
 FULL_PATH_NAMES        = NO
 
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
-# can be used to strip a user-defined part of the path. Stripping is 
-# only done if one of the specified strings matches the left-hand part of 
-# the path. The tag can be used to show relative paths in the file list. 
-# If left blank the directory from which doxygen is run is used as the 
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
 # path to strip.
 
-STRIP_FROM_PATH        = 
+STRIP_FROM_PATH        =
 
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
-# the path mentioned in the documentation of a class, which tells 
-# the reader which header file to include in order to use a class. 
-# If left blank only the name of the header file containing the class 
-# definition is used. Otherwise one should specify the include paths that 
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
 # are normally passed to the compiler using the -I flag.
 
-STRIP_FROM_INC_PATH    = 
+STRIP_FROM_INC_PATH    =
 
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
-# (but less readable) file names. This can be useful is your file systems 
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
 # doesn't support long names like on DOS, Mac, or CD-ROM.
 
 SHORT_NAMES            = NO
 
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
-# will interpret the first line (until the first dot) of a JavaDoc-style 
-# comment as the brief description. If set to NO, the JavaDoc 
-# comments will behave just like regular Qt-style comments 
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
 # (thus requiring an explicit @brief command for a brief description.)
 
 JAVADOC_AUTOBRIEF      = YES
 
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
-# interpret the first line (until the first dot) of a Qt-style 
-# comment as the brief description. If set to NO, the comments 
-# will behave just like regular Qt-style comments (thus requiring 
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
 # an explicit \brief command for a brief description.)
 
 QT_AUTOBRIEF           = NO
 
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
-# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
-# comments) as a brief description. This used to be the default behaviour. 
-# The new default is to treat a multi-line C++ comment block as a detailed 
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
 # description. Set this tag to YES if you prefer the old behaviour instead.
 
 MULTILINE_CPP_IS_BRIEF = NO
 
-# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
-# will output the detailed description near the top, like JavaDoc.
-# If set to NO, the detailed description appears after the member 
-# documentation.
-
-DETAILS_AT_TOP         = NO
-
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
-# member inherits the documentation from any documented member that it 
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
 # re-implements.
 
 INHERIT_DOCS           = YES
 
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
-# a new page for each member. If set to NO, the documentation of a member will 
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
 # be part of the file/class/namespace that contains it.
 
 SEPARATE_MEMBER_PAGES  = NO
 
-# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
 # Doxygen uses this value to replace tabs by spaces in code fragments.
 
 TAB_SIZE               = 4
 
-# This tag can be used to specify a number of aliases that acts 
-# as commands in the documentation. An alias has the form "name=value". 
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
-# put the command \sideeffect (or @sideeffect) in the documentation, which 
-# will result in a user-defined paragraph with heading "Side Effects:". 
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
 # You can put \n's in the value part of an alias to insert newlines.
 
 ALIASES                = "signal=- @ref  " \
@@ -195,25 +189,49 @@
                          "endsignals=  " \
                          "constreturn=@note The return value of this function must not be modified or freed. @return  "
 
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
-# sources only. Doxygen will then generate output that is more tailored for C. 
-# For instance, some of the names that are used will be different. The list 
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
 # of all members will be omitted, etc.
 
 OPTIMIZE_OUTPUT_FOR_C  = YES
 
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
-# sources only. Doxygen will then generate output that is more tailored for Java. 
-# For instance, namespaces will be presented as packages, qualified scopes 
-# will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
 
 OPTIMIZE_OUTPUT_JAVA   = NO
 
-# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to 
-# include (a tag file for) the STL sources as input, then you should 
-# set this tag to YES in order to let doxygen match functions declarations and 
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
-# func(std::string) {}). This also make the inheritance and collaboration 
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
 # diagrams that involve STL classes more complete and accurate.
 
 BUILTIN_STL_SUPPORT    = NO
@@ -223,266 +241,352 @@
 
 CPP_CLI_SUPPORT        = NO
 
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
-# tag is set to YES, then doxygen will reuse the documentation of the first 
-# member in the group (if any) for the other members of the group. By default 
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
 # all members of a group must be documented explicitly.
 
 DISTRIBUTE_GROUP_DOC   = NO
 
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
-# the same type (for instance a group of public functions) to be put as a 
-# subgroup of that type (e.g. under the Public Functions section). Set it to 
-# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
 # the \nosubgrouping command.
 
 SUBGROUPING            = YES
 
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
 
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
-# documentation are documented, even if no documentation was available. 
-# Private class members and static file members will be hidden unless 
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
 # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
 
 EXTRACT_ALL            = NO
 
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
 # will be included in the documentation.
 
 EXTRACT_PRIVATE        = NO
 
-# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
 # will be included in the documentation.
 
 EXTRACT_STATIC         = NO
 
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
-# defined locally in source files will be included in the documentation. 
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
 # If set to NO only classes defined in header files are included.
 
 EXTRACT_LOCAL_CLASSES  = YES
 
-# This flag is only useful for Objective-C code. When set to YES local 
-# methods, which are defined in the implementation section but not in 
-# the interface are included in the documentation. 
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
 # If set to NO (the default) only methods in the interface are included.
 
 EXTRACT_LOCAL_METHODS  = YES
 
-# If this flag is set to YES, the members of anonymous namespaces will be extracted 
-# and appear in the documentation as a namespace called 'anonymous_namespace{file}', 
-# where file will be replaced with the base name of the file that contains the anonymous 
-# namespace. By default anonymous namespace are hidden.
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
 
 EXTRACT_ANON_NSPACES   = NO
 
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
-# undocumented members of documented classes, files or namespaces. 
-# If set to NO (the default) these members will be included in the 
-# various overviews, but no documentation section is generated. 
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
 # This option has no effect if EXTRACT_ALL is enabled.
 
 HIDE_UNDOC_MEMBERS     = NO
 
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
-# undocumented classes that are normally visible in the class hierarchy. 
-# If set to NO (the default) these classes will be included in the various 
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
 # overviews. This option has no effect if EXTRACT_ALL is enabled.
 
 HIDE_UNDOC_CLASSES     = NO
 
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
-# friend (class|struct|union) declarations. 
-# If set to NO (the default) these declarations will be included in the 
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
 # documentation.
 
 HIDE_FRIEND_COMPOUNDS  = NO
 
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
-# documentation blocks found inside the body of a function. 
-# If set to NO (the default) these blocks will be appended to the 
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
 # function's detailed documentation block.
 
 HIDE_IN_BODY_DOCS      = NO
 
-# The INTERNAL_DOCS tag determines if documentation 
-# that is typed after a \internal command is included. If the tag is set 
-# to NO (the default) then the documentation will be excluded. 
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
 # Set it to YES to include the internal documentation.
 
 INTERNAL_DOCS          = NO
 
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
-# file names in lower-case letters. If set to YES upper-case letters are also 
-# allowed. This is useful if you have classes or files whose names only differ 
-# in case and if your file system supports case sensitive file names. Windows 
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
 # and Mac users are advised to set this option to NO.
 
 CASE_SENSE_NAMES       = YES
 
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
-# will show members with their full class and namespace scopes in the 
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
 # documentation. If set to YES the scope will be hidden.
 
 HIDE_SCOPE_NAMES       = NO
 
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
-# will put a list of the files that are included by a file in the documentation 
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
 # of that file.
 
 SHOW_INCLUDE_FILES     = YES
 
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
 # is inserted in the documentation for inline members.
 
 INLINE_INFO            = YES
 
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
-# will sort the (detailed) documentation of file and class members 
-# alphabetically by member name. If set to NO the members will appear in 
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
 # declaration order.
 
 SORT_MEMBER_DOCS       = YES
 
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
-# brief documentation of file, namespace and class members alphabetically 
-# by member name. If set to NO (the default) the members will appear in 
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
 # declaration order.
 
 SORT_BRIEF_DOCS        = NO
 
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
-# sorted by fully-qualified names, including namespaces. If set to 
-# NO (the default), the class list will be sorted only by class name, 
-# not including the namespace part. 
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
 # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the 
+# Note: This option applies only to the class list, not to the
 # alphabetical list.
 
 SORT_BY_SCOPE_NAME     = YES
 
-# The GENERATE_TODOLIST tag can be used to enable (YES) or 
-# disable (NO) the todo list. This list is created by putting \todo 
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
 # commands in the documentation.
 
 GENERATE_TODOLIST      = YES
 
-# The GENERATE_TESTLIST tag can be used to enable (YES) or 
-# disable (NO) the test list. This list is created by putting \test 
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
 # commands in the documentation.
 
 GENERATE_TESTLIST      = YES
 
-# The GENERATE_BUGLIST tag can be used to enable (YES) or 
-# disable (NO) the bug list. This list is created by putting \bug 
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
 # commands in the documentation.
 
 GENERATE_BUGLIST       = YES
 
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
-# disable (NO) the deprecated list. This list is created by putting 
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
 # \deprecated commands in the documentation.
 
 GENERATE_DEPRECATEDLIST= YES
 
-# The ENABLED_SECTIONS tag can be used to enable conditional 
+# The ENABLED_SECTIONS tag can be used to enable conditional
 # documentation sections, marked by \if sectionname ... \endif.
 
-ENABLED_SECTIONS       = 
+ENABLED_SECTIONS       =
 
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
-# the initial value of a variable or define consists of for it to appear in 
-# the documentation. If the initializer consists of more lines than specified 
-# here it will be hidden. Use a value of 0 to hide initializers completely. 
-# The appearance of the initializer of individual variables and defines in the 
-# documentation can be controlled using \showinitializer or \hideinitializer 
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
 # command in the documentation regardless of this setting.
 
 MAX_INITIALIZER_LINES  = 30
 
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
-# at the bottom of the documentation of classes and structs. If set to YES the 
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
 # list will mention the files that were used to generate the documentation.
 
 SHOW_USED_FILES        = YES
 
-# If the sources in your project are distributed over multiple directories 
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
 # in the documentation. The default is NO.
 
 SHOW_DIRECTORIES       = YES
 
-# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
-# doxygen should invoke to get the current version for each file (typically from the 
-# version control system). Doxygen will invoke the program by executing (via 
-# popen()) the command <command> <input-file>, where <command> is the value of 
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
-# provided by doxygen. Whatever the program writes to standard output 
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
 # is used as the file version. See the manual for examples.
 
-FILE_VERSION_FILTER    = 
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE            =
 
 #---------------------------------------------------------------------------
 # configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
 
-# The QUIET tag can be used to turn on/off the messages that are generated 
+# The QUIET tag can be used to turn on/off the messages that are generated
 # by doxygen. Possible values are YES and NO. If left blank NO is used.
 
 QUIET                  = NO
 
-# The WARNINGS tag can be used to turn on/off the warning messages that are 
-# generated by doxygen. Possible values are YES and NO. If left blank 
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
 # NO is used.
 
 WARNINGS               = YES
 
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
 # automatically be disabled.
 
 WARN_IF_UNDOCUMENTED   = NO
 
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
-# potential errors in the documentation, such as not documenting some 
-# parameters in a documented function, or documenting parameters that 
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
 # don't exist or using markup commands wrongly.
 
 WARN_IF_DOC_ERROR      = YES
 
-# This WARN_NO_PARAMDOC option can be abled to get warnings for 
-# functions that are documented, but have no documentation for their parameters 
-# or return value. If set to NO (the default) doxygen will only warn about 
-# wrong or incomplete parameter documentation, but not about the absence of 
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
 # documentation.
 
 WARN_NO_PARAMDOC       = YES
 
-# The WARN_FORMAT tag determines the format of the warning messages that 
-# doxygen can produce. The string should contain the $file, $line, and $text 
-# tags, which will be replaced by the file and line number from which the 
-# warning originated and the warning text. Optionally the format may contain 
-# $version, which will be replaced by the version of the file (if it could 
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
 # be obtained via FILE_VERSION_FILTER)
 
 WARN_FORMAT            = "$file:$line: $text  "
 
-# The WARN_LOGFILE tag can be used to specify a file to which warning 
-# and error messages should be written. If left blank the output is written 
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
 # to stderr.
 
-WARN_LOGFILE           = 
+WARN_LOGFILE           =
 
 #---------------------------------------------------------------------------
 # configuration options related to the input files
 #---------------------------------------------------------------------------
 
-# The INPUT tag can be used to specify the files and/or directories that contain 
-# documented source files. You may enter file names like "myfile.cpp" or 
-# directories like "/usr/src/myproject". Separate the files or directories 
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
 INPUT                  = @top_srcdir@/libpurple \
@@ -491,104 +595,109 @@
                          @top_srcdir@/pidgin \
                          @top_srcdir@/doc
 
-# This tag can be used to specify the character encoding of the source files that 
-# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default 
-# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. 
-# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
 
 INPUT_ENCODING         = UTF-8
 
-# If the value of the INPUT tag contains directories, you can use the 
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
-# blank the following patterns are tested: 
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
-# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
 
 FILE_PATTERNS          = *.h \
                          *.dox
 
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
-# should be searched for input files as well. Possible values are YES and NO. 
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
 # If left blank NO is used.
 
 RECURSIVE              = NO
 
-# The EXCLUDE tag can be used to specify files and/or directories that should 
-# excluded from the INPUT source files. This way you can easily exclude a 
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
 # subdirectory from a directory tree whose root is specified with the INPUT tag.
 
 EXCLUDE                = libpurple/purple-client.h \
                          libpurple/purple-client-bindings.h
 
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
-# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
 # from the input.
 
 EXCLUDE_SYMLINKS       = NO
 
-# If the value of the INPUT tag contains directories, you can use the 
-# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
-# certain files from those directories. Note that the wildcards are matched 
-# against the file with absolute path, so to exclude all test directories 
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
 # for example use the pattern */test/*
 
-EXCLUDE_PATTERNS       = 
+EXCLUDE_PATTERNS       =
 
-# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
-# (namespaces, classes, functions, etc.) that should be excluded from the output. 
-# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, 
-# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
 
-EXCLUDE_SYMBOLS        = 
+EXCLUDE_SYMBOLS        =
 
-# The EXAMPLE_PATH tag can be used to specify one or more files or 
-# directories that contain example code fragments that are included (see 
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
 # the \include command).
 
-EXAMPLE_PATH           = 
+EXAMPLE_PATH           =
 
-# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
-# and *.h) to filter out the source-files in the directories. If left 
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
 # blank all files are included.
 
-EXAMPLE_PATTERNS       = 
+EXAMPLE_PATTERNS       =
 
-# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
-# searched for input files to be used with the \include or \dontinclude 
-# commands irrespective of the value of the RECURSIVE tag. 
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
 # Possible values are YES and NO. If left blank NO is used.
 
 EXAMPLE_RECURSIVE      = NO
 
-# The IMAGE_PATH tag can be used to specify one or more files or 
-# directories that contain image that are included in the documentation (see 
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
 # the \image command).
 
-IMAGE_PATH             = 
+IMAGE_PATH             =
 
-# The INPUT_FILTER tag can be used to specify a program that doxygen should 
-# invoke to filter for each input file. Doxygen will invoke the filter program 
-# by executing (via popen()) the command <filter> <input-file>, where <filter> 
-# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
-# input file. Doxygen will then use the output that the filter program writes 
-# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
 # ignored.
 
-INPUT_FILTER           = 
+INPUT_FILTER           =
 
-# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
-# basis.  Doxygen will compare the file name with each pattern and apply the 
-# filter if there is a match.  The filters are a list of the form: 
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
-# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
 # is applied to all files.
 
-FILTER_PATTERNS        = 
+FILTER_PATTERNS        =
 
-# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
-# INPUT_FILTER) will be used to filter the input files when producing source 
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
 # files to browse (i.e. when SOURCE_BROWSER is set to YES).
 
 FILTER_SOURCE_FILES    = NO
@@ -597,34 +706,32 @@
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
 
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
-# be generated. Documented entities will be cross-referenced with these sources. 
-# Note: To get rid of all source code in the generated output, make sure also 
-# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH 
-# then you must also enable this option. If you don't then doxygen will produce 
-# a warning and turn it on anyway
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
 
 SOURCE_BROWSER         = YES
 
-# Setting the INLINE_SOURCES tag to YES will include the body 
+# Setting the INLINE_SOURCES tag to YES will include the body
 # of functions and classes directly in the documentation.
 
 INLINE_SOURCES         = NO
 
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
-# doxygen to hide any special comment blocks from generated source code 
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
 # fragments. Normal C and C++ comments will always remain visible.
 
 STRIP_CODE_COMMENTS    = YES
 
-# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
-# then for each documented function all documented 
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
 # functions referencing it will be listed.
 
 REFERENCED_BY_RELATION = YES
 
-# If the REFERENCES_RELATION tag is set to YES (the default) 
-# then for each documented function all documented entities 
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
 # called/used by that function will be listed.
 
 REFERENCES_RELATION    = YES
@@ -632,20 +739,21 @@
 # If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
 # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
 # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.  Otherwise they will link to the documentstion.
+# link to the source code.
+# Otherwise they will link to the documentation.
 
 REFERENCES_LINK_SOURCE = YES
 
-# If the USE_HTAGS tag is set to YES then the references to source code 
-# will point to the HTML generated by the htags(1) tool instead of doxygen 
-# built-in source browser. The htags tool is part of GNU's global source 
-# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
 # will need version 4.8.6 or higher.
 
 USE_HTAGS              = NO
 
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
-# will generate a verbatim copy of the header file for each class for 
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
 # which an include is specified. Set to NO to disable this.
 
 VERBATIM_HEADERS       = YES
@@ -654,21 +762,21 @@
 # configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
 
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
-# of all compounds will be generated. Enable this if the project 
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
 # contains a lot of classes, structs, unions or interfaces.
 
 ALPHABETICAL_INDEX     = YES
 
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
 # in which this list will be split (can be a number in the range [1..20])
 
 COLS_IN_ALPHA_INDEX    = 3
 
-# In case all classes in a project start with a common prefix, all 
-# classes will be put under the same header in the alphabetical index. 
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
 # should be ignored while generating the index headers.
 
 IGNORE_PREFIX          = Purple \
@@ -679,264 +787,465 @@
 # configuration options related to the HTML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
 # generate HTML output.
 
 GENERATE_HTML          = YES
 
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `html' will be used as the default path.
 
 HTML_OUTPUT            = html
 
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
 # doxygen will generate files with .html extension.
 
 HTML_FILE_EXTENSION    = .html
 
-# The HTML_HEADER tag can be used to specify a personal HTML header for 
-# each generated HTML page. If it is left blank doxygen will generate a 
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
 # standard header.
 
 HTML_HEADER            = @top_srcdir@/doc/TracHeader.html
 
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
-# each generated HTML page. If it is left blank doxygen will generate a 
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
 # standard footer.
 
 HTML_FOOTER            = @top_srcdir@/doc/TracFooter.html
 
-# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
-# style sheet that is used by each HTML page. It can be used to 
-# fine-tune the look of the HTML output. If the tag is left blank doxygen 
-# will generate a default style sheet. Note that doxygen will try to copy 
-# the style sheet file to the HTML output directory, so don't put your own 
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
 # stylesheet in the HTML output directory as well, or it will be erased!
 
-HTML_STYLESHEET        = 
+HTML_STYLESHEET        =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT    = 100
 
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
-# files or namespaces will be aligned in HTML using tables. If set to 
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
 # NO a bullet list will be used.
 
 HTML_ALIGN_MEMBERS     = YES
 
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
-# will be generated that can be used as input for tools like the 
-# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
 # of the generated HTML documentation.
 
 GENERATE_HTMLHELP      = YES
 
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
-# documentation will contain sections that can be hidden and shown after the 
-# page has loaded. For this to work a browser that supports 
-# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
-# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
-
-HTML_DYNAMIC_SECTIONS  = NO
-
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
-# be used to specify the file name of the resulting .chm file. You 
-# can add a path in front of the file if the result should not be 
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
 # written to the html output directory.
 
-CHM_FILE               = 
+CHM_FILE               =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
-# be used to specify the location (absolute path including file name) of 
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
 # the HTML help compiler on the generated index.hhp.
 
-HHC_LOCATION           = 
+HHC_LOCATION           =
 
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
-# controls if a separate .chi index file is generated (YES) or that 
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
 # it should be included in the master .chm file (NO).
 
 GENERATE_CHI           = NO
 
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
-# controls whether a binary table of contents is generated (YES) or a 
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
 # normal table of contents (NO) in the .chm file.
 
 BINARY_TOC             = NO
 
-# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
 # to the contents of the HTML help documentation and to the tree view.
 
 TOC_EXPAND             = YES
 
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
-# top of each HTML page. The value NO (the default) enables the index and 
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
 # the value YES disables it.
 
 DISABLE_INDEX          = NO
 
-# This tag can be used to set the number of enum values (range [1..20]) 
+# This tag can be used to set the number of enum values (range [1..20])
 # that doxygen will group on one line in the generated HTML documentation.
 
 ENUM_VALUES_PER_LINE   = 4
 
-# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
-# generated containing a tree-like index structure (just like the one that 
-# is generated for HTML Help). For this to work a browser that supports 
-# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
-# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
-# probably better off using the HTML help feature.
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
 
 GENERATE_TREEVIEW      = YES
 
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
-# used to set the initial width (in pixels) of the frame in which the tree 
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
 # is shown.
 
 TREEVIEW_WIDTH         = 250
 
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT    = YES
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
 # generate Latex output.
 
 GENERATE_LATEX         = NO
 
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `latex' will be used as the default path.
 
 LATEX_OUTPUT           = latex
 
-# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
 # invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
 
 LATEX_CMD_NAME         = latex
 
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
-# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
 # default command name.
 
 MAKEINDEX_CMD_NAME     = makeindex
 
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
-# LaTeX documents. This may be useful for small projects and may help to 
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
 # save some trees in general.
 
 COMPACT_LATEX          = NO
 
-# The PAPER_TYPE tag can be used to set the paper type that is used 
-# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
 # executive. If left blank a4wide will be used.
 
 PAPER_TYPE             = a4wide
 
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
 # packages that should be included in the LaTeX output.
 
-EXTRA_PACKAGES         = 
+EXTRA_PACKAGES         =
 
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
-# the generated latex document. The header should contain everything until 
-# the first chapter. If it is left blank doxygen will generate a 
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
 # standard header. Notice: only use this tag if you know what you are doing!
 
-LATEX_HEADER           = 
+LATEX_HEADER           =
 
-# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
-# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
-# contain links (just like the HTML output) instead of page references 
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
 # This makes the output suitable for online browsing using a pdf viewer.
 
 PDF_HYPERLINKS         = NO
 
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
-# plain latex in the generated Makefile. Set this option to YES to get a 
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
 # higher quality PDF documentation.
 
 USE_PDFLATEX           = NO
 
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
-# command to the generated LaTeX files. This will instruct LaTeX to keep 
-# running if errors occur, instead of asking the user for help. 
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
 # This option is also used when generating formulas in HTML.
 
 LATEX_BATCHMODE        = NO
 
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
-# include the index chapters (such as File Index, Compound Index, etc.) 
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
 # in the output.
 
 LATEX_HIDE_INDICES     = NO
 
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
 #---------------------------------------------------------------------------
 # configuration options related to the RTF output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
-# The RTF output is optimized for Word 97 and may not look very pretty with 
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
 # other RTF readers or editors.
 
 GENERATE_RTF           = NO
 
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `rtf' will be used as the default path.
 
 RTF_OUTPUT             = rtf
 
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
-# RTF documents. This may be useful for small projects and may help to 
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
 # save some trees in general.
 
 COMPACT_RTF            = NO
 
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
-# will contain hyperlink fields. The RTF file will 
-# contain links (just like the HTML output) instead of page references. 
-# This makes the output suitable for online browsing using WORD or other 
-# programs which support those fields. 
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
 # Note: wordpad (write) and others do not support links.
 
 RTF_HYPERLINKS         = NO
 
-# Load stylesheet definitions from file. Syntax is similar to doxygen's 
-# config file, i.e. a series of assignments. You only have to provide 
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
 # replacements, missing definitions are set to their default value.
 
-RTF_STYLESHEET_FILE    = 
+RTF_STYLESHEET_FILE    =
 
-# Set optional variables used in the generation of an rtf document. 
+# Set optional variables used in the generation of an rtf document.
 # Syntax is similar to doxygen's config file.
 
-RTF_EXTENSIONS_FILE    = 
+RTF_EXTENSIONS_FILE    =
 
 #---------------------------------------------------------------------------
 # configuration options related to the man page output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
 # generate man pages
 
 GENERATE_MAN           = NO
 
-# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `man' will be used as the default path.
 
 MAN_OUTPUT             = man
 
-# The MAN_EXTENSION tag determines the extension that is added to 
+# The MAN_EXTENSION tag determines the extension that is added to
 # the generated man pages (default is the subroutine's section .3)
 
 MAN_EXTENSION          = .3
 
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
-# then it will generate one additional man file for each entity 
-# documented in the real man page(s). These additional files 
-# only source the real man page, but without them the man command 
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
 # would be unable to find the correct page. The default is NO.
 
 MAN_LINKS              = NO
@@ -945,33 +1254,33 @@
 # configuration options related to the XML output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_XML tag is set to YES Doxygen will 
-# generate an XML file that captures the structure of 
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
 # the code including all documentation.
 
 GENERATE_XML           = YES
 
-# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `xml' will be used as the default path.
 
 XML_OUTPUT             = xml
 
-# The XML_SCHEMA tag can be used to specify an XML schema, 
-# which can be used by a validating XML parser to check the 
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
 # syntax of the XML files.
 
-XML_SCHEMA             = 
+XML_SCHEMA             =
 
-# The XML_DTD tag can be used to specify an XML DTD, 
-# which can be used by a validating XML parser to check the 
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
 # syntax of the XML files.
 
-XML_DTD                = 
+XML_DTD                =
 
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
-# dump the program listings (including syntax highlighting 
-# and cross-referencing information) to the XML output. Note that 
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
 # enabling this will significantly increase the size of the XML output.
 
 XML_PROGRAMLISTING     = YES
@@ -980,10 +1289,10 @@
 # configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
-# generate an AutoGen Definitions (see autogen.sf.net) file 
-# that captures the structure of the code including all 
-# documentation. Note that this feature is still experimental 
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
 # and incomplete at the moment.
 
 GENERATE_AUTOGEN_DEF   = NO
@@ -992,319 +1301,346 @@
 # configuration options related to the Perl module output
 #---------------------------------------------------------------------------
 
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
-# generate a Perl module file that captures the structure of 
-# the code including all documentation. Note that this 
-# feature is still experimental and incomplete at the 
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
 # moment.
 
 GENERATE_PERLMOD       = NO
 
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
 # to generate PDF and DVI output from the Perl module output.
 
 PERLMOD_LATEX          = NO
 
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
-# nicely formatted so it can be parsed by a human reader.  This is useful 
-# if you want to understand what is going on.  On the other hand, if this 
-# tag is set to NO the size of the Perl module output will be much smaller 
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
 # and Perl will parse it just the same.
 
 PERLMOD_PRETTY         = YES
 
-# The names of the make variables in the generated doxyrules.make file 
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
-# This is useful so different doxyrules.make files included by the same 
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
 # Makefile don't overwrite each other's variables.
 
-PERLMOD_MAKEVAR_PREFIX = 
+PERLMOD_MAKEVAR_PREFIX =
 
 #---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
+# Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
 
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
-# evaluate all C-preprocessor directives found in the sources and include 
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
 # files.
 
 ENABLE_PREPROCESSING   = YES
 
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
-# names in the source code. If set to NO (the default) only conditional 
-# compilation will be performed. Macro expansion can be done in a controlled 
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
 # way by setting EXPAND_ONLY_PREDEF to YES.
 
 MACRO_EXPANSION        = NO
 
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
-# then the macro expansion is limited to the macros specified with the 
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
 # PREDEFINED and EXPAND_AS_DEFINED tags.
 
 EXPAND_ONLY_PREDEF     = NO
 
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
 # in the INCLUDE_PATH (see below) will be search if a #include is found.
 
 SEARCH_INCLUDES        = YES
 
-# The INCLUDE_PATH tag can be used to specify one or more directories that 
-# contain include files that are not input files but should be processed by 
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
 # the preprocessor.
 
-INCLUDE_PATH           = 
+INCLUDE_PATH           =
 
-# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
-# patterns (like *.h and *.hpp) to filter out the header-files in the 
-# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
 # be used.
 
-INCLUDE_FILE_PATTERNS  = 
+INCLUDE_FILE_PATTERNS  =
 
-# The PREDEFINED tag can be used to specify one or more macro names that 
-# are defined before the preprocessor is started (similar to the -D option of 
-# gcc). The argument of the tag is a list of macros of the form: name 
-# or name=definition (no spaces). If the definition and the = are 
-# omitted =1 is assumed. To prevent a macro definition from being 
-# undefined via #undef or recursively expanded use the := operator 
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
 # instead of the = operator.
 
-PREDEFINED             = 
+PREDEFINED             =
 
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
-# this tag can be used to specify a list of macro names that should be expanded. 
-# The macro definition that is found in the sources will be used. 
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
 # Use the PREDEFINED tag if you want to use a different macro definition.
 
-EXPAND_AS_DEFINED      = 
+EXPAND_AS_DEFINED      =
 
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
-# doxygen's preprocessor will remove all function-like macros that are alone 
-# on a line, have an all uppercase name, and do not end with a semicolon. Such 
-# function macros are typically used for boiler-plate code, and will confuse 
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
 # the parser if not removed.
 
 SKIP_FUNCTION_MACROS   = YES
 
 #---------------------------------------------------------------------------
-# Configuration::additions related to external references   
+# Configuration::additions related to external references
 #---------------------------------------------------------------------------
 
-# The TAGFILES option can be used to specify one or more tagfiles. 
-# Optionally an initial location of the external documentation 
-# can be added for each tagfile. The format of a tag file without 
-# this location is as follows: 
-#   TAGFILES = file1 file2 ... 
-# Adding location for the tag files is done as follows: 
-#   TAGFILES = file1=loc1 "file2 = loc2" ... 
-# where "loc1" and "loc2" can be relative or absolute paths or 
-# URLs. If a location is present for each tag, the installdox tool 
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
 # does not have to be run to correct the links.
 # Note that each tag file must have a unique name
 # (where the name does NOT include the path)
-# If a tag file is not located in the directory in which doxygen 
+# If a tag file is not located in the directory in which doxygen
 # is run, you must also specify the path to the tagfile here.
 
-TAGFILES               = 
+TAGFILES               =
 
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
 # a tag file that is based on the input files it reads.
 
-GENERATE_TAGFILE       = 
+GENERATE_TAGFILE       =
 
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
-# in the class index. If set to NO only the inherited external classes 
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
 # will be listed.
 
 ALLEXTERNALS           = NO
 
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
-# in the modules index. If set to NO, only the current project's groups will 
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
 # be listed.
 
 EXTERNAL_GROUPS        = YES
 
-# The PERL_PATH should be the absolute path and name of the perl script 
+# The PERL_PATH should be the absolute path and name of the perl script
 # interpreter (i.e. the result of `which perl').
 
 PERL_PATH              = /usr/bin/perl
 
 #---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
+# Configuration options related to the dot tool
 #---------------------------------------------------------------------------
 
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
-# or super classes. Setting the tag to NO turns the diagrams off. Note that 
-# this option is superseded by the HAVE_DOT option below. This is only a 
-# fallback. It is recommended to install and use dot, since it yields more 
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
 # powerful graphs.
 
 CLASS_DIAGRAMS         = YES
 
-# You can define message sequence charts within doxygen comments using the \msc 
-# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to 
-# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to 
-# specify the directory where the mscgen tool resides. If left empty the tool is assumed to 
-# be found in the default search path.
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
 
-MSCGEN_PATH            = 
+MSCGEN_PATH            =
 
-# If set to YES, the inheritance and collaboration graphs will hide 
-# inheritance and usage relations if the target is undocumented 
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
 # or is not a class.
 
 HIDE_UNDOC_RELATIONS   = YES
 
-# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
-# available from the path. This tool is part of Graphviz, a graph visualization 
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
 # have no effect if this option is set to NO (the default)
 
 HAVE_DOT               = @enable_dot@
 
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect inheritance relations. Setting this tag to YES will force the 
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS        = 0
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
 # the CLASS_DIAGRAMS tag to NO.
 
 CLASS_GRAPH            = YES
 
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
-# will generate a graph for each documented class showing the direct and 
-# indirect implementation dependencies (inheritance, containment, and 
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
 # class references variables) of the class with other documented classes.
 
 COLLABORATION_GRAPH    = YES
 
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
 # will generate a graph for groups, showing the direct groups dependencies
 
 GROUP_GRAPHS           = YES
 
-# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
-# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
 # Language.
 
 UML_LOOK               = YES
 
-# If set to YES, the inheritance and collaboration graphs will show the 
+# If set to YES, the inheritance and collaboration graphs will show the
 # relations between templates and their instances.
 
 TEMPLATE_RELATIONS     = YES
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
-# tags are set to YES then doxygen will generate a graph for each documented 
-# file showing the direct and indirect include dependencies of the file with 
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
 # other documented files.
 
 INCLUDE_GRAPH          = YES
 
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
-# documented header file showing the documented files that directly or 
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
 # indirectly include this file.
 
 INCLUDED_BY_GRAPH      = YES
 
-# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will 
-# generate a call dependency graph for every global function or class method. 
-# Note that enabling this option will significantly increase the time of a run. 
-# So in most cases it will be better to enable call graphs for selected 
-# functions only using the \callgraph command.
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
 
 CALL_GRAPH             = YES
 
-# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will 
-# generate a caller dependency graph for every global function or class method. 
-# Note that enabling this option will significantly increase the time of a run. 
-# So in most cases it will be better to enable caller graphs for selected 
-# functions only using the \callergraph command.
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
 
 CALLER_GRAPH           = NO
 
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
 # will graphical hierarchy of all classes instead of a textual one.
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
-# then doxygen will show the dependencies a directory has on other directories 
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
 # in a graphical way. The dependency relations are determined by the #include
 # relations between the files in the directories.
 
 DIRECTORY_GRAPH        = YES
 
-# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
 # generated by dot. Possible values are png, jpg, or gif
 # If left blank png will be used.
 
 DOT_IMAGE_FORMAT       = png
 
-# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
 # found. If left blank, it is assumed the dot tool can be found in the path.
 
-DOT_PATH               = 
+DOT_PATH               =
 
-# The DOTFILE_DIRS tag can be used to specify one or more directories that 
-# contain dot files that are included in the documentation (see the 
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
 # \dotfile command).
 
-DOTFILE_DIRS           = 
+DOTFILE_DIRS           =
 
-# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
-# nodes that will be shown in the graph. If the number of nodes in a graph 
-# becomes larger than this value, doxygen will truncate the graph, which is 
-# visualized by representing a node as a red box. Note that doxygen if the number 
-# of direct children of the root node in a graph is already larger than 
-# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note 
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
 # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
 
 DOT_GRAPH_MAX_NODES    = 50
 
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
-# graphs generated by dot. A depth value of 3 means that only nodes reachable 
-# from the root by following a path via at most 3 edges will be shown. Nodes 
-# that lay further from the root node will be omitted. Note that setting this 
-# option to 1 or 2 may greatly reduce the computation time needed for large 
-# code bases. Also note that the size of a graph can be further restricted by 
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
 
 MAX_DOT_GRAPH_DEPTH    = 2
 
-# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
-# background. This is disabled by default, which results in a white background. 
-# Warning: Depending on the platform used, enabling this option may lead to 
-# badly anti-aliased labels on the edges of a graph (i.e. they become hard to 
-# read).
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
 
 DOT_TRANSPARENT        = NO
 
-# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
-# files in one run (i.e. multiple -o and -T options on the command line). This 
-# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
 # support this, this feature is disabled by default.
 
 DOT_MULTI_TARGETS      = YES
 
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
-# generate a legend page explaining the meaning of the various boxes and 
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
 # arrows in the dot generated graphs.
 
 GENERATE_LEGEND        = YES
 
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
-# remove the intermediate dot files that are used to generate 
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
 # the various graphs.
 
 DOT_CLEANUP            = YES
-
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine   
-#---------------------------------------------------------------------------
-
-# The SEARCHENGINE tag specifies whether or not a search engine should be 
-# used. If set to NO the values of all tags below this one will be ignored.
-
-SEARCHENGINE           = YES
--- a/INSTALL	Wed Jun 13 19:28:57 2012 -0400
+++ b/INSTALL	Wed Jun 13 19:30:27 2012 -0400
@@ -158,7 +158,7 @@
 
 	`--with-dynamic-prpls' takes a list of comma separated protocols also.  If used only those listed will be built.  If no protocols are listed with either `--with-static-prpls' or with `--with-dynamic-prpls' then Pidgin and Finch will be effectively useless.
 
-	If configure does not find python, it will build without DBUS support.  Thiswill disable scripts such as purple-remote and purple-uri-handler, effectively disabling integration with the browser.  You can tell configure where your python binary is located with `--with-python=PATH'
+	If configure does not find python, it will build without DBUS support.  This will disable scripts such as purple-remote and purple-uri-handler, effectively disabling integration with the browser.  You can tell configure where your python binary is located with `--with-python=PATH'
 
 Specifying the System Type
 ==========================
--- a/Makefile.am	Wed Jun 13 19:28:57 2012 -0400
+++ b/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -11,8 +11,6 @@
 		config.h.mingw \
 		doxy2devhelp.xsl \
 		fix-casts.sh \
-		gaim.pc.in \
-		gaim-uninstalled.pc.in \
 		intltool-extract.in \
 		intltool-merge.in \
 		intltool-update.in \
@@ -42,20 +40,12 @@
 # We don't want to release development versions.
 	test x`echo $(PACKAGE_VERSION) | grep dev` = x
 
-# Make sure there is a NEWS entry for this version
-	head NEWS | grep "^$(PACKAGE_VERSION) (`date +%m/%d/%Y`):$$" > /dev/null
-
-# Ensure NEWS has no spaces at the start of a line.
-# Using spaces instead of tabs there is a common mistake.
-	test x`grep "^ " NEWS` = x
-
 # When doing a new minor (or major) release (X.Y.0), there must be a section in
 # ChangeLog.API.
 	echo $(PACKAGE_VERSION) | grep -v "^[0-9]\+\.[0-9]\+\.0$$" >/dev/null || head ChangeLog.API | grep "^version $(PACKAGE_VERSION) (`date +%m/%d/%Y`):$$" >/dev/null
 
 # For all releases, check the ChangeLogs.
 	head ChangeLog | grep "^version $(PACKAGE_VERSION) (`date +%m/%d/%Y`):$$" >/dev/null
-	head ChangeLog.win32 | grep "^version $(PACKAGE_VERSION) (`date +%m/%d/%Y`):$$" >/dev/null
 	head po/ChangeLog | grep "^version $(PACKAGE_VERSION)$$" >/dev/null
 
 # Ensure we're working from a tag...
@@ -71,11 +61,20 @@
 	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
 
+if INSTALL_I18N
+PO_DIR=po
+DESKTOP_FILE=pidgin.desktop
+
 if ENABLE_GTK
 appsdir = $(datadir)/applications
 apps_in_files = pidgin.desktop.in
 apps_DATA = $(apps_in_files:.desktop.in=.desktop)
 @INTLTOOL_DESKTOP_RULE@
+endif #ENABLE_GTK
+
+endif #INSTALL_I18N
+
+if ENABLE_GTK
 GTK_DIR=pidgin
 endif
 
@@ -83,10 +82,6 @@
 GNT_DIR=finch
 endif
 
-if INSTALL_I18N
-PO_DIR=po
-endif
-
 # This is phony, so that we always try to rebuild it.  If it succeeds
 # in calculating changes, it produces its target; otherwise, its
 # target does not exist.
@@ -95,17 +90,17 @@
 # creates, and also make sure that the shell command exits
 # successfully; the rm -f ensures both
 package_revision_raw.txt:
-	REAL_BLDDIR=$$PWD/$(top_builddir); \
+	$(AM_V_GEN)REAL_BLDDIR=$$PWD/$(top_builddir); \
 	(hg --cwd $(srcdir) id -i --debug) 2>/dev/null >$@ \
 	|| rm -f $@
 package_revision.h: package_revision_raw.txt
-	if [ -f $< ]; then \
-	  sed 's/^\(.\+\)$$/#define REVISION "\1"/' $< > $@; \
+	$(AM_V_GEN)if test -f $<; then \
+	  echo "#define REVISION \"`cat $<`\"" > $@; \
 	fi
-	if [ ! -f $@ -a -f $(srcdir)/$@ ]; then \
+	$(AM_V_at)if test ! -f $@ -a -f $(srcdir)/$@; then \
 	  cp $(srcdir)/$@ $@; \
 	fi
-	[ -f $@ ] || echo "#define REVISION \"unknown\"" > $@
+	$(AM_V_at)test -f $@ || echo "#define REVISION \"unknown\"" > $@
 
 # This is a magic directive copy-and-pasted, then modified, from the
 # automake 1.9 manual, section 13.4, "Checking the distribution".
@@ -130,7 +125,7 @@
 if HAVE_XSLTPROC
 	@echo "Generating devhelp index..."
 	@xsltproc $(top_srcdir)/doxy2devhelp.xsl doc/xml/index.xml > doc/html/pidgin.devhelp
-	@echo "(Symlink doc/html to ~/.local/share/gtk-doc/html/pidgin to make devhelp see the documentation)"
+	@echo "(Symlink $$(pwd)/doc/html to ~/.local/share/gtk-doc/html/pidgin to make devhelp see the documentation)"
 else
 	@echo "Not generating devhelp index: xsltproc was not found by configure"
 endif
@@ -145,5 +140,5 @@
 distuninstallcheck_listfiles = \
 	find . -type f -print | grep -v perl | grep -v Purple.3pm
 
-DISTCLEANFILES= pidgin.desktop libpurple/gconf/purple.schemas intltool-extract \
+DISTCLEANFILES= $(DESKTOP_FILE) libpurple/gconf/purple.schemas intltool-extract \
 			intltool-merge intltool-update
--- a/Makefile.mingw	Wed Jun 13 19:28:57 2012 -0400
+++ b/Makefile.mingw	Wed Jun 13 19:30:27 2012 -0400
@@ -31,31 +31,34 @@
     exit; \
 }' VERSION)
 
-GTK_INSTALL_VERSION = $(shell \
-  source ../gtk_installer/version.sh; \
-  echo $$gtk_version \
-)
+GTK_INSTALL_VERSION = 2.16.6.0
 
 STRIPPED_RELEASE_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-win32bin
+DEBUG_SYMBOLS_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-dbgsym
 
 
 # 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
 EXTERNAL_DLLS = \
 	comerr32.dll \
+	exchndl.dll \
 	freebl3.dll \
 	gssapi32.dll \
 	k5sprt32.dll \
 	krb5_32.dll \
-	libgtkspell.dll \
+	libenchant.dll \
+	libenchant_ispell.dll \
+	libenchant_myspell.dll \
+	libgtkspell-0.dll \
 	libmeanwhile-1.dll \
+	libnspr4.dll \
+	libplc4.dll \
+	libplds4.dll \
 	libsasl.dll \
-	libxml2.dll \
-	nspr4.dll \
+	libxml2-2.dll \
 	nss3.dll \
 	nssckbi.dll \
-	plc4.dll \
-	plds4.dll \
+	nssutil3.dll \
 	saslANONYMOUS.dll \
 	saslCRAMMD5.dll \
 	saslDIGESTMD5.dll \
@@ -66,12 +69,15 @@
 	libsilcclient-1-1-2.dll \
 	smime3.dll \
 	softokn3.dll \
+	sqlite3.dll \
 	ssl3.dll
 
 #build an expression for `find` to use to ignore the above files
 EXTERNAL_DLLS_FIND_EXP = $(patsubst %,-o -name %,$(EXTERNAL_DLLS))
 
-.PHONY: all docs install installer installer_nogtk installer_debug installers clean uninstall create_release_install_dir
+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)
 
 all: $(PIDGIN_CONFIG_H) $(PIDGIN_REVISION_H)
 	$(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE)
@@ -88,33 +94,74 @@
 endif
 	$(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) install
 	$(MAKE) -C share/sounds -f $(MINGW_MAKEFILE) install
+	mkdir -p $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp $(GTKSPELL_TOP)/bin/libgtkspell-0.dll $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp $(ENCHANT_TOP)/bin/libenchant.dll $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp -R $(ENCHANT_TOP)/lib $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp $(WIN32_DEV_TOP)/pidgin-inst-deps-20100315/exchndl.dll $(PIDGIN_INSTALL_DIR)
+
+pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip:
+	pidgin/win32/nsis/generate_gtk_zip.sh `pwd`
+
+generate_installer_includes: create_release_install_dir pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip 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 {} ';' \
+	 | LC_ALL=C sort | sed -e s/^/\!insertmacro\ LANG_SECTION\ \"/ -e s/$$/\"/ \
+	 > pidgin/win32/nsis/pidgin-translations.nsh
+	#Convert the available.lst lines to "!insertmacro SPELLCHECK_SECTION lang lang_name lang_file"
+	sed -e "/^#/d" -e "s/^[^,]\{1,\},[^,]\{1,\},/\"/" \
+	 -e "s/,/\"\ \"/" -e "s/,/\"\ \"/" -e "s/[\ \t]*$$/\"/" \
+	 -e "s/^/\!insertmacro\ SPELLCHECK_SECTION\ /" \
+         pidgin/win32/nsis/available.lst \
+         > pidgin/win32/nsis/pidgin-spellcheck.nsh
+	#Convert the lines to "!insertmacro CHECK_SPELLCHECK_SECTION lang"
+	iconv -f latin1 -t utf-8 pidgin/win32/nsis/pidgin-spellcheck.nsh | \
+	 sed -e "s/SPELLCHECK_SECTION/CHECK_SPELLCHECK_SECTION/" \
+	 -e "s/ \"[^\"]*\"\ \"[^\"]*\"[\t\ ]*$$//" | \
+         iconv -f utf-8 -t latin1 \
+        > pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh
+	#Generate the Installer translations
+	echo "!define GCOMPRIS_NSIS_INCLUDE_PATH \".\"" > $(PIDGIN_TREE_TOP)/pidgin/win32/nsis/langmacros.nsh
+	echo "@INSERT_TRANSLATIONS@" >> $(PIDGIN_TREE_TOP)/pidgin/win32/nsis/langmacros.nsh
+	$(PERL) $(PIDGIN_TREE_TOP)/pidgin/win32/nsis/create_nsis_translations.pl \
+		$(PIDGIN_TREE_TOP)/pidgin/win32/nsis/nsis_translations.desktop \
+		$(PIDGIN_TREE_TOP)/pidgin/win32/nsis/langmacros.nsh \
+		$(PIDGIN_TREE_TOP)/pidgin/win32/nsis/translations
 
 create_release_install_dir: install
 	rm -rf $(STRIPPED_RELEASE_DIR)
-	cp -R $(PIDGIN_INSTALL_DIR) $(STRIPPED_RELEASE_DIR)
+	mkdir $(STRIPPED_RELEASE_DIR)
+	tar -cf - $(PIDGIN_INSTALL_DIR) --exclude=Gtk --exclude=spellcheck/share \
+	 | tar --strip 2 -xC $(STRIPPED_RELEASE_DIR) -f -
 	find $(STRIPPED_RELEASE_DIR) \( -name '*.dll' -o -name '*.exe' \) \
-	 -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) -exec $(STRIP) --strip-unneeded {} ';'
+	 -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) \
+	 -exec $(STRIP) --strip-unneeded {} ';'
 
-installer: create_release_install_dir
-	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DWITH_GTK $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
+installer: generate_installer_includes
+	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
 	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe ./
 
-installer_nogtk: create_release_install_dir
-	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
-	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-no-gtk.exe ./
-
-installer_debug: install
-	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(PIDGIN_INSTALL_DIR)" $(MAKENSISOPT)DDEBUG $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
-	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-debug.exe ./
+installer_offline: generate_installer_includes
+	$(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DOFFLINE_INSTALLER $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi
+	mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe ./
 
 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)
 
-installers: installer installer_nogtk installer_debug installer_zip
+debug_symbols_zip: install
+	rm -rf $(DEBUG_SYMBOLS_DIR) $(DEBUG_SYMBOLS_DIR).zip
+	mkdir $(DEBUG_SYMBOLS_DIR)
+	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 -
+	zip -9 -r $(DEBUG_SYMBOLS_DIR).zip $(DEBUG_SYMBOLS_DIR) 
+
+installers: installer installer_offline debug_symbols_zip installer_zip
 
 Doxyfile.mingw: Doxyfile.in
-	sed -e "s/@PACKAGE@/pidgin/" -e "s/@VERSION@/$(PIDGIN_VERSION)/" -e "s/@top_srcdir@/$(PIDGIN_TREE_TOP)/g" -e "s/@enable_dot@/NO/" Doxyfile.in > Doxyfile.mingw
+	sed -e "s/@PACKAGE@/pidgin/" -e "s/@VERSION@/$(PIDGIN_VERSION)/" -e "s/@top_srcdir@/$(PIDGIN_TREE_TOP)/g" -e "s/@enable_dot@/NO/" $< > $@
 
 docs: Doxyfile.mingw
 	@echo "Running doxygen..."
@@ -125,11 +172,12 @@
 	$(MAKE) -C $(PIDGIN_TOP) -f $(MINGW_MAKEFILE) clean
 	$(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE) clean
 	$(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) clean
-	rm -f $(PIDGIN_CONFIG_H) $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT) ./VERSION pidgin-$(PIDGIN_VERSION)*.exe pidgin-$(PIDGIN_VERSION)-win32-bin.zip
+	rm -f $(PIDGIN_CONFIG_H) $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT) ./VERSION pidgin-$(PIDGIN_VERSION)*.exe pidgin-$(PIDGIN_VERSION)-win32-bin.zip $(DEBUG_SYMBOLS_DIR).zip
 	rm -rf doc/html Doxyfile.mingw
 
 uninstall:
-	rm -rf $(PURPLE_INSTALL_PERL_DIR) $(PIDGIN_INSTALL_PLUGINS_DIR) $(PURPLE_INSTALL_PO_DIR) $(PIDGIN_INSTALL_DIR) $(STRIPPED_RELEASE_DIR)
+	rm -rf $(PURPLE_INSTALL_PERL_DIR) $(PIDGIN_INSTALL_PLUGINS_DIR) $(PURPLE_INSTALL_PO_DIR) $(PIDGIN_INSTALL_DIR) $(STRIPPED_RELEASE_DIR) $(DEBUG_SYMBOLS_DIR)
 	rm -f ./VERSION
 
 include $(PIDGIN_COMMON_TARGETS)
+
--- a/NEWS	Wed Jun 13 19:28:57 2012 -0400
+++ b/NEWS	Wed Jun 13 19:30:27 2012 -0400
@@ -2,6 +2,228 @@
 
 Our development blog is available at: http://planet.pidgin.im
 
+2.8.0 (06/07/2011):
+	Paul: I fixed a few things in this release, and committed some
+	patches, but am being vague because I can't remember any specifics.
+	Hopefully you enjoy this, and happy May!
+
+	John: Wow, this release has taken forever.  But there are a crapload
+	of patches that have been committed this time around, a lot of them by
+	me.  The infamous MSN bugs are still outstanding, but otherwise this
+	release has brought some excellent progress.  Oh, and we dropped the
+	hasn't-quite-worked-in-ages QQ plugin, too.  Enjoy!
+
+	Jorge: I managed to get one patch applied! Wii for end of semester!
+
+2.7.11 (03/10/2011):
+	John: Yet another release.  This time around we finally fixed that
+	annoying MSN buddy adding problem and a security issue in Yahoo.  You
+	know the drill--upgrade already!
+
+2.7.10 (02/06/2011):
+	John: It's release time again.  This release contains a bunch of stuff
+	committed from Trac.  This is another "thank a patch writer" release.
+	Unfortunately, no one has fixed our wonderful MSN issues yet.  There is
+	a tiny security fix in this release, as well.
+
+2.7.9 (12/26/2010):
+	John: Just a quick release for a security fix here.  Elliott has not
+	yet had a chance to work on the MSN breakage that's been present in
+	the last couple releases, but we hope he can do it before 2.7.10!
+
+2.7.8 (12/19/2010):
+	Elliott: OK, so I know a few things broke with the last release, and
+	it's too bad we had to rush it for that silly certificate thing that
+	the MSN people can't configure properly.  I've certainly done a lot of
+	small fixes this time, but it's too bad we haven't been able to get the
+	transfers with the official client fixed yet.  I promise it'll be in
+	the next release (barring any quick security issues).
+
+	John: So, it's been about a month since we last released.  Again, we've
+	assembled a bugfix release for your enjoyment.  While a few commonly
+	reported bugs remain, particularly in MSN, we're working on it for the
+	next release.  In the meantime, Merry Christmas and enjoy!
+
+2.7.7 (11/23/2010):
+	John: Well, this time around, we should finally have the certificate
+	issue really and fully fixed for all of you MSN users.  Also, we have
+	a few AIM-related fixes in this release, most notably the fix for the
+	new "SSL Handshake Failure" message some of you got after upgrading.
+	That one was an oversight on our part.  Enjoy the fixes!
+
+2.7.6 (11/21/2010):
+	Jorge: In this release I have merged two branches where I have spent
+	most of my time in the last months, the MSNP16 and SLP-rewrite.  I 
+	hope you all will enjoy the hability to be connected on multiple
+	instances at the same time. I also hope that the SLP rewrite
+	fix a lot of old bugs that we have in the tracker. I am really 
+	happy with this rewrite because there was untoched code from almost
+	5 years ago. I hope you like this release!
+
+	John: In this release, we give you some new features and a bunch of
+	bug fixes.  This includes shipping intermediate certificates to fix
+	certificate validation for MSN's servers.  Upgrade and enjoy!
+
+2.7.5 (10/31/2010):
+	John: A bugfix release for all of you!  This time we fixed a bunch of
+	bugs ranging from annoying regressions to long-standing bugs we didn't
+	realize until now were bugs.  Enjoy!
+
+2.7.4 (10/20/2010):
+	John: This release came at this particular time due to some security
+	issues Daniel discovered for us when investigating a bug.  There are
+	a ton of other changes, including some partial Yahoo file transfer
+	fixes and a bunch of other little things.  Enjoy!
+
+2.7.3 (08/10/2010):
+	Mark: Lots of little incremental[1] bug fixes and enhancements in this
+	release.
+
+	[1] No whales were harmed[2] during the creation of this release.
+	[2] Probably.
+
+	John: Finally got some fixes out there for you Yahoo users behind some
+	particularly annoying firewalls and proxies, among other fixes.  Enjoy!
+
+2.7.2 (07/21/2010):
+	Mark: We discovered a security issue in Pidgin 2.7.0 and 2.7.1 and
+	decided to release a patched version quickly.  This release contains
+	the fix for that crash, and a few other minor fixes.
+
+2.7.1 (05/29/2010):
+	Elliott: Hey, I'm first!  How did that happen?!  Maybe because of the
+	interesting changes in this release.  Sure there were quite a few bug
+	fixes, but I know what you've all been waiting for is the direct
+	connections in MSN.  Trust me, it's really really fast!!
+
+	John: Whoa, short turnaround for us.  This is just 17 days after our
+	previous release!  This fixes a number of bugs that you've all been
+	reporting a ton of duplicate tickets about and even gives you the new
+	direct connection file transfer support for MSN.  Enjoy!
+
+	Marcus: Quite a bit quicker to get this release out, compared with the
+	previous one :).  Fixes a number of bugs, and I'm sure the MSN direct
+	connections will please many users.  Enjoy!
+
+2.7.0 (05/12/2010):
+	John: We FINALLY got the ICQ X-Status stuff merged in!  And a few other
+	patches that have been sitting on Trac forever.  Couple that with some
+	new features and we have an excellent release for all of you!
+
+	Marcus: Finally time for a new release, seems like it took
+	quite a while this time.  But then again, there are some new features
+	in there, like file transfer preview (thumbnails) support (only on MSN
+	so far), plus a lot of bug fixes.  Enjoy!
+
+	Paul: Yay, a new release!  I don't think I added very much useful to
+	this release, other than fixing a paste bug from Chrome (no weird
+	characters appended to the end of your URI).  Enjoy!
+
+	Jorge: This is my first NEWS!  I'm not sure I added something nice
+	to this release.  I know it took a lot of time to bring this one
+	out, however I'm really excited by this release.  I hope everyone
+	likes the new features this release brings.
+
+	Elliott: This release took so long, I had to go check the ChangeLog
+	just to see what happened.  I doubt many people will notice, but we
+	dropped support for many old things, like GTK+<2.10 and MSNP9.  In more
+	exciting news, we have file transfer previews on MSN, and support for
+	setting moods on ICQ and XMPP (which has been waiting forever.)
+
+2.6.6 (02/18/2010):
+	Mark: This release includes some great little changes and fixes a few
+	security-related bugs.  See the ChangeLog for details.
+
+2.6.5 (01/08/2010):
+	Paul: This release fixes a pretty serious bug in the MSN code, so we're
+	releasing this build a little earlier than planned with only major
+	bugs fixed.  See the ChangeLog for details.  Enjoy!
+
+2.6.4 (11/29/2009):
+	John:  It's release time again.  Lots of bug fixes this time around, as
+	well as a new protocol plugin developed and maintained by the MXit folks
+	folks.  Elliott and I also did a ton of work on the Preferences window,
+	which will now hopefully fit on most people's small screens.  Enjoy!
+
+	Elliott: This release has been in the works for so long, I don't really
+	remember doing any work on it.  But I do know the MSN servers gave us a
+	little bit of trouble this time around, forgetting people's friendly
+	names.  Nothing too problematic, just a touch annoying.  Also, we've got
+	a nice new Preferences dialog.  You can thank John for that mostly, with
+	a couple of tweaks by me.
+
+	Sadrul: A lot of little fixes for a lot of things! Among them, a fix
+	for a long standing issue with displaying unicode in non-utf8 locale in
+	finch. We also have a new prpl for MXit. This release is very very cool
+	altogether. Enjoy!
+
+2.6.3 (10/16/2009):
+	Mark: Someone reported a fairly serious bug in our AIM/ICQ code
+	so we're releasing a special "severe bug fix only" build.  See the
+	ChangeLog for details.  Enjoy!
+
+2.6.2 (09/05/2009):
+	Mark: Woo boy it's been a busy two weeks.  There was a lot of new code
+	in 2.6.0, and with new code comes new bugs.  The cadre of relentless
+	developers responsible for Pidgin have been hard at work, and I believe
+	they have fixed all the major bugs that cropped up.  My thanks to all
+	those names listed as Current Developers in Pidgin's 'About' window.
+
+	Elliott: Well now, just as Mark said, there was a lot of new stuff that
+	probably came up with tons of bugs.  So I can't say I wrote anything
+	super-awesome, but I definitely fixed quite a few of those itty-bitty
+	why-didn't-this-work-this-way sort of bugs.
+
+2.6.1 (08/18/2009):
+	Mark: There were a lot of changes in 2.6.0, and so a few major bugs
+	crept in.  This is a very minor release to fix those bugs.  Sorry for
+	the inconvenience!
+
+2.6.0 (08/18/2009):
+	John:  Wow, four straight releases that I'm the first to NEWS on.  This
+	is getting kinda scary!  I'm beginning to wonder who else actually does
+	anything around here!  (Just kidding, of course.)  LOTS of new features
+	and a crapton of bugfixes this release.  There should pretty much be
+	something for everybody.  A great example of this is the ton of Yahoo
+	changes that have happened thanks to our SoC student from 2008, Sulabh
+	Mahajan.  Among his massive improvements are the ability to add MSN
+	buddies by adding them as "msn/user@domain.tld" and peer-to-peer file
+	transfers.  Of course, history shows we can't please everyine, so I'm
+	sure I'll see a complaint or five thousand in trac.  Enjoy, though!
+
+	Marcus: This is my first news! It's been quite a few microreleases this
+	time, but now we're finally at 2.6.0. I suppose the most anticipated
+	new feature in this release is the voice and video support, thanks to
+	Mike's heroic work. I've managed to slip in a few features too, like
+	in-band bytestream file transfers as a fallback on XMPP and idle time
+	reporting on XMPP. Enjoy!
+
+	Paul: This is my first news, too!  This release has definitely been a
+	long time coming; hopefully it won't disappoint since we've closed over
+	200 tickets.  Among other things, Tobias Markmann's GSoC project from
+	last year was merged, which means we now support BOSH (XMPP connections
+	over HTTP), and Andrei Mozzhuhin contributed an XMPP Service Discovery
+	Browser.  Also, thanks to Bernmeister for poking (at least) several
+	hundred old tickets!
+
+	Mike: Ditto. This is my first news as well. I have a feeling this is
+	getting repetitive at this point, but voice and video support is
+	finally here! Thanks to the rest of the Pidgin team, Farsight 2, and
+	GStreamer developers for making this possible! (I finally finished my
+	Summer of Code project :D)
+
+	Elliott: Hey, this is my firs... Wait, no it isn't.  Now I feel left
+	out.  So have you heard about this voice and video thing?
+	Unfortunately, not quite ready for all protocols, but it's getting
+	there.  MSN gained support for receiving voice clips at least, and
+	finally we have Ink receiving capabilities too.  Thanks to the guys
+	who wrote the original patch.  And finally, MSN no longer has over a
+	100 tickets open!
+
+2.5.9 (08/18/2009):
+	John:  This release is just a crash fix release to address a security
+	issue reported to us by CORE and a couple crashes Elliott found.
+
 2.5.8 (06/27/2009):
 	John:  This release is another somewhat rushed bugfix release to fix
 	a number of bugs that have come up since we released Pidgin 2.5.7.
@@ -51,10 +273,10 @@
 	a few patches, and we've dealt with what feels like a TON of tickets
 	about two very common issues.  Feels like time for a release to me.
 
-	Etan: My first NEWS in quite a while and I don't have much to say. I
+	Etan: My first NEWS in quite a while and I don't have much to say.  I
 	haven't been too active lately and I'm hoping that won't be the case
-	going forward. I managed to get in a few perl fixes and some UI
-	language tweaks this release. My plan is to work on some of the
+	going forward.  I managed to get in a few perl fixes and some UI
+	language tweaks this release.  My plan is to work on some of the
 	issues pointed out by mpt (during his expert review of pidgin a little
 	while back) in the near future.
 
@@ -92,14 +314,14 @@
 	messaging pleasure.
 
 	Sadrul: Despite our best efforts, this release got delayed by a
-	couple of weeks. But here it is! It is mostly a bug fix release, with
+	couple of weeks.  But here it is! It is mostly a bug fix release, with
 	a couple of important fixes, e.g. fix for the Yahoo! disconnect
-	problem. Also, welcome our newest Crazy Patch Writer, Marcus Lundblad,
+	problem.  Also, welcome our newest Crazy Patch Writer, Marcus Lundblad,
 	who, among various other fixes, has implemented custom smileys for the
-	XMPP protocol, included in this release. Enjoy!
+	XMPP protocol, included in this release.  Enjoy!
 
 	Stu: I guess this is the time of year for server migrations, and
-	I've just about had enough of them. Fortunately Pidgin is still fun,
+	I've just about had enough of them.  Fortunately Pidgin is still fun,
 	and this release should be superb.
 
 	John: Although our services were down for quite some time, we didn't
@@ -109,8 +331,8 @@
 	happy!
 
 	Elliott: This release took a while, but that was due to an unfortunate
-	server snafu. I didn't have much to do with it, but hopefully the new
-	servers will help us out a bit. Anyway, mostly bug-fixes this time.
+	server snafu.  I didn't have much to do with it, but hopefully the new
+	servers will help us out a bit.  Anyway, mostly bug-fixes this time.
 	Nothing spectacular, unless you happen to suffer from one of those bugs.
 	Oh, and don't forget, the "Has you" tooltip is back!
 
@@ -123,9 +345,9 @@
 	coming months.
 
 	Elliott: I'm just commenting so Kevin wouldn't be the only one in NEWS
-	and no-one else seems to want to. Anyway, there's a couple MSN login
-	fixes, so try it out. The contact list problems might still be around,
-	but you can probably find a workaround in trac. And there's a tooltip
+	and no-one else seems to want to.  Anyway, there's a couple MSN login
+	fixes, so try it out.  The contact list problems might still be around,
+	but you can probably find a workaround in trac.  And there's a tooltip
 	fix for our AIM friends, not that I had anything to do with it (except
 	closing many many duplicate tickets).
 
@@ -135,16 +357,16 @@
 	it to the world!  There are myriad bugfixes, including some important
 	ones so you should be sure to update.
 
-	Hylke: Finally MSNP15 support. To celebrate this I refreshed a lot of
+	Hylke: Finally MSNP15 support.  To celebrate this I refreshed a lot of
 	the smilies used in the protocol and added the long awaited indispensable
-	bunny icon. I think this is one of those releases that will make a lot
+	bunny icon.  I think this is one of those releases that will make a lot
 	of users happy, especially MSN users.
 
-	Elliott: Oh look, my first NEWS! Well anyway, with that new MSNP15
+	Elliott: Oh look, my first NEWS!  Well anyway, with that new MSNP15
 	support, this release is set up to be a huge success and a total flop
-	all at the same time. Here's hoping it's the "huge success" one for you.
+	all at the same time.  Here's hoping it's the "huge success" one for you.
 	Those icon changes that Hylke made, while minor, really make things look
-	a little cleaner, I think. Oh yea, did I mention that MSNP15 stuff?
+	a little cleaner, I think.  Oh yea, did I mention that MSNP15 stuff?
 
 	Mark: Speaking of MSNP15, we'd like to welcome Elliott Sales de Andrade
 	as a full fledged developer!  He took the last few strides mushing the
@@ -152,7 +374,7 @@
 	doing other great stuff.
 
 	Ka-Hing: "Reject"ing a certificate after your account is signed off is
-	not recommended. Deleting the file after you start sending it is also
+	not recommended.  Deleting the file after you start sending it is also
 	discouraged.
 
 2.4.3 (07/01/2008):
@@ -162,12 +384,12 @@
 2.4.2 (5/17/2008):
 	Sadrul: We added some usability changes in this release, including the
 	typing notification, buddyicon and input area size in the conversation
-	windows, escape to close conversation windows etc. These changes should
+	windows, escape to close conversation windows etc.  These changes should
 	make pidgin more usable and more fun for Everyone! *wink*
 
 	Stu: I fixed some memory leaks, but nothing like as many as Daniel did.
 	MSN buddy list synchronization should be significantly less painful now,
-	and opening MSN inboxes might work better too. SILC passphrase changes
+	and opening MSN inboxes might work better too.  SILC passphrase changes
 	and support for passphrase-less keys has been improved also.
 
 2.4.1 (3/31/2008):
@@ -184,27 +406,27 @@
 	John: While this release took what seems like forever to get out the
 	door, I think it's well worth the wait, especially for Yahoo! users.
 	This release serves up some fixes for long standing bugs and adds
-	file transfer for transfers with newer Yahoo! clients (finally!). As
+	file transfer for transfers with newer Yahoo! clients (finally!).  As
 	is standard with code I committed, where it works great thank the
-	patch writer, and where it's broken, feel free to yell at me. Enjoy!
-
-	Sadrul: Finch is more colourful and blinky in this release! There's
+	patch writer, and where it's broken, feel free to yell at me.  Enjoy!
+
+	Sadrul: Finch is more colourful and blinky in this release!  There's
 	now a log viewer, which is very useful, and also the ability to
-	block/unblock buddies. It's now also possible to find chat rooms on
-	many services, e.g. XMPP, IRC, Yahoo! etc. Happy Leap Day!
+	block/unblock buddies.  It's now also possible to find chat rooms on
+	many services, e.g. XMPP, IRC, Yahoo! etc.  Happy Leap Day!
 
 	Ka-Hing: I think all I've done for this release is committing some
 	patches written by other people.
 
-	Stu: Finally, 2.4.0 lands. I didn't do all that much except complain
-	about things I didn't like or just revert Sean's changes. I'm quite
+	Stu: Finally, 2.4.0 lands.  I didn't do all that much except complain
+	about things I didn't like or just revert Sean's changes.  I'm quite
 	pleased with how well it's turned out in the end.
 	Happy Birthday Fred, you must be nearly 10 now ;-)
 
 2.3.1 (12/7/2007):
 	Stu: I'm sorry for the MSN problems and the plugin crashes in 2.3.0.
-	Hopefully this will redeem us. This fixes a number of bugs. I'm a
-	bit late but I'd like to welcome John to the team. Enjoy!
+	Hopefully this will redeem us.  This fixes a number of bugs.  I'm a
+	bit late but I'd like to welcome John to the team.  Enjoy!
 
 	Luke: I've done absolutely nothing in the last 2 weeks, except watch
 	others commit bug and, more, leak fixes.  People should be noticing
@@ -246,23 +468,23 @@
 	last release looking at the tickets that have been submitted
 	and many of them have been closed.
 
-	Stu: I haven't NEWS'd in a while. I haven't actually done much for
-	too long also, maybe I'll find some time soon. This release is
+	Stu: I haven't NEWS'd in a while.  I haven't actually done much for
+	too long also, maybe I'll find some time soon.  This release is
 	basically what 2.2.0 should have been - it actually compiles this
 	time.
 
 2.2.0 (9/13/2007):
 	Sean: 2.2.0 contains the results of several major Google Summer
-	of Code branches bringing some new, extraordinary features. We
+	of Code branches bringing some new, extraordinary features.  We
 	have a new protocol, MySpaceIM, a bunch of new features for an
 	existing protocol, XMPP, and nifty new certificate management
 	to make sure your IM server is who it says it is.
 
 	Ka-Hing: A number of you noticed crashes when dragging windows
-	around when certain options are enabled. Well, that was my fault,
-	and Sadrul fixed it. So Props to him and poos to me. I haven't
+	around when certain options are enabled.  Well, that was my fault,
+	and Sadrul fixed it.  So Props to him and poos to me.  I haven't
 	done much for this release, but the next one should contain
-	something that I helped work on. Hint: students are cheap slave
+	something that I helped work on.  Hint: students are cheap slave
 	coders!
 
 	Kevin: I haven't really been coding much in Pidgin, and this
@@ -272,9 +494,9 @@
 
 2.1.1 (8/20/2007):
 	Sean: Continuing our schedule of frequent releases, Pidgin 2.1.1
-	is out. In it, we've addressed a lot of UI issues from our
+	is out.  In it, we've addressed a lot of UI issues from our
 	experimental new changes introduced in 2.1.0, and gave a lot of 
-	attention to Yahoo! and Bonjour. Thanks to everyone who 
+	attention to Yahoo! and Bonjour.  Thanks to everyone who 
 	contributed.
 
 	Luke: We have reworked some parts of the conversation windows in
@@ -285,14 +507,14 @@
 	various issues, testing fixes, and getting patches in.
 
 	Tim: Sean finally got me to fix some of the buddy list bugs with
-	Yahoo! when in version 15 mode. So now we have some Yahoo! to
-	MSN support, which is kind of nice. Looks like some others have
+	Yahoo! when in version 15 mode.  So now we have some Yahoo! to
+	MSN support, which is kind of nice.  Looks like some others have
 	been contributing to Yahoo! while I've been AWOL, so many thanks
 	to them.
 
 2.1.0 (7/28/2007):
 	Sean: This release took a bit longer than 3 weeks, but boy is it 
-	worth it! We're beginning to experiment with new UI concepts and
+	worth it!  We're beginning to experiment with new UI concepts and
 	this release features a largely re-designed conversation window.
 	We've closed 150 tickets for this release; much thanks go to all
 	the developers, translators, and testers who made this possible.
@@ -300,28 +522,28 @@
 	Ka-Hing: Sean said no one else NEWS'ed, so I figure I should.
 
 2.0.2 (6/14/2007):
-	Sean: Another big maintenance release. Again, about 100 tickets were
-	resolved in this release, and they keep coming in. Lots of bug fixes,
+	Sean: Another big maintenance release.  Again, about 100 tickets were
+	resolved in this release, and they keep coming in.  Lots of bug fixes,
 	some minor icon adjustements, hopefully we addressed some ICQ
 	internationalization issues, and support for Bonjour on Windows!
 	Our next release will be 2.1.0, and will come with some great new
 	features.
 
 	Stu: I think we're gradually getting the hang of this 3 week thing
-	again. This release includes yet more bug fixes. I'd also like to
+	again.  This release includes yet more bug fixes.  I'd also like to
 	specifically thank Pekka Riikonen for the patch to enable using SILC
 	Toolkit 1.1 with Pidgin/libpurple that is included in this release.
 
 2.0.1 (5/24/2007):
-	Sean: 2.0.1! Three weeks later, as scheduled! It is so nice to have
-	regular, frequent, releases again! This is a bugfix release; We have
+	Sean: 2.0.1!  Three weeks later, as scheduled!  It is so nice to have
+	regular, frequent, releases again!  This is a bugfix release; We have
 	fixed over 100 issues reported to us at http://developer.pidgin.im.
 	Thanks to everyone for their great work, and look for the next release
 	in another three weeks!
 
-	Stu: Lots'o'fixes in this. I don't know how you users find so many
-	things for us to fix. 24 hours in a day (sadly). 24 is divisible by the
-	sum of its digits and by their product. It is the smallest composite
+	Stu: Lots'o'fixes in this.  I don't know how you users find so many
+	things for us to fix.  24 hours in a day (sadly).  24 is divisible by the
+	sum of its digits and by their product.  It is the smallest composite
 	number, the product of whose divisors is a cube.
 
 	Luke: I requested that we have a bug fix release, and so we have!
@@ -343,18 +565,18 @@
 	definition of 'cool' to get it.
 
 	Etan: Perl plugins now have access to almost all of the savedstatus
-	API functions. I also removed a couple of the preferences from the
+	API functions.  I also removed a couple of the preferences from the
 	Pidgin GTK+ Theme Control plugin which should help many of the people
-	for whom the configuration dialog size was a problem. The removed
+	for whom the configuration dialog size was a problem.  The removed
 	preferences no longer had the effects they were added to have anyway.
 
 2.0.0 (5/3/2007):
 	Sean: 2.0.0! It's real exciting to finally release Pidgin 2.0.0! I'm
-	really proud of all the work we've all done. I'm pumped. And, while
+	really proud of all the work we've all done.  I'm pumped.  And, while
 	I could go on about all the amazing thing that have been added since
 	1.5.0, what I'm really excited about is getting back to a regular,
 	rapid, release cycle of active, open development, unhindered by legal
-	quandries. Huge thanks to everyone involved.
+	quandries.  Huge thanks to everyone involved.
 
 	Luke: We have finally managed to get 2.0.0 out the door, after nearly
 	but not quite 2 years of effort and fustration.  No one regrets more
@@ -371,24 +593,24 @@
 	attempt to avoid knee-jerk reactions.
 
 	Evan: One small step for bird, one giant leap for birdkind... except
-	this is hardly one small step. A lot more has changed from Gaim 1.5.0
+	this is hardly one small step.  A lot more has changed from Gaim 1.5.0
 	than just the name. Pidgin has a *very* attractive new look, a whole
 	new member of the family (Finch, formerly gaim-console) has been born,
 	and libpurple has come into its own as a solid, full-featured library
-	powering the greatest IM clients around. Bugs were fixed and
+	powering the greatest IM clients around.  Bugs were fixed and
 	features were added by the hundreds (thousands?) since the last
 	major release, all while improving performance and resisting feature
-	creep. As Luke said, a ton of thought and effort has gone into
+	creep.  As Luke said, a ton of thought and effort has gone into
 	Pidgin 2.0.0; I'm proud to have played a part.
 
-	Stu: We did it! finally, we have 2.0.0. It's been a long time coming,
-	but there's a great deal of goodness here. When I say a long time, I'm
+	Stu: We did it! finally, we have 2.0.0.  It's been a long time coming,
+	but there's a great deal of goodness here.  When I say a long time, I'm
 	not kidding - it's been 972 days since we branched off "oldstatus"
-	(aka 1.x). The early Greeks were uncertain as to whether 2 was a
+	(aka 1.x).  The early Greeks were uncertain as to whether 2 was a
 	number at all (or if we'd ever make this release) - it has a beginning
 	and an end but no middle (much like our unfortunately quiet development
-	period). 2 is the first prime number and the only even prime. 2 is also
-	the first deficient number (oh well). There are only 10 types of people
+	period).  2 is the first prime number and the only even prime. 2 is also
+	the first deficient number (oh well).  There are only 10 types of people
 	in the world - those who like our new names and those who do not.
 	Enjoy!
 
@@ -398,7 +620,7 @@
 	where they are today.  Congratulations everyone!
 
 	Sadrul: My first NEWS, and on what an occasion! Pidgin 2.0.0 is finally
-	released!! And it's *really* very good!!! Give your soul a break ...
+	released!!  And it's *really* very good!!!  Give your soul a break ...
 	Use Pidgin!
 
 	Daniel: There has been a fair amount of weeping and gnashing of teeth
--- a/PLUGIN_HOWTO	Wed Jun 13 19:28:57 2012 -0400
+++ b/PLUGIN_HOWTO	Wed Jun 13 19:30:27 2012 -0400
@@ -1,6 +1,6 @@
 For information on writing a plugin for Purple, Pidgin or Finch, go
-http://developer.pidgin.im and click on API.  From there, see the HOWTOs in the
-"Related Pages" section.
+http://developer.pidgin.im and click on API.  From there, see the
+HOWTOs in the "Related Pages" section.
 
 You can also generate this documentation locally by installing
 doxygen and graphviz dot, then running "make docs" in the
--- a/autogen.sh	Wed Jun 13 19:28:57 2012 -0400
+++ b/autogen.sh	Wed Jun 13 19:30:27 2012 -0400
@@ -83,7 +83,7 @@
 
 	OUTPUT=`mktemp autogen-XXXXXX`
 
-	printf "%s" "running ${CMD} ${@}... "
+	printf "running %s %s... " ${CMD} "$*"
 	${CMD} ${@} >${OUTPUT} 2>&1
 
 	if [ $? != 0 ] ; then
@@ -99,9 +99,17 @@
 	fi
 }
 
+cleanup () {
+	rm -f autogen-??????
+	echo
+	exit 2
+}
+
 ###############################################################################
 # We really start here, yes, very sneaky!
 ###############################################################################
+trap cleanup 2
+
 FIGLET=`which figlet 2> /dev/null`
 if [ x"${FIGLET}" != x"" ] ; then
 	${FIGLET} -f small ${PACKAGE}
@@ -129,6 +137,7 @@
 check "$libtoolize";		LIBTOOLIZE=${BIN};
 check "glib-gettextize";	GLIB_GETTEXTIZE=${BIN};
 check "intltoolize";		INTLTOOLIZE=${BIN};
+check "sed";				SED=${BIN};
 check "aclocal";		ACLOCAL=${BIN};
 check "autoheader";		AUTOHEADER=${BIN};
 check "automake";		AUTOMAKE=${BIN};
@@ -140,6 +149,9 @@
 run_or_die ${LIBTOOLIZE} ${LIBTOOLIZE_FLAGS:-"-c -f --automake"}
 run_or_die ${GLIB_GETTEXTIZE} ${GLIB_GETTEXTIZE_FLAGS:-"--force --copy"}
 run_or_die ${INTLTOOLIZE} ${INTLTOOLIZE_FLAGS:-"-c -f --automake"}
+# This call to sed is needed to work around an annoying bug in intltool 0.40.6
+# See http://developer.pidgin.im/ticket/9520 for details
+run_or_die ${SED} -i.bak -e "s:'\^\$\$lang\$\$':\^\$\$lang\$\$:g" po/Makefile.in.in
 run_or_die ${ACLOCAL} ${ACLOCAL_FLAGS:-"-I m4macros"}
 run_or_die ${AUTOHEADER} ${AUTOHEADER_FLAGS}
 run_or_die ${AUTOMAKE} ${AUTOMAKE_FLAGS:-"-a -c --gnu"}
--- a/configure.ac	Wed Jun 13 19:28:57 2012 -0400
+++ b/configure.ac	Wed Jun 13 19:30:27 2012 -0400
@@ -43,19 +43,19 @@
 #
 # Make sure to update finch/libgnt/configure.ac with libgnt version changes.
 #
-m4_define([purple_lt_current], [6])
-m4_define([purple_major_version], [2])
-m4_define([purple_minor_version], [6])
+m4_define([purple_lt_current], [20])
+m4_define([purple_major_version], [3])
+m4_define([purple_minor_version], [0])
 m4_define([purple_micro_version], [0])
 m4_define([purple_version_suffix], [devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
 m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix]))
 
-m4_define([gnt_lt_current], [6])
+m4_define([gnt_lt_current], [8])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [6])
-m4_define([gnt_micro_version], [0])
+m4_define([gnt_minor_version], [8])
+m4_define([gnt_micro_version], [9])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
@@ -72,9 +72,11 @@
 ])
 fi
 
-AC_CANONICAL_SYSTEM
-AM_CONFIG_HEADER(config.h)
+AC_CANONICAL_HOST
+AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([1.9 -Wno-portability dist-bzip2])
+dnl TODO: Always use AM_SILENT_RULES when we depend on automake >= 1.11
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 
 PURPLE_MAJOR_VERSION=purple_major_version
 PURPLE_MINOR_VERSION=purple_minor_version
@@ -112,64 +114,11 @@
 AC_PROG_LIBTOOL
 LIBTOOL="$LIBTOOL --silent"
 AC_PROG_INSTALL
-AC_PROG_INTLTOOL
 PKG_PROG_PKG_CONFIG
 AC_FUNC_ALLOCA
-GETTEXT_PACKAGE=pidgin
-AC_SUBST(GETTEXT_PACKAGE)
 
-
-# before gettexting, in case iconv matters
-case "$host_os" in
-darwin*)
-	AC_CHECK_LIB(resolv, res_query)
-
-	AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [
-		AC_CHECK_HEADER(IOKit/IOKitLib.h, [
-			AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit])
-			LIBS="$LIBS -framework IOKit -framework CoreFoundation"
-		], [])
-	], [])
-
-	AC_MSG_CHECKING([for fink])
-	if test -d /sw; then
-		AC_MSG_RESULT([found, adding /sw to search paths])
-		CPPFLAGS="$CPPFLAGS -I/sw/include"
-		LDFLAGS="$LDFLAGS -L/sw/lib"
-	else
-		AC_MSG_RESULT([not found])
-	fi
-	;;
-*)
-	;;
-esac
-
-ALL_LINGUAS="af am ar az be@latin bg bn bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr ga gl gu he hi hu hy id it ja ka km kn ko ku lo lt mk mn my_MM nb ne nl nn oc pa pl pt_BR pt ps ro ru si sk sl sq sr sr@latin sv sw ta te th tr uk ur vi xh zh_CN zh_HK zh_TW"
-AM_GLIB_GNU_GETTEXT
-
-dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
-dnl AM_GLIB_GNU_GETTEXT found it.
-
-if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT$INTLTOOL_MSGFMT = x
-then
-	AC_ERROR([
-
-The msgfmt command is required to build libpurple.  If it is installed
-on your system, ensure that it is in your path.  If it is not, install
-GNU gettext to continue.
-
-If you have msgfmt installed, but for some reason this error message
-is still displayed, you have encountered what appears to be a bug in
-third-party configure macros.  Try setting the MSGFMT environment
-variable to the absolute path to your msgfmt binary and trying
-configure again, like this:
-
-MSGFMT=/path/to/msgfmt ./configure ...
-])
-fi
-
-dnl we don't use autobreak on cygwin!!
-dnl AC_CYGWIN
+dnl Check for Sun compiler
+AC_CHECK_DECL([__SUNPRO_C], [SUNCC="yes"], [SUNCC="no"])
 
 dnl Checks for header files.
 AC_HEADER_STDC
@@ -198,11 +147,11 @@
 
 dnl Check for inet_aton
 AC_CHECK_FUNC(inet_aton, , [AC_CHECK_LIB(resolv, inet_aton, ,
-				         [AC_ERROR(inet_aton not found)])])
+				         [AC_MSG_ERROR([inet_aton not found])])])
 AC_CHECK_LIB(resolv, __res_query)
 AC_CHECK_LIB(nsl, gethostent)
 AC_CHECK_FUNC(socket, ,
-	[AC_CHECK_LIB(socket, socket, , [AC_ERROR([socket not found])])])
+	[AC_CHECK_LIB(socket, socket, , [AC_MSG_ERROR([socket not found])])])
 dnl If all goes well, by this point the previous two checks will have
 dnl pulled in -lsocket and -lnsl if we need them.
 AC_CHECK_FUNC(getaddrinfo,
@@ -211,21 +160,21 @@
 	[AC_CHECK_LIB(socket, getaddrinfo,
 		[AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lsnl $LIBS"], , , -lnsl)])
 AC_CHECK_FUNCS(inet_ntop)
+AC_CHECK_FUNCS(getifaddrs)
 dnl Check for socklen_t (in Unix98)
 AC_MSG_CHECKING(for socklen_t)
-AC_TRY_COMPILE([
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 	#include <sys/types.h>
 	#include <sys/socket.h>
 	socklen_t x;
-], [],
-[
+]], [[]])], [
 	AC_MSG_RESULT(yes)
 ], [
-	AC_TRY_COMPILE([
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 		#include <sys/types.h>
 		#include <sys/socket.h>
 		int accept(int, struct sockaddr *, size_t *);
-	], [], [
+	]], [[]])], [
 		AC_MSG_RESULT(size_t)
 		AC_DEFINE(socklen_t, size_t, [socklen_t size])
 	], [
@@ -240,6 +189,12 @@
 	[Define if struct sockaddr has an sa_len member])],[:],
 	[#include <sys/socket.h>])
 
+dnl Check for v6-only sockets
+AC_CHECK_DECL([IPV6_V6ONLY],
+	[AC_DEFINE([HAVE_IPV6_V6ONLY],[1],
+	[Define if the IPV6_V6ONLY setsockopt option exists])],
+	[], [#include <netinet/in.h>])
+
 dnl to prevent the g_stat()/g_unlink() crash,
 dnl (09:50:07) Robot101: LSchiere2: it's easy. +LC_SYS_LARGEFILE somewhere in configure.ac
 AC_SYS_LARGEFILE
@@ -247,8 +202,14 @@
 dnl FreeBSD doesn't have libdl, dlopen is provided by libc
 AC_CHECK_FUNC(dlopen, LIBDL="", [AC_CHECK_LIB(dl, dlopen, LIBDL="-ldl")])
 
+dnl Windows and Haiku do not use libm for the math functions, they are part
+dnl of the C library
+AC_SEARCH_LIBS([ceil], [m], [], [
+  AC_MSG_ERROR([unable to find the ceil() function])
+])
+
 AC_MSG_CHECKING(for fileno())
-AC_TRY_RUN([
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <stdio.h>
 
 int main(int argc, char *argv[])
@@ -259,7 +220,7 @@
 
 	return !(fd > 0);
 }
-], [
+]])], [
 	AC_MSG_RESULT(yes)
 	AC_DEFINE([HAVE_FILENO], [1],
 	          [Define to 1 if your stdio has int fileno(FILE *).])
@@ -272,7 +233,7 @@
 ])
 
 AC_MSG_CHECKING(for the %z format string in strftime())
-AC_TRY_RUN([
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
@@ -296,7 +257,7 @@
 	         (buf[4] >= '0' && buf[4] <= '9')
 	        );
 }
-], [
+]])], [
 	AC_MSG_RESULT(yes)
 	AC_DEFINE([HAVE_STRFTIME_Z_FORMAT], [1],
 		[Define to 1 if you have a strftime() that supports the %z format string.])
@@ -306,17 +267,81 @@
 	# Fallback for Cross Compiling...
 	# This will enable the compatibility code.
 	AC_MSG_RESULT(no)
-]
-)
+])
+
+# before gettexting, in case iconv matters
+case "$host_os" in
+darwin*)
+	AC_CHECK_LIB(resolv, res_query)
+
+	AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [
+		AC_CHECK_HEADER(IOKit/IOKitLib.h, [
+			AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit])
+			LIBS="$LIBS -framework IOKit -framework CoreFoundation"
+		], [])
+	], [])
+
+	AC_MSG_CHECKING([for fink])
+	if test -d /sw; then
+		AC_MSG_RESULT([found, adding /sw to search paths])
+		CPPFLAGS="$CPPFLAGS -I/sw/include"
+		LDFLAGS="$LDFLAGS -L/sw/lib"
+	else
+		AC_MSG_RESULT([not found])
+	fi
+	;;
+*)
+	;;
+esac
 
 dnl #######################################################################
-dnl # Check for GLib 2.0 (required)
+dnl # Disable creation and installation of translation files
 dnl #######################################################################
-PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.0.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
+AC_ARG_ENABLE(nls, AC_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
+	GETTEXT_PACKAGE=pidgin
+	AC_SUBST(GETTEXT_PACKAGE)
+
+	ALL_LINGUAS="af am ar az be@latin bg bn bn_IN bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr ga gl gu he hi hr hu hy id it ja ka km kn ko ku lo lt mai mhr mk mn mr ms_MY my_MM nb ne nl nn oc or pa pl pt_BR pt ps ro ru si sk sl sq sr sr@latin sv sw ta te th tr uk ur vi xh zh_CN zh_HK zh_TW"
+	AM_GLIB_GNU_GETTEXT
+
+	dnl If we don't have msgfmt, then po/ is going to fail -- ensure that
+	dnl AM_GLIB_GNU_GETTEXT found it.
+
+	if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT$INTLTOOL_MSGFMT = x
+	then
+		AC_MSG_ERROR([
+
+The msgfmt command is required to build libpurple.  If it is installed
+on your system, ensure that it is in your path.  If it is not, install
+GNU gettext to continue.
+
+If you have msgfmt installed, but for some reason this error message
+is still displayed, you have encountered what appears to be a bug in
+third-party configure macros.  Try setting the MSGFMT environment
+variable to the absolute path to your msgfmt binary and trying
+configure again, like this:
+
+MSGFMT=/path/to/msgfmt ./configure ...
+	])
+	fi
+fi #enable_i18n
+
+AM_CONDITIONAL(INSTALL_I18N, test "x$enable_i18n" = "xyes")
+
+dnl #######################################################################
+dnl # Check for GLib 2.16 (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], , [
 	AC_MSG_RESULT(no)
 	AC_MSG_ERROR([
 
-You must have the GLib 2.0 development headers installed to build.
+You must have GLib 2.16.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.
@@ -352,7 +377,7 @@
 	[enable_consoleui=$enableval force_finch=$enableval], [enable_consoleui=yes force_finch=no])
 
 dnl #######################################################################
-dnl # Check for GTK+ 2.0 and other things used by the GTK UI
+dnl # Check for GTK+ 2.10 and other things used by the GTK UI
 dnl #######################################################################
 AC_ARG_ENABLE(screensaver,
 	[AC_HELP_STRING([--disable-screensaver],
@@ -382,6 +407,10 @@
 	[AC_HELP_STRING([--disable-gestures],
 		[compile without the gestures plugin])],
 	enable_gestures="$enableval", enable_gestures="yes")
+AC_ARG_ENABLE(gcr,
+	[AC_HELP_STRING([--enable-gcr],
+		[compile with GCR certificate widgets])],
+	enable_gcr="$enableval", enable_gcr="no")
 
 AC_PATH_XTRA
 # We can't assume that $x_libraries will be set, because autoconf does not
@@ -399,12 +428,13 @@
 fi
 
 if test "x$enable_gtkui" = "xyes" ; then
-	PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.0.0], , [
+	PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.10.0], , [
 		AC_MSG_RESULT(no)
 		AC_MSG_ERROR([
 
-You must have the GTK+ 2.0 development headers installed to compile Pidgin.
-If you want to build only Finch then specify --disable-gtkui when running configure.
+You must have GTK+ 2.10.0 or newer development headers installed to compile
+Pidgin.  If you want to build only Finch then specify --disable-gtkui when
+running configure.
 ])])
 
 	AC_SUBST(GTK_CFLAGS)
@@ -414,6 +444,16 @@
 	PKG_CHECK_MODULES(PANGO, [pango >= 1.4.0],
 			AC_DEFINE(HAVE_PANGO14, 1, [Define if we have Pango 1.4 or newer.]),:)
 
+	PKG_CHECK_MODULES(WEBKIT, [webkit-1.0 >= 1.1.1], , [
+		AC_MSG_RESULT(no)
+		AC_MSG_ERROR([
+You must have WebKit 1.1.1 or newer development headers installed to compile
+Pidgin.  If you want to build only Finch then specify --disable-gtkui when
+running configure.
+])])
+	AC_SUBST(WEBKIT_CFLAGS)
+	AC_SUBST(WEBKIT_LIBS)
+
 	dnl #######################################################################
 	dnl # Check if we should compile with X support
 	dnl #######################################################################
@@ -425,6 +465,7 @@
 					X11_LIBS="$x_libpath_add"
 					X11_CFLAGS="$x_incpath_add"
 				else
+					with_x="no"
 					if test "x$force_deps" = "xyes" ; then
 						AC_MSG_ERROR([
 X11 development headers not found.
@@ -455,10 +496,10 @@
 			if test "x$XSS_LIBS" != "x"; then
 				oldCPPFLAGS="$CPPFLAGS"
 				CPPFLAGS="$CPPFLAGS $x_incpath_add"
-				AC_TRY_COMPILE([
+				AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 					#include <X11/Xlib.h>
 					#include <X11/extensions/scrnsaver.h>
-					], [], [], [enable_screensaver=no])
+					]], [[]])], [], [enable_screensaver=no])
 				CPPFLAGS="$oldCPPFLAGS"
 			else
 				enable_screensaver=no
@@ -522,31 +563,12 @@
 	fi
 
 	dnl #######################################################################
-	dnl # Check for startup notification
-	dnl #######################################################################
-	if test "x$enable_startup_notification" = "xyes"; then
-		PKG_CHECK_MODULES(STARTUP_NOTIFICATION, [libstartup-notification-1.0 >= 0.5], , [
-			AC_MSG_RESULT(no)
-			if test "x$force_deps" = "xyes" ; then
-				AC_MSG_ERROR([
-Startup notification development headers not found.
-Use --disable-startup-notification if you do not need it.
-])
-			fi])
-
-		if test "x$enable_startup_notification" = "xyes"; then
-			AC_DEFINE(HAVE_STARTUP_NOTIFICATION, 1, [Define if we're using libstartup-notification.])
-			AC_SUBST(STARTUP_NOTIFICATION_CFLAGS)
-			AC_SUBST(STARTUP_NOTIFICATION_LIBS)
-		fi
-	fi
-
-	dnl #######################################################################
 	dnl # Check for GtkSpell
 	dnl #######################################################################
 	if test "x$enable_gtkspell" = "xyes" ; then
 		PKG_CHECK_MODULES(GTKSPELL, gtkspell-2.0 >= 2.0.2, , [
 			AC_MSG_RESULT(no)
+			enable_gtkspell="no"
 			if test "x$force_deps" = "xyes" ; then
 				AC_MSG_ERROR([
 GtkSpell development headers not found.
@@ -566,15 +588,12 @@
 	if test "x$enable_gevolution" = "xyes"; then
 		evo_deps="libebook-1.2 libedata-book-1.2"
 		PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, , [
-			AC_MSG_RESULT(yes)
 			enable_gevolution="no"
 		])
 		if test "x$enable_gevolution" = "xno"; then
 			evo_deps="libebook-1.0 libedata-book-1.0"
 			PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, [
 				enable_gevolution="yes"
-			], [
-				AC_MSG_RESULT(yes)
 			])
 		fi
 		if test "x$enable_gevolution" = "xyes"; then
@@ -597,6 +616,7 @@
 	if test "x$enable_cap" = "xyes"; then
 		PKG_CHECK_MODULES(SQLITE3, sqlite3 >= 3.3,,[
 			AC_MSG_RESULT(no)
+			enable_cap="no"
 			if test "x$force_deps" = "xyes" ; then
 				AC_MSG_ERROR([
 sqlite3 development headers not found.
@@ -604,9 +624,26 @@
 ])
 			fi])
 	fi
-        
+
+	dnl #######################################################################
+	dnl # Check for GCR for its certificate widgets
+	dnl #######################################################################
+	if test "x$enable_gcr" = "xyes"; then
+		PKG_CHECK_MODULES(GCR, gcr-0, [
+			AC_DEFINE(ENABLE_GCR, 1, [Define to 1 if GCR is found.])], [
+			AC_MSG_RESULT(no)
+			enable_gcr="no"
+			if test "x$force_deps" = "xyes" ; then
+				AC_MSG_ERROR([
+GCR development headers not found.
+Use --disable-gcr if you do not need GCR certificate widgets.
+])
+			fi])
+	fi
+
 
 else # GTK
+	enable_gcr=no
 	enable_cap=no
 	enable_gevolution=no
 	enable_gtkspell=no
@@ -619,6 +656,7 @@
 AM_CONDITIONAL(BUILD_GEVOLUTION, test "x$enable_gevolution" = "xyes")
 AM_CONDITIONAL(ENABLE_CAP, test "x$enable_cap" = "xyes")
 AM_CONDITIONAL(ENABLE_GESTURES, test "x$enable_gestures" = "xyes")
+AM_CONDITIONAL(ENABLE_GCR, test "x$enable_gcr" = "xyes")
 
 
 dnl #######################################################################
@@ -631,7 +669,8 @@
 		[ac_ncurses_includes="$withval"], [ac_ncurses_includes=""])
 if test "x$enable_consoleui" = "xyes"; then
 	AC_CHECK_LIB(ncursesw, initscr, [GNT_LIBS="-lncursesw"], [enable_consoleui=no])
-	AC_CHECK_LIB(panelw, update_panels, [GNT_LIBS="$GNT_LIBS -lpanelw"], [enable_consoleui=no])
+	AC_CHECK_LIB(panelw, update_panels, [GNT_LIBS="$GNT_LIBS -lpanelw"],
+	    [enable_consoleui=no], [$GNT_LIBS])
 
 	if test "x$enable_consoleui" = "xyes"; then
 		dnl # Some distros put the headers in ncursesw/, some don't
@@ -639,16 +678,20 @@
 		for location in $ac_ncurses_includes $NCURSES_HEADERS /usr/include/ncursesw /usr/include
 		do
 			f="$location/ncurses.h"
+			orig_CFLAGS="$CFLAGS"
+			orig_CPPFLAGS="$CPPFLAGS"
+			CFLAGS="$CFLAGS -I$location"
+			CPPFLAGS="$CPPFLAGS -I$location"
 			AC_CHECK_HEADER($f,[
 				AC_MSG_CHECKING([if $f supports wide characters])
-				AC_TRY_COMPILE([
+				AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 					#define _XOPEN_SOURCE_EXTENDED
 					#include <$f>
-				], [
+				]], [[
 					#ifndef get_wch
 					# error get_wch not found!
 					#endif
-				], [
+				]])], [
 					dir=$location
 					if test x"$dir" != x"." ; then
 						GNT_CFLAGS="-I$dir/"
@@ -657,9 +700,13 @@
 					fi
 
 					found_ncurses_h=yes
+					CFLAGS="$orig_CFLAGS"
+					CPPFLAGS="$orig_CPPFLAGS"
 					AC_MSG_RESULT([yes])
 					break
 				], [
+					CFLAGS="$orig_CFLAGS"
+					CPPFLAGS="$orig_CPPFLAGS"
 					AC_MSG_RESULT([no])
 				])
 			])
@@ -674,7 +721,8 @@
 		# ncursesw was not found. Look for plain old ncurses
 		enable_consoleui=yes
 		AC_CHECK_LIB(ncurses, initscr, [GNT_LIBS="-lncurses"], [enable_consoleui=no])
-		AC_CHECK_LIB(panel, update_panels, [GNT_LIBS="$GNT_LIBS -lpanel"], [enable_consoleui=no])
+		AC_CHECK_LIB(panel, update_panels, [GNT_LIBS="$GNT_LIBS -lpanel"],
+		    [enable_consoleui=no], [$GNT_LIBS])
 		AC_DEFINE(NO_WIDECHAR, 1, [Define to 1 if you don't have wide-character support.])
 		if test x"$ac_ncurses_includes" != "x"; then
 			GNT_CFLAGS="-I$ac_ncurses_includes"
@@ -743,6 +791,7 @@
 			[], [$GSTREAMER_LIBS])
 	], [
 		AC_MSG_RESULT(no)
+		enable_gst="no"
 		if test "x$force_deps" = "xyes" ; then
 			AC_MSG_ERROR([
 GStreamer development headers not found.
@@ -792,13 +841,13 @@
 dnl #######################################################################
 AC_ARG_ENABLE(vv,
 	[AC_HELP_STRING([--disable-vv], [compile without voice and video support])],
-	[enable_vv="$enableval" force_vv=$enableval], [enable_vv="yes" force_vv=no])
+	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_farsight" != "xno"; then
 		AC_DEFINE(USE_VV, 1, [Use voice and video])
 	else
 		enable_vv="no"
-		if test "x$force_vv" = "xyes"; then
+		if test "x$force_deps" = "xyes"; then
 			AC_MSG_ERROR([
 Dependencies for voice/video were not met.
 Install the necessary gstreamer and farsight packages first.
@@ -807,6 +856,11 @@
 		fi
 	fi
 fi
+AM_CONDITIONAL(USE_VV, test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farsight" != "xno")
+
+dnl #######################################################################
+dnl # Check for Internationalized Domain Name support
+dnl #######################################################################
 
 AC_ARG_ENABLE(idn,
 	[AC_HELP_STRING([--disable-idn], [compile without IDN support])],
@@ -818,6 +872,7 @@
 		AC_SUBST(IDN_LIBS)
 	], [
 		AC_MSG_RESULT(no)
+		enable_idn="no"
 		if test "x$force_deps" = "xyes" ; then
 			AC_MSG_ERROR([
 GNU Libidn development headers not found.
@@ -856,42 +911,48 @@
 	[AC_HELP_STRING([--disable-avahi],
 		[compile without avahi (required for Bonjour support)])],
 	enable_avahi="$enableval", enable_avahi="yes")
-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"])
-AVAHI_CFLAGS=""
-AVAHI_LIBS=""
+
+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"])
+	AVAHI_CFLAGS=""
+	AVAHI_LIBS=""
 
-dnl Attempt to autodetect Avahi
-PKG_CHECK_MODULES(AVAHI, [avahi-client avahi-glib], [
-	avahiincludes="yes"
-	avahilibs="yes"
-], [
-	avahiincludes="no"
-	avahilibs="no"
-])
+	dnl Attempt to autodetect Avahi
+	PKG_CHECK_MODULES(AVAHI, [avahi-client avahi-glib], [
+		avahiincludes="yes"
+		avahilibs="yes"
+	], [
+		avahiincludes="no"
+		avahilibs="no"
+	])
 
-dnl Override AVAHI_CFLAGS if the user specified an include dir
-if test "$ac_avahi_client_includes" != "no"; then
-	AVAHI_CFLAGS="-I$ac_avahi_client_includes"
+	dnl Override AVAHI_CFLAGS if the user specified an include dir
+	if test "$ac_avahi_client_includes" != "no"; then
+		AVAHI_CFLAGS="-I$ac_avahi_client_includes"
+	fi
+	CPPFLAGS_save="$CPPFLAGS"
+	CPPFLAGS="$CPPFLAGS $AVAHI_CFLAGS"
+	AC_CHECK_HEADER(avahi-client/client.h, [avahiincludes=yes], [avahiincludes=no])
+	CPPFLAGS="$CPPFLAGS $AVAHI_CFLAGS $GLIB_CFLAGS"
+	AC_CHECK_HEADER(avahi-glib/glib-malloc.h, [avahiincludes=yes], [avahiincludes=no])
+	CPPFLAGS="$CPPFLAGS_save"
+
+	dnl Override AVAHI_LIBS if the user specified a libs dir
+	if test "$ac_avahi_client_libs" != "no"; then
+		AVAHI_LIBS="-L$ac_avahi_client_libs -lavahi-common -lavahi-client -lavahi-glib "
+	fi
+	AC_CHECK_LIB(avahi-client, avahi_client_new, [avahilibs=yes], [avahilibs=no], $AVAHI_LIBS)
 fi
-CPPFLAGS_save="$CPPFLAGS"
-CPPFLAGS="$CPPFLAGS $AVAHI_CFLAGS"
-AC_CHECK_HEADER(avahi-client/client.h, [avahiincludes=yes], [avahiincludes=no])
-CPPFLAGS="$CPPFLAGS $AVAHI_CFLAGS $GLIB_CFLAGS"
-AC_CHECK_HEADER(avahi-glib/glib-malloc.h, [avahiincludes=yes], [avahiincludes=no])
-CPPFLAGS="$CPPFLAGS_save"
 
-dnl Override AVAHI_LIBS if the user specified a libs dir
-if test "$ac_avahi_client_libs" != "no"; then
-	AVAHI_LIBS="-L$ac_avahi_client_libs -lavahi-common -lavahi-client -lavahi-glib "
-fi
-AC_CHECK_LIB(avahi-client, avahi_client_new, [avahilibs=yes], [avahilibs=no], $AVAHI_LIBS)
-
-if test "x$enable_avahi" = "xyes" -a "x$force_deps" = "xyes" -a \( "x$avahiincludes" = "xno" -o "x$avahilibs" = "xno" \); then
-	AC_MSG_ERROR([
+if test "x$enable_avahi" = "xyes" -a \( "x$avahiincludes" = "xno" -o "x$avahilibs" = "xno" \); then
+	enable_avahi="no"
+	if test "x$force_deps" = "xyes"; then
+		AC_MSG_ERROR([
 avahi development headers not found.
 Use --disable-avahi if you do not need avahi (Bonjour) support.
 ])
+	fi
 fi
 AC_SUBST(AVAHI_CFLAGS)
 AC_SUBST(AVAHI_LIBS)
@@ -918,25 +979,6 @@
 	], [
 		have_silc="no"
 	])
-	if test "x$have_silc" = "xno"; then
-		PKG_CHECK_MODULES(SILC, silcclient, [
-			have_silc="yes"
-			silc10includes="yes"
-			silc10client="yes"
-		], [
-			have_silc="no"
-		])
-		dnl If silcclient.pc wasn't found, check for just silc.pc
-		if test "x$have_silc" = "xno"; then
-			PKG_CHECK_MODULES(SILC, silc, [
-				have_silc="yes"
-				silc10includes="yes"
-				silc10client="yes"
-			], [
-				have_silc="no"
-			])
-		fi
-	fi
 else
 	if test "$ac_silc_includes" != "no"; then
 		SILC_CFLAGS="-I$ac_silc_includes"
@@ -954,17 +996,6 @@
 
 	if test "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes"; then
 		have_silc="yes"
-	else
-		CPPFLAGS_save="$CPPFLAGS"
-		CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
-		AC_CHECK_HEADER(silcincludes.h, [silc10includes=yes])
-		CPPFLAGS="$CPPFLAGS_save"
-
-		SILC_LIBS="$SILC_LIBS -lsilc -lsilcclient -lpthread $LIBDL"
-		AC_CHECK_LIB(silcclient, silc_client_init, [silc10client=yes], , $SILC_LIBS)
-		if test "x$silc10includes" = "xyes" -a "x$silc10client" = "xyes"; then
-			have_silc="yes"
-		fi
 	fi
 fi
 AC_SUBST(SILC_LIBS)
@@ -972,20 +1003,6 @@
 dnl SILC Toolkit >= 1.0.1 has a new MIME API
 if test "x$silcclient" = "xyes"; then
 	AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h])
-elif test "x$silc10client" = "xyes"; then
-	CPPFLAGS_save="$CPPFLAGS"
-	CPPFLAGS="$CPPFLAGS $SILC_CFLAGS"
-		AC_MSG_CHECKING(for silcmime.h)
-		AC_TRY_COMPILE([
-#include <silcincludes.h>
-#include <silcmime.h>
-		], [], [
-		AC_MSG_RESULT(yes)
-		AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h])
-		], [
-		AC_MSG_RESULT(no)
-		])
-	CPPFLAGS="$CPPFLAGS_save"
 fi
 
 dnl #######################################################################
@@ -1001,7 +1018,7 @@
 	gadu_manual_check="no"
 fi
 if test "x$gadu_manual_check" = "xno"; then
-	PKG_CHECK_MODULES(GADU, libgadu, [
+	PKG_CHECK_MODULES(GADU, [libgadu >= 1.11.0], [
 		gadu_includes="yes"
 		gadu_libs="yes"
 	], [
@@ -1028,14 +1045,33 @@
 	AC_MSG_CHECKING(for libgadu GPL compatibility)
 	CPPFLAGS_save="$CPPFLAGS"
 	CPPFLAGS="$CPPFLAGS $GADU_CFLAGS"
-	AC_TRY_COMPILE([#include <libgadu.h>], [
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[
 #if defined(__GG_LIBGADU_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_OPENSSL)
 #error "libgadu is not compatible with the GPL when compiled with OpenSSL support."
 #endif
-	], [
-		AC_MSG_RESULT(yes)
-		AC_DEFINE([HAVE_LIBGADU], [1],
-			[Define to 1 if you have libgadu.])
+	]])], [
+		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[
+#if GG_DEFAULT_PROTOCOL_VERSION < 0x2e
+#error "Your libgadu version is too old. libpurple requires 1.11.0 or higher."
+#endif
+		]])], [
+			AC_MSG_RESULT(yes)
+			AC_DEFINE([HAVE_LIBGADU], [1],
+				[Define to 1 if you have libgadu.])
+		], [
+			AC_MSG_RESULT(no)
+			echo
+			echo
+			echo "Your supplied copy of libgadu is too old."
+			echo "Install version 1.11.0 or newer."
+			echo "Then rerun this ./configure"
+			echo
+			echo "Falling back to using our own copy of libgadu"
+			echo
+			GADU_LIBS=""
+			GADU_CFLAGS=""
+			gadu_libs=no
+		])
 	], [
 		AC_MSG_RESULT(no)
 		echo
@@ -1056,12 +1092,13 @@
 
 AM_CONDITIONAL(USE_INTERNAL_LIBGADU, test "x$gadu_libs" != "xyes")
 
+if test "x$gadu_libs" = "x"; then
+	gadu_libs=no
+fi
+
 AC_SUBST(GADU_LIBS)
 AC_SUBST(GADU_CFLAGS)
 
-# change the next line to not make MSNP15 the default (s/disable/enable/; s/yes/no/;)
-AC_ARG_ENABLE(msnp15,[AC_HELP_STRING([--disable-msnp15], [Disable the newer MSNP15 protocol])],enable_msnp15=$enableval,enable_msnp15=yes)
-
 AC_ARG_ENABLE(distrib,,,enable_distrib=no)
 AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes")
 DYNAMIC_PRPLS=all
@@ -1071,7 +1108,7 @@
 fi
 
 if test "x$STATIC_PRPLS" = "xall" ; then
-	STATIC_PRPLS="bonjour gg irc jabber msn myspace novell oscar qq sametime silc simple yahoo zephyr"
+	STATIC_PRPLS="bonjour gg irc jabber msn mxit myspace novell oscar sametime silc simple yahoo zephyr"
 fi
 if test "x$have_meanwhile" != "xyes" ; then
 	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'`
@@ -1079,14 +1116,8 @@
 if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
 	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'`
 fi
-if test "x$enable_msnp15" != "xyes" ; then
-	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/msn/msnp9/'`
-fi
 if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
-	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc/silc10/'`
-fi
-if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then
-	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc10//'`
+	STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc//'`
 fi
 AC_SUBST(STATIC_PRPLS)
 STATIC_LINK_LIBS=
@@ -1110,10 +1141,6 @@
 	else
 		if test "x$i" = "xsilc"; then
 			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.la"
-		elif test "x$i" = "xsilc10"; then
-			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libsilcpurple.la"
-		elif test "x$i" = "xmsnp9"; then
-			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libmsn.la"
 		else
 			STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la"
 		fi
@@ -1126,16 +1153,14 @@
 		irc)		static_irc=yes ;;
 		jabber)		static_jabber=yes ;;
 		msn)		static_msn=yes ;;
-		msnp9)		static_msn=yes ;;
+		mxit)		static_mxit=yes ;;
 		myspace)	static_myspace=yes ;;
 		novell)		static_novell=yes ;;
 		oscar)		static_oscar=yes ;;
 		aim)		static_oscar=yes ;;
 		icq)		static_oscar=yes ;;
-		qq)			static_qq=yes ;;
 		sametime)	static_sametime=yes ;;
 		silc)		static_silc=yes ;;
-		silc10)		static_silc=yes ;;
 		simple)		static_simple=yes ;;
 		yahoo)		static_yahoo=yes ;;
 		zephyr)		static_zephyr=yes ;;
@@ -1147,10 +1172,10 @@
 AM_CONDITIONAL(STATIC_IRC, test "x$static_irc" = "xyes")
 AM_CONDITIONAL(STATIC_JABBER, test "x$static_jabber" = "xyes")
 AM_CONDITIONAL(STATIC_MSN, test "x$static_msn" = "xyes")
+AM_CONDITIONAL(STATIC_MXIT, test "x$static_mxit" = "xyes")
 AM_CONDITIONAL(STATIC_MYSPACE, test "x$static_myspace" = "xyes")
 AM_CONDITIONAL(STATIC_NOVELL, test "x$static_novell" = "xyes")
 AM_CONDITIONAL(STATIC_OSCAR, test "x$static_oscar" = "xyes")
-AM_CONDITIONAL(STATIC_QQ, test "x$static_qq" = "xyes")
 AM_CONDITIONAL(STATIC_SAMETIME, test "x$static_sametime" = "xyes" -a "x$have_meanwhile" = "xyes")
 AM_CONDITIONAL(STATIC_SILC, test "x$static_silc" = "xyes" -a "x$have_silc" = "xyes")
 AM_CONDITIONAL(STATIC_SIMPLE, test "x$static_simple" = "xyes")
@@ -1162,7 +1187,7 @@
 
 AC_ARG_WITH(dynamic_prpls, [AC_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 myspace novell oscar qq sametime silc simple yahoo zephyr"
+	DYNAMIC_PRPLS="bonjour gg irc jabber msn mxit myspace novell oscar sametime silc simple yahoo zephyr"
 fi
 if test "x$have_meanwhile" != "xyes"; then
 	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'`
@@ -1170,14 +1195,8 @@
 if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then
 	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'`
 fi
-if test "x$enable_msnp15" != "xyes" ; then
-	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/msn/msnp9/'`
-fi
 if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
-	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc/silc10/'`
-fi
-if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then
-	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc10//'`
+	DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc//'`
 fi
 AC_SUBST(DYNAMIC_PRPLS)
 for i in $DYNAMIC_PRPLS ; do
@@ -1187,17 +1206,15 @@
 		irc)		dynamic_irc=yes ;;
 		jabber)		dynamic_jabber=yes ;;
 		msn)		dynamic_msn=yes ;;
-		msnp9)		dynamic_msn=yes ;;
+		mxit)		dynamic_mxit=yes ;;
 		myspace)	dynamic_myspace=yes ;;
 		novell)		dynamic_novell=yes ;;
 		null)		dynamic_null=yes ;;
 		oscar)		dynamic_oscar=yes ;;
 		aim)		dynamic_oscar=yes ;;
 		icq)		dynamic_oscar=yes ;;
-		qq)			dynamic_qq=yes ;;
 		sametime)	dynamic_sametime=yes ;;
 		silc)		dynamic_silc=yes ;;
-		silc10)		dynamic_silc=yes ;;
 		simple)		dynamic_simple=yes ;;
 		yahoo)		dynamic_yahoo=yes ;;
 		zephyr)		dynamic_zephyr=yes ;;
@@ -1257,9 +1274,9 @@
 		orig_CFLAGS="$CFLAGS"
 		CFLAGS="$CFLAGS $newflag"
 		AC_MSG_CHECKING(for $newflag option to gcc)
-		AC_TRY_COMPILE([], [
+		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[
 			int main() {return 0;}
-		], [
+		]])], [
 			AC_MSG_RESULT(yes)
 			CFLAGS="$orig_CFLAGS"
 			DEBUG_CFLAGS="$DEBUG_CFLAGS $newflag"
@@ -1271,7 +1288,7 @@
 
 	if test "x$enable_fortify" = "xyes"; then
 		AC_MSG_CHECKING(for FORTIFY_SOURCE support)
-		AC_TRY_COMPILE([#include <features.h>], [
+		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <features.h>]], [[
 			int main() {
 			#if !(__GNUC_PREREQ (4, 1) \
 				|| (defined __GNUC_RH_RELEASE__ && __GNUC_PREREQ (4, 0)) \
@@ -1283,7 +1300,7 @@
 			#endif
 				return 0;
 			}
-		], [
+		]])], [
 			AC_MSG_RESULT(yes)
 			DEBUG_CFLAGS="$DEBUG_CFLAGS -Wp,-D_FORTIFY_SOURCE=2"
 		], [
@@ -1294,6 +1311,10 @@
 	DEBUG_CFLAGS="-Wall $DEBUG_CFLAGS"
 	CFLAGS="-g $CFLAGS"
 fi
+
+if test "x$SUNCC" = "xyes"; then
+        CFLAGS="$CFLAGS -features=extensions" 
+fi
 AC_SUBST(CFLAGS)
 
 AC_PATH_PROG(pidginpath, pidgin)
@@ -1365,7 +1386,7 @@
 						   [which python interpreter to use for dbus code generation]),
 			PYTHON=$withval)
 
-if test "x$enable_dbus" = "xyes" ; then
+if test "x$enable_dbus" = "xyes" || test "x$enable_consoleui" = "xyes" ; then
 	if test -z "$PYTHON" -o "x$PYTHON" = "xyes"; then
 		AC_PATH_PROG([PYTHON], [python], [no])
 	fi
@@ -1374,9 +1395,7 @@
 		AC_MSG_WARN([python interpreter not found in your path])
 		enable_dbus=no
 	fi
-fi
 
-if test "x$enable_dbus" = "xyes" ; then
 	if $PYTHON -c "import sys; sys.exit(sys.version[[:3]] >= '2.4')" ; then
 		AC_MSG_WARN([python version >= 2.4 required])
 		enable_dbus=no
@@ -1426,7 +1445,7 @@
 			done
 
 			if test -z $DBUS_SERVICES_DIR ; then
-				AC_MSG_ERROR([D-Bus services directory was not found!  Please use --with-dbus-services and specify it's location.])
+				AC_MSG_ERROR([D-Bus services directory was not found!  Please use --with-dbus-services and specify its location.])
 			fi
 		else
 			DBUS_SERVICES_DIR="$datadir/dbus-1/services"
@@ -1447,22 +1466,24 @@
 
 dnl Check for Python headers (currently useful only for libgnt)
 dnl (Thanks to XChat)
-AC_PATH_PROG(pythonpath, python)
-if test "_$pythonpath" != _ ; then
+if test "x$enable_consoleui" = "xyes" -a ! -z "$PYTHON" -a x"$PYTHON" != x"no" ; then
 	AC_MSG_CHECKING(for Python compile flags)
-	PY_PREFIX=`$pythonpath -c 'import sys ; print sys.prefix'`
-	PY_EXEC_PREFIX=`$pythonpath -c 'import sys ; print sys.exec_prefix'`
+	PY_PREFIX=`$PYTHON -c 'import sys ; print sys.prefix'`
+	PY_EXEC_PREFIX=`$PYTHON -c 'import sys ; print sys.exec_prefix'`
 	changequote(<<, >>)dnl
-	PY_VERSION=`$pythonpath -c 'import sys ; print sys.version[0:3]'`
-	PY_MAJOR=`$pythonpath -c 'import sys ; print sys.version[0:2]'`
+	PY_VERSION=`$PYTHON -c 'import sys ; print sys.version[0:3]'`
+	PY_MAJOR=`$PYTHON -c 'import sys ; print sys.version[0:2]'`
 	changequote([, ])dnl
 	if test -f $PY_PREFIX/include/python$PY_VERSION/Python.h -a "$PY_MAJOR" = "2."; then
+		AC_MSG_RESULT()
 		AC_CHECK_LIB(pthread, pthread_create, )
 		AC_CHECK_LIB(util, openpty, )
 		AC_CHECK_LIB(db, dbopen, )
-		PY_LIBS="-lpython$PY_VERSION -L$PY_EXEC_PREFIX/lib/python$PY_VERSION/config"
+		PY_LIBS="-L$PY_EXEC_PREFIX/lib/python$PY_VERSION/config -lpython$PY_VERSION"
 		PY_CFLAGS="-I$PY_PREFIX/include/python$PY_VERSION"
 		AC_DEFINE(USE_PYTHON, [1], [Define if python headers are available.])
+		dnl Because the above AC_CHECK_LIB get in the way...
+		AC_MSG_CHECKING(for Python compile flags)
 		AC_MSG_RESULT(ok)
 	else
 		AC_MSG_RESULT([Can't find Python.h])
@@ -1644,6 +1665,9 @@
 
 SSL_CERTIFICATES_DIR=""
 if ! test -z "$ssl_certificates_dir" ; then
+	if test "x$ssl_certificates_dir" = "xyes" ; then
+		AC_MSG_ERROR([--with-system-ssl-certs requires that a location is specified, eg. --with-system-ssl-certs=/etc/pki/tls/certs])
+	fi
 	if ! test -d "$ssl_certificates_dir" ; then
 		AC_MSG_ERROR([$ssl_certificates_dir does not exist, if this is the correct location please make sure that it exists.])
 	fi
@@ -1751,13 +1775,13 @@
 AC_SUBST(GNUTLS_LIBS)
 
 if test "x$enable_gnutls" = "xyes"; then
-	AC_MSG_CHECKING(for gnutls_priority_set_direct)
+	AC_MSG_CHECKING(for gnutls_priority_set_direct and gnutls_priority_set)
 	LIBS_save="$LIBS"
 	LIBS="$LIBS $GNUTLS_LIBS"
 	CPPFLAGS_save="$CPPFLAGS"
 	CPPFLAGS="$CPPFLAGS $GNUTLS_CFLAGS"
 	AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <gnutls/gnutls.h>]],
-                                        [[gnutls_session s; gnutls_priority_set_direct(s, NULL, NULL);]])],
+                                        [[gnutls_session s; gnutls_priority_set_direct(s, NULL, NULL); gnutls_priority_set(s, NULL);]])],
 	               [AC_DEFINE([HAVE_GNUTLS_PRIORITY_FUNCS], 1,
                                   [Define if your gnutls has gnutls_priority_set_direct and friends])
 	                AC_MSG_RESULT(yes)],
@@ -1766,6 +1790,23 @@
         LIBS="$LIBS_save"
 fi
 
+if test "x$enable_gnutls" = "xyes"; then
+	AC_MSG_CHECKING(for GNUTLS_CERT_INSECURE_ALGORITHM)
+	LIBS_save="$LIBS"
+	LIBS="$LIBS $GNUTLS_LIBS"
+	CPPFLAGS_save="$CPPFLAGS"
+	CPPFLAGS="$CPPFLAGS $GNUTLS_CFLAGS"
+	AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <gnutls/gnutls.h>]],
+                                        [[unsigned int verify = GNUTLS_CERT_INSECURE_ALGORITHM;]])],
+	               [AC_DEFINE([HAVE_GNUTLS_CERT_INSECURE_ALGORITHM], 1,
+                                  [Define if your gnutls has the GNUTLS_CERT_INSECURE_ALGORITHM flag])
+	                AC_MSG_RESULT(yes)],
+	               [AC_MSG_RESULT(no)])
+	CPPFLAGS="$CPPFLAGS_save"
+        LIBS="$LIBS_save"
+fi
+
+
 AM_CONDITIONAL(USE_GNUTLS, test "x$enable_gnutls" = "xyes")
 
 
@@ -2103,6 +2144,7 @@
 		if test -f $dir/tclConfig.sh; then
 			TCLCONFIG=$dir/tclConfig.sh
 			AC_MSG_RESULT([yes ($TCLCONFIG)])
+			break
 		fi
 	done
 	if test "$TCLCONFIG" = "no"; then
@@ -2128,8 +2170,8 @@
 			CPPFLAGS="$CPPFLAGS $TCL_INCLUDE_SPEC -I$TCL_PREFIX/include"
 			oldLIBS=$LIBS
 			LIBS="$LIBS $TCL_LIB_SPEC"
-			AC_TRY_LINK([#include <tcl.h>],
-				[Tcl_Interp *interp=NULL; Tcl_Init(interp)],
+			AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <tcl.h>]],
+				[[Tcl_Interp *interp=NULL; Tcl_Init(interp)]])],
 				[AC_MSG_RESULT([yes]);enable_tcl=yes],
 				[AC_MSG_RESULT([no]);enable_tcl=no])
 			CPPFLAGS="$oldCPPFLAGS"
@@ -2165,6 +2207,7 @@
 	TKCONFIG=no
 	TKCONFIGDIRS="/usr/lib \
 			/usr/lib64 \
+			/usr/lib/tk8.5 \
 			/usr/lib/tk8.4 \
 			/usr/lib/tk8.3 \
 			/usr/lib/tk8.2 \
@@ -2173,6 +2216,7 @@
 		if test -f $dir/tkConfig.sh; then
 			TKCONFIG=$dir/tkConfig.sh
 			AC_MSG_RESULT([yes ($TKCONFIG)])
+			break
 		fi
 	done
 	if test "$TKCONFIG" = "no"; then
@@ -2192,8 +2236,8 @@
 		CPPFLAGS="$CPPFLAGS $TCL_CFLAGS"
 		oldLIBS=$LIBS
 		LIBS="$LIBS $TCL_LIB_SPEC $TK_LIB_SPEC"
-		AC_TRY_LINK([#include <tk.h>],
-				[Tcl_Interp *interp=NULL; Tcl_Init(interp); Tk_Init(interp);],
+		AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <tk.h>]],
+				[[Tcl_Interp *interp=NULL; Tcl_Init(interp); Tk_Init(interp);]])],
 				[AC_MSG_RESULT([yes]);enable_tk=yes],
 				[AC_MSG_RESULT([no]);enable_tk=no])
 		CPPFLAGS="$oldCPPFLAGS"
@@ -2240,11 +2284,15 @@
 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)
 if test "x$enable_cyrus_sasl" = "xyes" ; then
 	AC_CHECK_LIB(sasl2, sasl_client_init, [
+			AM_CONDITIONAL(USE_CYRUS_SASL, true)
 			AC_DEFINE(HAVE_CYRUS_SASL, [1], [Define to 1 if Cyrus SASL is present])
 			SASL_LIBS=-"lsasl2"
 		], [
-			AC_ERROR(Cyrus SASL library not found)
+			AM_CONDITIONAL(USE_CYRUS_SASL, false)
+			AC_MSG_ERROR([Cyrus SASL library not found])
 		])
+else
+	AM_CONDITIONAL(USE_CYRUS_SASL, false)
 fi
 
 dnl #######################################################################
@@ -2274,7 +2322,7 @@
 			[KRB4_LIBS="-lkrb4 -ldes425 -lkrb5 -lk5crypto -lcom_err"],
 			[AC_CHECK_LIB(krb, krb_rd_req,
 				[KRB4_LIBS="-lkrb -ldes"],
-				[AC_ERROR(Kerberos 4 libraries not found)],
+				[AC_MSG_ERROR([Kerberos 4 libraries not found])],
 				-ldes)],
 			-ldes425 -lkrb5 -lk5crypto -lcom_err)
 	orig_LIBS="$LIBS"
@@ -2308,7 +2356,7 @@
 	LDFLAGS="$LDFLAGS $ZEPHYR_LDFLAGS"
 	AC_CHECK_LIB(zephyr, ZInitialize,
 		[ZEPHYR_LIBS="-lzephyr"],
-		[AC_ERROR(Zephyr libraries not found)],
+		[AC_MSG_ERROR([Zephyr libraries not found])],
 		-lzephyr)
 	orig_LIBS="$LIBS"
 	LIBS="$orig_LIBS"
@@ -2317,7 +2365,7 @@
 
 AC_MSG_CHECKING(for me pot o' gold)
 AC_MSG_RESULT(no)
-AC_CHECK_FUNCS(gethostid lrand48)
+AC_CHECK_FUNCS(gethostid lrand48 timegm)
 AC_CHECK_FUNCS(memcpy memmove random strchr strerror vprintf)
 AC_CHECK_HEADERS(malloc.h paths.h sgtty.h stdarg.h sys/cdefs.h)
 AC_CHECK_HEADERS(sys/file.h sys/filio.h sys/ioctl.h sys/msgbuf.h)
@@ -2339,18 +2387,18 @@
 AC_VAR_TIMEZONE_EXTERNALS
 
 AC_CACHE_CHECK(for tm_gmtoff in struct tm, ac_cv_struct_tm_gmtoff,
-        AC_TRY_COMPILE([
+        AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
                 #include <time.h>
-        ], [
+        ]], [[
                 struct tm tm;
                 tm.tm_gmtoff = 1;
-        ], ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no))
+        ]])], [ac_cv_struct_tm_gmtoff=yes], [ac_cv_struct_tm_gmtoff=no]))
 if test $ac_cv_struct_tm_gmtoff = yes; then
         AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define if you have a tm_gmtoff member in struct tm])
 fi
 
 AC_CACHE_CHECK([whether va_lists can be copied by value], ac_cv_va_val_copy,[
-	AC_TRY_RUN([#include <stdarg.h>
+	AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdarg.h>
 #include <stdlib.h>
 	void f (int i, ...) {
 	va_list args1, args2;
@@ -2363,7 +2411,7 @@
 	int main() {
 	  f (0, 42);
 	  return 0;
-	}],
+	}]])],
 	[ac_cv_va_val_copy=yes],
 	[ac_cv_va_val_copy=no],
 	[ac_cv_va_val_copy=yes])
@@ -2389,11 +2437,11 @@
 AM_CONDITIONAL(INSTALL_PIXMAPS, test "x$enable_pixmaps" = "xyes")
 
 dnl #######################################################################
-dnl # Disable installation of translation files
+dnl # Tweak status tray icon installation directory
 dnl #######################################################################
-AC_ARG_ENABLE(nls, AC_HELP_STRING([--disable-nls], [disable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes)
+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)
 
-AM_CONDITIONAL(INSTALL_I18N, test "x$enable_i18n" = "xyes")
+AM_CONDITIONAL(ENABLE_TRAYCOMPAT, test "x$enable_traycompat" = "xyes")
 
 dnl #######################################################################
 dnl # Check for Doxygen and dot (part of GraphViz)
@@ -2461,7 +2509,7 @@
 
 AM_CONDITIONAL(PURPLE_AVAILABLE, true)
 
-AC_OUTPUT([Makefile
+AC_CONFIG_FILES([Makefile
 		   Doxyfile
 		   doc/Makefile
 		   doc/pidgin.1
@@ -2469,8 +2517,8 @@
 		   m4macros/Makefile
 		   pidgin.apspec
 		   pidgin/Makefile
-		   pidgin/pidgin.pc
-		   pidgin/pidgin-uninstalled.pc
+		   pidgin/pidgin-3.pc
+		   pidgin/pidgin-3-uninstalled.pc
 		   pidgin/pixmaps/Makefile
 		   pidgin/pixmaps/emotes/default/24/Makefile
 		   pidgin/pixmaps/emotes/none/Makefile
@@ -2484,10 +2532,12 @@
 		   pidgin/plugins/perl/Makefile
 		   pidgin/plugins/perl/common/Makefile.PL
 		   pidgin/plugins/ticker/Makefile
+		   pidgin/themes/Makefile
+		   libpurple/ciphers/Makefile
 		   libpurple/example/Makefile
 		   libpurple/gconf/Makefile
-		   libpurple/purple.pc
-		   libpurple/purple-uninstalled.pc
+		   libpurple/purple-3.pc
+		   libpurple/purple-3-uninstalled.pc
 		   libpurple/plugins/Makefile
 		   libpurple/plugins/mono/Makefile
 		   libpurple/plugins/mono/api/Makefile
@@ -2503,15 +2553,13 @@
 		   libpurple/protocols/irc/Makefile
 		   libpurple/protocols/jabber/Makefile
 		   libpurple/protocols/msn/Makefile
-		   libpurple/protocols/msnp9/Makefile
 		   libpurple/protocols/myspace/Makefile
+		   libpurple/protocols/mxit/Makefile
 		   libpurple/protocols/novell/Makefile
 		   libpurple/protocols/null/Makefile
 		   libpurple/protocols/oscar/Makefile
-		   libpurple/protocols/qq/Makefile
 		   libpurple/protocols/sametime/Makefile
 		   libpurple/protocols/silc/Makefile
-		   libpurple/protocols/silc10/Makefile
 		   libpurple/protocols/simple/Makefile
 		   libpurple/protocols/yahoo/Makefile
 		   libpurple/protocols/zephyr/Makefile
@@ -2529,6 +2577,7 @@
 		   po/Makefile.in
 		   pidgin.spec
 		  ])
+AC_OUTPUT
 
 echo
 echo $PACKAGE $VERSION
@@ -2557,7 +2606,9 @@
 echo Build with Cyrus SASL support. : $enable_cyrus_sasl
 echo Use kerberos 4 with zephyr.... : $kerberos
 echo Use external libzephyr........ : $zephyr
+echo Use external libgadu.......... : $gadu_libs
 echo Install pixmaps............... : $enable_pixmaps
+echo Old tray icon compatibility... : $enable_traycompat
 echo Install translations.......... : $enable_i18n
 echo Has you....................... : yes
 echo
@@ -2565,6 +2616,7 @@
 echo Use X Session Management...... : $enable_sm
 echo Use startup notification...... : $enable_startup_notification
 echo Build with GtkSpell support... : $enable_gtkspell
+echo Build with GCR widgets........ : $enable_gcr
 echo
 echo Build with plugin support..... : $enable_plugins
 echo Build with Mono support....... : $enable_mono
@@ -2581,7 +2633,13 @@
 if test "x$enable_pixmaps" = "xno" ; then
 	echo
 	echo Warning: You have disabled the installation of pixmap data, but Pidgin
-	echo still requires installed pixmaps.  Be sure you know what you\'re doing.
+	echo still requires installed pixmaps.  Be sure you know what you are doing.
+fi
+if test "x$enable_i18n" = "xno" ; then
+	echo
+	echo Warning: You have disabled the building and installation of translation
+	echo data.  This will prevent building pidgin.desktop and the GConf schemas.
+	echo Be sure you know what you are doing.
 fi
 echo
 echo configure complete, now type \'make\'
--- a/doc/Makefile.am	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -32,6 +32,7 @@
 	gtkimhtml-signals.dox \
 	gtkrc-2.0 \
 	imgstore-signals.dox \
+	jabber-signals.dox \
 	log-signals.dox \
 	notify-signals.dox \
 	pidgin.1.in \
--- a/doc/PERL-HOWTO.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/PERL-HOWTO.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -72,8 +72,8 @@
 sub plugin_load {
 	$plugin = shift;
 
-	# Testing was done using Oscar, but this should work regardless of the protocol chosen
-	my $protocol = "prpl-oscar";
+	# Testing was done using AIM, but this should work regardless of the protocol chosen
+	my $protocol = "prpl-aim";
 	my $account_name = "test";
 
 	# Create a new Account
@@ -149,7 +149,7 @@
 sub plugin_load {
 	my $plugin = shift;
 
-	my $protocol = "prpl-oscar";
+	my $protocol = "prpl-aim";
 	my $account_name = "test";
 
 	# This is how we get an account to use in the following tests.  You should replace the username
@@ -232,7 +232,7 @@
 @code
 sub plugin_load {
 	my $plugin = shift;
-	my $protocol = "prpl-oscar";
+	my $protocol = "prpl-aim";
 	my $account_name = "test";
 
 	$account = Purple::Accounts::find($account_name, $protocol);
--- a/doc/account-signals.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/account-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -14,9 +14,13 @@
   @signal account-actions-changed
   @signal account-alias-changed
   @signal account-authorization-requested
+  @signal account-authorization-requested-with-message
   @signal account-authorization-denied
   @signal account-authorization-granted
   @signal account-error-changed
+  @signal account-signed-on
+  @signal account-signed-off
+  @signal account-connection-error
  @endsignals
 
  @see account.h
@@ -30,7 +34,6 @@
   @signaldesc
    Emitted when an account is created by calling purple_account_new.
   @param account The account.
-  @since 2.6.0
  @endsignaldef
 
  @signaldef account-destroying
@@ -40,7 +43,6 @@
   @signaldesc
    Emitted when an account is about to be destroyed.
   @param account The account.
-  @since 2.6.0
  @endsignaldef
 
  @signaldef account-added
@@ -152,7 +154,22 @@
   @return Less than zero to deny the request without prompting, greater
           than zero if the request should be granted. If zero is returned,
           then the user will be prompted with the request.
-  @since 2.3.0
+ @endsignaldef
+
+ @signaldef account-authorization-requested-with-message
+  @signalproto
+int (*account_authorization_requested)(PurpleAccount *account, const char *user, const char *message);
+  @endsignalproto
+  @signaldesc
+   Emitted when a user requests authorization.
+  @param account The account.
+  @param user    The name of the user requesting authorization.
+  @param message The authorization request message
+  @return PURPLE_ACCOUNT_RESPONSE_IGNORE to silently ignore the request,
+          PURPLE_ACCOUNT_RESPONSE_DENY to block the request (the sender might
+          get informed, PURPLE_ACCOUNT_RESPONSE_ACCEPT if the request should be
+          granted. If PURPLE_ACCOUNT_RESPONSE_PASS is returned, then the user
+          will be prompted with the request.
  @endsignaldef
 
  @signaldef account-authorization-denied
@@ -163,7 +180,6 @@
    Emitted when the authorization request for a buddy is denied.
   @param account The account.
   @param user    The name of the user requesting authorization.
-  @since 2.3.0
  @endsignaldef
 
  @signaldef account-authorization-granted
@@ -174,7 +190,6 @@
    Emitted when the authorization request for a buddy is granted.
   @param account The account.
   @param user    The name of the user requesting authorization.
-  @since 2.3.0
  @endsignaldef
 
  @signaldef account-error-changed
@@ -193,8 +208,35 @@
                    pointer just after the next time this signal is emitted
                    for this @a account.
   @see purple_account_get_current_error()
-  @since 2.3.0
+ @endsignaldef
+
+ @signaldef account-signed-on
+  @signalproto
+void (*signed_on)(PurpleAccount *account);
+  @endsignalproto
+  @signaldesc
+   Emitted when an account has signed on.
+  @param account The account that has signed on.
  @endsignaldef
 
+ @signaldef account-signed-off
+  @signalproto
+void (*signed_off)(PurpleAccount *account);
+  @endsignalproto
+  @signaldesc
+   Emitted when an account has signed off.
+  @param account The account that has signed off.
+ @endsignaldef
+
+ @signaldef account-connection-error
+  @signalproto
+void (*connection_error)(PurpleAccount *gc, PurpleConnectionError err, const gchar *desc)
+  @endsignalproto
+  @signaldesc
+   Emitted when a connection error occurs, before @ref signed-off.
+   @param account The account on which the error has occurred
+   @param err     The error that occurred
+   @param desc    A description of the error, giving more information.
+ @endsignaldef
  */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/blist-signals.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/blist-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -11,6 +11,8 @@
   @signal buddy-removed
   @signal buddy-icon-changed
   @signal blist-node-aliased
+  @signal buddy-caps-changed
+  @signal ui-caps-changed
  @endsignals
 
  @see blist.h
@@ -124,5 +126,27 @@
    Emitted when a blist node (buddy, chat, or contact) is aliased.
   @endsignaldef
 
+ @signaldef buddy-caps-changed
+  @signalproto
+void (*buddy_caps_changed)(PurpleBuddy *buddy, PurpleMediaCaps newcaps,
+    PurpleMediaCaps oldcaps)
+  @endsignalproto
+  @signaldesc
+    Emitted when updating a buddy's media capabilities.
+  @param buddy	  The buddy
+  @param newcaps
+  @param oldcaps
+ @endsignaldef
+
+ @signaldef ui-caps-changed
+  @signalproto
+void (*ui_caps_changed)(PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps)
+  @endsignalproto
+  @signaldesc
+    Emitted when updating the media capabilities of the UI.
+  @param newcaps
+  @param oldcaps
+ @endsignaldef
+ 
  */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/connection-signals.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/connection-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -3,6 +3,7 @@
  @signals
   @signal signing-on
   @signal signed-on
+  @signal autojoin
   @signal signing-off
   @signal signed-off
   @signal connection-error
@@ -30,6 +31,21 @@
   @param gc The connection that has signed on.
  @endsignaldef
 
+ @signaldef autojoin
+  @signalproto
+gboolean (*autojoin)(PurpleConnection *gc);
+  @endsignalproto
+  @signaldesc
+   Emitted when a connection has signed on, after the signed-on signal, to
+   signal UIs to autojoin chats if they wish.  UIs should connect to this
+   with @c PURPLE_SIGNAL_PRIORITY_HIGHEST to allow plugins to block this
+   signal before the UI sees it and then re-emit it later.
+  @param gc The connection that has signed on.
+  @return @c TRUE if the signal was handled or @c FALSE otherwise.  In
+          practice, the return value is irrelevant, as it really only
+          exists so plugins can block the UI's autojoin.
+ @endsignaldef
+
  @signaldef signing-off
   @signalproto
 void (*signing_off)(PurpleConnection *gc);
--- a/doc/conversation-signals.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/conversation-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -32,7 +32,10 @@
   @signal chat-join-failed
   @signal chat-left
   @signal chat-topic-changed
+  @signal cleared-message-history
   @signal conversation-extended-menu
+  @signal sent-attention
+  @signal got-attention
  @endsignals
 
  @see conversation.h
@@ -146,7 +149,6 @@
   @param message The message that was blocked.
   @param flags   The IM message flags.
   @param when    The time the message was sent.
-  @since 2.5.0
  @endsignaldef
 
  @signaldef writing-chat-msg
@@ -433,7 +435,6 @@
   @param name     The name of the chat invited to.
   @param message  The invitation message sent.
   @param data     Hashtable containing data about the invited chat.
-  @since 2.5.0
  @endsignaldef
 
  @signaldef chat-joined
@@ -474,7 +475,41 @@
    conversation.
   @param conv   The conversation.
   @param list   A pointer to the list of actions.
-  @since 2.1.0
+ @endsignaldef
+
+ @signaldef cleared-message-history
+  @signalproto
+void (*cleared_message_history)(PurpleConversation *conv);
+  @endsignalproto
+  @signaldesc
+    Emitted when the conversation history is cleared.
+  @param conv   The conversation.
+ @endsignaldef
+
+ @signaldef sent-attention
+  @signalproto
+void (*got_attention)(PurpleAccount *account, const char *who, 
+	PurpleConversation *conv, guint type)
+  @endsignalproto
+  @signaldesc
+    Emitted when receiving an attention message (buzz, nudge, etc.).
+  @param account  The account
+  @param who      The name of the person receiving the attention
+  @param conv     The conversation
+  @param type     The attention type (an index starting at 0)
+ @endsignaldef
+
+ @signaldef got-attention
+  @signalproto
+void (*got_attention)(PurpleAccount *account, const char *who, 
+	PurpleConversation *conv, guint type)
+  @endsignalproto
+  @signaldesc
+    Emitted when receiving an attention message (buzz, nudge, etc.).
+  @param account  The account
+  @param who      The name of the person sending the attention
+  @param conv     The conversation
+  @param type     The attention type (an index starting at 0)
  @endsignaldef
 */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/core-signals.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/core-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -2,6 +2,7 @@
 
  @signals
   @signal quitting
+  @signal uri-handler
  @endsignals
 
  @see core.h
@@ -16,5 +17,16 @@
    Emitted when libpurple is quitting.
  @endsignaldef
 
+ @signaldef uri-handler
+  @signalproto
+gboolean (*uri_handler)(const gchar *proto, const gchar *cmd, GHashTable *params);
+  @endsignalproto
+  @signaldesc
+   Emitted when handling a registered URI.
+  @param proto The protocol of the URI.
+  @param cmd The 'command' of the URI.
+  @param params Any key/value parameters from the URI.
+ @endsignaldef
+
  */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/finch.1.in	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/finch.1.in	Wed Jun 13 19:30:27 2012 -0400
@@ -40,14 +40,14 @@
 The following options are provided by \fBfinch\fR using the standard GNU
 command line syntax:
 .TP
+.B \-c, \-\-config=\fIDIR\fB
+Use \fIDIR\fR as the directory for config files instead of \fI~/.purple\fR.
+.TP
 .B \-d, \-\-debug
 Print debugging messages to stderr and start with the \fBDebug\fR window. The
 messages shown in the \fBDebug\fR window are the same as the ones printed in
 stderr.
 .TP
-.B \-c, \-\-config=\fIDIR\fB
-Use \fIDIR\fR as the directory for config files instead of \fI~/.purple\fR.
-.TP
 .B \-h, \-\-help
 Print this help and exit.
 .TP
@@ -59,7 +59,7 @@
 Display the version information window.
 
 .SH GNT Shortcuts
-You can use the following shortcuts (see the "\*QWidget Actions\*U" section for a more complete list):
+You can use the following shortcuts (see the "Widget Actions" section for a more complete list):
 .TP
 .B Alt \+ a
 Bring up a list of available actions. You can use this list to access the
@@ -114,7 +114,7 @@
 .B Ctrl \+ o \fR or \fB F10
 Bring up the menu (if there is one) for a window.
 .TP
-.B F11
+.B F11 \fR or \fB Ctrl \+ x
 Popup the context menu (if there is one) for the selected widget.
 .TP
 .B Alt \+ /
@@ -305,6 +305,13 @@
 left = focus-prev
 
 .br
+[GntComboBox::binding]
+.br
+down = dropdown
+.br
+up = dropdown
+
+.br
 [GntEntry::binding]
 .br
 c-a = cursor-home
@@ -339,6 +346,10 @@
 .br
 up = suggest-prev
 .br
+page-down = suggest-next-page
+.br
+page-up = suggest-prev-page
+.br
 c-w = delete-prev-word
 .br
 a-b = cursor-prev-word
@@ -348,6 +359,16 @@
 a-d = delete-next-word
 .br
 c-v = clipboard-paste
+.br
+c-p = history-prev
+.br
+c-n = history-next
+.br
+c-r = history-search
+.br
+c-up = history-prev
+.br
+c-down = history-next
 
 .br
 [GntTree::binding]
@@ -366,6 +387,10 @@
 .br
 backspace = move-parent
 .br
+home = move-first
+.br
+end = move-last
+.br
 # Following is the default binding for the context-menu
 .br
 menu = context-menu
@@ -482,6 +507,10 @@
 .br
 a-/ = help-for-widget
 .br
+a-c-j = window-scroll-down
+.br
+a-c-k = window-scroll-up
+.br
 # The following action is still incomplete, and doesn't have a default binding
 .br
 # switch-window-n
--- a/doc/funniest_home_convos.txt	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/funniest_home_convos.txt	Wed Jun 13 19:30:27 2012 -0400
@@ -530,6 +530,17 @@
 
 --
 
+(14:30:09) linux_user: i have a ?
+(14:31:03) linux_user: when i install this on wine and run it it come up as
+           boxes on the screen
+(14:31:26) nosnilmot: why on earth would you install Pidgin under wine?
+(14:31:41) Err: heh
+(14:31:48) Err: I think my brain just segfaulted on that
+(14:31:58) linux_user: well i am on linux
+(14:32:10) linux_user: linux mint and thay is the only way i can run it
+
+--
+
 14:39 <rrobbertt> Does anyone know a way to get text to speech with pidgin?
 14:41 <elb> do you want to be rooted sooner, or later?
 14:42 <seanegan> good question"; rm -rf ~
@@ -572,3 +583,42 @@
 15:46 <khc> well, there was a Grand Smiley Theme Database
 15:47 <SimGuy> the GSTD sounds like a bad acronym
 15:47 <khc> I realized after typing that
+
+--
+
+(01:51:38 AM) user entered the room.
+(01:52:46 AM) user: .addKeyActionListener(new KeyActionListener() onKeyPress() {if (event.geyKeyPresss().equals(Key.UP_ARROW) { inputbox.text = history.pop() }}}}}}});
+(01:52:51 AM) user: THERE, FOR **** SAKE
+(01:52:53 AM) user: its 2009
+(01:53:06 AM) user: oh wait. ctrl up works
+(01:53:07 AM) user: lol
+(01:53:11 AM) user: yey me
+(01:53:16 AM) user left the room.
+(01:55:31 AM) darkrain42: Wow.
+(01:58:15 AM) QuLogic: I think he failed to realize we'd have to re-write pidgin in java to do that
+(01:59:44 AM) khc: history.pop() is clearly wrong too
+
+--
+
+Some time later:
+(02:41:55 AM) user entered the room.
+(02:42:24 AM) user: didn't I read some idiot post, about 2 years ago, before pidgin was renamed / forked, over one dev refusing to make minimize on close?
+(02:43:12 AM) QuLogic: I see you've learned to at least ask a question before jumping to random conclusions
+(02:44:01 AM) user: QuLogic: :-))))))))))))))))))))))
+(02:44:12 AM) user: hey, I submitted a code patch!
+(02:44:36 AM) user: now, anyway, what happened? why did I get the buddy list (empty) stealing focus, and why did it exit on close?
+(02:44:40 AM) QuLogic: it's not really a patch if it's in the wrong language
+(02:44:42 AM) user: I've had this argument before, in 2006
+(02:44:55 AM) user: QuLogic: simple, rewrite the rest ;-)
+(02:44:58 AM) khc: pidgin never steals focus
+(02:45:10 AM) khc: if it exit on close, it's because you didn't turn on the systray icon
+(02:47:17 AM) user: khc - and that isn't default... why? anyway. I recall something on the matter, and I think this was the project (pre-fork?) or is this the unforked, renamed? I forget.
+(02:47:42 AM) user: Whoever it was arguing about it (and font sizes I believe) was an idiot... not one of you I suppose, just making idle chit chat.
+(02:47:43 AM) user: thanks
+(02:47:48 AM) darkrain42: It is on by default. Some distros change that.
+(02:48:00 AM) darkrain42: And I don't even know what you're arguing about at this point.
+(02:48:11 AM) user: ... /leave - That command doesn't work on this protocol... /leave #pidgin ...That comm..... :-(((
+(02:48:18 AM) user: darkrain42: now arguing, just remembering something
+(02:48:27 AM) user left the room.
+(02:49:04 AM) darkrain42: Wow. (again)
+
--- a/doc/gtkblist-signals.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/gtkblist-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -5,6 +5,7 @@
   @signal gtkblist-unhiding
   @signal gtkblist-created
   @signal drawing-tooltip
+  @signal drawing-buddy
  @endsignals
 
  @see gtkblist.h
@@ -53,5 +54,17 @@
   @param full Whether we're doing a full tooltip for the priority buddy or
               a compact tooltip for a non-priority buddy.
  @endsignaldef
+
+ @signaldef drawing-buddy
+  @signalproto
+char *(*drawing-buddy)(PurpleBuddy *buddy);
+  @endsignalproto
+  @signaldesc
+   Emitted to allow plugins to handle markup within a buddy's name or to
+   override the default of no formatting for names shown in the buddy list.
+   @param buddy A pointer to the PurpleBuddy that will be displayed.
+   @return The text to display (must be allocated), or @c NULL if no
+           changes to the default behavior are desired.
+ @endsignaldef
 */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/gtkconv-signals.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/gtkconv-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -129,7 +129,6 @@
   @signaldesc
    Emitted immediately before an existing conversation is hidden.
   @param gtkconv  The PidginConversation
-  @since 2.2.0
  @endsignaldef
 
  @signaldef conversation-displayed
@@ -139,7 +138,6 @@
   @signaldesc
    Emitted right after the Pidgin UI is attached to a new or a hidden conversation.
   @param gtkconv  The PidginConversation
-  @since 2.2.0
  @endsignaldef
 
 */
--- a/doc/gtkimhtml-signals.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/gtkimhtml-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -6,6 +6,7 @@
   @signal format_function_clear
   @signal format_function_toggle
   @signal format_function_update
+  @signal paste
  @endsignals
 
  @see gtkimhtml.h
@@ -57,6 +58,17 @@
   @signaldesc Emitted when the cursor has moved and formatting has changed
   @param imhtml The GtkIMHtml emitting the signal.
   @param data   User defined data.
+
+ @signaldef paste
+ 	@signalproto
+void (*paste) (GtkIMHtml *imhtml, char *format)
+	@endsignalproto
+	@signaldesc Emitted when paste from the clipboard is requested.
+	@param imhtml  The GtkIMHtml emitting the signal.
+	@param format  If 'text', then the formatting of the clipboard content
+	               will be removed before pasting. If empty or 'html', then
+		       the formatting will not be removed. Any other value for
+		       this parameter is ignored and nothing is pasted.
  @endsignaldef
 */
 // vim: syntax=c.doxygen tw=75 et
--- a/doc/gtkrc-2.0	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/gtkrc-2.0	Wed Jun 13 19:30:27 2012 -0400
@@ -81,6 +81,12 @@
 	bind "<alt>F2" { "format_toggle" (2) }
 # Ctrl-alt-shift-f3 toggles underline
 	bind "<ctrl><alt><shift>F3" { "format_toggle" (4) }
+
+# Ctrl-v to paste as plain text
+	bind "<ctrl>v" { "paste" ("text") }
+
+# Ctrl-Shift-v for normal 'Paste'
+	bind "<ctrl><shift>v" { "paste" ("html") }
 }
   
 widget "*pidgin_conv_entry" binding "my-bindings"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/jabber-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,138 @@
+/** @page jabber-signals Jabber Signals
+
+ @signals
+  @signal jabber-receiving-iq
+  @signal jabber-receiving-message
+  @signal jabber-receiving-presence
+  @signal jabber-watched-iq
+  @signal jabber-register-namespace-watcher
+  @signal jabber-unregister-namespace-watcher
+  @signal jabber-sending-xmlnode
+  @signal jabber-receiving-xmlnode
+ @endsignals
+
+ <hr>
+
+ @signaldef jabber-receiving-iq
+  @signalproto
+gboolean (*iq_received)(PurpleConnection *gc, const char *type, const char *id,
+                     const char *from, xmlnode *iq);
+  @endsignalproto
+  @signaldesc
+   Emitted when an XMPP IQ stanza is received. Allows a plugin to process IQ
+   stanzas.
+  @param gc        The connection on which the stanza is received
+  @param type      The IQ type ('get', 'set', 'result', or 'error')
+  @param id        The ID attribute from the stanza. MUST NOT be NULL.
+  @param from      The originator of the stanza. MAY BE NULL if the stanza
+                   originated from the user's server.
+  @param iq        The full stanza received.
+  @return TRUE if the plugin processed this stanza and *nobody else* should
+          process it. FALSE otherwise.
+ @endsignaldef
+
+ @signaldef jabber-receiving-message
+  @signalproto
+gboolean (*message_received)(PurpleConnection *gc, const char *type,
+                              const char *id, const char *from, const char *to,
+                              xmlnode *message);
+  @endsignalproto
+  @signaldesc
+   Emitted when an XMPP message stanza is received. Allows a plugin to
+   process message stanzas.
+  @param gc        The connection on which the stanza is received
+  @param type      The message type (see rfc3921 or rfc3921bis)
+  @param id        The ID attribute from the stanza. MAY BE NULL.
+  @param from      The originator of the stanza. MAY BE NULL if the stanza
+                   originated from the user's server.
+  @param to        The destination of the stanza. This is probably either the
+                   full JID of the receiver or the receiver's bare JID.
+  @param message   The full stanza received.
+  @return TRUE if the plugin processed this stanza and *nobody else* should
+          process it. FALSE otherwise.
+ @endsignaldef
+
+ @signaldef jabber-receiving-presence
+  @signalproto
+gboolean (*presence_received)(PurpleConnection *gc, const char *type,
+                               const char *from, xmlnode *presence);
+  @endsignalproto
+  @signaldesc
+   Emitted when an XMPP presence stanza is received. Allows a plugin to process
+   presence stanzas.
+  @param gc        The connection on which the stanza is received
+  @param type      The presence type (see rfc3921 or rfc3921bis). NULL indicates
+                   this is an "available" (i.e. online) presence.
+  @param from      The originator of the stanza. MAY BE NULL if the stanza
+                   originated from the user's server.
+  @param presence  The full stanza received.
+  @return TRUE if the plugin processed this stanza and *nobody else* should
+          process it. FALSE otherwise.
+ @endsignaldef
+
+ @signaldef jabber-watched-iq
+  @signalproto
+gboolean (*watched_iq)(PurpleConnection *gc, const char *type, const char *id,
+                       const char *from, xmlnode *child);
+  @endsignalproto
+  @signaldesc
+   Emitted when an IQ with a watched (child, namespace) pair is received.  See
+   jabber-register-namespace-watcher and jabber-unregister-namespace-watcher.
+  @param gc        The connection on which the stanza is received
+  @param type      The IQ type ('get', 'set', 'result', or 'error')
+  @param id        The ID attribute from the stanza. MUST NOT be NULL.
+  @param from      The originator of the stanza. MAY BE NULL if the stanza
+                   originated from the user's server.
+  @param child     The child node with namespace.
+  @return TRUE if the plugin processed this stanza and *nobody else* should
+          process it. FALSE otherwise.
+ @endsignaldef
+
+ @signaldef jabber-register-namespace-watcher
+  @signalproto
+void (register_namespace_watcher)(const char *node, const char *namespace);
+  @endsignalproto
+  @signaldesc
+   Emit this signal to register your desire to have specific IQ stanzas to be
+   emitted via the jabber-watched-iq signal when received.
+  @param node      The IQ child name to longer watch.
+  @param namespace The IQ child namespace to longer watch.
+ @endsignaldef
+
+ @signaldef jabber-unregister-namespace-watcher
+  @signalproto
+void (unregister_namespace_watcher)(const char *node, const char *namespace);
+  @endsignalproto
+  @signaldesc
+   Emit this signal to unregister your desire to have specific IQ stanzas to be
+   emitted via the jabber-watched-iq signal when received.
+  @param node      The IQ child name to no longer watch.
+  @param namespace The IQ child namespace to no longer watch.
+ @endsignaldef
+
+ @signaldef jabber-sending-xmlnode
+  @signalproto
+void (sending_xmlnode)(PurpleConnection *gc, xmlnode **stanza);
+  @endsignalproto
+  @signaldesc
+   Emit this signal (@c purple_signal_emit) to send a stanza. It is preferred
+   to use this instead of prpl_info->send_raw.
+   @param gc      The connection on which to send the stanza.
+   @param stanza  The stanza to send. If stanza is not NULL after being sent,
+                  the emitter should free it.
+ @endsignaldef
+
+ @signaldef jabber-receiving-xmlnode
+  @signalproto
+void (receiving_xmlnode)(PurpleConnection *gc, xmlnode **stanza);
+  @endsignalproto
+  @signaldesc
+   Emitted when an XMPP stanza is received. Allows a plugin to process any
+   stanza.
+   @param gc     The connection on which the stanza was received.
+   @param stanza The received stanza. Set stanza to NULL (and free it) to
+                 stop processing the stanza.
+ @endsignaldef
+
+*/
+// vim: syntax=c.doxygen tw=75 et
--- a/doc/notify-signals.dox	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/notify-signals.dox	Wed Jun 13 19:30:27 2012 -0400
@@ -35,7 +35,6 @@
   @param from      Who the email is from.
   @param to        Who the email is to.
   @param url       A url to view the email.
-  @since 2.1.0
  @endsignaldef
 
  @signaldef displaying-emails-notification
@@ -53,7 +52,6 @@
   @param tos        Who the emails are to.
   @param urls       The urls to view the emails.
   @param count      Number of emails being notified of.
-  @since 2.1.0
  @endsignaldef
 
 */
--- a/doc/pidgin.1.in	Wed Jun 13 19:28:57 2012 -0400
+++ b/doc/pidgin.1.in	Wed Jun 13 19:30:27 2012 -0400
@@ -49,6 +49,10 @@
 Print debugging messages to stdout.  These are the same debugging messages
 that are displayed in the \fBDebug Window\fR.
 .TP
+.B \-f, \-\-force-online
+Try to be online even if the network is reported (by Windows, or NetworkManager
+on Linux) to be unavailable.
+.TP
 .B \-h, \-\-help
 Print a summary of command line options and exit.
 .TP
@@ -620,10 +624,14 @@
 .br
   Casey Harkins (developer)
 .br
+  Ivan Komarov
+.br
   Gary 'grim' Kramlich (developer)
 .br
   Richard 'rlaager' Laager (developer) <\fIrlaager@pidgin.im\fR>
 .br
+  Sulabh 'sulabh_m' Mahajan (developer)
+.br
   Richard 'wabz' Nelson (developer)
 .br
   Christopher 'siege' O'Brien (developer)
@@ -656,8 +664,6 @@
 
 Our crazy patch writers include:
 .br
-  Paul Aurich
-.br
   Marcus 'malu' Lundblad
 .br
   Dennis 'EvilDennisR' Ristuccia
--- a/finch/Makefile.am	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -27,6 +27,7 @@
 	gntidle.c \
 	gntlog.c \
 	gntmedia.c \
+	gntmenuutil.c \
 	gntnotify.c \
 	gntplugin.c \
 	gntpounce.c \
@@ -49,6 +50,7 @@
 	gntidle.h \
 	gntlog.h \
 	gntmedia.h \
+	gntmenuutil.h \
 	gntnotify.h \
 	gntplugin.h \
 	gntpounce.h \
@@ -78,7 +80,6 @@
 
 AM_CPPFLAGS = \
 	-DSTANDALONE \
-	-DBR_PTHREADS=0 \
 	-DDATADIR=\"$(datadir)\" \
 	-DLIBDIR=\"$(libdir)/finch/\" \
 	-DLOCALEDIR=\"$(datadir)/locale\" \
--- a/finch/finch.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/finch.c	Wed Jun 13 19:30:27 2012 -0400
@@ -19,8 +19,8 @@
  * 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 "finch.h"
-#include <internal.h>
 
 #include "account.h"
 #include "conversation.h"
@@ -74,11 +74,28 @@
 		 * account "markdoliner."  Please don't use this key for other
 		 * applications.  You can either not specify a client key, in
 		 * which case the default "libpurple" key will be used, or you
-		 * can register for your own client key at
-		 * http://developer.aim.com/manageKeys.jsp
+		 * can try to register your own at the AIM or ICQ web sites
+		 * (although this functionality was removed at some point, it's
+		 * possible it has been re-added).  AOL's old key management
+		 * page is http://developer.aim.com/manageKeys.jsp
 		 */
 		g_hash_table_insert(ui_info, "prpl-aim-clientkey", "ma19sqWV9ymU6UYc");
-		g_hash_table_insert(ui_info, "prpl-icq-clientkey", "ma19sqWV9ymU6UYc");
+
+		/*
+		 * This is the client key for "Pidgin."  It is owned by the AIM
+		 * account "markdoliner."  Please don't use this key for other
+		 * applications.  You can either not specify a client key, in
+		 * which case the default "libpurple" key will be used, or you
+		 * can try to register your own at the AIM or ICQ web sites
+		 * (although this functionality was removed at some point, it's
+		 * possible it has been re-added).  AOL's old key management
+		 * page is http://developer.aim.com/manageKeys.jsp
+		 *
+		 * We used to have a Finch-specific devId/clientkey
+		 * (ma19sqWV9ymU6UYc), but it stopped working, so we switched
+		 * to this one.
+		 */
+		g_hash_table_insert(ui_info, "prpl-icq-clientkey", "ma1cSASNCKFtrdv9");
 
 		/*
 		 * This is the distid for Finch, given to us by AOL.  Please
@@ -252,6 +269,7 @@
 	gboolean opt_version = FALSE;
 	char *opt_config_dir_arg = NULL;
 	gboolean debug_enabled = FALSE;
+	struct stat st;
 
 	struct option long_options[] = {
 		{"config",   required_argument, NULL, 'c'},
@@ -321,7 +339,17 @@
 
 	/* set a user-specified config directory */
 	if (opt_config_dir_arg != NULL) {
-		purple_util_set_user_dir(opt_config_dir_arg);
+		if (g_path_is_absolute(opt_config_dir_arg)) {
+			purple_util_set_user_dir(opt_config_dir_arg);
+		} else {
+			/* Make an absolute (if not canonical) path */
+			char *cwd = g_get_current_dir();
+			char *path = g_build_path(G_DIR_SEPARATOR_S, cwd, opt_config_dir_arg, NULL);
+			purple_util_set_user_dir(path);
+			g_free(path);
+			g_free(cwd);
+		}
+
 		g_free(opt_config_dir_arg);
 	}
 
@@ -333,34 +361,13 @@
 	/* We don't want debug-messages to show up and corrupt the display */
 	purple_debug_set_enabled(debug_enabled);
 
-	/* If we're using a custom configuration directory, we
-	 * do NOT want to migrate, or weird things will happen. */
-	if (opt_config_dir_arg == NULL)
-	{
-		if (!purple_core_migrate())
-		{
-			char *old = g_strconcat(purple_home_dir(),
-			                        G_DIR_SEPARATOR_S ".gaim", NULL);
-			char *text = g_strdup_printf(_(
-				"%s encountered errors migrating your settings "
-				"from %s to %s. Please investigate and complete the "
-				"migration by hand. Please report this error at http://developer.pidgin.im"), _("Finch"),
-				old, purple_user_dir());
-
-			g_free(old);
-
-			purple_print_utf8_to_console(stderr, text);
-			g_free(text);
-
-			return 0;
-		}
-	}
-
 	purple_core_set_ui_ops(gnt_core_get_ui_ops());
 	purple_eventloop_set_ui_ops(gnt_eventloop_get_ui_ops());
 	purple_idle_set_ui_ops(finch_idle_get_ui_ops());
 
 	path = g_build_filename(purple_user_dir(), "plugins", NULL);
+	if (!g_stat(path, &st))
+		g_mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR);
 	purple_plugins_add_search_path(path);
 	g_free(path);
 
@@ -419,7 +426,7 @@
 	/* Initialize the libpurple stuff */
 	if (!init_libpurple(*argc, *argv))
 		return FALSE;
- 
+
 	purple_blist_show();
 	return TRUE;
 }
@@ -431,9 +438,7 @@
 	g_thread_init(NULL);
 
 	g_set_prgname("Finch");
-#if GLIB_CHECK_VERSION(2,2,0)
 	g_set_application_name(_("Finch"));
-#endif
 
 	if (gnt_start(&argc, &argv)) {
 		gnt_main();
--- a/finch/getopt.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/getopt.c	Wed Jun 13 19:30:27 2012 -0400
@@ -178,7 +178,7 @@
 {
   REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
 } ordering;
-
+
 #ifdef	__GNU_LIBRARY__
 /* We want to avoid inclusion of string.h with non-GNU libraries
    because there are many ways it can cause trouble.
@@ -219,7 +219,7 @@
     to[i] = from[i];
 }
 #endif				/* GNU C library.  */
-
+
 /* Handle permutation of arguments.  */
 
 /* Describe the part of ARGV that contains non-options that have
@@ -259,7 +259,7 @@
   first_nonopt += (optind - last_nonopt);
   last_nonopt = optind;
 }
-
+
 /* Scan elements of ARGV (whose length is ARGC) for option characters
    given in OPTSTRING.
 
@@ -663,7 +663,7 @@
 }
 
 #endif	/* _LIBC or not __GNU_LIBRARY__.  */
-
+
 #ifdef TEST
 
 /* Compile with -DTEST to make an executable for use in testing
--- a/finch/getopt1.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/getopt1.c	Wed Jun 13 19:30:27 2012 -0400
@@ -81,7 +81,7 @@
 
 
 #endif	/* _LIBC or not __GNU_LIBRARY__.  */
-
+
 #ifdef TEST
 
 #include <stdio.h>
--- a/finch/gntaccount.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntaccount.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,6 +23,8 @@
  * 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 <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -36,7 +38,6 @@
 #include <gntwindow.h>
 
 #include "finch.h"
-#include <internal.h>
 
 #include <account.h>
 #include <accountopt.h>
@@ -124,7 +125,8 @@
 
 	if (value == NULL || *value == '\0')
 	{
-		purple_notify_error(NULL, _("Error"), _("Account was not added"),
+		purple_notify_error(NULL, _("Error"),
+				dialog->account ? _("Account was not modified") : _("Account was not added"),
 				_("Username of an account must be non-empty."));
 		return;
 	}
@@ -159,15 +161,34 @@
 		account = dialog->account;
 
 		/* Protocol */
-		purple_account_set_protocol_id(account, purple_plugin_get_id(plugin));
-		purple_account_set_username(account, username->str);
+		if (purple_account_is_disconnected(account)) {
+			purple_account_set_protocol_id(account, purple_plugin_get_id(plugin));
+			purple_account_set_username(account, username->str);
+		} else {
+			const char *old = purple_account_get_protocol_id(account);
+			char *oldprpl;
+			if (strcmp(old, purple_plugin_get_id(plugin))) {
+				purple_notify_error(NULL, _("Error"), _("Account was not modified"),
+						_("The account's protocol cannot be changed while it is connected to the server."));
+				return;
+			}
+
+			oldprpl = g_strdup(purple_normalize(account, purple_account_get_username(account)));
+			if (g_utf8_collate(oldprpl, purple_normalize(account, username->str))) {
+				purple_notify_error(NULL, _("Error"), _("Account was not modified"),
+						_("The account's username cannot be changed while it is connected to the server."));
+				g_free(oldprpl);
+				return;
+			}
+			g_free(oldprpl);
+			purple_account_set_username(account, username->str);
+		}
 	}
 	g_string_free(username, TRUE);
 
 	/* Alias */
 	value = gnt_entry_get_text(GNT_ENTRY(dialog->alias));
-	if (value && *value)
-		purple_account_set_alias(account, value);
+	purple_account_set_alias(account, value);
 
 	/* Remember password and password */
 	purple_account_set_remember_password(account,
@@ -215,7 +236,8 @@
 			}
 			else if (type == PURPLE_PREF_STRING_LIST)
 			{
-				/* TODO: */
+				gchar *value = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(entry));
+				purple_account_set_string(account, setting, value);
 			}
 			else
 			{
@@ -245,6 +267,17 @@
 		}
 	}
 
+	/* In case of a new account, the 'Accounts' window is updated from the account-added
+	 * callback. In case of changes in an existing account, we need to explicitly do it
+	 * here.
+	 */
+	if (dialog->account != NULL && accounts.window) {
+		gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account,
+				0, purple_account_get_username(dialog->account));
+		gnt_tree_change_text(GNT_TREE(accounts.tree), dialog->account,
+				1, purple_account_get_protocol_name(dialog->account));
+	}
+
 	gnt_widget_destroy(dialog->window);
 }
 
@@ -275,7 +308,7 @@
 	if (!plugin)
 		return;
 	prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
-	
+
 	username = dialog->account ? g_strdup(purple_account_get_username(dialog->account)) : NULL;
 
 	for (iter = prplinfo->user_splits; iter; iter = iter->next)
@@ -398,8 +431,26 @@
 
 			if (type == PURPLE_PREF_STRING_LIST)
 			{
-				/* TODO: Use a combobox */
-				/*       Don't forget to append the widget to prpl_entries */
+				GntWidget *combo = gnt_combo_box_new();
+				GList *opt_iter = purple_account_option_get_list(option);
+				const char *dv = purple_account_option_get_default_list_value(option);
+				const char *active = dv;
+
+				if (account)
+					active = purple_account_get_string(account,
+						purple_account_option_get_setting(option), dv);
+
+				gnt_box_add_widget(GNT_BOX(box), combo);
+				dialog->prpl_entries = g_list_append(dialog->prpl_entries, combo);
+
+				for ( ; opt_iter; opt_iter = opt_iter->next)
+				{
+					PurpleKeyValuePair *kvp = opt_iter->data;
+					gnt_combo_box_add_data(GNT_COMBO_BOX(combo), kvp->value, kvp->key);
+
+					if (g_str_equal(kvp->value, active))
+						gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), kvp->value);
+				}
 			}
 			else
 			{
@@ -489,6 +540,7 @@
 	GntWidget *combo, *button, *entry;
 	GList *list, *iter;
 	AccountEditDialog *dialog;
+	PurplePlugin *plugin;
 
 	if (account)
 	{
@@ -532,9 +584,10 @@
 				((PurplePlugin*)iter->data)->info->name);
 	}
 
-	if (account)
-		gnt_combo_box_set_selected(GNT_COMBO_BOX(combo),
-				purple_plugins_find_with_id(purple_account_get_protocol_id(account)));
+	plugin = purple_plugins_find_with_id(purple_account_get_protocol_id(account));
+
+	if (account && plugin)
+		gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), plugin);
 	else
 		gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), list->data);
 
@@ -601,7 +654,7 @@
 	button = gnt_button_new(_("Cancel"));
 	gnt_box_add_widget(GNT_BOX(hbox), button);
 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), window);
-	
+
 	button = gnt_button_new(_("Save"));
 	gnt_box_add_widget(GNT_BOX(hbox), button);
 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(save_account_cb), dialog);
--- a/finch/gntaccount.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntaccount.h	Wed Jun 13 19:30:27 2012 -0400
@@ -59,8 +59,6 @@
  * Show the edit dialog for an account.
  *
  * @param account  The account to edit, or @c NULL to create a new account.
- *
- * @since 2.2.0
  */
 void finch_account_dialog_show(PurpleAccount *account);
 
--- a/finch/gntblist.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntblist.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,8 +23,8 @@
  * 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 "finch.h"
-#include <internal.h>
 
 #include <account.h>
 #include <blist.h>
@@ -50,6 +50,7 @@
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 #include "gntmenuitemcheck.h"
+#include "gntmenuutil.h"
 #include "gntpounce.h"
 #include "gntstyle.h"
 #include "gnttree.h"
@@ -144,7 +145,7 @@
 static void blist_show(PurpleBuddyList *list);
 static void update_node_display(PurpleBlistNode *buddy, FinchBlist *ggblist);
 static void update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist);
-static void account_signed_on_cb(PurpleConnection *pc, gpointer null);
+static gboolean account_autojoin_cb(PurpleConnection *pc, gpointer null);
 static void finch_request_add_buddy(PurpleAccount *account, const char *username, const char *grp, const char *alias);
 static void menu_group_set_cb(GntMenuItem *item, gpointer null);
 
@@ -169,7 +170,7 @@
 
 	if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		PurpleBuddy *buddy = (PurpleBuddy*)node;
-		FinchBlistNode *fnode = FINCH_GET_DATA(node);
+		FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
 		if (!purple_buddy_get_contact(buddy))
 			return FALSE; /* When a new buddy is added and show-offline is set */
 		if (PURPLE_BUDDY_IS_ONLINE(buddy))
@@ -329,11 +330,11 @@
 static FinchBlistNode *
 create_finch_blist_node(PurpleBlistNode *node, gpointer row)
 {
-	FinchBlistNode *fnode = FINCH_GET_DATA(node);
+	FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
 	if (!fnode) {
 		fnode = g_new0(FinchBlistNode, 1);
 		fnode->signed_timer = 0;
-		FINCH_SET_DATA(node, fnode);
+		purple_blist_node_set_ui_data(node, fnode);
 	}
 	fnode->row = row;
 	return fnode;
@@ -342,13 +343,13 @@
 static void
 reset_blist_node_ui_data(PurpleBlistNode *node)
 {
-	FinchBlistNode *fnode = FINCH_GET_DATA(node);
+	FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
 	if (fnode == NULL)
 		return;
 	if (fnode->signed_timer)
 		purple_timeout_remove(fnode->signed_timer);
 	g_free(fnode);
-	FINCH_SET_DATA(node, NULL);
+	purple_blist_node_set_ui_data(node, NULL);
 }
 
 static int
@@ -381,7 +382,7 @@
 get_blist_node_flag(PurpleBlistNode *node)
 {
 	GntTextFormatFlags flag = 0;
-	FinchBlistNode *fnode = FINCH_GET_DATA(node);
+	FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
 
 	if (ggblist->tagged && g_list_find(ggblist->tagged, node))
 		flag |= GNT_TEXT_FLAG_BOLD;
@@ -390,7 +391,7 @@
 		flag |= GNT_TEXT_FLAG_BLINK;
 	else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
-		fnode = FINCH_GET_DATA(node);
+		fnode = purple_blist_node_get_ui_data(node);
 		if (fnode && fnode->signed_timer)
 			flag |= GNT_TEXT_FLAG_BLINK;
 	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
@@ -406,7 +407,7 @@
 					node = purple_blist_node_get_sibling_next(node)) {
 				PurpleBlistNode *pnode;
 				pnode = purple_contact_get_priority_buddy((PurpleContact*)node);
-				fnode = FINCH_GET_DATA(node);
+				fnode = purple_blist_node_get_ui_data(node);
 				if (fnode && fnode->signed_timer) {
 					flag |= GNT_TEXT_FLAG_BLINK;
 					break;
@@ -433,7 +434,7 @@
 	PurpleBlistNode *node;
 	for (node = purple_blist_node_get_first_child(((PurpleBlistNode*)contact)); node;
 			node = purple_blist_node_get_sibling_next(node)) {
-		FinchBlistNode *fnode = FINCH_GET_DATA(node);
+		FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
 		if (PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node) ||
 				(fnode && fnode->signed_timer))
 			return TRUE;
@@ -465,7 +466,7 @@
 static void
 add_node(PurpleBlistNode *node, FinchBlist *ggblist)
 {
-	if (FINCH_GET_DATA(node))
+	if (purple_blist_node_get_ui_data(node))
 		return;
 
 	if (!ggblist->manager->can_add_node(node))
@@ -502,7 +503,7 @@
 	FinchBlist *ggblist = FINCH_GET_DATA(list);
 	PurpleBlistNode *parent;
 
-	if (ggblist == NULL || FINCH_GET_DATA(node) == NULL)
+	if (ggblist == NULL || purple_blist_node_get_ui_data(node) == NULL)
 		return;
 
 	if (PURPLE_BLIST_NODE_IS_GROUP(node) && ggblist->new_group) {
@@ -543,7 +544,7 @@
 	if (ggblist->window == NULL)
 		return;
 
-	if (FINCH_GET_DATA(node)!= NULL) {
+	if (purple_blist_node_get_ui_data(node)!= NULL) {
 		gnt_tree_change_text(GNT_TREE(ggblist->tree), node,
 				0, get_display_name(node));
 		gnt_tree_sort_row(GNT_TREE(ggblist->tree), node);
@@ -560,7 +561,7 @@
 	} else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
 		add_node(node, FINCH_GET_DATA(list));
 	} else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
-		if (FINCH_GET_DATA(node)== NULL) {
+		if (purple_blist_node_get_ui_data(node)== NULL) {
 			/* The core seems to expect the UI to add the buddies. */
 			for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node))
 				add_node(node, FINCH_GET_DATA(list));
@@ -589,6 +590,16 @@
 		ggblist->manager = &default_manager;
 }
 
+static void destroy_list(PurpleBuddyList *list)
+{
+	if (ggblist == NULL)
+		return;
+
+	gnt_widget_destroy(ggblist->window);
+	g_free(ggblist);
+	ggblist = NULL;
+}
+
 static gboolean
 remove_new_empty_group(gpointer data)
 {
@@ -616,6 +627,7 @@
 	const char *username = purple_request_fields_get_string(allfields, "screenname");
 	const char *alias = purple_request_fields_get_string(allfields, "alias");
 	const char *group = purple_request_fields_get_string(allfields, "group");
+	const char *invite = purple_request_fields_get_string(allfields, "invite");
 	PurpleAccount *account = purple_request_fields_get_account(allfields, "account");
 	const char *error = NULL;
 	PurpleGroup *grp;
@@ -652,7 +664,7 @@
 		purple_blist_add_buddy(buddy, NULL, grp, NULL);
 	}
 
-	purple_account_add_buddy(account, buddy);
+	purple_account_add_buddy(account, buddy, invite);
 }
 
 static void
@@ -670,6 +682,9 @@
 	field = purple_request_field_string_new("alias", _("Alias (optional)"), alias, FALSE);
 	purple_request_field_group_add_field(group, field);
 
+	field = purple_request_field_string_new("invite", _("Invite message (optional)"), NULL, FALSE);
+	purple_request_field_group_add_field(group, field);
+
 	field = purple_request_field_string_new("group", _("Add in group"), grp, FALSE);
 	purple_request_field_group_add_field(group, field);
 	purple_request_field_set_type_hint(field, "group");
@@ -825,7 +840,7 @@
 
 	/* Select the group */
 	if (ggblist->tree) {
-		FinchBlistNode *fnode = FINCH_GET_DATA((PurpleBlistNode*)grp);
+		FinchBlistNode *fnode = purple_blist_node_get_ui_data((PurpleBlistNode*)grp);
 		if (!fnode)
 			add_node((PurpleBlistNode*)grp, ggblist);
 		gnt_tree_set_selected(GNT_TREE(ggblist->tree), grp);
@@ -849,7 +864,7 @@
 	blist_show,
 	node_update,
 	node_remove,
-	NULL,
+	destroy_list,
 	NULL,
 	finch_request_add_buddy,
 	finch_request_add_chat,
@@ -873,7 +888,7 @@
 {
 	gpointer parent;
 	PurpleBlistNode *node = (PurpleBlistNode *)group;
-	if (FINCH_GET_DATA(node))
+	if (purple_blist_node_get_ui_data(node))
 		return;
 	parent = ggblist->manager->find_parent((PurpleBlistNode*)group);
 	create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group,
@@ -946,7 +961,7 @@
 {
 	gpointer parent;
 	PurpleBlistNode *node = (PurpleBlistNode *)chat;
-	if (FINCH_GET_DATA(node))
+	if (purple_blist_node_get_ui_data(node))
 		return;
 	if (!purple_account_is_connected(purple_chat_get_account(chat)))
 		return;
@@ -965,7 +980,7 @@
 	PurpleBlistNode *node = (PurpleBlistNode*)contact;
 	const char *name;
 
-	if (FINCH_GET_DATA(node))
+	if (purple_blist_node_get_ui_data(node))
 		return;
 
 	name = get_display_name(node);
@@ -988,7 +1003,7 @@
 	PurpleBlistNode *node = (PurpleBlistNode *)buddy;
 	PurpleContact *contact;
 
-	if (FINCH_GET_DATA(node))
+	if (purple_blist_node_get_ui_data(node))
 		return;
 
 	contact = purple_buddy_get_contact(buddy);
@@ -1046,7 +1061,7 @@
 						purple_buddy_get_account(buddy),
 						purple_buddy_get_name(buddy));
 		} else {
-			FinchConv *ggconv = FINCH_GET_DATA(conv);
+			FinchConv *ggconv = FINCH_CONV(conv);
 			gnt_window_present(ggconv->window);
 		}
 		finch_conversation_set_active(conv);
@@ -1058,43 +1073,6 @@
 }
 
 static void
-context_menu_callback(GntMenuItem *item, gpointer data)
-{
-	PurpleMenuAction *action = data;
-	PurpleBlistNode *node = ggblist->cnode;
-	if (action) {
-		void (*callback)(PurpleBlistNode *, gpointer);
-		callback = (void (*)(PurpleBlistNode *, gpointer))action->callback;
-		if (callback)
-			callback(node, action->data);
-		else
-			return;
-	}
-}
-
-static void
-gnt_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer parent)
-{
-	GList *list;
-	GntMenuItem *item;
-
-	if (action == NULL)
-		return;
-
-	item = gnt_menuitem_new(action->label);
-	if (action->callback)
-		gnt_menuitem_set_callback(GNT_MENU_ITEM(item), context_menu_callback, action);
-	gnt_menu_add_item(menu, GNT_MENU_ITEM(item));
-
-	if (action->children) {
-		GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
-		gnt_menuitem_set_submenu(item, GNT_MENU(sub));
-		for (list = action->children; list; list = list->next)
-			gnt_append_menu_action(GNT_MENU(sub), list->data, action);
-	}
-}
-
-static void
 append_proto_menu(GntMenu *menu, PurpleConnection *gc, PurpleBlistNode *node)
 {
 	GList *list;
@@ -1107,8 +1085,10 @@
 			list = g_list_delete_link(list, list))
 	{
 		PurpleMenuAction *act = (PurpleMenuAction *) list->data;
-		act->data = node;
-		gnt_append_menu_action(menu, act, NULL);
+		if (!act)
+			continue;
+		purple_menu_action_set_data(act, node);
+		gnt_append_menu_action(menu, act, node);
 	}
 }
 
@@ -1118,8 +1098,6 @@
 {
 	PurpleMenuAction *action = purple_menu_action_new(label, callback, data, NULL);
 	gnt_append_menu_action(menu, action, NULL);
-	g_signal_connect_swapped(G_OBJECT(menu), "destroy",
-			G_CALLBACK(purple_menu_action_free), action);
 }
 
 static void
@@ -1198,7 +1176,7 @@
 autojoin_toggled(GntMenuItem *item, gpointer data)
 {
 	PurpleMenuAction *action = data;
-	purple_blist_node_set_bool(action->data, "gnt-autojoin",
+	purple_blist_node_set_bool(purple_menu_action_get_data(action), "gnt-autojoin",
 				gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item)));
 }
 
@@ -1206,7 +1184,8 @@
 create_chat_menu(GntMenu *menu, PurpleChat *chat)
 {
 	PurpleMenuAction *action = purple_menu_action_new(_("Auto-join"), NULL, chat, NULL);
-	GntMenuItem *check = gnt_menuitem_check_new(action->label);
+	GntMenuItem *check = gnt_menuitem_check_new(
+			purple_menu_action_get_label(action));
 	gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(check),
 				purple_blist_node_get_bool((PurpleBlistNode*)chat, "gnt-autojoin"));
 	gnt_menu_add_item(menu, check);
@@ -1250,7 +1229,7 @@
 {
 	PurpleNotifyUserInfo *info = purple_notify_user_info_new();
 	gpointer uihandle;
-	purple_notify_user_info_add_pair(info, _("Information"), _("Retrieving..."));
+	purple_notify_user_info_add_pair_plaintext(info, _("Information"), _("Retrieving..."));
 	uihandle = purple_notify_userinfo(conn, name, info, NULL, NULL);
 	purple_notify_user_info_destroy(info);
 
@@ -1357,7 +1336,7 @@
 	for (iter = purple_blist_node_get_extended_menu(node);
 			iter; iter = g_list_delete_link(iter, iter))
 	{
-		gnt_append_menu_action(menu, iter->data, NULL);
+		gnt_append_menu_action(menu, iter->data, node);
 	}
 }
 
@@ -1537,7 +1516,7 @@
 	if (PURPLE_BLIST_NODE_IS_CONTACT(node)) {
 		PurpleContact *c = (PurpleContact*)node;
 		name = purple_contact_get_alias(c);
-		if (c->totalsize > 1)
+		if (purple_contact_get_contact_size(c, TRUE) > 1)
 			sec = _("Removing this contact will also remove all the buddies in the contact");
 	} else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
 		name = purple_buddy_get_name((PurpleBuddy*)node);
@@ -1763,15 +1742,13 @@
 	presence = purple_buddy_get_presence(buddy);
 
 	if (!full || g_utf8_collate(purple_buddy_get_name(buddy), alias)) {
-		char *esc = g_markup_escape_text(alias, -1);
-		purple_notify_user_info_add_pair(user_info, _("Nickname"), esc);
-		g_free(esc);
+		purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), alias);
 	}
 
 	tmp = g_strdup_printf("%s (%s)",
 			purple_account_get_username(account),
 			purple_account_get_protocol_name(account));
-	purple_notify_user_info_add_pair(user_info, _("Account"), tmp);
+	purple_notify_user_info_add_pair_plaintext(user_info, _("Account"), tmp);
 	g_free(tmp);
 
 	prpl = purple_find_prpl(purple_account_get_protocol_id(account));
@@ -1786,12 +1763,12 @@
 			time_t idle = purple_presence_get_idle_time(pre);
 			if (idle > 0) {
 				char *st = purple_str_seconds_to_string(time(NULL) - idle);
-				purple_notify_user_info_add_pair(user_info, _("Idle"), st);
+				purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), st);
 				g_free(st);
 			}
 		}
 	}
-	
+
 	tmp = purple_notify_user_info_get_text_with_newline(user_info, "<BR>");
 	purple_notify_user_info_destroy(user_info);
 
@@ -1930,7 +1907,7 @@
 	} else if (!gnt_tree_is_searching(GNT_TREE(ggblist->tree))) {
 		if (strcmp(text, "t") == 0) {
 			finch_blist_toggle_tag_buddy(gnt_tree_get_selection_data(GNT_TREE(ggblist->tree)));
-			gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist->tree), "move-down");
+			gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist->tree), "move-down", NULL);
 		} else if (strcmp(text, "a") == 0) {
 			finch_blist_place_tagged(gnt_tree_get_selection_data(GNT_TREE(ggblist->tree)));
 		} else
@@ -2050,7 +2027,7 @@
 		gnt_tree_set_compare_func(GNT_TREE(ggblist->tree),
 			(GCompareFunc)blist_node_compare_log);
 	}
-	
+
 	list = purple_get_blist();
 	node = purple_blist_get_root();
 	while (node)
@@ -2199,8 +2176,10 @@
 	purple_prefs_connect_callback(finch_blist_get_handle(),
 			PREF_ROOT "/grouping", redraw_blist, NULL);
 
-	purple_signal_connect(purple_connections_get_handle(), "signed-on", purple_blist_get_handle(),
-			G_CALLBACK(account_signed_on_cb), NULL);
+	purple_signal_connect_priority(purple_connections_get_handle(),
+	                               "autojoin", purple_blist_get_handle(),
+			               G_CALLBACK(account_autojoin_cb), NULL,
+	                               PURPLE_SIGNAL_PRIORITY_HIGHEST);
 
 	finch_blist_install_manager(&default_manager);
 
@@ -2521,7 +2500,7 @@
 buddy_recent_signed_on_off(gpointer data)
 {
 	PurpleBlistNode *node = data;
-	FinchBlistNode *fnode = FINCH_GET_DATA(node);
+	FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
 
 	purple_timeout_remove(fnode->signed_timer);
 	fnode->signed_timer = 0;
@@ -2541,7 +2520,7 @@
 buddy_signed_on_off_cb(gpointer data)
 {
 	PurpleBlistNode *node = data;
-	FinchBlistNode *fnode = FINCH_GET_DATA(node);
+	FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
 	if (!ggblist || !fnode)
 		return FALSE;
 
@@ -2670,10 +2649,11 @@
 	return FALSE;
 }
 
-static void
-account_signed_on_cb(PurpleConnection *gc, gpointer null)
+static gboolean
+account_autojoin_cb(PurpleConnection *gc, gpointer null)
 {
 	g_idle_add(auto_join_chats, gc);
+	return TRUE;
 }
 
 static void toggle_pref_cb(GntMenuItem *item, gpointer n)
@@ -3184,12 +3164,6 @@
 
 void finch_blist_uninit()
 {
-	if (ggblist == NULL)
-		return;
-
-	gnt_widget_destroy(ggblist->window);
-	g_free(ggblist);
-	ggblist = NULL;
 }
 
 gboolean finch_blist_get_position(int *x, int *y)
--- a/finch/gntblist.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntblist.h	Wed Jun 13 19:30:27 2012 -0400
@@ -115,15 +115,12 @@
  * @param name   The user to get information about.
  *
  * @return  Returns the ui-handle for the userinfo notification.
- *
- * @since 2.1.0
  */
 gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name);
 
 /**
  * Get the tree list of the buddy list.
  * @return  The GntTree widget.
- * @since 2.4.0
  */
 GntTree * finch_blist_get_tree(void);
 
@@ -131,7 +128,6 @@
  * Add an alternate buddy list manager.
  *
  * @param manager   The alternate buddylist manager.
- * @since 2.4.0
  */
 void finch_blist_install_manager(const FinchBlistManager *manager);
 
@@ -139,7 +135,6 @@
  * Remove an alternate buddy list manager.
  *
  * @param manager   The buddy list manager to remove.
- * @since 2.4.0
  */
 void finch_blist_uninstall_manager(const FinchBlistManager *manager);
 
@@ -149,7 +144,6 @@
  * @param id   The identifier for the desired buddy list manager.
  *
  * @return  The manager with the requested identifier, if available. @c NULL otherwise.
- * @since 2.4.0
  */
 FinchBlistManager * finch_blist_manager_find(const char *id);
 
@@ -157,7 +151,6 @@
  * Request the active buddy list manager to add a node.
  *
  * @param node  The node to add
- * @since 2.4.0
  */
 void finch_blist_manager_add_node(PurpleBlistNode *node);
 
--- a/finch/gntcertmgr.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntcertmgr.c	Wed Jun 13 19:30:27 2012 -0400
@@ -25,8 +25,8 @@
  *
  */
 
+#include <internal.h>
 #include "finch.h"
-#include <internal.h>
 
 #include "certificate.h"
 #include "debug.h"
@@ -193,7 +193,7 @@
 	subject = purple_certificate_get_subject_name(crt);
 
 	secondary = g_strdup_printf(_("Common name: %s\n\nSHA1 fingerprint:\n%s"), subject, fpr_sha1_asc);
-	
+
 	purple_notify_info(NULL,
 			   _("SSL Host Certificate"), primary, secondary);
 
--- a/finch/gntconn.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntconn.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,8 +23,8 @@
  * 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 "finch.h"
-#include <internal.h>
 
 #include "account.h"
 #include "core.h"
@@ -107,7 +107,6 @@
 {
 	FinchAutoRecon *info;
 	PurpleAccount *account = purple_connection_get_account(gc);
-	GList *list;
 
 	if (!purple_connection_error_is_fatal(reason)) {
 		info = g_hash_table_lookup(hash, account);
@@ -144,21 +143,6 @@
 		g_free(secondary);
 		purple_account_set_enabled(account, FINCH_UI, FALSE);
 	}
-
-	/* If we have any open chats, we probably want to rejoin when we get back online. */
-	list = purple_get_chats();
-	while (list) {
-		PurpleConversation *conv = list->data;
-		list = list->next;
-		if (purple_conversation_get_account(conv) != account ||
-				purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)))
-			continue;
-		purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE));
-		purple_conversation_write(conv, NULL, _("The account has disconnected and you are no "
-					"longer in this chat. You will be automatically rejoined in the chat when "
-					"the account reconnects."),
-				PURPLE_MESSAGE_SYSTEM, time(NULL));
-	}
 }
 
 static void
@@ -175,13 +159,12 @@
 	return &handle;
 }
 
-static PurpleConnectionUiOps ops = 
+static PurpleConnectionUiOps ops =
 {
 	NULL, /* connect_progress */
 	NULL, /* connected */
 	NULL, /* disconnected */
 	NULL, /* notice */
-	NULL,
 	NULL, /* network_connected */
 	NULL, /* network_disconnected */
 	finch_connection_report_disconnect,
--- a/finch/gntconv.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntconv.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,12 +23,12 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-#include <string.h>
 
+#include <internal.h>
 #include "finch.h"
-#include <internal.h>
 
 #include <cmds.h>
+#include <core.h>
 #include <idle.h>
 #include <prefs.h>
 #include <util.h>
@@ -52,6 +52,7 @@
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 #include "gntmenuitemcheck.h"
+#include "gntmenuutil.h"
 #include "gntstyle.h"
 #include "gnttextview.h"
 #include "gnttree.h"
@@ -123,7 +124,7 @@
 			if (send || (purple_conv_im_get_type_again(im) != 0 &&
 						  time(NULL) > purple_conv_im_get_type_again(im))) {
 				unsigned int timeout;
-				timeout = serv_send_typing(purple_conversation_get_gc(conv),
+				timeout = serv_send_typing(purple_conversation_get_connection(conv),
 										   purple_conversation_get_name(conv),
 										   PURPLE_TYPING);
 				purple_conv_im_set_type_again(im, timeout);
@@ -131,7 +132,7 @@
 		} else {
 			purple_conv_im_stop_send_typed_timeout(im);
 
-			serv_send_typing(purple_conversation_get_gc(conv),
+			serv_send_typing(purple_conversation_get_connection(conv),
 							 purple_conversation_get_name(conv),
 							 PURPLE_NOT_TYPING);
 		}
@@ -282,7 +283,7 @@
 		return;
 
 	im = PURPLE_CONV_IM(conv);
-	ggc = FINCH_GET_DATA(conv);
+	ggc = FINCH_CONV(conv);
 
 	if (purple_conv_im_get_typing_state(im) == PURPLE_TYPING) {
 		int scroll;
@@ -321,7 +322,7 @@
 	PurpleConversation *conv = find_conv_with_contact(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy));
 	if (conv == NULL)
 		return;
-	generate_send_to_menu(FINCH_GET_DATA(conv));
+	generate_send_to_menu(FINCH_CONV(conv));
 }
 
 static void
@@ -333,7 +334,7 @@
 		PurpleConversation *cc = find_conv_with_contact(
 				purple_conversation_get_account(conv), purple_conversation_get_name(conv));
 		if (cc)
-			generate_send_to_menu(FINCH_GET_DATA(cc));
+			generate_send_to_menu(FINCH_CONV(cc));
 		list = list->next;
 	}
 
@@ -366,6 +367,28 @@
 	}
 }
 
+static void
+account_signing_off(PurpleConnection *gc)
+{
+	GList *list = purple_get_chats();
+	PurpleAccount *account = purple_connection_get_account(gc);
+
+	/* We are about to sign off. See which chats we are currently in, and mark
+	 * them for rejoin on reconnect. */
+	while (list) {
+		PurpleConversation *conv = list->data;
+		if (!purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv)) &&
+				purple_conversation_get_account(conv) == account) {
+			purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE));
+			purple_conversation_write(conv, NULL, _("The account has disconnected and you are no "
+						"longer in this chat. You will be automatically rejoined in the chat when "
+						"the account reconnects."),
+					PURPLE_MESSAGE_SYSTEM, time(NULL));
+		}
+		list = list->next;
+	}
+}
+
 static gpointer
 finch_conv_get_handle(void)
 {
@@ -374,17 +397,49 @@
 }
 
 static void
+cleared_message_history_cb(PurpleConversation *conv, gpointer data)
+{
+	FinchConv *ggc = FINCH_CONV(conv);
+	if (ggc)
+		gnt_text_view_clear(GNT_TEXT_VIEW(ggc->tv));
+}
+
+static void
+gg_extended_menu(FinchConv *ggc)
+{
+	GntWidget *sub;
+	GList *list;
+
+	sub = gnt_menu_new(GNT_MENU_POPUP);
+	gnt_menuitem_set_submenu(ggc->plugins, GNT_MENU(sub));
+
+	for (list = purple_conversation_get_extended_menu(ggc->active_conv);
+			list; list = g_list_delete_link(list, list))
+	{
+		gnt_append_menu_action(GNT_MENU(sub), list->data, ggc->active_conv);
+	}
+}
+
+static void
+conv_updated(PurpleConversation *conv, PurpleConvUpdateType type)
+{
+	if (type == PURPLE_CONV_UPDATE_FEATURES) {
+		gg_extended_menu(purple_conversation_get_ui_data(conv));
+	}
+}
+
+static void
 clear_scrollback_cb(GntMenuItem *item, gpointer ggconv)
 {
 	FinchConv *ggc = ggconv;
-	gnt_text_view_clear(GNT_TEXT_VIEW(ggc->tv));
+	purple_conversation_clear_message_history(ggc->active_conv);
 }
 
 static void
 send_file_cb(GntMenuItem *item, gpointer ggconv)
 {
 	FinchConv *ggc = ggconv;
-	serv_send_file(purple_conversation_get_gc(ggc->active_conv),
+	serv_send_file(purple_conversation_get_connection(ggc->active_conv),
 			purple_conversation_get_name(ggc->active_conv), NULL);
 }
 
@@ -401,7 +456,7 @@
 get_info_cb(GntMenuItem *item, gpointer ggconv)
 {
 	FinchConv *ggc = ggconv;
-	finch_retrieve_user_info(purple_conversation_get_gc(ggc->active_conv),
+	finch_retrieve_user_info(purple_conversation_get_connection(ggc->active_conv),
 			purple_conversation_get_name(ggc->active_conv));
 }
 
@@ -565,6 +620,12 @@
 }
 
 static void
+plugin_changed_cb(PurplePlugin *p, gpointer data)
+{
+	gg_extended_menu(data);
+}
+
+static void
 gg_create_menu(FinchConv *ggc)
 {
 	GntWidget *menu, *sub;
@@ -635,14 +696,37 @@
 			!(ggc->flags & FINCH_CONV_NO_SOUND));
 	gnt_menu_add_item(GNT_MENU(sub), item);
 	gnt_menuitem_set_callback(item, toggle_sound_cb, ggc);
+
+	item = gnt_menuitem_new(_("Plugins"));
+	gnt_menu_add_item(GNT_MENU(menu), item);
+	ggc->plugins = item;
+
+	gg_extended_menu(ggc);
 }
 
 static void
 create_conv_from_userlist(GntWidget *widget, FinchConv *fc)
 {
 	PurpleAccount *account = purple_conversation_get_account(fc->active_conv);
-	char *name = gnt_tree_get_selection_data(GNT_TREE(widget));
-	purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
+	PurpleConnection *gc = purple_account_get_connection(account);
+	PurplePluginProtocolInfo *prpl_info = NULL;
+	char *name, *realname;
+
+	if (!gc) {
+		purple_conversation_write(fc->active_conv, NULL, _("You are not connected."),
+				PURPLE_MESSAGE_SYSTEM, time(NULL));
+		return;
+	}
+
+	name = gnt_tree_get_selection_data(GNT_TREE(widget));
+
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
+	if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_cb_real_name))
+		realname = prpl_info->get_cb_real_name(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(fc->active_conv)), name);
+	else
+		realname = NULL;
+	purple_conversation_new(PURPLE_CONV_TYPE_IM, account, realname ? realname : name);
+	g_free(realname);
 }
 
 static void
@@ -702,7 +786,7 @@
 static void
 finch_create_conversation(PurpleConversation *conv)
 {
-	FinchConv *ggc = FINCH_GET_DATA(conv);
+	FinchConv *ggc = FINCH_CONV(conv);
 	char *title;
 	PurpleConversationType type;
 	PurpleConversation *cc;
@@ -716,8 +800,8 @@
 
 	account = purple_conversation_get_account(conv);
 	cc = find_conv_with_contact(account, purple_conversation_get_name(conv));
-	if (cc && FINCH_GET_DATA(cc))
-		ggc = FINCH_GET_DATA(cc);
+	if (cc && FINCH_CONV(cc))
+		ggc = FINCH_CONV(cc);
 	else
 		ggc = g_new0(FinchConv, 1);
 
@@ -729,9 +813,9 @@
 
 	ggc->list = g_list_prepend(ggc->list, conv);
 	ggc->active_conv = conv;
-	FINCH_SET_DATA(conv, ggc);
+	purple_conversation_set_ui_data(conv, ggc);
 
-	if (cc && FINCH_GET_DATA(cc) && cc != conv) {
+	if (cc && FINCH_CONV(cc) && cc != conv) {
 		finch_conversation_set_active(conv);
 		return;
 	}
@@ -829,6 +913,11 @@
 	purple_signal_connect(purple_cmds_get_handle(), "cmd-removed", ggc,
 			G_CALLBACK(cmd_removed_cb), ggc);
 
+	purple_signal_connect(purple_plugins_get_handle(), "plugin-load", ggc,
+				PURPLE_CALLBACK(plugin_changed_cb), ggc);
+	purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", ggc,
+				PURPLE_CALLBACK(plugin_changed_cb), ggc);
+
 	g_free(title);
 	gnt_box_give_focus_to_child(GNT_BOX(ggc->window), ggc->entry);
 	g_signal_connect(G_OBJECT(ggc->window), "gained-focus", G_CALLBACK(gained_focus_cb), ggc);
@@ -838,7 +927,7 @@
 finch_destroy_conversation(PurpleConversation *conv)
 {
 	/* do stuff here */
-	FinchConv *ggc = FINCH_GET_DATA(conv);
+	FinchConv *ggc = FINCH_CONV(conv);
 	ggc->list = g_list_remove(ggc->list, conv);
 	if (ggc->list && conv == ggc->active_conv) {
 		ggc->active_conv = ggc->list->data;
@@ -858,7 +947,7 @@
 finch_write_common(PurpleConversation *conv, const char *who, const char *message,
 		PurpleMessageFlags flags, time_t mtime)
 {
-	FinchConv *ggconv = FINCH_GET_DATA(conv);
+	FinchConv *ggconv = FINCH_CONV(conv);
 	char *strip, *newline;
 	GntTextFormatFlags fl = 0;
 	int pos;
@@ -1022,7 +1111,7 @@
 static void
 finch_chat_add_users(PurpleConversation *conv, GList *users, gboolean new_arrivals)
 {
-	FinchConv *ggc = FINCH_GET_DATA(conv);
+	FinchConv *ggc = FINCH_CONV(conv);
 	GntEntry *entry = GNT_ENTRY(ggc->entry);
 
 	if (!new_arrivals)
@@ -1037,10 +1126,10 @@
 		for (iter = users; iter; iter = iter->next)
 		{
 			PurpleConvChatBuddy *cbuddy = iter->data;
-			char *str;
+			const char *str;
 
-			if ((str = cbuddy->alias) == NULL)
-				str = cbuddy->name;
+			if ((str = purple_conv_chat_cb_get_alias(cbuddy)) == NULL)
+				str = purple_conv_chat_cb_get_name(cbuddy);
 			g_string_append_printf(string, "[ %s ]", str);
 		}
 
@@ -1053,10 +1142,10 @@
 	{
 		PurpleConvChatBuddy *cbuddy = users->data;
 		GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
-		gnt_entry_add_suggest(entry, cbuddy->name);
-		gnt_entry_add_suggest(entry, cbuddy->alias);
-		gnt_tree_add_row_after(tree, g_strdup(cbuddy->name),
-				gnt_tree_create_row(tree, chat_flag_text(cbuddy->flags), cbuddy->alias), NULL, NULL);
+		gnt_entry_add_suggest(entry, purple_conv_chat_cb_get_name(cbuddy));
+		gnt_entry_add_suggest(entry, purple_conv_chat_cb_get_alias(cbuddy));
+		gnt_tree_add_row_after(tree, g_strdup(purple_conv_chat_cb_get_name(cbuddy)),
+				gnt_tree_create_row(tree, chat_flag_text(purple_conv_chat_cb_get_flags(cbuddy)), purple_conv_chat_cb_get_alias(cbuddy)), NULL, NULL);
 	}
 }
 
@@ -1064,7 +1153,7 @@
 finch_chat_rename_user(PurpleConversation *conv, const char *old, const char *new_n, const char *new_a)
 {
 	/* Update the name for string completion */
-	FinchConv *ggc = FINCH_GET_DATA(conv);
+	FinchConv *ggc = FINCH_CONV(conv);
 	GntEntry *entry = GNT_ENTRY(ggc->entry);
 	GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
 	PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), new_n);
@@ -1075,14 +1164,14 @@
 	gnt_entry_add_suggest(entry, new_n);
 	gnt_entry_add_suggest(entry, new_a);
 	gnt_tree_add_row_after(tree, g_strdup(new_n),
-			gnt_tree_create_row(tree, chat_flag_text(cb->flags), new_a), NULL, NULL);
+			gnt_tree_create_row(tree, chat_flag_text(purple_conv_chat_cb_get_flags(cb)), new_a), NULL, NULL);
 }
 
 static void
 finch_chat_remove_users(PurpleConversation *conv, GList *list)
 {
 	/* Remove the name from string completion */
-	FinchConv *ggc = FINCH_GET_DATA(conv);
+	FinchConv *ggc = FINCH_CONV(conv);
 	GntEntry *entry = GNT_ENTRY(ggc->entry);
 	for (; list; list = list->next) {
 		GntTree *tree = GNT_TREE(ggc->u.chat->userlist);
@@ -1095,8 +1184,8 @@
 finch_chat_update_user(PurpleConversation *conv, const char *user)
 {
 	PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), user);
-	FinchConv *ggc = FINCH_GET_DATA(conv);
-	gnt_tree_change_text(GNT_TREE(ggc->u.chat->userlist), (gpointer)user, 0, chat_flag_text(cb->flags));
+	FinchConv *ggc = FINCH_CONV(conv);
+	gnt_tree_change_text(GNT_TREE(ggc->u.chat->userlist), (gpointer)user, 0, chat_flag_text(purple_conv_chat_cb_get_flags(cb)));
 }
 
 static void
@@ -1116,7 +1205,7 @@
 	return FALSE;
 }
 
-static PurpleConversationUiOps conv_ui_ops = 
+static PurpleConversationUiOps conv_ui_ops =
 {
 	finch_create_conversation,
 	finch_destroy_conversation,
@@ -1181,22 +1270,43 @@
                  const char *cmd, char **args, char **error, void *data)
 {
 	char *tmp, *markup;
-	PurpleCmdStatus status;
 
 	if (!g_ascii_strcasecmp(args[0], "version")) {
-		tmp = g_strdup_printf("me is using Finch v%s.", DISPLAY_VERSION);
-		markup = g_markup_escape_text(tmp, -1);
-
-		status = purple_cmd_do_command(conv, tmp, markup, error);
+		tmp = g_strdup_printf("Using Finch v%s with libpurple v%s.",
+				DISPLAY_VERSION, purple_core_get_version());
+	} else if (!g_ascii_strcasecmp(args[0], "plugins")) {
+		/* Show all the loaded plugins, including the protocol plugins and plugin loaders.
+		 * This is intentional, since third party prpls are often sources of bugs, and some
+		 * plugin loaders (e.g. mono) can also be buggy.
+		 */
+		GString *str = g_string_new("Loaded Plugins: ");
+		const GList *plugins = purple_plugins_get_loaded();
+		if (plugins) {
+			for (; plugins; plugins = plugins->next) {
+				str = g_string_append(str, purple_plugin_get_name(plugins->data));
+				if (plugins->next)
+					str = g_string_append(str, ", ");
+			}
+		} else {
+			str = g_string_append(str, "(none)");
+		}
 
-		g_free(tmp);
-		g_free(markup);
-		return status;
+		tmp = g_string_free(str, FALSE);
 	} else {
-		purple_conversation_write(conv, NULL, _("Supported debug options are:  version"),
+		purple_conversation_write(conv, NULL, _("Supported debug options are: plugins version"),
 		                        PURPLE_MESSAGE_NO_LOG|PURPLE_MESSAGE_ERROR, time(NULL));
-		return PURPLE_CMD_STATUS_OK;
+		return PURPLE_CMD_RET_OK;
 	}
+
+	markup = g_markup_escape_text(tmp, -1);
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM)
+		purple_conv_im_send(PURPLE_CONV_IM(conv), markup);
+	else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT)
+		purple_conv_chat_send(PURPLE_CONV_CHAT(conv), markup);
+
+	g_free(tmp);
+	g_free(markup);
+	return PURPLE_CMD_RET_OK;
 }
 
 /* Xerox */
@@ -1204,10 +1314,8 @@
 clear_command_cb(PurpleConversation *conv,
                  const char *cmd, char **args, char **error, void *data)
 {
-	FinchConv *ggconv = FINCH_GET_DATA(conv);
-	gnt_text_view_clear(GNT_TEXT_VIEW(ggconv->tv));
 	purple_conversation_clear_message_history(conv);
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 /* Xerox */
@@ -1247,7 +1355,7 @@
 	purple_conversation_write(conv, NULL, s->str, PURPLE_MESSAGE_NO_LOG, time(NULL));
 	g_string_free(s, TRUE);
 
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 static PurpleCmdRet
@@ -1255,10 +1363,9 @@
 {
 	void (*callback)(void) = data;
 	callback();
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
-#if GLIB_CHECK_VERSION(2,6,0)
 static PurpleCmdRet
 cmd_message_color(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
 {
@@ -1278,36 +1385,35 @@
 	else {
 		if (error)
 			*error = g_strdup_printf(_("%s is not a valid message class. See '/help msgcolor' for valid message classes."), args[0]);
-		return PURPLE_CMD_STATUS_FAILED;
+		return PURPLE_CMD_RET_FAILED;
 	}
 
 	fg = gnt_colors_get_color(args[1]);
 	if (fg == -EINVAL) {
 		if (error)
 			*error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[1]);
-		return PURPLE_CMD_STATUS_FAILED;
+		return PURPLE_CMD_RET_FAILED;
 	}
 
 	bg = gnt_colors_get_color(args[2]);
 	if (bg == -EINVAL) {
 		if (error)
 			*error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[2]);
-		return PURPLE_CMD_STATUS_FAILED;
+		return PURPLE_CMD_RET_FAILED;
 	}
 
 	init_pair(*msgclass, fg, bg);
 
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
-#endif
 
 static PurpleCmdRet
 users_command_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
 {
-	FinchConv *fc = FINCH_GET_DATA(conv);
+	FinchConv *fc = FINCH_CONV(conv);
 	FinchConvChat *ch;
 	if (!fc)
-		return PURPLE_CMD_STATUS_FAILED;
+		return PURPLE_CMD_RET_FAILED;
 
 	ch = fc->u.chat;
 	gnt_widget_set_visible(ch->userlist,
@@ -1315,7 +1421,7 @@
 	gnt_box_readjust(GNT_BOX(fc->window));
 	gnt_box_give_focus_to_child(GNT_BOX(fc->window), fc->entry);
 	purple_prefs_set_bool(PREF_USERLIST, !(GNT_WIDGET_IS_FLAG_SET(ch->userlist, GNT_WIDGET_INVISIBLE)));
-	return PURPLE_CMD_STATUS_OK;
+	return PURPLE_CMD_RET_OK;
 }
 
 void finch_conversation_init()
@@ -1385,7 +1491,6 @@
 	                  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
 	                  cmd_show_window, _("statuses: Show the savedstatuses window."), finch_savedstatus_show_all);
 
-#if GLIB_CHECK_VERSION(2,6,0)
 	/* Allow customizing the message colors using a command during run-time */
 	purple_cmd_register("msgcolor", "www", PURPLE_CMD_P_DEFAULT,
 			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
@@ -1395,7 +1500,14 @@
 				                 "    &lt;foreground/background&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, default<br><br>"
 								 "EXAMPLE:<br>    msgcolor send cyan default"),
 			NULL);
-#endif
+	purple_cmd_register("msgcolour", "www", PURPLE_CMD_P_DEFAULT,
+			PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
+			cmd_message_color, _("msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: "
+				                 "Set the color for different classes of messages in the conversation window.<br>"
+				                 "    &lt;class&gt;: receive, send, highlight, action, timestamp<br>"
+				                 "    &lt;foreground/background&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, default<br><br>"
+								 "EXAMPLE:<br>    msgcolor send cyan default"),
+			NULL);
 
 	purple_signal_connect(purple_conversations_get_handle(), "buddy-typing", finch_conv_get_handle(),
 					PURPLE_CALLBACK(update_buddy_typing), NULL);
@@ -1403,6 +1515,10 @@
 					PURPLE_CALLBACK(update_buddy_typing), NULL);
 	purple_signal_connect(purple_conversations_get_handle(), "chat-left", finch_conv_get_handle(),
 					PURPLE_CALLBACK(chat_left_cb), NULL);
+	purple_signal_connect(purple_conversations_get_handle(), "cleared-message-history", finch_conv_get_handle(),
+					PURPLE_CALLBACK(cleared_message_history_cb), NULL);
+	purple_signal_connect(purple_conversations_get_handle(), "conversation-updated", finch_conv_get_handle(),
+					PURPLE_CALLBACK(conv_updated), NULL);
 	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_conv_get_handle(),
 					PURPLE_CALLBACK(buddy_signed_on_off), NULL);
 	purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_conv_get_handle(),
@@ -1411,6 +1527,8 @@
 					PURPLE_CALLBACK(account_signed_on_off), NULL);
 	purple_signal_connect(purple_connections_get_handle(), "signed-off", finch_conv_get_handle(),
 					PURPLE_CALLBACK(account_signed_on_off), NULL);
+	purple_signal_connect(purple_connections_get_handle(), "signing-off", finch_conv_get_handle(),
+					PURPLE_CALLBACK(account_signing_off), NULL);
 }
 
 void finch_conversation_uninit()
@@ -1420,7 +1538,7 @@
 
 void finch_conversation_set_active(PurpleConversation *conv)
 {
-	FinchConv *ggconv = FINCH_GET_DATA(conv);
+	FinchConv *ggconv = FINCH_CONV(conv);
 	PurpleAccount *account;
 	char *title;
 
@@ -1439,7 +1557,7 @@
 
 void finch_conversation_set_info_widget(PurpleConversation *conv, GntWidget *widget)
 {
-	FinchConv *fc = FINCH_GET_DATA(conv);
+	FinchConv *fc = FINCH_CONV(conv);
 	int height, width;
 
 	gnt_box_remove_all(GNT_BOX(fc->info));
--- a/finch/gntconv.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntconv.h	Wed Jun 13 19:30:27 2012 -0400
@@ -33,7 +33,7 @@
 #include "conversation.h"
 
 /* Grabs the conv out of a PurpleConverstation */
-#define FINCH_CONV(conv) ((FinchConv *)(conv)->ui_data)
+#define FINCH_CONV(conv) ((FinchConv *)purple_conversation_get_ui_data(conv))
 
 /***************************************************************************
  * @name GNT Conversations API
@@ -59,6 +59,7 @@
 	GntWidget *tv;            /* text-view */
 	GntWidget *menu;
 	GntWidget *info;
+	GntMenuItem *plugins;
 	FinchConversationFlag flags;
 
 	union
--- a/finch/gntdebug.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntdebug.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,6 +23,9 @@
  * 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 <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -35,7 +38,6 @@
 
 #include "gntdebug.h"
 #include "finch.h"
-#include <internal.h>
 #include "notify.h"
 #include "util.h"
 
@@ -75,7 +77,10 @@
 		}
 		return;
 	}
-	pipe(pipes);
+	if (pipe(pipes)) {
+		readhandle = -1;
+		return;
+	};
 	dup2(pipes[1], STDERR_FILENO);
 
 	stderrch = g_io_channel_unix_new(pipes[0]);
--- a/finch/gntft.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntft.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,8 +23,8 @@
  * 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 "finch.h"
-#include <internal.h>
 
 #include <gnt.h>
 #include <gntbox.h>
@@ -42,9 +42,6 @@
 #include "gntft.h"
 #include "prefs.h"
 
-#define FINCHXFER(xfer) \
-	(PurpleGntXferUiData *)FINCH_GET_DATA(xfer)
-
 typedef struct
 {
 	gboolean keep_open;
@@ -152,7 +149,7 @@
 		while (iter) {
 			PurpleXfer *xfer = iter->data;
 			iter = iter->next;
-			if (purple_xfer_is_completed(xfer) || purple_xfer_is_canceled(xfer))
+			if (purple_xfer_is_completed(xfer) || purple_xfer_is_cancelled(xfer))
 			finch_xfer_dialog_remove_xfer(xfer);
 		}
 	}
@@ -163,7 +160,7 @@
 {
 	PurpleXfer *selected_xfer = gnt_tree_get_selection_data(GNT_TREE(xfer_dialog->tree));
 	if (selected_xfer && (purple_xfer_is_completed(selected_xfer) ||
-				purple_xfer_is_canceled(selected_xfer))) {
+				purple_xfer_is_cancelled(selected_xfer))) {
 		finch_xfer_dialog_remove_xfer(selected_xfer);
 	}
 }
@@ -262,7 +259,7 @@
 
 	for (iter = purple_xfers_get_all(); iter; iter = iter->next) {
 		PurpleXfer *xfer = (PurpleXfer *)iter->data;
-		PurpleGntXferUiData *data = FINCHXFER(xfer);
+		PurpleGntXferUiData *data = purple_xfer_get_ui_data(xfer);
 		if (data->in_list) {
 			finch_xfer_dialog_add_xfer(xfer);
 			finch_xfer_dialog_update_xfer(xfer);
@@ -302,7 +299,7 @@
 
 	purple_xfer_ref(xfer);
 
-	data = FINCHXFER(xfer);
+	data = purple_xfer_get_ui_data(xfer);
 	data->in_list = TRUE;
 
 	finch_xfer_dialog_show();
@@ -340,7 +337,7 @@
 	g_return_if_fail(xfer_dialog != NULL);
 	g_return_if_fail(xfer != NULL);
 
-	data = FINCHXFER(xfer);
+	data = purple_xfer_get_ui_data(xfer);
 
 	if (data == NULL)
 		return;
@@ -356,7 +353,7 @@
 
 	if (xfer_dialog->num_transfers == 0 && !xfer_dialog->keep_open)
 		finch_xfer_dialog_destroy();
-	else 
+	else
 		update_title_progress();
 	purple_xfer_unref(xfer);
 }
@@ -370,7 +367,7 @@
 	g_return_if_fail(xfer_dialog != NULL);
 	g_return_if_fail(xfer != NULL);
 
-	data = FINCHXFER(xfer);
+	data = purple_xfer_get_ui_data(xfer);
 
 	if (data == NULL)
 		return;
@@ -383,12 +380,10 @@
 		return;
 	}
 
-	data = FINCHXFER(xfer);
-
 	update_title_progress();
 
-	if (purple_xfer_is_canceled(xfer))
-		status = _("Canceled");
+	if (purple_xfer_is_cancelled(xfer))
+		status = _("Cancelled");
 	else
 		status = _("Failed");
 
@@ -402,7 +397,7 @@
 	char *size_str, *remaining_str;
 	time_t current_time;
 	char prog_str[5];
-	double kb_sent, kb_rem;
+	double kb_sent;
 	double kbps = 0.0;
 	time_t elapsed, now;
 	char *kbsec;
@@ -412,14 +407,13 @@
 		now = time(NULL);
 
 	kb_sent = purple_xfer_get_bytes_sent(xfer) / 1024.0;
-	kb_rem  = purple_xfer_get_bytes_remaining(xfer) / 1024.0;
 	elapsed = (purple_xfer_get_start_time(xfer) > 0 ? now - purple_xfer_get_start_time(xfer) : 0);
 	kbps    = (elapsed > 0 ? (kb_sent / elapsed) : 0);
 
 	g_return_if_fail(xfer_dialog != NULL);
 	g_return_if_fail(xfer != NULL);
 
-	if ((data = FINCHXFER(xfer)) == NULL)
+	if ((data = purple_xfer_get_ui_data(xfer)) == NULL)
 		return;
 
 	if (data->in_list == FALSE || data->notified)
@@ -474,9 +468,9 @@
 {
 	PurpleGntXferUiData *data;
 
-	/* This is where we're setting xfer->ui_data for the first time. */
+	/* This is where we're setting xfer's "ui_data" for the first time. */
 	data = g_new0(PurpleGntXferUiData, 1);
-	FINCH_SET_DATA(xfer, data);
+	purple_xfer_set_ui_data(xfer, data);
 }
 
 static void
@@ -484,11 +478,11 @@
 {
 	PurpleGntXferUiData *data;
 
-	data = FINCHXFER(xfer);
+	data = purple_xfer_get_ui_data(xfer);
 	if (data) {
 		g_free(data->name);
 		g_free(data);
-		FINCH_SET_DATA(xfer, NULL);
+		purple_xfer_set_ui_data(xfer, NULL);
 	}
 }
 
--- a/finch/gntft.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntft.h	Wed Jun 13 19:30:27 2012 -0400
@@ -72,9 +72,9 @@
 void finch_xfer_dialog_remove_xfer(PurpleXfer *xfer);
 
 /**
- * Indicate in a file transfer dialog that a transfer was canceled.
+ * Indicate in a file transfer dialog that a transfer was cancelled.
  *
- * @param xfer   The file transfer that was canceled.
+ * @param xfer   The file transfer that was cancelled.
  */
 void finch_xfer_dialog_cancel_xfer(PurpleXfer *xfer);
 
--- a/finch/gntidle.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntidle.c	Wed Jun 13 19:30:27 2012 -0400
@@ -21,6 +21,8 @@
  *
  */
 
+#include <internal.h>
+
 #include "finch.h"
 #include "gntidle.h"
 #include "gntwm.h"
--- a/finch/gntlog.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntlog.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,8 +23,8 @@
  * 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 "finch.h"
-#include <internal.h>
 
 #include <gnt.h>
 #include <gntbox.h>
--- a/finch/gntlog.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntlog.h	Wed Jun 13 19:30:27 2012 -0400
@@ -8,7 +8,7 @@
  * Finch 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
--- a/finch/gntmedia.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntmedia.c	Wed Jun 13 19:30:27 2012 -0400
@@ -24,8 +24,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include <internal.h>
 #include "finch.h"
-#include <internal.h>
 #include "gntconv.h"
 #include "gntmedia.h"
 
@@ -156,7 +156,7 @@
 {
 	media->priv = FINCH_MEDIA_GET_PRIVATE(media);
 
-	media->priv->calling = gnt_label_new(_("Calling ... "));
+	media->priv->calling = gnt_label_new(_("Calling..."));
 	media->priv->hangup = gnt_button_new(_("Hangup"));
 	media->priv->accept = gnt_button_new(_("Accept"));
 	media->priv->reject = gnt_button_new(_("Reject"));
@@ -252,7 +252,7 @@
 	} else if (state == PURPLE_MEDIA_STATE_CONNECTED) {
 		finch_media_connected_cb(media, gntmedia);
 	} else if (state == PURPLE_MEDIA_STATE_NEW &&
-			sid != NULL && name != NULL && 
+			sid != NULL && name != NULL &&
 			purple_media_is_initiator(media, sid, name) == FALSE) {
 		PurpleAccount *account;
 		PurpleBuddy *buddy;
@@ -360,7 +360,7 @@
 			g_value_set_object(value, media->priv->media);
 			break;
 		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);	
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
 	}
 }
@@ -417,11 +417,7 @@
 create_default_audio_src(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant)
 {
-	GstElement *bin, *src, *volume;
-	GstPad *pad, *ghost;
-	double input_volume = purple_prefs_get_int(
-			"/finch/media/audio/volume/input")/10.0;
-
+	GstElement *src;
 	src = gst_element_factory_make("gconfaudiosrc", NULL);
 	if (src == NULL)
 		src = gst_element_factory_make("autoaudiosrc", NULL);
@@ -436,28 +432,15 @@
 				"element for the default audio source.\n");
 		return NULL;
 	}
-
-	bin = gst_bin_new("finchdefaultaudiosrc");
-	volume = gst_element_factory_make("volume", "purpleaudioinputvolume");
-	g_object_set(volume, "volume", input_volume, NULL);
-	gst_bin_add_many(GST_BIN(bin), src, volume, NULL);
-	gst_element_link(src, volume);
-	pad = gst_element_get_pad(volume, "src");
-	ghost = gst_ghost_pad_new("ghostsrc", pad);
-	gst_element_add_pad(bin, ghost);
-
-	return bin;
+	gst_element_set_name(src, "finchdefaultaudiosrc");
+	return src;
 }
 
 static GstElement *
 create_default_audio_sink(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant)
 {
-	GstElement *bin, *sink, *volume, *queue;
-	GstPad *pad, *ghost;
-	double output_volume = purple_prefs_get_int(
-			"/finch/media/audio/volume/output")/10.0;
-
+	GstElement *sink;
 	sink = gst_element_factory_make("gconfaudiosink", NULL);
 	if (sink == NULL)
 		sink = gst_element_factory_make("autoaudiosink",NULL);
@@ -466,19 +449,7 @@
 				"element for the default audio sink.\n");
 		return NULL;
 	}
-
-	bin = gst_bin_new("finchdefaultaudiosink");
-	volume = gst_element_factory_make("volume", "purpleaudiooutputvolume");
-	g_object_set(volume, "volume", output_volume, NULL);
-	queue = gst_element_factory_make("queue", NULL);
-	gst_bin_add_many(GST_BIN(bin), sink, volume, queue, NULL);
-	gst_element_link(volume, sink);
-	gst_element_link(queue, volume);
-	pad = gst_element_get_pad(queue, "sink");
-	ghost = gst_ghost_pad_new("ghostsink", pad);
-	gst_element_add_pad(bin, ghost);
-
-	return bin;
+	return sink;
 }
 #endif  /* USE_VV */
 
@@ -509,19 +480,13 @@
 			PURPLE_CMD_FLAG_IM, NULL,
 			call_cmd_cb, _("call: Make an audio call."), NULL);
 
-	purple_media_manager_set_ui_caps(manager, 
+	purple_media_manager_set_ui_caps(manager,
 			PURPLE_MEDIA_CAPS_AUDIO |
 			PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION);
 
 	purple_debug_info("gntmedia", "Registering media element types\n");
 	purple_media_manager_set_active_element(manager, default_audio_src);
 	purple_media_manager_set_active_element(manager, default_audio_sink);
-
-	purple_prefs_add_none("/finch/media");
-	purple_prefs_add_none("/finch/media/audio");
-	purple_prefs_add_none("/finch/media/audio/volume");
-	purple_prefs_add_int("/finch/media/audio/volume/input", 10);
-	purple_prefs_add_int("/finch/media/audio/volume/output", 10);
 #endif
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/gntmenuutil.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,79 @@
+/**
+ * @file gntmenuutil.c GNT Menu Utility Functions
+ * @ingroup finch
+ */
+
+/* finch
+ *
+ * Finch 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 "finch.h"
+
+#include "gnt.h"
+#include "gntmenu.h"
+#include "gntmenuitem.h"
+#include "gntmenuutil.h"
+
+static void
+context_menu_callback(GntMenuItem *item, gpointer data)
+{
+	PurpleMenuAction *action = data;
+	if (action) {
+		void (*callback)(gpointer, gpointer);
+		callback = (void (*)(gpointer, gpointer))
+			purple_menu_action_get_callback(action);
+		if (callback) {
+			gpointer ctx = g_object_get_data(G_OBJECT(item), "menuctx");
+			callback(ctx, purple_menu_action_get_data(action));
+		}
+	}
+}
+
+void
+gnt_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer ctx)
+{
+	GList *list;
+	GntMenuItem *item;
+
+	if (action == NULL)
+		return;
+
+	item = gnt_menuitem_new(purple_menu_action_get_label(action));
+	if (purple_menu_action_get_callback(action)) {
+		gnt_menuitem_set_callback(item, context_menu_callback, action);
+		g_object_set_data(G_OBJECT(item), "menuctx", ctx);
+	}
+	gnt_menu_add_item(menu, item);
+
+	list = purple_menu_action_get_children(action);
+
+	if (list) {
+		GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
+		gnt_menuitem_set_submenu(item, GNT_MENU(sub));
+		for (; list; list = g_list_delete_link(list, list))
+			gnt_append_menu_action(GNT_MENU(sub), list->data, action);
+		purple_menu_action_set_children(action, NULL);
+	}
+
+	g_signal_connect_swapped(G_OBJECT(menu), "destroy",
+		G_CALLBACK(purple_menu_action_free), action);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/finch/gntmenuutil.h	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,49 @@
+/**
+ * @file gntmenuutil.h GNT Menu Utility Functions
+ * @ingroup finch
+ */
+
+/* finch
+ *
+ * Finch 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 _GNT_MENUUTIL_H
+#define _GNT_MENUUTIL_H
+
+#include <gnt.h>
+#include <gntmenu.h>
+
+/***************************************************************************
+ * @name GNT Menu Utility Functions
+ ***************************************************************************/
+/*@{*/
+
+/**
+ * Add a PurpleMenuAction to a GntMenu.
+ *
+ * @param menu   the GntMenu to add to
+ * @param action the PurpleMenuAction to add
+ * @param ctx    the callback context, passed as the first argument to
+ *               the PurpleMenuAction's PurpleCallback function.
+ */
+void gnt_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer ctx);
+
+/*@}*/
+
+#endif
--- a/finch/gntnotify.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntnotify.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,6 +23,8 @@
  * 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 <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -32,7 +34,6 @@
 #include <gntwindow.h>
 
 #include "finch.h"
-#include <internal.h>
 
 #include <util.h>
 
@@ -83,6 +84,10 @@
 
 	if (secondary) {
 		GntWidget *msg;
+		/* XXX: This is broken.  type is PurpleNotifyMsgType, not
+		 * PurpleNotifyType.  Also, the if() followed by the
+		 * inner switch doesn't make much sense.
+		 */
 		if (type == PURPLE_NOTIFY_FORMATTED) {
 			int width = -1, height = -1;
 			char *plain = (char*)secondary;
@@ -129,7 +134,7 @@
 
 	while (widget->parent)
 		widget = widget->parent;
-	
+
 	if (type == PURPLE_NOTIFY_SEARCHRESULTS)
 		purple_notify_searchresults_free(g_object_get_data(handle, "notify-results"));
 #if 1
@@ -285,7 +290,7 @@
 
 	text = g_string_new("<span>");
 
-	for (l = purple_notify_user_info_get_entries(user_info); l != NULL;
+	for (l = purple_notify_user_info_get_entries(user_info)->head; l != NULL;
 			l = l->next) {
 		PurpleNotifyUserInfoEntry *user_info_entry = l->data;
 		PurpleNotifyUserInfoEntryType type = purple_notify_user_info_entry_get_type(user_info_entry);
@@ -381,6 +386,7 @@
 {
 	GntTree *tree = GNT_TREE(data);
 	GList *o;
+	GntTreeRow *prev = NULL;
 
 	/* XXX: Do I need to empty the tree here? */
 
@@ -388,10 +394,17 @@
 	{
 		gnt_tree_add_row_after(GNT_TREE(tree), o->data,
 				gnt_tree_create_row_from_list(GNT_TREE(tree), o->data),
-				NULL, NULL);
+				NULL, prev);
+		prev = o->data;
 	}
 }
 
+static void
+notify_sr_destroy_cb(GntWidget *window, void *data)
+{
+	purple_notify_close(PURPLE_NOTIFY_SEARCHRESULTS, window);
+}
+
 static void *
 finch_notify_searchresults(PurpleConnection *gc, const char *title,
 		const char *primary, const char *secondary,
@@ -424,7 +437,10 @@
 	for (iter = results->columns; iter; iter = iter->next)
 	{
 		PurpleNotifySearchColumn *column = iter->data;
-		gnt_tree_set_column_title(GNT_TREE(tree), i, column->title);
+		gnt_tree_set_column_title(GNT_TREE(tree), i, purple_notify_searchresult_column_get_title(column));
+
+		if (!purple_notify_searchresult_column_is_visible(column))
+			gnt_tree_set_column_visible(GNT_TREE(tree), i, FALSE);
 		i++;
 	}
 
@@ -473,6 +489,8 @@
 	}
 
 	gnt_box_add_widget(GNT_BOX(window), box);
+	g_signal_connect(G_OBJECT(tree), "destroy",
+			G_CALLBACK(notify_sr_destroy_cb), NULL);
 
 	finch_notify_sr_new_rows(gc, results, tree);
 
@@ -488,7 +506,7 @@
 	return finch_notify_message(PURPLE_NOTIFY_URI, _("URI"), url, NULL);
 }
 
-static PurpleNotifyUiOps ops = 
+static PurpleNotifyUiOps ops =
 {
 	finch_notify_message,
 	finch_notify_email,
--- a/finch/gntplugin.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntplugin.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,6 +23,8 @@
  * 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 <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -32,7 +34,6 @@
 #include <gntutils.h>
 
 #include "finch.h"
-#include <internal.h>
 
 #include "debug.h"
 #include "notify.h"
@@ -63,7 +64,7 @@
 static void
 decide_conf_button(PurplePlugin *plugin)
 {
-	if (purple_plugin_is_loaded(plugin) && 
+	if (purple_plugin_is_loaded(plugin) &&
 		((PURPLE_IS_GNT_PLUGIN(plugin) &&
 			FINCH_PLUGIN_UI_INFO(plugin) != NULL) ||
 		(plugin->info->prefs_info &&
@@ -261,7 +262,7 @@
 	PurplePlugin *plugin;
 
 	g_return_if_fail(plugins.window);
-	
+
 	plugin = purple_plugin_probe(filename);
 	if (!plugin) {
 		purple_notify_error(handle, _("Error loading plugin"),
@@ -497,7 +498,7 @@
 						break;
 				}
 				stringlist = g_list_prepend(stringlist, value);
-				purple_request_field_list_add(field, label, value);
+				purple_request_field_list_add_icon(field, label, NULL, value);
 				if (strcmp(value, current_value) == 0)
 					purple_request_field_list_add_selected(field, label);
 				list = list->next->next;
--- a/finch/gntpounce.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntpounce.c	Wed Jun 13 19:30:27 2012 -0400
@@ -24,6 +24,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  *
  */
+#include <internal.h>
+
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -36,7 +38,6 @@
 #include <gntutils.h>
 
 #include "finch.h"
-#include <internal.h>
 
 #include "account.h"
 #include "conversation.h"
--- a/finch/gntprefs.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntprefs.c	Wed Jun 13 19:30:27 2012 -0400
@@ -62,14 +62,6 @@
 
 void finch_prefs_update_old()
 {
-	const char *str = NULL;
-
-	purple_prefs_rename("/gaim/gnt", "/finch");
-	purple_prefs_rename("/purple/gnt", "/finch");
-
-	if ((str = purple_prefs_get_string("/purple/away/idle_reporting")) &&
-			strcmp(str, "gaim") == 0)
-		purple_prefs_set_string("/purple/away/idle_reporting", "purple");
 }
 
 typedef struct
@@ -171,7 +163,7 @@
 				default:
 					break;
 			}
-			purple_request_field_list_add(field, data, iter->data);
+			purple_request_field_list_add_icon(field, data, NULL, iter->data);
 			if (select)
 				purple_request_field_list_add_selected(field, data);
 		}
@@ -180,21 +172,21 @@
 	return field;
 }
 
-static Prefs blist[] = 
+static Prefs blist[] =
 {
 	{PURPLE_PREF_BOOLEAN, "/finch/blist/idletime", N_("Show Idle Time"), NULL},
 	{PURPLE_PREF_BOOLEAN, "/finch/blist/showoffline", N_("Show Offline Buddies"), NULL},
 	{PURPLE_PREF_NONE, NULL, NULL, NULL}
 };
 
-static Prefs convs[] = 
+static Prefs convs[] =
 {
 	{PURPLE_PREF_BOOLEAN, "/finch/conversations/timestamps", N_("Show Timestamps"), NULL},
 	{PURPLE_PREF_BOOLEAN, "/finch/conversations/notify_typing", N_("Notify buddies when you are typing"), NULL},
 	{PURPLE_PREF_NONE, NULL, NULL, NULL}
 };
 
-static Prefs logging[] = 
+static Prefs logging[] =
 {
 	{PURPLE_PREF_STRING, "/purple/logging/format", N_("Log format"), get_log_options},
 	{PURPLE_PREF_BOOLEAN, "/purple/logging/log_ims", N_("Log IMs"), NULL},
--- a/finch/gntrequest.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntrequest.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,6 +23,8 @@
  * 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 <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -35,17 +37,10 @@
 #include <gnttree.h>
 
 #include "finch.h"
-#include <internal.h>
 #include "gntrequest.h"
 #include "debug.h"
 #include "util.h"
 
-/* XXX: Until gobjectification ... */
-#undef FINCH_GET_DATA
-#undef FINCH_SET_DATA
-#define FINCH_GET_DATA(obj)  purple_request_field_get_ui_data(obj)
-#define FINCH_SET_DATA(obj, data)  purple_request_field_set_ui_data(obj, data)
-
 typedef struct
 {
 	void *user_data;
@@ -242,7 +237,7 @@
 
 	setup_default_callback(window, cancel_cb, user_data);
 	gnt_widget_show(window);
-	
+
 	return window;
 }
 
@@ -322,37 +317,36 @@
 				continue;
 			if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
 			{
-				GntWidget *check = FINCH_GET_DATA(field);
+				GntWidget *check = purple_request_field_get_ui_data(field);
 				gboolean value = gnt_check_box_get_checked(GNT_CHECK_BOX(check));
 				purple_request_field_bool_set_value(field, value);
 			}
 			else if (type == PURPLE_REQUEST_FIELD_STRING)
 			{
-				GntWidget *entry = FINCH_GET_DATA(field);
+				GntWidget *entry = purple_request_field_get_ui_data(field);
 				const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
 				purple_request_field_string_set_value(field, (text && *text) ? text : NULL);
 			}
 			else if (type == PURPLE_REQUEST_FIELD_INTEGER)
 			{
-				GntWidget *entry = FINCH_GET_DATA(field);
+				GntWidget *entry = purple_request_field_get_ui_data(field);
 				const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
 				int value = (text && *text) ? atoi(text) : 0;
 				purple_request_field_int_set_value(field, value);
 			}
 			else if (type == PURPLE_REQUEST_FIELD_CHOICE)
 			{
-				GntWidget *combo = FINCH_GET_DATA(field);
+				GntWidget *combo = purple_request_field_get_ui_data(field);
 				int id;
 				id = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo)));
 				purple_request_field_choice_set_value(field, id);
 			}
 			else if (type == PURPLE_REQUEST_FIELD_LIST)
 			{
-				GList *list = NULL;
+				GList *list = NULL, *iter;
 				if (purple_request_field_list_get_multi_select(field))
 				{
-					GList *iter;
-					GntWidget *tree = FINCH_GET_DATA(field);
+					GntWidget *tree = purple_request_field_get_ui_data(field);
 
 					iter = purple_request_field_list_get_items(field);
 					for (; iter; iter = iter->next)
@@ -360,14 +354,23 @@
 						const char *text = iter->data;
 						gpointer key = purple_request_field_list_get_data(field, text);
 						if (gnt_tree_get_choice(GNT_TREE(tree), key))
-							list = g_list_prepend(list, key);
+							list = g_list_prepend(list, (gpointer)text);
 					}
 				}
 				else
 				{
-					GntWidget *combo = FINCH_GET_DATA(field);
+					GntWidget *combo = purple_request_field_get_ui_data(field);
 					gpointer data = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
-					list = g_list_append(list, data);
+
+					iter = purple_request_field_list_get_items(field);
+					for (; iter; iter = iter->next) {
+						const char *text = iter->data;
+						gpointer key = purple_request_field_list_get_data(field, text);
+						if (key == data) {
+							list = g_list_prepend(list, (gpointer)text);
+							break;
+						}
+					}
 				}
 
 				purple_request_field_list_set_selected(field, list);
@@ -375,7 +378,7 @@
 			}
 			else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
 			{
-				GntWidget *combo = FINCH_GET_DATA(field);
+				GntWidget *combo = purple_request_field_get_ui_data(field);
 				PurpleAccount *acc = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
 				purple_request_field_account_set_value(field, acc);
 			}
@@ -624,36 +627,36 @@
 
 			if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
 			{
-				FINCH_SET_DATA(field, create_boolean_field(field));
+				purple_request_field_set_ui_data(field, create_boolean_field(field));
 			}
 			else if (type == PURPLE_REQUEST_FIELD_STRING)
 			{
-				FINCH_SET_DATA(field, create_string_field(field, &username));
+				purple_request_field_set_ui_data(field, create_string_field(field, &username));
 			}
 			else if (type == PURPLE_REQUEST_FIELD_INTEGER)
 			{
-				FINCH_SET_DATA(field, create_integer_field(field));
+				purple_request_field_set_ui_data(field, create_integer_field(field));
 			}
 			else if (type == PURPLE_REQUEST_FIELD_CHOICE)
 			{
-				FINCH_SET_DATA(field, create_choice_field(field));
+				purple_request_field_set_ui_data(field, create_choice_field(field));
 			}
 			else if (type == PURPLE_REQUEST_FIELD_LIST)
 			{
-				FINCH_SET_DATA(field, create_list_field(field));
+				purple_request_field_set_ui_data(field, create_list_field(field));
 			}
 			else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
 			{
 				accountlist = create_account_field(field);
-				FINCH_SET_DATA(field, accountlist);
+				purple_request_field_set_ui_data(field, accountlist);
 			}
 			else
 			{
-				FINCH_SET_DATA(field, gnt_label_new_with_format(_("Not implemented yet."),
+				purple_request_field_set_ui_data(field, gnt_label_new_with_format(_("Not implemented yet."),
 						GNT_TEXT_FLAG_BOLD));
 			}
 			gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID);
-			gnt_box_add_widget(GNT_BOX(hbox), GNT_WIDGET(FINCH_GET_DATA(field)));
+			gnt_box_add_widget(GNT_BOX(hbox), GNT_WIDGET(purple_request_field_get_ui_data(field)));
 		}
 		if (grlist->next)
 			gnt_box_add_widget(GNT_BOX(box), gnt_hline_new());
@@ -814,7 +817,7 @@
 	for (list = purple_request_fields_get_groups(allfields); list; list = list->next) {
 		PurpleRequestFieldGroup *group = list->data;
 		GList *fields = purple_request_field_group_get_fields(group);
-		
+
 		for (; fields ; fields = fields->next) {
 			PurpleRequestField *field = fields->data;
 			PurpleRequestFieldType type = purple_request_field_get_type(field);
@@ -825,6 +828,7 @@
 			switch (type) {
 				case PURPLE_REQUEST_FIELD_LIST:
 					val = purple_request_field_list_get_selected(field)->data;
+					val = purple_request_field_list_get_data(field, val);
 					break;
 				case PURPLE_REQUEST_FIELD_BOOLEAN:
 					val = GINT_TO_POINTER(purple_request_field_bool_get_value(field));
--- a/finch/gntrequest.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntrequest.h	Wed Jun 13 19:30:27 2012 -0400
@@ -63,7 +63,6 @@
  * @param field   The request field.
  *
  * @return A GntWidget for the request field.
- * @since 2.4.0
  */
 GntWidget *finch_request_field_get_widget(PurpleRequestField *field);
 /*@}*/
--- a/finch/gntroomlist.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntroomlist.c	Wed Jun 13 19:30:27 2012 -0400
@@ -41,6 +41,7 @@
 
 #define PREF_ROOT "/finch/roomlist"
 
+
 /* Yes, just one roomlist at a time. Let's not get greedy. Aight? */
 struct _FinchRoomlist
 {
@@ -146,9 +147,9 @@
 			purple_roomlist_room_join(froomlist.roomlist, room);
 			break;
 		case PURPLE_ROOMLIST_ROOMTYPE_CATEGORY:
-			if (!room->expanded_once) {
+			if (!purple_roomlist_room_get_expanded_once(room)) {
 				purple_roomlist_expand_category(froomlist.roomlist, room);
-				room->expanded_once = TRUE;
+				purple_roomlist_room_set_expanded_once(room, TRUE);
 			}
 			break;
 	}
@@ -343,7 +344,7 @@
 static void
 fl_create(PurpleRoomlist *list)
 {
-	FINCH_SET_DATA(list, &froomlist);
+	purple_roomlist_set_ui_data(list, &froomlist);
 	setup_roomlist(NULL);
 	update_roomlist(list);
 }
--- a/finch/gntsound.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntsound.c	Wed Jun 13 19:30:27 2012 -0400
@@ -104,7 +104,8 @@
 	{PURPLE_SOUND_CHAT_YOU_SAY, N_("You talk in chat"), "send_chat_msg", "send.wav", NULL},
 	{PURPLE_SOUND_CHAT_SAY,     N_("Others talk in chat"), "chat_msg_recv", "receive.wav", NULL},
 	{PURPLE_SOUND_POUNCE_DEFAULT, NULL, "pounce_default", "alert.wav", NULL},
-	{PURPLE_SOUND_CHAT_NICK,    N_("Someone says your username in chat"), "nick_said", "alert.wav", NULL}
+	{PURPLE_SOUND_CHAT_NICK,    N_("Someone says your username in chat"), "nick_said", "alert.wav", NULL},
+	{PURPLE_SOUND_GOT_ATTENTION, N_("Attention received"), "got_attention", "alert.wav", NULL}
 };
 
 const char *
@@ -149,7 +150,7 @@
 		return ret;
 
 	account = purple_conversation_get_account(conv);
-	nick = g_strdup(purple_normalize(account, chat->nick));
+	nick = g_strdup(purple_normalize(account, purple_conv_chat_get_nick(chat)));
 	name = g_strdup(purple_normalize(account, aname));
 
 	if (g_utf8_collate(nick, name) == 0)
@@ -267,12 +268,20 @@
 	if (chat_nick_matches_name(conv, sender))
 		return;
 
-	if (flags & PURPLE_MESSAGE_NICK || purple_utf8_has_word(message, chat->nick))
+	if (flags & PURPLE_MESSAGE_NICK || purple_utf8_has_word(message, purple_conv_chat_get_nick(chat)))
 		play_conv_event(conv, PURPLE_SOUND_CHAT_NICK);
 	else
 		play_conv_event(conv, event);
 }
 
+static void
+got_attention_cb(PurpleAccount *account, const char *who,
+	PurpleConversation *conv, guint type, PurpleSoundEventID event)
+{
+	play_conv_event(conv, event);
+}
+
+
 /*
  * We mute sounds for the 10 seconds after you log in so that
  * you don't get flooded with sounds when the blist shows all
@@ -300,34 +309,22 @@
 static void
 initialize_profile(const char *name, PurplePrefType type, gconstpointer val, gpointer null)
 {
+	FinchSoundEvent *event;
 	if (purple_prefs_exists(make_pref("")))
 		return;
 
 	purple_prefs_add_none(make_pref(""));
 	purple_prefs_add_none(make_pref("/enabled"));
 	purple_prefs_add_none(make_pref("/file"));
-	purple_prefs_add_bool(make_pref("/enabled/login"), FALSE);
-	purple_prefs_add_path(make_pref("/file/login"), "");
-	purple_prefs_add_bool(make_pref("/enabled/logout"), FALSE);
-	purple_prefs_add_path(make_pref("/file/logout"), "");
-	purple_prefs_add_bool(make_pref("/enabled/im_recv"), FALSE);
-	purple_prefs_add_path(make_pref("/file/im_recv"), "");
-	purple_prefs_add_bool(make_pref("/enabled/first_im_recv"), FALSE);
-	purple_prefs_add_path(make_pref("/file/first_im_recv"), "");
-	purple_prefs_add_bool(make_pref("/enabled/send_im"), FALSE);
-	purple_prefs_add_path(make_pref("/file/send_im"), "");
-	purple_prefs_add_bool(make_pref("/enabled/join_chat"), FALSE);
-	purple_prefs_add_path(make_pref("/file/join_chat"), "");
-	purple_prefs_add_bool(make_pref("/enabled/left_chat"), FALSE);
-	purple_prefs_add_path(make_pref("/file/left_chat"), "");
-	purple_prefs_add_bool(make_pref("/enabled/send_chat_msg"), FALSE);
-	purple_prefs_add_path(make_pref("/file/send_chat_msg"), "");
-	purple_prefs_add_bool(make_pref("/enabled/chat_msg_recv"), FALSE);
-	purple_prefs_add_path(make_pref("/file/chat_msg_recv"), "");
-	purple_prefs_add_bool(make_pref("/enabled/nick_said"), FALSE);
-	purple_prefs_add_path(make_pref("/file/nick_said"), "");
-	purple_prefs_add_bool(make_pref("/enabled/pounce_default"), FALSE);
-	purple_prefs_add_path(make_pref("/file/pounce_default"), "");
+
+	for (event = sounds; event - sounds < PURPLE_NUM_SOUNDS; event++) {
+		char pref[512];
+		g_snprintf(pref, sizeof(pref), "/enabled/%s", event->pref);
+		purple_prefs_add_bool(make_pref(pref), FALSE);
+		g_snprintf(pref, sizeof(pref), "/file/%s", event->pref);
+		purple_prefs_add_path(make_pref(pref), "");
+	}
+
 	purple_prefs_add_bool(make_pref("/conv_focus"), FALSE);
 	purple_prefs_add_bool(make_pref("/mute"), FALSE);
 	purple_prefs_add_path(make_pref("/command"), "");
@@ -336,6 +333,25 @@
 }
 
 static void
+update_profiles(void)
+{
+	GList *list = finch_sound_get_profiles();
+	for (; list; list = g_list_delete_link(list, list)) {
+		char pname[512];
+
+		/* got_attention was added in libpurple 2.7.0 */
+		g_snprintf(pname, sizeof(pname), FINCH_PREFS_ROOT "/sound/profiles/%s%s",
+				(char *)list->data, "/enabled/got_attention");
+		purple_prefs_add_bool(pname, FALSE);
+		g_snprintf(pname, sizeof(pname), FINCH_PREFS_ROOT "/sound/profiles/%s%s",
+				(char *)list->data, "/file/got_attention");
+		purple_prefs_add_path(pname, "");
+
+		g_free(list->data);
+	}
+}
+
+static void
 finch_sound_init(void)
 {
 	void *gnt_sound_handle = finch_sound_get_handle();
@@ -356,7 +372,7 @@
 	purple_prefs_connect_callback(gnt_sound_handle, FINCH_PREFS_ROOT "/sound/actprofile", initialize_profile, NULL);
 	purple_prefs_trigger_callback(FINCH_PREFS_ROOT "/sound/actprofile");
 
-	
+
 #ifdef USE_GSTREAMER
 	purple_debug_info("sound", "Initializing sound output drivers.\n");
 #if (GST_VERSION_MAJOR > 0 || \
@@ -399,6 +415,11 @@
 	purple_signal_connect(conv_handle, "received-chat-msg",
 						gnt_sound_handle, PURPLE_CALLBACK(chat_msg_received_cb),
 						GINT_TO_POINTER(PURPLE_SOUND_CHAT_SAY));
+	purple_signal_connect(conv_handle, "got-attention",
+						gnt_sound_handle, PURPLE_CALLBACK(got_attention_cb),
+						GINT_TO_POINTER(PURPLE_SOUND_GOT_ATTENTION));
+
+	update_profiles();
 }
 
 static void
@@ -533,11 +554,11 @@
 	}
 
 	play = gst_element_factory_make("playbin", "play");
-	
+
 	if (play == NULL) {
 		return;
 	}
-	
+
 	uri = g_strdup_printf("file://%s", filename);
 
 	g_object_set(G_OBJECT(play), "uri", uri,
@@ -559,18 +580,12 @@
 #else /* _WIN32 */
 	purple_debug_info("sound", "Playing %s\n", filename);
 
-	if (G_WIN32_HAVE_WIDECHAR_API ()) {
+	{
 		wchar_t *wc_filename = g_utf8_to_utf16(filename,
 				-1, NULL, NULL, NULL);
 		if (!PlaySoundW(wc_filename, NULL, SND_ASYNC | SND_FILENAME))
 			purple_debug(PURPLE_DEBUG_ERROR, "sound", "Error playing sound.\n");
 		g_free(wc_filename);
-	} else {
-		char *l_filename = g_locale_from_utf8(filename,
-				-1, NULL, NULL, NULL);
-		if (!PlaySoundA(l_filename, NULL, SND_ASYNC | SND_FILENAME))
-			purple_debug(PURPLE_DEBUG_ERROR, "sound", "Error playing sound.\n");
-		g_free(l_filename);
 	}
 #endif /* _WIN32 */
 }
@@ -583,7 +598,8 @@
 	if ((event == PURPLE_SOUND_BUDDY_ARRIVE) && mute_login_sounds)
 		return;
 
-	if (event >= PURPLE_NUM_SOUNDS) {
+	if (event >= PURPLE_NUM_SOUNDS ||
+			event >= G_N_ELEMENTS(sounds)) {
 		purple_debug_error("sound", "got request for unknown sound: %d\n", event);
 		return;
 	}
@@ -672,7 +688,7 @@
 
 	gnt_tree_change_text(GNT_TREE(pref_dialog->events), GINT_TO_POINTER(event->id), 1, file);
 	gnt_tree_set_choice(GNT_TREE(pref_dialog->events), GINT_TO_POINTER(event->id), TRUE);
-	
+
 	gnt_widget_destroy(GNT_WIDGET(w));
 }
 
@@ -715,7 +731,7 @@
 reset_cb(GntWidget *button, gpointer null)
 {
 	/* Don't dereference this pointer ! */
-	gpointer key = gnt_tree_get_selection_data(GNT_TREE(pref_dialog->events)); 
+	gpointer key = gnt_tree_get_selection_data(GNT_TREE(pref_dialog->events));
 
 	FinchSoundEvent * event = &sounds[GPOINTER_TO_INT(key)];
 	g_free(event->file);
@@ -865,7 +881,7 @@
 		gnt_entry_set_text(entry, "");
 		gnt_tree_set_selected(GNT_TREE(pref_dialog->profiles), key);
 		finch_sound_set_active_profile(key);
-	} else 
+	} else
 		reload_pref_window(profile);
 }
 
@@ -966,13 +982,13 @@
 	gnt_combo_box_add_data(GNT_COMBO_BOX(cmbox), "nosound", _("No Sound"));
 
 	label = gnt_label_new_with_format(_("Sound Method"), GNT_TEXT_FLAG_BOLD);
-	gnt_box_add_widget(GNT_BOX(box), label); 
+	gnt_box_add_widget(GNT_BOX(box), label);
 	tmpbox = gnt_hbox_new(TRUE);
 	gnt_box_set_fill(GNT_BOX(tmpbox), FALSE);
 	gnt_box_set_pad(GNT_BOX(tmpbox), 0);
 	gnt_box_add_widget(GNT_BOX(tmpbox), gnt_label_new(_("Method: ")));
 	gnt_box_add_widget(GNT_BOX(tmpbox), cmbox);
-	gnt_box_add_widget(GNT_BOX(box), tmpbox); 
+	gnt_box_add_widget(GNT_BOX(box), tmpbox);
 
 	tmpbox = gnt_hbox_new(TRUE);
 	gnt_box_set_pad(GNT_BOX(tmpbox), 0);
@@ -985,7 +1001,7 @@
 	gnt_box_add_widget(GNT_BOX(box), gnt_line_new(FALSE));
 
 	/* Sound options */
-	gnt_box_add_widget(GNT_BOX(box), gnt_label_new_with_format(_("Sound Options"), GNT_TEXT_FLAG_BOLD)); 
+	gnt_box_add_widget(GNT_BOX(box), gnt_label_new_with_format(_("Sound Options"), GNT_TEXT_FLAG_BOLD));
 	pref_dialog->conv_focus = chkbox = gnt_check_box_new(_("Sounds when conversation has focus"));
 	gnt_box_add_widget(GNT_BOX(box), chkbox);
 
@@ -1022,7 +1038,7 @@
 	gnt_box_add_widget(GNT_BOX(win), gnt_hline_new());
 
 	/* Sound events */
-	gnt_box_add_widget(GNT_BOX(win), gnt_label_new_with_format(_("Sound Events"), GNT_TEXT_FLAG_BOLD)); 
+	gnt_box_add_widget(GNT_BOX(win), gnt_label_new_with_format(_("Sound Events"), GNT_TEXT_FLAG_BOLD));
 	pref_dialog->events = tree = gnt_tree_new_with_columns(2);
 	gnt_tree_set_column_titles(GNT_TREE(tree), _("Event"), _("File"));
 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
--- a/finch/gntsound.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntsound.h	Wed Jun 13 19:30:27 2012 -0400
@@ -37,17 +37,13 @@
  * Get the name of the active sound profile.
  *
  * @return The name of the profile
- *
- * @since 2.1.0
  */
 const char *finch_sound_get_active_profile(void);
 
 /**
  * Set the active profile.  If the profile doesn't exist, nothing is changed.
- * 
+ *
  * @param name  The name of the profile
- *
- * @since 2.1.0
  */
 void finch_sound_set_active_profile(const char *name);
 
@@ -56,8 +52,6 @@
  *
  * @return A list of strings denoting sound profile names.
  *         Caller must free the list (but not the data).
- *
- * @since 2.1.0
  */
 GList *finch_sound_get_profiles(void);
 
@@ -66,8 +60,6 @@
  *
  * @return Returns FALSE if preference is set to 'No sound', or if volume is
  *         set to zero.
- *
- * @since 2.2.0
  */
 gboolean finch_sound_is_enabled(void);
 
@@ -75,15 +67,11 @@
  * Gets GNT sound UI ops.
  *
  * @return The UI operations structure.
- *
- * @since 2.1.0
  */
 PurpleSoundUiOps *finch_sound_get_ui_ops(void);
 
 /**
  * Show the sound settings dialog.
- *
- * @since 2.1.0
  */
 void finch_sounds_show_all(void);
 
--- a/finch/gntstatus.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntstatus.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,6 +23,8 @@
  * 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 <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -34,7 +36,6 @@
 #include <gntutils.h>
 
 #include "finch.h"
-#include <internal.h>
 
 #include <notify.h>
 #include <request.h>
@@ -313,7 +314,7 @@
 		gnt_box_give_focus_to_child(GNT_BOX(edit->window), edit->title);
 		return;
 	}
-	
+
 	if (edit->saved == NULL)
 	{
 		edit->saved = purple_savedstatus_new(title, prim);
@@ -408,7 +409,7 @@
 	gnt_tree_change_text(GNT_TREE(sub->parent->tree), row, 1,
 			purple_status_type_get_name(type));
 	gnt_tree_change_text(GNT_TREE(sub->parent->tree), row, 2, message);
-	
+
 	gnt_widget_destroy(sub->window);
 }
 
--- a/finch/gntui.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/gntui.c	Wed Jun 13 19:30:27 2012 -0400
@@ -51,7 +51,7 @@
 #endif
 
 	purple_prefs_add_none("/purple/gnt");
-	
+
 	/* Accounts */
 	finch_accounts_init();
 	purple_accounts_set_ui_ops(finch_accounts_get_ui_ops());
--- a/finch/libgnt/configure.ac	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/configure.ac	Wed Jun 13 19:30:27 2012 -0400
@@ -24,11 +24,11 @@
 # Make sure to update ../../configure.ac with libgnt version changes.
 #
 
-m4_define([gnt_lt_current], [5])
+m4_define([gnt_lt_current], [8])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [5])
+m4_define([gnt_minor_version], [8])
 m4_define([gnt_micro_version], [0])
-m4_define([gnt_version_suffix], [])
+m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
 m4_define([gnt_display_version], gnt_version[]m4_ifdef([gnt_version_suffix],[gnt_version_suffix]))
@@ -265,6 +265,10 @@
 	for location in $ac_ncurses_includes /usr/include/ncursesw /usr/include
 	do
 		f="$location/ncurses.h"
+		orig_CFLAGS="$CFLAGS"
+		orig_CPPFLAGS="$CPPFLAGS"
+		CFLAGS="$CFLAGS -I$location"
+		CPPFLAGS="$CPPFLAGS -I$location"
 		AC_CHECK_HEADER($f,[
 			AC_MSG_CHECKING([if $f supports wide characters])
 			AC_TRY_COMPILE([
@@ -283,9 +287,13 @@
 				fi
 
 				found_ncurses_h=yes
+				CFLAGS="$orig_CFLAGS"
+				CPPFLAGS="$orig_CPPFLAGS"
 				AC_MSG_RESULT([yes])
 				break
 			], [
+				CFLAGS="$orig_CFLAGS"
+				CPPFLAGS="$orig_CPPFLAGS"
 				AC_MSG_RESULT([no])
 			])
 		])
--- a/finch/libgnt/gnt-skel.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gnt-skel.h	Wed Jun 13 19:30:27 2012 -0400
@@ -61,14 +61,14 @@
 G_BEGIN_DECLS
 
 /**
- * 
+ *
  *
  * @return
  */
 GType gnt_skel_get_gtype(void);
 
 /**
- * 
+ *
  *
  * @return
  */
--- a/finch/libgnt/gntbindable.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntbindable.c	Wed Jun 13 19:30:27 2012 -0400
@@ -48,7 +48,7 @@
 	GList * params;       /* The list of paramaters */
 } rebind_info;
 
-static void 
+static void
 gnt_bindable_free_rebind_info(void)
 {
 	g_free(rebind_info.name);
@@ -92,7 +92,7 @@
 		if (!strcmp(text, GNT_KEY_CTRL_I) || !strcmp(text, GNT_KEY_ENTER)) {
 			return FALSE;
 		}
-		
+
 		tmp = gnt_key_lookup(text);
 		new_text = g_strdup_printf("KEY: \"%s\"", tmp);
 		gnt_text_view_clear(textview);
@@ -105,7 +105,7 @@
 		return TRUE;
 	}
 	return FALSE;
-} 
+}
 static void
 gnt_bindable_rebinding_activate(GntBindable *data, gpointer bindable)
 {
@@ -154,18 +154,18 @@
 	g_signal_connect(G_OBJECT(win), "key_pressed", G_CALLBACK(gnt_bindable_rebinding_grab_key), key_textview);
 
 	button_box = gnt_box_new(FALSE, FALSE);
-	
+
 	bind_button = gnt_button_new("BIND");
 	gnt_widget_set_name(bind_button, "bind");
 	gnt_box_add_widget(GNT_BOX(button_box), bind_button);
-	
+
 	cancel_button = gnt_button_new("Cancel");
 	gnt_widget_set_name(cancel_button, "cancel");
 	gnt_box_add_widget(GNT_BOX(button_box), cancel_button);
-	
+
 	g_signal_connect(G_OBJECT(bind_button), "activate", G_CALLBACK(gnt_bindable_rebinding_rebind), win);
 	g_signal_connect(G_OBJECT(cancel_button), "activate", G_CALLBACK(gnt_bindable_rebinding_cancel), win);
-	
+
 	gnt_box_add_widget(GNT_BOX(vbox), button_box);
 
 	gnt_box_add_widget(GNT_BOX(win), vbox);
@@ -320,7 +320,7 @@
 	while ((p = va_arg(args, void *)) != NULL)
 		list = g_list_append(list, p);
 	va_end(args);
-	
+
 	action = g_hash_table_lookup(klass->actions, name);
 	if (action && action->u.action) {
 		return action->u.action(bindable, list);
--- a/finch/libgnt/gntbindable.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntbindable.h	Wed Jun 13 19:30:27 2012 -0400
@@ -67,7 +67,7 @@
 G_BEGIN_DECLS
 
 /**
- * 
+ *
  *
  * @return
  */
@@ -166,7 +166,7 @@
  *
  * @return  @c TRUE if the action was performed successfully, @c FALSE otherwise.
  */
-gboolean gnt_bindable_perform_action_named(GntBindable *bindable, const char *name, ...);
+gboolean gnt_bindable_perform_action_named(GntBindable *bindable, const char *name, ...) G_GNUC_NULL_TERMINATED;
 
 /**
  * Returns a GntTree populated with "key" -> "binding" for the widget.
--- a/finch/libgnt/gntbox.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntbox.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,12 +20,16 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntbox.h"
 #include "gntstyle.h"
 #include "gntutils.h"
 
 #include <string.h>
 
+#define PROP_LAST_RESIZE_S "last-resize"
+#define PROP_SIZE_QUEUED_S "size-queued"
+
 enum
 {
 	PROP_0,
@@ -60,7 +64,7 @@
 	GntWidget *widget = GNT_WIDGET(box);
 	int len;
 	char *end = (char*)gnt_util_onscreen_width_to_pointer(title, widget->priv.width - 4, &len);
-	
+
 	if (p)
 		*p = (widget->priv.width - len) / 2;
 	if (r)
@@ -90,7 +94,7 @@
 		else
 			wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_TITLE_D));
 		mvwaddch(widget->window, 0, pos-1, ACS_RTEE | gnt_color_pair(GNT_COLOR_NORMAL));
-		mvwaddstr(widget->window, 0, pos, title);
+		mvwaddstr(widget->window, 0, pos, C_(title));
 		mvwaddch(widget->window, 0, right, ACS_LTEE | gnt_color_pair(GNT_COLOR_NORMAL));
 		g_free(title);
 	}
@@ -193,7 +197,7 @@
 	GntBox *box = GNT_BOX(widget);
 	GList *iter;
 	int maxw = 0, maxh = 0;
-	
+
 	g_list_foreach(box->list, (GFunc)gnt_widget_size_request, NULL);
 
 	for (iter = box->list; iter; iter = iter->next)
@@ -397,6 +401,7 @@
 	GList *iter;
 	GntBox *box = GNT_BOX(widget);
 	int wchange, hchange;
+	GntWidget *child, *last;
 
 	if (!box->list)
 		return TRUE;
@@ -405,67 +410,64 @@
 	hchange = widget->priv.height - height;
 
 	if (wchange == 0 && hchange == 0)
-		return TRUE;		/* Quit playing games */
+		return TRUE;		/* Quit playing games with my size */
 
-	/* XXX: Right now, I am trying to just apply all the changes to 
-	 * just one widget. It should be possible to distribute the
-	 * changes to all the widgets in the box. */
-	for (iter = box->list; iter; iter = iter->next)
-	{
+	child = NULL;
+	last = g_object_get_data(G_OBJECT(box), PROP_LAST_RESIZE_S);
+
+	/* First, make sure all the widgets will fit into the box after resizing. */
+	for (iter = box->list; iter; iter = iter->next) {
 		GntWidget *wid = iter->data;
 		int w, h;
 
 		gnt_widget_get_size(wid, &w, &h);
 
-		if (gnt_widget_confirm_size(wid, w - wchange, h - hchange))
-		{
-			GList *i;
-
-			for (i = box->list; i; i = i->next)
-			{
-				int tw, th;
-				if (i == iter) continue;
-				gnt_widget_get_size(GNT_WIDGET(i->data), &tw, &th);
-				if (box->vertical)
-				{
-					if (!gnt_widget_confirm_size(i->data, tw - wchange, th)) {
-						/* If we are decreasing the size and the widget is going
-						 * to be too large to fit into the box, then do not allow
-						 * resizing. */
-						if (wchange > 0 && tw >= widget->priv.width)
-							return FALSE;
-					}
-				}
-				else
-				{
-					if (!gnt_widget_confirm_size(i->data, tw, th - hchange)) {
-						if (hchange > 0 && th >= widget->priv.height)
-							return FALSE;
-						return FALSE;
-					}
-				}
-			}
-#if 0
-			gnt_widget_set_size(wid, w - wchange, h - hchange);
-			if (box->vertical)
-				hchange = 0;
-			else
-				wchange = 0;
-
-			for (i = box->list; i; i = i->next)
-			{
-				int tw, th;
-				if (i == iter) continue;
-				gnt_widget_get_size(GNT_WIDGET(i->data), &tw, &th);
-				gnt_widget_set_size(i->data, tw - wchange, th - hchange);
-			}
-#endif
-			g_object_set_data(G_OBJECT(box), "size-queued", wid);
-			return TRUE;
+		if (wid != last && !child && w > 0 && h > 0 &&
+				!GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_INVISIBLE) &&
+				gnt_widget_confirm_size(wid, w - wchange, h - hchange)) {
+			child = wid;
+			break;
 		}
 	}
 
-	return FALSE;
+	if (!child && (child = last)) {
+		int w, h;
+		gnt_widget_get_size(child, &w, &h);
+		if (!gnt_widget_confirm_size(child, w - wchange, h - hchange))
+			child = NULL;
+	}
+
+	g_object_set_data(G_OBJECT(box), PROP_SIZE_QUEUED_S, child);
+
+	if (child) {
+		for (iter = box->list; iter; iter = iter->next) {
+			GntWidget *wid = iter->data;
+			int w, h;
+
+			if (wid == child)
+				continue;
+
+			gnt_widget_get_size(wid, &w, &h);
+			if (box->vertical) {
+				/* For a vertical box, if we are changing the width, make sure the widgets
+				 * in the box will fit after resizing the width. */
+				if (wchange > 0 &&
+						w >= child->priv.width &&
+						!gnt_widget_confirm_size(wid, w - wchange, h))
+					return FALSE;
+			} else {
+				/* If we are changing the height, make sure the widgets in the box fit after
+				 * the resize. */
+				if (hchange > 0 &&
+						h >= child->priv.height &&
+						!gnt_widget_confirm_size(wid, w, h - hchange))
+					return FALSE;
+			}
+
+		}
+	}
+
+	return (child != NULL);
 }
 
 static void
@@ -476,16 +478,16 @@
 	GntBox *box = GNT_BOX(widget);
 	GntWidget *wid;
 	int tw, th;
-		
+
 	wchange = widget->priv.width - oldw;
 	hchange = widget->priv.height - oldh;
-	
-	wid = g_object_get_data(G_OBJECT(box), "size-queued");
-	if (wid)
-	{
+
+	wid = g_object_get_data(G_OBJECT(box), PROP_SIZE_QUEUED_S);
+	if (wid) {
 		gnt_widget_get_size(wid, &tw, &th);
 		gnt_widget_set_size(wid, tw + wchange, th + hchange);
-		g_object_set_data(G_OBJECT(box), "size-queued", NULL);
+		g_object_set_data(G_OBJECT(box), PROP_SIZE_QUEUED_S, NULL);
+		g_object_set_data(G_OBJECT(box), PROP_LAST_RESIZE_S, wid);
 	}
 
 	if (box->vertical)
@@ -687,8 +689,8 @@
 		get_title_thingies(b, prev, &pos, &right);
 		mvwhline(w->window, 0, pos - 1, ACS_HLINE | gnt_color_pair(GNT_COLOR_NORMAL),
 				right - pos + 2);
-		g_free(prev);
 	}
+	g_free(prev);
 }
 
 void gnt_box_set_pad(GntBox *box, int pad)
--- a/finch/libgnt/gntbutton.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntbutton.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "gntinternal.h"
 #include "gntbutton.h"
 #include "gntstyle.h"
 #include "gntutils.h"
@@ -48,7 +49,7 @@
 		type = GNT_COLOR_NORMAL;
 
 	wbkgdset(widget->window, '\0' | gnt_color_pair(type));
-	mvwaddstr(widget->window, (small_button) ? 0 : 1, 2, button->priv->text);
+	mvwaddstr(widget->window, (small_button) ? 0 : 1, 2, C_(button->priv->text));
 	if (small_button) {
 		type = GNT_COLOR_HIGHLIGHT;
 		mvwchgat(widget->window, 0, 0, widget->priv.width, focus ? A_BOLD : A_REVERSE, type, NULL);
--- a/finch/libgnt/gntcheckbox.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntcheckbox.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntcheckbox.h"
 
 enum
@@ -36,21 +37,21 @@
 {
 	GntCheckBox *cb = GNT_CHECK_BOX(widget);
 	GntColorType type;
-	char *text;
+	gboolean focus = gnt_widget_has_focus(widget);
 
-	if (gnt_widget_has_focus(widget))
+	if (focus)
 		type = GNT_COLOR_HIGHLIGHT;
 	else
 		type = GNT_COLOR_NORMAL;
 
 	wbkgdset(widget->window, '\0' | gnt_color_pair(type));
 
-	text = g_strdup_printf("[%c]", cb->checked ? 'X' : ' ');
-	mvwaddstr(widget->window, 0, 0, text);
-	g_free(text);
+	mvwaddch(widget->window, 0, 0, '[');
+	mvwaddch(widget->window, 0, 1, (cb->checked ? 'X' : ' ') | (focus ? A_UNDERLINE : A_NORMAL));
+	mvwaddch(widget->window, 0, 2, ']');
 
 	wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_NORMAL));
-	mvwaddstr(widget->window, 0, 4, GNT_BUTTON(cb)->priv->text);
+	mvwaddstr(widget->window, 0, 4, C_(GNT_BUTTON(cb)->priv->text));
 	wmove(widget->window, 0, 1);
 
 	GNTDEBUG;
@@ -99,7 +100,7 @@
 	wclass->key_pressed = gnt_check_box_key_pressed;
 	wclass->clicked = gnt_check_box_clicked;
 
-	signals[SIG_TOGGLED] = 
+	signals[SIG_TOGGLED] =
 		g_signal_new("toggled",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
--- a/finch/libgnt/gntclipboard.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntclipboard.c	Wed Jun 13 19:30:27 2012 -0400
@@ -32,7 +32,7 @@
 static void
 gnt_clipboard_class_init(GntClipboardClass *klass)
 {
-	signals[SIG_CLIPBOARD] = 
+	signals[SIG_CLIPBOARD] =
 		g_signal_new("clipboard_changed",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
--- a/finch/libgnt/gntcolors.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntcolors.c	Wed Jun 13 19:30:27 2012 -0400
@@ -161,9 +161,9 @@
 		color = custom ? GNT_COLOR_BLUE : COLOR_BLUE;
 	else if (strcmp(key, "white") == 0)
 		color = custom ? GNT_COLOR_WHITE : COLOR_WHITE;
-	else if (strcmp(key, "gray") == 0)
+	else if (strcmp(key, "gray") == 0 || strcmp(key, "grey") == 0)
 		color = custom ? GNT_COLOR_GRAY : COLOR_YELLOW;  /* eh? */
-	else if (strcmp(key, "darkgray") == 0)
+	else if (strcmp(key, "darkgray") == 0 || strcmp(key, "darkgrey") == 0)
 		color = custom ? GNT_COLOR_DARK_GRAY : COLOR_BLACK;
 	else if (strcmp(key, "magenta") == 0)
 		color = COLOR_MAGENTA;
@@ -208,8 +208,10 @@
 				key = g_ascii_strdown(key, -1);
 				color = gnt_colors_get_color(key);
 				g_free(key);
-				if (color == -EINVAL)
+				if (color == -EINVAL) {
+					g_strfreev(list);
 					continue;
+				}
 
 				init_color(color, r, g, b);
 			}
@@ -251,8 +253,10 @@
 			int bg = gnt_colors_get_color(bgc);
 			g_free(fgc);
 			g_free(bgc);
-			if (fg == -EINVAL || bg == -EINVAL)
+			if (fg == -EINVAL || bg == -EINVAL) {
+				g_strfreev(list);
 				continue;
+			}
 
 			key = g_ascii_strdown(key, -1);
 
@@ -275,6 +279,7 @@
 			else if (strcmp(key, "urgent") == 0)
 				type = GNT_COLOR_URGENT;
 			else {
+				g_strfreev(list);
 				g_free(key);
 				continue;
 			}
--- a/finch/libgnt/gntcombobox.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntcombobox.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,10 +20,12 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntbox.h"
 #include "gntcombobox.h"
 #include "gnttree.h"
 #include "gntmarshal.h"
+#include "gntstyle.h"
 #include "gntutils.h"
 
 #include <string.h>
@@ -90,7 +92,7 @@
 	s = (char*)gnt_util_onscreen_width_to_pointer(text, widget->priv.width - 4, &len);
 	*s = '\0';
 
-	mvwaddstr(widget->window, 1, 1, text);
+	mvwaddstr(widget->window, 1, 1, C_(text));
 	whline(widget->window, ' ' | gnt_color_pair(type), widget->priv.width - 4 - len);
 	mvwaddch(widget->window, 1, widget->priv.width - 3, ACS_VLINE | gnt_color_pair(GNT_COLOR_NORMAL));
 	mvwaddch(widget->window, 1, widget->priv.width - 2, ACS_DARROW | gnt_color_pair(GNT_COLOR_NORMAL));
@@ -148,12 +150,11 @@
 gnt_combo_box_key_pressed(GntWidget *widget, const char *text)
 {
 	GntComboBox *box = GNT_COMBO_BOX(widget);
-	if (GNT_WIDGET_IS_FLAG_SET(box->dropdown->parent, GNT_WIDGET_MAPPED))
-	{
-		if (text[1] == 0)
-		{
-			switch (text[0])
-			{
+	gboolean showing = !!GNT_WIDGET_IS_FLAG_SET(box->dropdown->parent, GNT_WIDGET_MAPPED);
+
+	if (showing) {
+		if (text[1] == 0) {
+			switch (text[0]) {
 				case '\r':
 				case '\t':
 				case '\n':
@@ -164,20 +165,42 @@
 					return TRUE;
 			}
 		}
-		if (gnt_widget_key_pressed(box->dropdown, text))
-			return TRUE;
 	}
-	else
+
+	if (gnt_widget_key_pressed(box->dropdown, text)) {
+		if (!showing)
+			popup_dropdown(box);
+		return TRUE;
+	}
+
 	{
-		if (text[0] == 27)
-		{
-			if (strcmp(text, GNT_KEY_UP) == 0 ||
-					strcmp(text, GNT_KEY_DOWN) == 0)
-			{
-				popup_dropdown(box);
-				return TRUE;
-			}
-		}
+#define SEARCH_IN_RANGE(start, end) do { \
+		GntTreeRow *row; \
+		for (row = start; row != end; \
+				row = gnt_tree_row_get_next(tree, row)) { \
+			gpointer key = gnt_tree_row_get_key(tree, row); \
+			GList *list = gnt_tree_get_row_text_list(tree, key); \
+			gboolean found = FALSE; \
+			found = (list->data && g_ascii_strncasecmp(text, list->data, len) == 0); \
+			g_list_foreach(list, (GFunc)g_free, NULL); \
+			g_list_free(list); \
+			if (found) { \
+				if (!showing) \
+					popup_dropdown(box); \
+				gnt_tree_set_selected(tree, key); \
+				return TRUE; \
+			} \
+		} \
+} while (0)
+
+		int len = strlen(text);
+		GntTree *tree = GNT_TREE(box->dropdown);
+		GntTreeRow *current = tree->current;
+
+		SEARCH_IN_RANGE(gnt_tree_row_get_next(tree, current), NULL);
+		SEARCH_IN_RANGE(tree->top, current);
+
+#undef SEARCH_IN_RANGE
 	}
 
 	return FALSE;
@@ -228,9 +251,20 @@
 	gnt_widget_set_size(box->dropdown, widget->priv.width - 1, box->dropdown->priv.height);
 }
 
+static gboolean
+dropdown_menu(GntBindable *b, GList *null)
+{
+	if (GNT_WIDGET_IS_FLAG_SET(GNT_COMBO_BOX(b)->dropdown->parent, GNT_WIDGET_MAPPED))
+		return FALSE;
+	popup_dropdown(GNT_COMBO_BOX(b));
+	return TRUE;
+}
+
 static void
 gnt_combo_box_class_init(GntComboBoxClass *klass)
 {
+	GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass);
+
 	parent_class = GNT_WIDGET_CLASS(klass);
 
 	parent_class->destroy = gnt_combo_box_destroy;
@@ -244,7 +278,7 @@
 	widget_lost_focus = parent_class->lost_focus;
 	parent_class->lost_focus = gnt_combo_box_lost_focus;
 
-	signals[SIG_SELECTION_CHANGED] = 
+	signals[SIG_SELECTION_CHANGED] =
 		g_signal_new("selection-changed",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -253,6 +287,12 @@
 					 gnt_closure_marshal_VOID__POINTER_POINTER,
 					 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
 
+	gnt_bindable_class_register_action(bindable, "dropdown", dropdown_menu,
+			GNT_KEY_DOWN, NULL);
+	gnt_bindable_register_binding(bindable, "dropdown", GNT_KEY_UP, NULL);
+
+	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable);
+
 	GNTDEBUG;
 }
 
@@ -271,7 +311,7 @@
 	GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_NO_SHADOW | GNT_WIDGET_NO_BORDER | GNT_WIDGET_TRANSIENT);
 	gnt_box_set_pad(GNT_BOX(box), 0);
 	gnt_box_add_widget(GNT_BOX(box), combo->dropdown);
-	
+
 	widget->priv.minw = 4;
 	widget->priv.minh = 3;
 	GNTDEBUG;
--- a/finch/libgnt/gntcombobox.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntcombobox.h	Wed Jun 13 19:30:27 2012 -0400
@@ -91,7 +91,7 @@
 
 /**
  * Remove an entry
- * 
+ *
  * @param box The GntComboBox
  * @param key The data to be removed
  */
--- a/finch/libgnt/gntentry.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntentry.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,6 +23,7 @@
 #include <ctype.h>
 #include <string.h>
 
+#include "gntinternal.h"
 #include "gntbox.h"
 #include "gntentry.h"
 #include "gntmarshal.h"
@@ -54,6 +55,11 @@
 	GntEntryAction last;
 };
 
+struct _GntEntrySearch
+{
+	char *needle;
+};
+
 static guint signals[SIGS] = { 0 };
 
 static GntWidgetClass *parent_class = NULL;
@@ -284,7 +290,7 @@
 				g_utf8_pointer_to_offset(entry->scroll, entry->end));
 	}
 	else
-		mvwprintw(widget->window, 0, 0, "%s", entry->scroll);
+		mvwprintw(widget->window, 0, 0, "%s", C_(entry->scroll));
 
 	stop = gnt_util_onscreen_width(entry->scroll, entry->end);
 	if (stop < widget->priv.width)
@@ -470,6 +476,52 @@
 }
 
 static gboolean
+history_search(GntBindable *bind, GList *null)
+{
+	GntEntry *entry = GNT_ENTRY(bind);
+	GList *iter;
+	const char *current;
+
+	if (entry->history->prev && entry->search->needle)
+		current = entry->search->needle;
+	else
+		current = gnt_entry_get_text(entry);
+
+	if (!entry->histlength || !entry->history->next || !*current)
+		return FALSE;
+
+	for (iter = entry->history->next; iter; iter = iter->next) {
+		const char *str = iter->data;
+		/* A more utf8-friendly version of strstr would have been better, but
+		 * for now, this will have to do. */
+		if (strstr(str, current) != NULL)
+			break;
+	}
+
+	if (!iter)
+		return TRUE;
+
+	if (entry->history->prev == NULL) {
+		/* We are doing it for the first time. Save the current contents */
+		char *text = g_strdup(gnt_entry_get_text(entry));
+
+		g_free(entry->search->needle);
+		entry->search->needle = g_strdup(current);
+
+		g_free(entry->history->data);
+		entry->history->data = text;
+	}
+
+	entry->history = iter;
+	gnt_entry_set_text_internal(entry, entry->history->data);
+	destroy_suggest(entry);
+	entry_text_changed(entry);
+
+	update_kill_ring(entry, ENTRY_JAIL, NULL, 0);
+	return TRUE;
+}
+
+static gboolean
 clipboard_paste(GntBindable *bind, GList *n)
 {
 	GntEntry *entry = GNT_ENTRY(bind);
@@ -495,7 +547,7 @@
 {
 	GntEntry *entry = GNT_ENTRY(bind);
 	if (entry->ddown) {
-		gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "move-down");
+		gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "move-down", NULL);
 		return TRUE;
 	}
 	return show_suggest_dropdown(entry);
@@ -524,6 +576,28 @@
 }
 
 static gboolean
+suggest_next_page(GntBindable *bind, GList *null)
+{
+	GntEntry *entry = GNT_ENTRY(bind);
+	if (entry->ddown) {
+		gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "page-down", NULL);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
+suggest_prev_page(GntBindable *bind, GList *null)
+{
+	GntEntry *entry = GNT_ENTRY(bind);
+	if (entry->ddown) {
+		gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "page-up", NULL);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static gboolean
 del_to_home(GntBindable *bind, GList *null)
 {
 	GntEntry *entry = GNT_ENTRY(bind);
@@ -832,6 +906,9 @@
 		gnt_widget_destroy(entry->ddown->parent);
 	}
 
+	g_free(entry->search->needle);
+	g_free(entry->search);
+
 	jail_killring(entry->killring);
 }
 
@@ -928,12 +1005,18 @@
 				GNT_KEY_DOWN, NULL);
 	gnt_bindable_class_register_action(bindable, "suggest-prev", suggest_prev,
 				GNT_KEY_UP, NULL);
+	gnt_bindable_class_register_action(bindable, "suggest-next-page", suggest_next_page,
+				GNT_KEY_PGDOWN, NULL);
+	gnt_bindable_class_register_action(bindable, "suggest-prev-page", suggest_prev_page,
+				GNT_KEY_PGUP, NULL);
 	gnt_bindable_class_register_action(bindable, "history-next", history_next,
 				GNT_KEY_CTRL_DOWN, NULL);
 	gnt_bindable_class_register_action(bindable, "history-prev", history_prev,
 				GNT_KEY_CTRL_UP, NULL);
 	gnt_bindable_register_binding(bindable, "history-prev", GNT_KEY_CTRL_P, NULL);
 	gnt_bindable_register_binding(bindable, "history-next", GNT_KEY_CTRL_N, NULL);
+	gnt_bindable_class_register_action(bindable, "history-search", history_search,
+				GNT_KEY_CTRL_R, NULL);
 	gnt_bindable_class_register_action(bindable, "clipboard-paste", clipboard_paste,
 				GNT_KEY_CTRL_V, NULL);
 
@@ -965,6 +1048,7 @@
 	entry->always = FALSE;
 	entry->suggests = NULL;
 	entry->killring = new_killring();
+	entry->search = g_new0(GntEntrySearch, 1);
 
 	GNT_WIDGET_SET_FLAGS(GNT_WIDGET(entry),
 			GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW | GNT_WIDGET_CAN_TAKE_FOCUS);
@@ -1044,8 +1128,11 @@
 		snprintf(entry->start, len + 1, "%s", text);
 	entry->end = entry->start + len;
 
-	entry->scroll = entry->start + scroll;
-	entry->cursor = entry->end - cursor;
+	if ((entry->scroll = entry->start + scroll) > entry->end)
+		entry->scroll = entry->end;
+
+	if ((entry->cursor = entry->end - cursor) > entry->end)
+		entry->cursor = entry->end;
 
 	if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(entry), GNT_WIDGET_MAPPED))
 		entry_redraw(GNT_WIDGET(entry));
--- a/finch/libgnt/gntentry.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntentry.h	Wed Jun 13 19:30:27 2012 -0400
@@ -49,6 +49,7 @@
 typedef struct _GntEntryPriv		GntEntryPriv;
 typedef struct _GntEntryClass	GntEntryClass;
 typedef struct _GntEntryKillRing    GntEntryKillRing;
+typedef struct _GntEntrySearch		GntEntrySearch;
 
 typedef enum
 {
@@ -86,6 +87,7 @@
 	gboolean always;    /* Should the list of suggestions show at all times, or only on tab-press? */
 	GntWidget *ddown;   /* The dropdown with the suggested list */
 	GntEntryKillRing *killring; /**< @since 2.3.0 */
+	GntEntrySearch *search;		/**< @since 2.7.0 */
 };
 
 struct _GntEntryClass
--- a/finch/libgnt/gntfilesel.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntfilesel.c	Wed Jun 13 19:30:27 2012 -0400
@@ -176,9 +176,13 @@
 	splits = g_strsplit(path, G_DIR_SEPARATOR_S, -1);
 	for (i = 0, j = 0; splits[i]; i++) {
 		if (strcmp(splits[i], ".") == 0) {
+			g_free(splits[i]);
+			splits[i] = NULL;
 		} else if (strcmp(splits[i], "..") == 0) {
 			if (j)
 				j--;
+			g_free(splits[i]);
+			splits[i] = NULL;
 		} else {
 			if (i != j) {
 				g_free(splits[j]);
@@ -241,7 +245,7 @@
 	GDir *dir;
 	GntFile *file;
 	const char *str;
-	
+
 	dir = g_dir_open(path, 0, error);
 	if (dir == NULL || (error && *error)) {
 		return FALSE;
@@ -311,7 +315,7 @@
 		success = sel->read_fn(sel->current, &files, err);
 	else
 		success = local_read_fn(sel->current, &files, err);
-	
+
 	if (!success || *err) {
 		gnt_warning("error opening location %s (%s)",
 			sel->current, *err ? (*err)->message : "reason unknown");
@@ -352,7 +356,7 @@
 
 		if (!str)
 			return TRUE;
-		
+
 		path = g_build_filename(sel->current, str, NULL);
 		dir = g_path_get_basename(sel->current);
 		if (!gnt_file_sel_set_current_location(sel, path)) {
@@ -593,7 +597,7 @@
 	orig_size_request = kl->size_request;
 	kl->size_request = gnt_file_sel_size_request;
 
-	signals[SIG_FILE_SELECTED] = 
+	signals[SIG_FILE_SELECTED] =
 		g_signal_new("file_selected",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -625,6 +629,7 @@
 
 	sel->files = gnt_tree_new_with_columns(2);  /* Name, Size */
 	gnt_tree_set_compare_func(GNT_TREE(sel->files), (GCompareFunc)g_utf8_collate);
+	gnt_tree_set_hash_fns(GNT_TREE(sel->files), g_str_hash, g_str_equal, g_free);
 	gnt_tree_set_column_titles(GNT_TREE(sel->files), "Filename", "Size");
 	gnt_tree_set_show_title(GNT_TREE(sel->files), TRUE);
 	gnt_tree_set_col_width(GNT_TREE(sel->files), 0, 25);
--- a/finch/libgnt/gntfilesel.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntfilesel.h	Wed Jun 13 19:30:27 2012 -0400
@@ -81,7 +81,7 @@
 	void (*gnt_reserved4)(void);
 };
 
-typedef enum _GntFileType
+typedef enum
 {
 	GNT_FILE_REGULAR,
 	GNT_FILE_DIR
--- a/finch/libgnt/gntinternal.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntinternal.h	Wed Jun 13 19:30:27 2012 -0400
@@ -19,6 +19,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+#include <glib.h>
 #undef G_LOG_DOMAIN
 #define G_LOG_DOMAIN "Gnt"
 
@@ -31,3 +32,14 @@
 # define gnt_warning g_warning
 #endif
 
+#ifndef G_GNUC_NULL_TERMINATED
+#	if defined(__GNUC__) && __GNUC__ >= 4
+#		define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+#	else
+#		define G_GNUC_NULL_TERMINATED
+#	endif
+#endif
+
+extern int gnt_need_conversation_to_locale;
+extern const char *C_(const char *x);
+
--- a/finch/libgnt/gntkeys.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntkeys.c	Wed Jun 13 19:30:27 2012 -0400
@@ -166,7 +166,7 @@
 				strstr(term, "xterm") == term ||
 				strstr(term, "vt100") == term)
 			*(text + 1) = 'O';
-	} else if (*(unsigned char*)text == 195) {
+	} else if (g_utf8_get_char(text) == 195) {
 		if (*(text + 2) == 0 && strstr(term, "xterm") == term) {
 			*(text) = 27;
 			*(text + 1) -= 64;  /* Say wha? */
--- a/finch/libgnt/gntkeys.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntkeys.h	Wed Jun 13 19:30:27 2012 -0400
@@ -65,7 +65,7 @@
 #define GNT_KEY_BACKSPACE SAFE(key_backspace)
 #define GNT_KEY_DEL    SAFE(key_dc)
 #define GNT_KEY_INS    SAFE(key_ic)
-#define GNT_KEY_BACK_TAB SAFE(back_tab)
+#define GNT_KEY_BACK_TAB (back_tab ? back_tab : SAFE(key_btab))
 
 #define GNT_KEY_CTRL_A     "\001"
 #define GNT_KEY_CTRL_B     "\002"
@@ -158,7 +158,7 @@
  */
 int gnt_keys_find_combination(const char *key);
 
-/* A lot of commonly used variable names are defined in <term.h>. 
+/* A lot of commonly used variable names are defined in <term.h>.
  * #undef them to make life easier for everyone. */
 
 #undef columns
--- a/finch/libgnt/gntlabel.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntlabel.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntlabel.h"
 #include "gntutils.h"
 
@@ -53,7 +54,7 @@
 	chtype flag = gnt_text_format_flag_to_chtype(label->flags);
 
 	wbkgdset(widget->window, '\0' | flag);
-	mvwaddstr(widget->window, 0, 0, label->text);
+	mvwaddstr(widget->window, 0, 0, C_(label->text));
 
 	GNTDEBUG;
 }
--- a/finch/libgnt/gntline.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntline.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntline.h"
 
 enum
--- a/finch/libgnt/gntmain.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntmain.c	Wed Jun 13 19:30:27 2012 -0400
@@ -69,7 +69,8 @@
  */
 
 static GIOChannel *channel = NULL;
-static int channel_read_callback;
+static guint channel_read_callback = 0;
+static guint channel_error_callback = 0;
 
 static gboolean ascii_only;
 static gboolean mouse_enabled;
@@ -81,6 +82,8 @@
 static GntWM *wm;
 static GntClipboard *clipboard;
 
+int gnt_need_conversation_to_locale;
+
 #define HOLDING_ESCAPE  (escape_stuff.timer != 0)
 
 static struct {
@@ -123,7 +126,7 @@
 
 	if (!wm->cws->ordered || buffer[0] != 27)
 		return FALSE;
-	
+
 	buffer++;
 	if (strlen(buffer) < 5)
 		return FALSE;
@@ -174,7 +177,7 @@
 
 	if (widget && gnt_wm_process_click(wm, event, x, y, widget))
 		return TRUE;
-	
+
 	if (event == GNT_LEFT_MOUSE_DOWN && widget && widget != wm->_list.window &&
 			!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) {
 		if (widget != wm->cws->ordered->data) {
@@ -314,11 +317,11 @@
 	channel_read_callback = result = g_io_add_watch_full(channel,  G_PRIORITY_HIGH,
 					(G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI),
 					io_invoke, NULL, NULL);
-	
-	g_io_add_watch_full(channel,  G_PRIORITY_HIGH,
+
+	channel_error_callback = g_io_add_watch_full(channel,  G_PRIORITY_HIGH,
 					(G_IO_NVAL),
 					io_invoke_error, GINT_TO_POINTER(result), NULL);
-	
+
 	g_io_channel_unref(channel);  /* Apparently this caused crashes for some people.
 	                                 But irssi does this, so I am going to assume the
 	                                 crashes were caused by some other stuff. */
@@ -435,7 +438,7 @@
 {
 	const char *name = gnt_style_get(GNT_STYLE_WM);
 	gpointer handle;
-	
+
 	if (name && *name) {
 		handle = g_module_open(name, G_MODULE_BIND_LAZY);
 		if (handle) {
@@ -456,7 +459,7 @@
 
 	if (channel)
 		return;
-	
+
 	locale = setlocale(LC_ALL, "");
 
 	setup_io();
@@ -464,10 +467,12 @@
 #ifdef NO_WIDECHAR
 	ascii_only = TRUE;
 #else
-	if (locale && (strstr(locale, "UTF") || strstr(locale, "utf")))
+	if (locale && (strstr(locale, "UTF") || strstr(locale, "utf"))) {
 		ascii_only = FALSE;
-	else
+	} else {
 		ascii_only = TRUE;
+		gnt_need_conversation_to_locale = TRUE;
+	}
 #endif
 
 	initscr();
@@ -549,7 +554,7 @@
 	GntWidget *w;
 	if (!widget)
 		return FALSE;
-	
+
 	if (GNT_IS_MENU(widget))
 		return TRUE;
 
@@ -583,6 +588,13 @@
 
 void gnt_quit()
 {
+	/* Prevent io_invoke() from being called after wm is destroyed */
+	g_source_remove(channel_error_callback);
+	g_source_remove(channel_read_callback);
+
+	channel_error_callback = 0;
+	channel_read_callback = 0;
+
 	g_object_unref(G_OBJECT(wm));
 	wm = NULL;
 
@@ -723,3 +735,24 @@
 #endif
 }
 
+const char *C_(const char *x)
+{
+	static char *c = NULL;
+	if (gnt_need_conversation_to_locale) {
+		GError *error = NULL;
+		g_free(c);
+		c = g_locale_from_utf8(x, -1, NULL, NULL, &error);
+		if (c == NULL || error) {
+			char *store = c;
+			c = NULL;
+			gnt_warning("Error: %s\n", error ? error->message : "(unknown)");
+			g_error_free(error);
+			error = NULL;
+			g_free(c);
+			c = store;
+		}
+		return c ? c : x;
+	} else
+		return x;
+}
+
--- a/finch/libgnt/gntmenu.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntmenu.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntmenu.h"
 #include "gntmenuitemcheck.h"
 
@@ -92,7 +93,7 @@
 			item->priv.x = getcurx(widget->window) + widget->priv.x;
 			item->priv.y = getcury(widget->window) + widget->priv.y + 1;
 			wbkgdset(widget->window, type);
-			wprintw(widget->window, " %s   ", item->text);
+			wprintw(widget->window, " %s   ", C_(item->text));
 		}
 	} else {
 		org_draw(widget);
--- a/finch/libgnt/gntmenu.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntmenu.h	Wed Jun 13 19:30:27 2012 -0400
@@ -63,7 +63,7 @@
 {
 	GntTree parent;
 	GntMenuType type;
-	
+
 	GList *list;
 	int selected;
 
--- a/finch/libgnt/gntmenuitem.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntmenuitem.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntmenu.h"
 #include "gntmenuitem.h"
 
--- a/finch/libgnt/gntmenuitemcheck.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntmenuitemcheck.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntmenuitemcheck.h"
 
 static GntMenuItemClass *parent_class = NULL;
--- a/finch/libgnt/gntprogressbar.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntprogressbar.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  **/
 
+#include "gntinternal.h"
 #include "gntprogressbar.h"
 #include "gntutils.h"
 
@@ -167,7 +168,7 @@
 			0,                            /* n_preallocs */
 			gnt_progress_bar_init,        /* instance_init */
 			NULL                          /* value_table */
-		}; 
+		};
 
 		type = g_type_register_static (GNT_TYPE_WIDGET, "GntProgressBar", &info, 0);
 	}
--- a/finch/libgnt/gntprogressbar.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntprogressbar.h	Wed Jun 13 19:30:27 2012 -0400
@@ -37,7 +37,7 @@
 #define GNT_IS_PROGRESS_BAR_CLASS(k)   (G_TYPE_CHECK_CLASS_TYPE ((k), GNT_TYPE_PROGRESS_BAR))
 #define GNT_PROGRESS_BAR_GET_CLASS(o)  (G_TYPE_INSTANCE_GET_CLASS ((o), GNT_TYPE_PROGRESS_BAR, GntProgressBarClass))
 
-typedef enum _GntProgressBarOrientation
+typedef enum
 {
    GNT_PROGRESS_LEFT_TO_RIGHT,
    GNT_PROGRESS_RIGHT_TO_LEFT,
--- a/finch/libgnt/gntslider.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntslider.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntcolors.h"
 #include "gntkeys.h"
 #include "gntslider.h"
--- a/finch/libgnt/gntslider.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntslider.h	Wed Jun 13 19:30:27 2012 -0400
@@ -109,7 +109,7 @@
 
 /**
  * Sets the amount of change at each step.
- * 
+ *
  * @param slider  The slider
  * @param step    The amount for each step
  *
@@ -119,7 +119,7 @@
 
 /**
  * Sets the amount of change a small step.
- * 
+ *
  * @param slider  The slider
  * @param step    The amount for a small step (for the slider)
  *
@@ -129,7 +129,7 @@
 
 /**
  * Sets the amount of change a large step.
- * 
+ *
  * @param slider  The slider
  * @param step    The amount for a large step (for the slider)
  *
--- a/finch/libgnt/gntstyle.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntstyle.c	Wed Jun 13 19:30:27 2012 -0400
@@ -105,7 +105,7 @@
 
 	if (bool_styles[style] != -1)
 		return bool_styles[style];
-	
+
 	str = gnt_style_get(style);
 
 	bool_styles[style] = str ? gnt_style_parse_bool(str) : def;
@@ -226,7 +226,7 @@
 	{
 		gsize len = 0;
 		char **keys;
-		
+
 		keys = g_key_file_get_keys(gkfile, name, &len, &error);
 		if (error)
 		{
@@ -280,7 +280,7 @@
 	{
 		gsize len = 0;
 		char **keys;
-		
+
 		keys = g_key_file_get_keys(gkfile, kname, &len, &error);
 		if (error)
 		{
@@ -331,14 +331,14 @@
 #if GLIB_CHECK_VERSION(2,6,0)
 	char *name;
 	GError *error = NULL;
-	
+
 	name = g_strdup_printf("%s::remap", g_type_name(type));
 
 	if (g_key_file_has_group(gkfile, name))
 	{
 		gsize len = 0;
 		char **keys;
-		
+
 		keys = g_key_file_get_keys(gkfile, name, &len, &error);
 		if (error)
 		{
--- a/finch/libgnt/gnttextview.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gnttextview.c	Wed Jun 13 19:30:27 2012 -0400
@@ -67,6 +67,12 @@
 
 static void reset_text_view(GntTextView *view);
 
+static gboolean
+text_view_contains(GntTextView *view, const char *str)
+{
+	return (str >= view->string->str && str < view->string->str + view->string->len);
+}
+
 static void
 gnt_text_view_draw(GntWidget *widget)
 {
@@ -109,10 +115,10 @@
 			char back = *end;
 			chtype fl = seg->flags;
 			*end = '\0';
-			if (select_start < view->string->str + seg->start && select_end > view->string->str + seg->end) {
+			if (select_start && select_start < view->string->str + seg->start && select_end > view->string->str + seg->end) {
 				fl |= A_REVERSE;
 				wattrset(widget->window, fl);
-				wprintw(widget->window, "%s", (view->string->str + seg->start));
+				wprintw(widget->window, "%s", C_(view->string->str + seg->start));
 			} else if (select_start && select_end &&
 				((select_start >= view->string->str + seg->start && select_start <= view->string->str + seg->end) ||
 				(select_end <= view->string->str + seg->end && select_start <= view->string->str + seg->start))) {
@@ -126,13 +132,13 @@
 						fl = seg->flags;
 					str = g_strndup(cur, last - cur);
 					wattrset(widget->window, fl);
-					waddstr(widget->window, str);
+					waddstr(widget->window, C_(str));
 					g_free(str);
 					cur = g_utf8_next_char(cur);
 				}
 			} else {
 				wattrset(widget->window, fl);
-				wprintw(widget->window, "%s", (view->string->str + seg->start));
+				wprintw(widget->window, "%s", C_(view->string->str + seg->start));
 			}
 			*end = back;
 		}
@@ -159,7 +165,7 @@
 
 		if (showing + position > rows)
 			position = rows - showing;
-		
+
 		if (showing + position == rows && view->list && view->list->prev)
 			position = MAX(1, rows - 1 - showing);
 		else if (showing + position < rows && view->list && !view->list->prev)
@@ -326,9 +332,10 @@
 		select_start = gnt_text_view_get_p(GNT_TEXT_VIEW(widget), x - widget->priv.x, y - widget->priv.y);
 		g_timeout_add(500, too_slow, NULL);
 	} else if (event == GNT_MOUSE_UP) {
-		if (select_start) {
+		GntTextView *view = GNT_TEXT_VIEW(widget);
+		if (text_view_contains(view, select_start)) {
 			GString *clip;
-			select_end = gnt_text_view_get_p(GNT_TEXT_VIEW(widget), x - widget->priv.x, y - widget->priv.y);
+			select_end = gnt_text_view_get_p(view, x - widget->priv.x, y - widget->priv.y);
 			if (select_end < select_start) {
 				gchar *t = select_start;
 				select_start = select_end;
@@ -336,7 +343,7 @@
 			}
 			if (select_start == select_end) {
 				if (double_click) {
-					clip = select_word_text(GNT_TEXT_VIEW(widget), select_start);
+					clip = select_word_text(view, select_start);
 					double_click = FALSE;
 				} else {
 					double_click = TRUE;
@@ -449,7 +456,7 @@
 	GntTextView *view = GNT_TEXT_VIEW(widget);
 	GntTextLine *line = g_new0(GntTextLine, 1);
 
-	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW | 
+	GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW |
             GNT_WIDGET_GROW_Y | GNT_WIDGET_GROW_X);
 	widget->priv.minw = 5;
 	widget->priv.minh = 2;
@@ -629,7 +636,7 @@
 			list = g_list_last(view->list);
 		view->list = list;
 	}
-		
+
 	gnt_widget_draw(GNT_WIDGET(view));
 }
 
@@ -637,7 +644,7 @@
 {
 	GntTextLine *line = g_new0(GntTextLine, 1);
 	GList *list = view->list;
-	
+
 	view->list = g_list_prepend(g_list_first(view->list), line);
 	view->list = list;
 	gnt_widget_draw(GNT_WIDGET(view));
@@ -704,7 +711,7 @@
 int gnt_text_view_get_lines_above(GntTextView *view)
 {
 	int above = 0;
-	GList *list = view->list;
+	GList *list;
 	list = g_list_nth(view->list, GNT_WIDGET(view)->priv.height);
 	if (!list)
 		return 0;
@@ -767,6 +774,7 @@
 							line->segments = g_list_delete_link(line->segments, segs);
 							if (line->segments == NULL) {
 								free_text_line(line, NULL);
+								line = NULL;
 								if (view->list == iter) {
 									if (inext)
 										view->list = inext;
@@ -780,7 +788,8 @@
 							seg->start = tag->start;
 							seg->end = tag->end - change;
 						}
-						line->length -= change;
+						if (line)
+							line->length -= change;
 						/* XXX: Make things work if the tagged text spans over several lines. */
 					} else {
 						/* XXX: handle the rest of the conditions */
--- a/finch/libgnt/gnttree.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gnttree.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntmarshal.h"
 #include "gntstyle.h"
 #include "gnttree.h"
@@ -274,7 +275,7 @@
 	return get_root_distance(get_prev(row)) + 1;
 }
 
-/* Returns the distance between a and b. 
+/* Returns the distance between a and b.
  * If a is 'above' b, then the distance is positive */
 static int
 get_distance(GntTreeRow *a, GntTreeRow *b)
@@ -549,7 +550,7 @@
 		}
 
 		wbkgdset(widget->window, '\0' | attr);
-		mvwaddstr(widget->window, i, pos, str);
+		mvwaddstr(widget->window, i, pos, C_(str));
 		whline(widget->window, ' ', scrcol - wr);
 		tree->bottom = row;
 		g_free(str);
@@ -626,7 +627,7 @@
 	GntTree *tree = GNT_TREE(widget);
 
 	redraw_tree(tree);
-	
+
 	GNTDEBUG;
 }
 
@@ -815,7 +816,7 @@
 		gnt_widget_activate(widget);
 	} else if (tree->priv->search) {
 		gboolean changed = TRUE;
-		if (isalnum(*text)) {
+		if (g_unichar_isprint(*text)) {
 			tree->priv->search = g_string_append_c(tree->priv->search, *text);
 		} else if (g_utf8_collate(text, GNT_KEY_BACKSPACE) == 0) {
 			if (tree->priv->search->len)
@@ -956,6 +957,45 @@
 	return TRUE;
 }
 
+static gboolean
+move_first_action(GntBindable *bind, GList *null)
+{
+	GntTree *tree = GNT_TREE(bind);
+	GntTreeRow *row = tree->root;
+	GntTreeRow *old = tree->current;
+	if (row && !row_matches_search(row))
+		row = get_next(row);
+	if (row) {
+		tree->current = row;
+		redraw_tree(tree);
+		if (old != tree->current)
+			tree_selection_changed(tree, old, tree->current);
+	}
+
+	return TRUE;
+}
+
+static gboolean
+move_last_action(GntBindable *bind, GList *null)
+{
+	GntTree *tree = GNT_TREE(bind);
+	GntTreeRow *old = tree->current;
+	GntTreeRow *row = tree->bottom;
+	GntTreeRow *next;
+
+	while ((next = get_next(row)))
+		row = next;
+
+	if (row) {
+		tree->current = row;
+		redraw_tree(tree);
+		if (old != tree->current)
+			tree_selection_changed(tree, old, tree->current);
+	}
+
+	return TRUE;
+}
+
 static void
 gnt_tree_set_property(GObject *obj, guint prop_id, const GValue *value,
 		GParamSpec *spec)
@@ -1026,7 +1066,7 @@
 			)
 		);
 
-	signals[SIG_SELECTION_CHANGED] = 
+	signals[SIG_SELECTION_CHANGED] =
 		g_signal_new("selection-changed",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1034,7 +1074,7 @@
 					 NULL, NULL,
 					 gnt_closure_marshal_VOID__POINTER_POINTER,
 					 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
-	signals[SIG_SCROLLED] = 
+	signals[SIG_SCROLLED] =
 		g_signal_new("scrolled",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1042,7 +1082,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__INT,
 					 G_TYPE_NONE, 1, G_TYPE_INT);
-	signals[SIG_TOGGLED] = 
+	signals[SIG_TOGGLED] =
 		g_signal_new("toggled",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1050,7 +1090,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__POINTER,
 					 G_TYPE_NONE, 1, G_TYPE_POINTER);
-	signals[SIG_COLLAPSED] = 
+	signals[SIG_COLLAPSED] =
 		g_signal_new("collapse-toggled",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1075,6 +1115,10 @@
 				"/", NULL);
 	gnt_bindable_class_register_action(bindable, "end-search", end_search_action,
 				"\033", NULL);
+	gnt_bindable_class_register_action(bindable, "move-first", move_first_action,
+			GNT_KEY_HOME, NULL);
+	gnt_bindable_class_register_action(bindable, "move-last", move_last_action,
+			GNT_KEY_END, NULL);
 
 	gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable);
 	GNTDEBUG;
@@ -1302,6 +1346,10 @@
 {
 	GntTreeRow *pr = NULL;
 
+	if (g_hash_table_lookup(tree->hash, key)) {
+		gnt_tree_remove(tree, key);
+	}
+
 	row->tree = tree;
 	row->key = key;
 	row->data = NULL;
@@ -1336,7 +1384,7 @@
 			}
 		}
 
-		if (pr == NULL && parent)	
+		if (pr == NULL && parent)
 		{
 			pr = g_hash_table_lookup(tree->hash, parent);
 			if (pr)
@@ -1516,7 +1564,7 @@
 	GntTreeCol *col;
 
 	g_return_if_fail(colno < tree->ncol);
-	
+
 	row = g_hash_table_lookup(tree->hash, key);
 	if (row)
 	{
@@ -1553,7 +1601,7 @@
 				while (r->next)
 					r = r->next;
 				bigbro = r->key;
-			} 
+			}
 		}
 	}
 	row = gnt_tree_add_row_after(tree, key, row, parent, bigbro);
@@ -1837,7 +1885,7 @@
 void gnt_tree_set_column_is_binary(GntTree *tree, int col, gboolean bin)
 {
 	g_return_if_fail(col < tree->ncol);
-	set_column_flag(tree, col, GNT_TREE_COLUMN_FIXED_SIZE, bin);
+	set_column_flag(tree, col, GNT_TREE_COLUMN_BINARY_DATA, bin);
 }
 
 void gnt_tree_set_column_is_right_aligned(GntTree *tree, int col, gboolean right)
@@ -1878,3 +1926,33 @@
 	return (row && row->parent) ? row->parent->key : NULL;
 }
 
+gpointer gnt_tree_row_get_key(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->key;
+}
+
+GntTreeRow * gnt_tree_row_get_next(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->next;
+}
+
+GntTreeRow * gnt_tree_row_get_prev(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->prev;
+}
+
+GntTreeRow * gnt_tree_row_get_child(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->child;
+}
+
+GntTreeRow * gnt_tree_row_get_parent(GntTree *tree, GntTreeRow *row)
+{
+	g_return_val_if_fail(row && row->tree == tree, NULL);
+	return row->parent;
+}
+
--- a/finch/libgnt/gnttree.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gnttree.h	Wed Jun 13 19:30:27 2012 -0400
@@ -47,7 +47,7 @@
 typedef struct _GntTreeRow		GntTreeRow;
 typedef struct _GntTreeCol		GntTreeCol;
 
-typedef enum _GntTreeColumnFlag {
+typedef enum {
 	GNT_TREE_COLUMN_INVISIBLE    = 1 << 0,
 	GNT_TREE_COLUMN_FIXED_SIZE   = 1 << 1,
 	GNT_TREE_COLUMN_BINARY_DATA  = 1 << 2,
@@ -62,9 +62,9 @@
 
 	GntTreeRow *top;        /* The topmost visible item */
 	GntTreeRow *bottom;     /* The bottommost visible item */
-	
+
 	GntTreeRow *root;       /* The root of all evil */
-	
+
 	GList *list;            /* List of GntTreeRow s */
 	GHashTable *hash;       /* We need this for quickly referencing the rows */
 	guint (*hash_func)(gconstpointer);
@@ -216,12 +216,67 @@
  * @return A list of texts of a row. The list and its data should be
  *         freed by the caller. The caller should make sure that if
  *         any column of the tree contains binary data, it's not freed.
- * @see gnt_tree_get_selection_text_list 
+ * @see gnt_tree_get_selection_text_list
  * @see gnt_tree_get_selection_text
  */
 GList * gnt_tree_get_row_text_list(GntTree *tree, gpointer key);
 
 /**
+ * Get the key of a row.
+ *
+ * @param tree   The tree
+ * @param row    The GntTreeRow object
+ *
+ * @return The key of the row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+gpointer gnt_tree_row_get_key(GntTree *tree, GntTreeRow *row);
+
+/**
+ * Get the next row.
+ *
+ * @param tree The tree
+ * @param row  The GntTreeRow object
+ *
+ * @return The next row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+GntTreeRow * gnt_tree_row_get_next(GntTree *tree, GntTreeRow *row);
+
+/**
+ * Get the previous row.
+ *
+ * @param tree The tree
+ * @param row  The GntTreeRow object
+ *
+ * @return The previous row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+GntTreeRow * gnt_tree_row_get_prev(GntTree *tree, GntTreeRow *row);
+
+/**
+ * Get the child row.
+ *
+ * @param tree The tree
+ * @param row  The GntTreeRow object
+ *
+ * @return The child row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+GntTreeRow * gnt_tree_row_get_child(GntTree *tree, GntTreeRow *row);
+
+/**
+ * Get the parent row.
+ *
+ * @param tree The tree
+ * @param row  The GntTreeRow object
+ *
+ * @return The parent row.
+ * @since 2.8.0 (gnt), 2.7.2 (pidgin)
+ */
+GntTreeRow * gnt_tree_row_get_parent(GntTree *tree, GntTreeRow *row);
+
+/**
  * Get a list of text of the current row.
  *
  * @param tree  The tree
@@ -427,7 +482,7 @@
  * @param func  The comparison function, which is used to compare
  *              the keys
  *
- * @see gnt_tree_sort_row 
+ * @see gnt_tree_sort_row
  */
 void gnt_tree_set_compare_func(GntTree *tree, GCompareFunc func);
 
@@ -480,7 +535,7 @@
  * Set whether a column is visible or not.
  * This can be useful when, for example, we want to store some data
  * which we don't want/need to display.
- * 
+ *
  * @param tree  The tree
  * @param col   The index of the column
  * @param vis   If @c FALSE, the column will not be displayed
@@ -490,7 +545,7 @@
 /**
  * Set whether a column can be resized to keep the same ratio when the
  * tree is resized.
- * 
+ *
  * @param tree  The tree
  * @param col   The index of the column
  * @param res   If @c FALSE, the column will not be resized when the
--- a/finch/libgnt/gntutils.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntutils.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "config.h"
+
 #include "gntinternal.h"
 #undef GNT_LOG_DOMAIN
 #define GNT_LOG_DOMAIN "Utils"
@@ -35,8 +37,6 @@
 #include "gntutils.h"
 #include "gntwindow.h"
 
-#include "config.h"
-
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
@@ -46,8 +46,6 @@
 #include <libxml/tree.h>
 #endif
 
-#include "config.h"
-
 void gnt_util_get_text_bound(const char *text, int *width, int *height)
 {
 	const char *s = text, *last;
@@ -374,7 +372,7 @@
 	gnt_widget_from_xmlnode(node, data, num);
 
 	xmlFreeDoc(doc);
-	xmlCleanupParser();
+	xmlFreeParserCtxt(ctxt);
 	va_end(list);
 	g_free(data);
 #endif
@@ -387,7 +385,6 @@
 	const char *name;
 	char *content;
 	xmlNode *ch;
-	gboolean processed = FALSE;
 	char *url = NULL;
 	gboolean insert_nl_s = FALSE, insert_nl_e = FALSE;
 
@@ -428,17 +425,14 @@
 
 	for (ch = node->children; ch; ch = ch->next) {
 		if (ch->type == XML_ELEMENT_NODE) {
-			processed = TRUE;
 			util_parse_html_to_tv(ch, tv, flag);
+		} else if (ch->type == XML_TEXT_NODE) {
+			content = (char*)xmlNodeGetContent(ch);
+			gnt_text_view_append_text_with_flags(tv, content, flag);
+			xmlFree(content);
 		}
 	}
 
-	if (!processed) {
-		content = (char*)xmlNodeGetContent(node);
-		gnt_text_view_append_text_with_flags(tv, content, flag);
-		xmlFree(content);
-	}
-
 	if (url) {
 		char *href = g_strdup_printf(" (%s)", url);
 		gnt_text_view_append_text_with_flags(tv, href, flag);
@@ -470,7 +464,7 @@
 		xmlFreeDoc(doc);
 		ret = TRUE;
 	}
-	xmlCleanupParser();
+	xmlFreeParserCtxt(ctxt);
 	return ret;
 #endif
 }
--- a/finch/libgnt/gntutils.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntutils.h	Wed Jun 13 19:30:27 2012 -0400
@@ -66,7 +66,7 @@
 const char *gnt_util_onscreen_width_to_pointer(const char *str, int len, int *w);
 
 /**
- * Inserts newlines in 'string' where necessary so that its onscreen width is 
+ * Inserts newlines in 'string' where necessary so that its onscreen width is
  * no more than 'maxw'.
  *
  * @param string  The string.
@@ -107,7 +107,7 @@
 
 /**
  * Get a helpful display about the bindings of a widget.
- * 
+ *
  * @param widget The widget to get bindings for.
  *
  * @return Returns a GntTree populated with "key" -> "binding" for the widget.
@@ -126,7 +126,7 @@
  *      </vwindow>",
  *   2, &win, &button);
  * @endcode
- * 
+ *
  * @param string  The XML string.
  * @param num     The number of widgets to return, followed by 'num' GntWidget **
  */
--- a/finch/libgnt/gntwidget.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntwidget.c	Wed Jun 13 19:30:27 2012 -0400
@@ -22,6 +22,7 @@
 
 /* Stuff brutally ripped from Gflib */
 
+#include "gntinternal.h"
 #include "gntwidget.h"
 #include "gntstyle.h"
 #include "gntmarshal.h"
@@ -125,12 +126,12 @@
 	klass->lost_focus = gnt_widget_focus_change;
 	klass->gained_focus = gnt_widget_focus_change;
 	klass->confirm_size = gnt_widget_dummy_confirm_size;
-	
+
 	klass->key_pressed = NULL;
 	klass->activate = NULL;
 	klass->clicked = NULL;
-	
-	signals[SIG_DESTROY] = 
+
+	signals[SIG_DESTROY] =
 		g_signal_new("destroy",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -138,7 +139,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
-	signals[SIG_GIVE_FOCUS] = 
+	signals[SIG_GIVE_FOCUS] =
 		g_signal_new("gained-focus",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -146,7 +147,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
-	signals[SIG_LOST_FOCUS] = 
+	signals[SIG_LOST_FOCUS] =
 		g_signal_new("lost-focus",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -154,7 +155,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
-	signals[SIG_ACTIVATE] = 
+	signals[SIG_ACTIVATE] =
 		g_signal_new("activate",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -162,7 +163,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
-	signals[SIG_MAP] = 
+	signals[SIG_MAP] =
 		g_signal_new("map",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -170,7 +171,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
-	signals[SIG_DRAW] = 
+	signals[SIG_DRAW] =
 		g_signal_new("draw",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -178,7 +179,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
-	signals[SIG_HIDE] = 
+	signals[SIG_HIDE] =
 		g_signal_new("hide",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -186,7 +187,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
-	signals[SIG_EXPOSE] = 
+	signals[SIG_EXPOSE] =
 		g_signal_new("expose",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -194,7 +195,7 @@
 					 NULL, NULL,
 					 gnt_closure_marshal_VOID__INT_INT_INT_INT,
 					 G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
-	signals[SIG_POSITION] = 
+	signals[SIG_POSITION] =
 		g_signal_new("position-set",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -202,7 +203,7 @@
 					 NULL, NULL,
 					 gnt_closure_marshal_VOID__INT_INT,
 					 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
-	signals[SIG_SIZE_REQUEST] = 
+	signals[SIG_SIZE_REQUEST] =
 		g_signal_new("size_request",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -210,7 +211,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
-	signals[SIG_SIZE_CHANGED] = 
+	signals[SIG_SIZE_CHANGED] =
 		g_signal_new("size_changed",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -218,7 +219,7 @@
 					 NULL, NULL,
 					 gnt_closure_marshal_VOID__INT_INT,
 					 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
-	signals[SIG_CONFIRM_SIZE] = 
+	signals[SIG_CONFIRM_SIZE] =
 		g_signal_new("confirm_size",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -226,7 +227,7 @@
 					 NULL, NULL,
 					 gnt_closure_marshal_BOOLEAN__INT_INT,
 					 G_TYPE_BOOLEAN, 2, G_TYPE_INT, G_TYPE_INT);
-	signals[SIG_KEY_PRESSED] = 
+	signals[SIG_KEY_PRESSED] =
 		g_signal_new("key_pressed",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -235,7 +236,7 @@
 					 gnt_closure_marshal_BOOLEAN__STRING,
 					 G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
 
-	signals[SIG_CLICKED] = 
+	signals[SIG_CLICKED] =
 		g_signal_new("clicked",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -244,7 +245,7 @@
 					 gnt_closure_marshal_BOOLEAN__INT_INT_INT,
 					 G_TYPE_BOOLEAN, 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
 
-	signals[SIG_CONTEXT_MENU] = 
+	signals[SIG_CONTEXT_MENU] =
 		g_signal_new("context-menu",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
--- a/finch/libgnt/gntwidget.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntwidget.h	Wed Jun 13 19:30:27 2012 -0400
@@ -49,7 +49,7 @@
 typedef struct _GntWidgetPriv		GntWidgetPriv;
 typedef struct _GntWidgetClass		GntWidgetClass;
 
-typedef enum _GntWidgetFlags
+typedef enum
 {
 	GNT_WIDGET_DESTROYING     = 1 << 0,
 	GNT_WIDGET_CAN_TAKE_FOCUS = 1 << 1,
@@ -69,7 +69,7 @@
 } GntWidgetFlags;
 
 /* XXX: This will probably move elsewhere */
-typedef enum _GntMouseEvent
+typedef enum
 {
 	GNT_LEFT_MOUSE_DOWN = 1,
 	GNT_RIGHT_MOUSE_DOWN,
@@ -80,7 +80,7 @@
 } GntMouseEvent;
 
 /* XXX: I'll have to ask grim what he's using this for in guifications. */
-typedef enum _GntParamFlags
+typedef enum
 {
 	GNT_PARAM_SERIALIZABLE	= 1 << G_PARAM_USER_SHIFT
 } GntParamFlags;
--- a/finch/libgnt/gntwindow.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntwindow.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#include "gntinternal.h"
 #include "gntstyle.h"
 #include "gntwindow.h"
 
--- a/finch/libgnt/gntwm.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntwm.c	Wed Jun 13 19:30:27 2012 -0400
@@ -24,12 +24,17 @@
 
 #ifdef USE_PYTHON
 #include <Python.h>
-#else
+#endif
+
+/* Python.h may define _GNU_SOURCE and _XOPEN_SOURCE_EXTENDED, so protect
+ * these checks with #ifndef/!defined() */
+#ifndef _GNU_SOURCE
 #define _GNU_SOURCE
-#if (defined(__APPLE__) || defined(__unix__)) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+#endif
+
+#if !defined _XOPEN_SOURCE_EXTENDED && (defined(__APPLE__) || defined(__unix__)) && !defined(__FreeBSD__)
 #define _XOPEN_SOURCE_EXTENDED
 #endif
-#endif
 
 #include <glib.h>
 #if GLIB_CHECK_VERSION(2,6,0)
@@ -151,7 +156,7 @@
 /**
  * The following is a workaround for a bug in most versions of ncursesw.
  * Read about it in: http://article.gmane.org/gmane.comp.lib.ncurses.bugs/2751
- * 
+ *
  * In short, if a panel hides one cell of a multi-cell character, then the rest
  * of the characters in that line get screwed. The workaround here is to erase
  * any such character preemptively.
@@ -665,7 +670,7 @@
 	tree = wm->windows->tree;
 
 	gnt_box_set_title(GNT_BOX(win), workspace ? "Workspace List" : "Window List");
-	
+
 	populate_window_list(wm, workspace);
 
 	if (wm->cws->ordered)
@@ -762,7 +767,7 @@
 				{  \
 					fprintf(file, "%s", end);  \
 				}  \
-			} while (0) 
+			} while (0)
 
 			CHECK(A_BOLD, "<b>", "</b>");
 			CHECK(A_UNDERLINE, "<u>", "</u>");
@@ -800,7 +805,7 @@
 				ADJUST(bg.r);
 				ADJUST(bg.b);
 				ADJUST(bg.g);
-				
+
 				if (x) fprintf(file, "</span>");
 				fprintf(file, "<span style=\"background:#%02x%02x%02x;color:#%02x%02x%02x\">",
 						bg.r, bg.g, bg.b, fg.r, fg.g, fg.b);
@@ -889,6 +894,13 @@
 	all = g_list_delete_link(all, list);
 	wm->cws->list = all;
 	gnt_ws_draw_taskbar(wm->cws, FALSE);
+	if (wm->cws->ordered) {
+		GntWidget *w = wm->cws->ordered->data;
+		GntNode *node = g_hash_table_lookup(wm->nodes, w);
+		top_panel(node->panel);
+		update_panels();
+		doupdate();
+	}
 }
 
 static gboolean
@@ -909,7 +921,7 @@
 shift_right(GntBindable *bindable, GList *null)
 {
 	GntWM *wm = GNT_WM(bindable);
-	
+
 	if (wm->_list.window)
 		return TRUE;
 
@@ -1025,7 +1037,7 @@
 
 	if (GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_NO_BORDER))
 		return;
-	
+
 	d = win->window;
 	gnt_widget_get_size(win, &w, &h);
 
@@ -1106,12 +1118,20 @@
 refresh_screen(GntBindable *bindable, GList *null)
 {
 	GntWM *wm = GNT_WM(bindable);
+	GList *iter;
 
 	endwin();
 	refresh();
 
 	g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, GINT_TO_POINTER(TRUE));
 	g_signal_emit(wm, signals[SIG_TERMINAL_REFRESH], 0);
+
+	for (iter = g_list_last(wm->cws->ordered); iter; iter = iter->prev) {
+		GntWidget *w = iter->data;
+		GntNode *node = g_hash_table_lookup(wm->nodes, w);
+		top_panel(node->panel);
+	}
+
 	gnt_ws_draw_taskbar(wm->cws, TRUE);
 	update_screen(wm);
 	curs_set(0);   /* endwin resets the cursor to normal */
@@ -1390,8 +1410,8 @@
 	klass->key_pressed  = NULL;
 	klass->mouse_clicked = NULL;
 	klass->give_focus = gnt_wm_give_focus;
-	
-	signals[SIG_NEW_WIN] = 
+
+	signals[SIG_NEW_WIN] =
 		g_signal_new("new_win",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1399,7 +1419,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__POINTER,
 					 G_TYPE_NONE, 1, G_TYPE_POINTER);
-	signals[SIG_DECORATE_WIN] = 
+	signals[SIG_DECORATE_WIN] =
 		g_signal_new("decorate_win",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1407,7 +1427,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__POINTER,
 					 G_TYPE_NONE, 1, G_TYPE_POINTER);
-	signals[SIG_CLOSE_WIN] = 
+	signals[SIG_CLOSE_WIN] =
 		g_signal_new("close_win",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1415,7 +1435,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__POINTER,
 					 G_TYPE_NONE, 1, G_TYPE_POINTER);
-	signals[SIG_CONFIRM_RESIZE] = 
+	signals[SIG_CONFIRM_RESIZE] =
 		g_signal_new("confirm_resize",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1424,7 +1444,7 @@
 					 gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER,
 					 G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
 
-	signals[SIG_CONFIRM_MOVE] = 
+	signals[SIG_CONFIRM_MOVE] =
 		g_signal_new("confirm_move",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1433,7 +1453,7 @@
 					 gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER,
 					 G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER);
 
-	signals[SIG_RESIZED] = 
+	signals[SIG_RESIZED] =
 		g_signal_new("window_resized",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1441,7 +1461,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__POINTER,
 					 G_TYPE_NONE, 1, G_TYPE_POINTER);
-	signals[SIG_MOVED] = 
+	signals[SIG_MOVED] =
 		g_signal_new("window_moved",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1449,7 +1469,7 @@
 					 NULL, NULL,
 					 g_cclosure_marshal_VOID__POINTER,
 					 G_TYPE_NONE, 1, G_TYPE_POINTER);
-	signals[SIG_UPDATE_WIN] = 
+	signals[SIG_UPDATE_WIN] =
 		g_signal_new("window_update",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1458,7 +1478,7 @@
 					 g_cclosure_marshal_VOID__POINTER,
 					 G_TYPE_NONE, 1, G_TYPE_POINTER);
 
-	signals[SIG_GIVE_FOCUS] = 
+	signals[SIG_GIVE_FOCUS] =
 		g_signal_new("give_focus",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1467,7 +1487,7 @@
 					 g_cclosure_marshal_VOID__POINTER,
 					 G_TYPE_NONE, 1, G_TYPE_POINTER);
 
-	signals[SIG_MOUSE_CLICK] = 
+	signals[SIG_MOUSE_CLICK] =
 		g_signal_new("mouse_clicked",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
@@ -1476,7 +1496,7 @@
 					 gnt_closure_marshal_BOOLEAN__INT_INT_INT_POINTER,
 					 G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_POINTER);
 
-	signals[SIG_TERMINAL_REFRESH] = 
+	signals[SIG_TERMINAL_REFRESH] =
 		g_signal_new("terminal-refresh",
 					 G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST,
--- a/finch/libgnt/gntwm.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntwm.h	Wed Jun 13 19:30:27 2012 -0400
@@ -41,7 +41,7 @@
 #define GNT_IS_WM_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_WM))
 #define GNT_WM_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_WM, GntWMClass))
 
-typedef enum _GntKeyPressMode
+typedef enum
 {
 	GNT_KP_MODE_NORMAL,
 	GNT_KP_MODE_RESIZE,
--- a/finch/libgnt/gntws.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntws.c	Wed Jun 13 19:30:27 2012 -0400
@@ -1,5 +1,28 @@
+/*
+ * GNT - The GLib Ncurses Toolkit
+ *
+ * GNT 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 library 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 <gmodule.h>
 
+#include "gntinternal.h"
 #include "gntbox.h"
 #include "gntwidget.h"
 #include "gntwindow.h"
@@ -73,7 +96,7 @@
 		else
 			mvwhline(taskbar, 0, width * i, ' ' | gnt_color_pair(color), getmaxx(stdscr) - width * i);
 		title = GNT_BOX(w)->title;
-		mvwprintw(taskbar, 0, width * i, "%s", title ? title : "<gnt>");
+		mvwprintw(taskbar, 0, width * i, "%s", title ? C_(title) : "<gnt>");
 		if (i)
 			mvwaddch(taskbar, 0, width *i - 1, ACS_VLINE | A_STANDOUT | gnt_color_pair(GNT_COLOR_NORMAL));
 	}
--- a/finch/libgnt/gntws.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/gntws.h	Wed Jun 13 19:30:27 2012 -0400
@@ -46,7 +46,7 @@
 	GList *list;
 	GList *ordered;
 	gpointer ui_data;
-	
+
 	void *res1;
 	void *res2;
 	void *res3;
--- a/finch/libgnt/test/combo.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/test/combo.c	Wed Jun 13 19:30:27 2012 -0400
@@ -37,7 +37,7 @@
 	freopen(".error", "w", stderr);
 	gnt_init();
 #endif
-	
+
 	box = gnt_box_new(FALSE, TRUE);
 	gnt_widget_set_name(box, "box");
 	gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID);
--- a/finch/libgnt/test/focus.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/test/focus.c	Wed Jun 13 19:30:27 2012 -0400
@@ -24,7 +24,7 @@
 	freopen(".error", "w", stderr);
 	gnt_init();
 #endif
-	
+
 	GntWidget *label = gnt_label_new("So wassup dudes and dudettes!!\u4e0a1\u6d772\u67003\u4f4e4\u67085\nSo this is, like,\nthe third line!! \\o/");
 	GntWidget *vbox, *hbox, *tree, *box, *button;
 	WINDOW *test;
@@ -78,7 +78,7 @@
 	gnt_box_add_widget(GNT_BOX(vbox), button);
 
 	gnt_box_add_widget(GNT_BOX(hbox), vbox);
-	
+
 	gnt_widget_show(hbox);
 
 #ifdef STANDALONE
--- a/finch/libgnt/test/tv.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/test/tv.c	Wed Jun 13 19:30:27 2012 -0400
@@ -45,7 +45,7 @@
 			return FALSE;
 		return TRUE;
 	}
-		
+
 	return FALSE;
 }
 
--- a/finch/libgnt/test/wm.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/test/wm.c	Wed Jun 13 19:30:27 2012 -0400
@@ -35,7 +35,7 @@
 			gnt_widget_show(widget);
 		}
 	}
-	
+
 	return TRUE;
 }
 
@@ -61,7 +61,7 @@
 	gnt_main();
 
 	gnt_quit();
-	
+
 	return 0;
 }
 
--- a/finch/libgnt/wms/irssi.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/wms/irssi.c	Wed Jun 13 19:30:27 2012 -0400
@@ -33,6 +33,8 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include "gntinternal.h"
+
 #include "gnt.h"
 #include "gntbox.h"
 #include "gntmenu.h"
@@ -205,11 +207,15 @@
 update_conv_window_title(GntNode *node)
 {
 	char title[256];
+	int x, y;
 	snprintf(title, sizeof(title), "%d: %s",
 			GPOINTER_TO_INT(g_object_get_data(G_OBJECT(node->me), "irssi-index")) + 1,
 			GNT_BOX(node->me)->title);
+
+	getyx(node->window, y, x);
 	wbkgdset(node->window, '\0' | COLOR_PAIR(gnt_widget_has_focus(node->me) ? GNT_COLOR_TITLE : GNT_COLOR_TITLE_D));
 	mvwaddstr(node->window, 0, 0, title);
+	wmove(node->window, y, x);
 	if (!gnt_is_refugee()) {
 		update_panels();
 		doupdate();
@@ -294,10 +300,15 @@
 
 	name = gnt_widget_get_name(widget);
 	if (name && strstr(name, "conversation-window")) {
+		int cx, cy, cw, ch;
+		gnt_widget_get_position(widget, &cx, &cy);
+		gnt_widget_get_size(widget, &cw, &ch);
 		find_window_position(irssi, widget, &hor, &vert);
 		get_xywh_for_frame(irssi, hor, vert, &x, &y, &w, &h);
-		gnt_wm_move_window(GNT_WM(irssi), widget, x, y);
-		gnt_wm_resize_window(GNT_WM(irssi), widget, w, h);
+		if (x != cx || y != cy)
+			gnt_wm_move_window(GNT_WM(irssi), widget, x, y);
+		if (w != cw || h != ch)
+			gnt_wm_resize_window(GNT_WM(irssi), widget, w, h);
 	}
 }
 
--- a/finch/libgnt/wms/s.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/libgnt/wms/s.c	Wed Jun 13 19:30:27 2012 -0400
@@ -1,8 +1,8 @@
+#include "internal.h"
+
 #include <string.h>
 #include <sys/types.h>
 
-#include "internal.h"
-
 #include "gnt.h"
 #include "gntbox.h"
 #include "gntmenu.h"
@@ -146,11 +146,11 @@
 	if (!widget)
 		return FALSE;
 		/* This might be a place to bring up a context menu */
-	
+
 	if (event != GNT_LEFT_MOUSE_DOWN ||
 			GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER))
 		return FALSE;
-	
+
 	gnt_widget_get_position(widget, &x, &y);
 	gnt_widget_get_size(widget, &w, &h);
 
--- a/finch/plugins/gntgf.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/plugins/gntgf.c	Wed Jun 13 19:30:27 2012 -0400
@@ -134,7 +134,7 @@
 	ids = getenv("WINDOWID");
 	if (ids == NULL)
 		return;
-	
+
 	id = atoi(ids);
 
 	dpy = XOpenDisplay(NULL);
@@ -168,7 +168,7 @@
 		beep();
 
 	if (conv != NULL) {
-		FinchConv *fc = conv->ui_data;
+		FinchConv *fc = FINCH_CONV(conv);
 		if (gnt_widget_has_focus(fc->window))
 			return;
 	}
@@ -255,8 +255,8 @@
 
 	if (flags & PURPLE_MESSAGE_WHISPER)
 		return;
-	
-	nick = PURPLE_CONV_CHAT(conv)->nick;
+
+	nick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(conv));
 
 	if (g_utf8_collate(sender, nick) == 0)
 		return;
@@ -401,7 +401,7 @@
 {
 	purple_prefs_add_none("/plugins");
 	purple_prefs_add_none("/plugins/gnt");
-	
+
 	purple_prefs_add_none("/plugins/gnt/gntgf");
 	purple_prefs_add_none(PREFS_EVENT);
 
--- a/finch/plugins/gnthistory.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/plugins/gnthistory.c	Wed Jun 13 19:30:27 2012 -0400
@@ -158,7 +158,7 @@
 		while (list) {
 			const char *label = _(list->data);
 			list = g_list_delete_link(list, list);
-			purple_request_field_list_add(field, label, list->data);
+			purple_request_field_list_add_icon(field, label, NULL, list->data);
 			if (system && strcmp(system, list->data) == 0)
 				purple_request_field_list_add_selected(field, label);
 			list = g_list_delete_link(list, list);
--- a/finch/plugins/gnttinyurl.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/plugins/gnttinyurl.c	Wed Jun 13 19:30:27 2012 -0400
@@ -41,7 +41,10 @@
 #include <gntconv.h>
 
 #include <gntplugin.h>
+
+#include <gntlabel.h>
 #include <gnttextview.h>
+#include <gntwindow.h>
 
 static int tag_num = 0;
 
@@ -52,6 +55,8 @@
 	int num;
 } CbInfo;
 
+static void process_urls(PurpleConversation *conv, GList *urls);
+
 /* 3 functions from util.c */
 static gboolean
 badchar(char c)
@@ -83,7 +88,8 @@
 	return FALSE;
 }
 
-static GList *extract_urls(char *text) {
+static GList *extract_urls(const char *text)
+{
 	const char *t, *c, *q = NULL;
 	char *url_buf;
 	GList *ret = NULL;
@@ -142,7 +148,9 @@
 					url_buf = g_strndup(c, t - c);
 					if (!g_list_find_custom(ret, url_buf, (GCompareFunc)strcmp)) {
 						purple_debug_info("TinyURL", "Added URL %s\n", url_buf);
-						ret = g_list_append(ret, g_strdup(url_buf));
+						ret = g_list_append(ret, url_buf);
+					} else {
+						g_free(url_buf);
 					}
 					c = t;
 					break;
@@ -173,6 +181,8 @@
 						if (!g_list_find_custom(ret, url_buf, (GCompareFunc)strcmp)) {
 							purple_debug_info("TinyURL", "Added URL %s\n", url_buf);
 							ret = g_list_append(ret, url_buf);
+						} else {
+							g_free(url_buf);
 						}
 						c = t;
 						break;
@@ -207,10 +217,12 @@
 			gnt_text_view_tag_change(tv, data->tag, str, FALSE);
 			g_free(str);
 			g_free(data->tag);
+			g_free(data);
 			return;
 		}
 	}
 	g_free(data->tag);
+	g_free(data);
 	purple_debug_info("TinyURL", "Conversation no longer exists... :(\n");
 }
 
@@ -219,13 +231,14 @@
 	g_free(data);
 }
 
-static gboolean receiving_msg(PurpleAccount *account, char **sender, char **message,
-				PurpleConversation *conv, PurpleMessageFlags *flags) {
+static gboolean writing_msg(PurpleAccount *account, char *sender, char **message,
+				PurpleConversation *conv, PurpleMessageFlags flags)
+{
 	GString *t;
-	GList *iter, *urls;
+	GList *iter, *urls, *next;
 	int c = 0;
 
-	if (!(*flags & PURPLE_MESSAGE_RECV) || *flags & PURPLE_MESSAGE_INVISIBLE)
+	if ((flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_INVISIBLE)))
 		return FALSE;
 
 	urls = purple_conversation_get_data(conv, "TinyURLs");
@@ -238,7 +251,8 @@
 
 	t = g_string_new(*message);
 	g_free(*message);
-	for (iter = urls; iter; iter = iter->next) {
+	for (iter = urls; iter; iter = next) {
+		next = iter->next;
 		if (g_utf8_strlen((char *)iter->data, -1) >= purple_prefs_get_int(PREF_LENGTH)) {
 			int pos, x = 0;
 			gchar *j, *s, *str, *orig;
@@ -256,36 +270,40 @@
 			g_free(str);
 			continue;
 		} else {
-			if (iter->prev) {
-				iter = iter->prev;
-				g_free(iter->next->data);
-				urls = g_list_delete_link(urls, iter->next);
-			} else {
-				g_free(iter->data);
-				g_list_free(urls);
-				urls = NULL;
-			}
+			g_free(iter->data);
+			urls = g_list_delete_link(urls, iter);
 		}
 	}
 	*message = t->str;
 	g_string_free(t, FALSE);
 	if (conv == NULL)
-		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, *sender);
+		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender);
 	purple_conversation_set_data(conv, "TinyURLs", urls);
 	return FALSE;
 }
 
-static void received_msg(PurpleAccount *account, char *sender, char *message,
-				PurpleConversation *conv, PurpleMessageFlags flags) {
+static void wrote_msg(PurpleAccount *account, char *sender, char *message,
+				PurpleConversation *conv, PurpleMessageFlags flags)
+{
+	GList *urls;
+
+	urls = purple_conversation_get_data(conv, "TinyURLs");
+	if ((flags & PURPLE_MESSAGE_SEND) || urls == NULL)
+		return;
+
+	process_urls(conv, urls);
+	purple_conversation_set_data(conv, "TinyURLs", NULL);
+}
+
+/* Frees 'urls' */
+static void
+process_urls(PurpleConversation *conv, GList *urls)
+{
+	GList *iter;
 	int c;
-	GList *urls, *iter;
 	FinchConv *fconv = FINCH_CONV(conv);
 	GntTextView *tv = GNT_TEXT_VIEW(fconv->tv);
 
-	urls = purple_conversation_get_data(conv, "TinyURLs");
-	if (!(flags & PURPLE_MESSAGE_RECV) || urls == NULL)
-		return;
-
 	for (iter = urls, c = 0; iter; iter = iter->next) {
 		int i;
 		CbInfo *cbdata;
@@ -301,7 +319,7 @@
 			url = g_strdup_printf("%s%s", purple_prefs_get_string(PREF_URL), purple_url_encode(tmp));
 		}
 		g_free(tmp);
-		purple_util_fetch_url(url, TRUE, "finch", FALSE, url_fetched, cbdata);
+		purple_util_fetch_url(url, TRUE, "finch", FALSE, -1, url_fetched, cbdata);
 		i = gnt_text_view_get_lines_below(tv);
 		str = g_strdup_printf(_("\nFetching TinyURL..."));
 		gnt_text_view_append_text_with_tag((tv), str, GNT_TEXT_FLAG_DIM, cbdata->tag);
@@ -312,7 +330,6 @@
 		g_free(url);
 	}
 	g_list_free(urls);
-	purple_conversation_set_data(conv, "TinyURLs", NULL);
 }
 
 static void
@@ -324,20 +341,75 @@
 	g_list_free(urls);
 }
 
+static void tinyurl_notify_fetch_cb(PurpleUtilFetchUrlData *urldata, gpointer cbdata,
+		const gchar *urltext, gsize len, const gchar *error)
+{
+	GntWidget *win = cbdata;
+	GntWidget *label = g_object_get_data(G_OBJECT(win), "info-widget");
+	char *message;
+
+	message = g_strdup_printf(_("TinyURL for above: %s"), urltext);
+	gnt_label_set_text(GNT_LABEL(label), message);
+	g_free(message);
+
+	g_signal_handlers_disconnect_matched(G_OBJECT(win), G_SIGNAL_MATCH_FUNC,
+			0, 0, NULL,
+			G_CALLBACK(purple_util_fetch_url_cancel), NULL);
+}
+
+static void *
+tinyurl_notify_uri(const char *uri)
+{
+	char *fullurl = NULL;
+	GntWidget *win;
+	PurpleUtilFetchUrlData *urlcb;
+
+	/* XXX: The following expects that finch_notify_message gets called. This
+	 * may not always happen, e.g. when another plugin sets its own
+	 * notify_message. So tread carefully. */
+	win = purple_notify_message(NULL, PURPLE_NOTIFY_URI, _("URI"), uri,
+			_("Please wait while TinyURL fetches a shorter URL ..."), NULL, NULL);
+	if (!GNT_IS_WINDOW(win) || !g_object_get_data(G_OBJECT(win), "info-widget"))
+		return win;
+
+	if (g_ascii_strncasecmp(uri, "http://", 7) && g_ascii_strncasecmp(uri, "https://", 8)) {
+		fullurl = g_strdup_printf("%shttp%%3A%%2F%%2F%s",
+				purple_prefs_get_string(PREF_URL), purple_url_encode(uri));
+	} else {
+		fullurl = g_strdup_printf("%s%s", purple_prefs_get_string(PREF_URL),
+				purple_url_encode(uri));
+	}
+
+	/* Store the return value of _fetch_url and destroy that when win is
+	   destroyed, so that the callback for _fetch_url does not try to molest a
+	   non-existent window */
+	urlcb = purple_util_fetch_url(fullurl, TRUE, "finch", FALSE, -1, tinyurl_notify_fetch_cb, win);
+	g_free(fullurl);
+	g_signal_connect_swapped(G_OBJECT(win), "destroy",
+			G_CALLBACK(purple_util_fetch_url_cancel), urlcb);
+
+	return win;
+}
+
 static gboolean
-plugin_load(PurplePlugin *plugin) {
+plugin_load(PurplePlugin *plugin)
+{
+	PurpleNotifyUiOps *ops = purple_notify_get_ui_ops();
+	plugin->extra = ops->notify_uri;
+	ops->notify_uri = tinyurl_notify_uri;
+
 	purple_signal_connect(purple_conversations_get_handle(),
 			"wrote-im-msg",
-			plugin, PURPLE_CALLBACK(received_msg), NULL);
+			plugin, PURPLE_CALLBACK(wrote_msg), NULL);
 	purple_signal_connect(purple_conversations_get_handle(),
 			"wrote-chat-msg",
-			plugin, PURPLE_CALLBACK(received_msg), NULL);
+			plugin, PURPLE_CALLBACK(wrote_msg), NULL);
 	purple_signal_connect(purple_conversations_get_handle(),
-			"receiving-im-msg",
-			plugin, PURPLE_CALLBACK(receiving_msg), NULL);
+			"writing-im-msg",
+			plugin, PURPLE_CALLBACK(writing_msg), NULL);
 	purple_signal_connect(purple_conversations_get_handle(),
-			"receiving-chat-msg",
-			plugin, PURPLE_CALLBACK(receiving_msg), NULL);
+			"writing-chat-msg",
+			plugin, PURPLE_CALLBACK(writing_msg), NULL);
 	purple_signal_connect(purple_conversations_get_handle(),
 			"deleting-conversation",
 			plugin, PURPLE_CALLBACK(free_conv_urls), NULL);
@@ -345,6 +417,15 @@
 	return TRUE;
 }
 
+static gboolean
+plugin_unload(PurplePlugin *plugin)
+{
+	PurpleNotifyUiOps *ops = purple_notify_get_ui_ops();
+	if (ops->notify_uri == tinyurl_notify_uri)
+		ops->notify_uri = plugin->extra;
+	return TRUE;
+}
+
 static PurplePluginPrefFrame *
 get_plugin_pref_frame(PurplePlugin *plugin) {
 
@@ -354,7 +435,7 @@
   frame = purple_plugin_pref_frame_new();
 
   pref = purple_plugin_pref_new_with_name(PREF_LENGTH);
-  purple_plugin_pref_set_label(pref, _("Only create TinyURL for urls"
+  purple_plugin_pref_set_label(pref, _("Only create TinyURL for URLs"
 				     " of this length or greater"));
   purple_plugin_pref_frame_add(frame, pref);
   pref = purple_plugin_pref_new_with_name(PREF_URL);
@@ -390,11 +471,11 @@
 	N_("TinyURL"),
 	DISPLAY_VERSION,
 	N_("TinyURL plugin"),
-	N_("When receiving a message with URL(s), TinyURL for easier copying"),
+	N_("When receiving a message with URL(s), use TinyURL for easier copying"),
 	"Richard Nelson <wabz@whatsbeef.net>",
 	PURPLE_WEBSITE,
 	plugin_load,
-	NULL,
+	plugin_unload,
 	NULL,
 	NULL,
 	NULL,
--- a/finch/plugins/grouping.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/plugins/grouping.c	Wed Jun 13 19:30:27 2012 -0400
@@ -15,7 +15,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA
  */
 
 #define PURPLE_PLUGIN
@@ -54,7 +54,7 @@
 		case PURPLE_BLIST_CONTACT_NODE:
 			{
 				PurpleContact *contact = (PurpleContact*)node;
-				if (contact->currentsize > 0)
+				if (purple_contact_get_contact_size(contact, FALSE) > 0)
 					return TRUE;
 				return FALSE;
 			}
--- a/finch/plugins/lastlog.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/finch/plugins/lastlog.c	Wed Jun 13 19:30:27 2012 -0400
@@ -60,7 +60,7 @@
 static PurpleCmdRet
 lastlog_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer null)
 {
-	FinchConv *ggconv = conv->ui_data;
+	FinchConv *ggconv = FINCH_CONV(conv);
 	char **strings = g_strsplit(GNT_TEXT_VIEW(ggconv->tv)->string->str, "\n", 0);
 	GntWidget *win, *tv;
 	int i, j;
--- a/gaim-uninstalled.pc.in	Wed Jun 13 19:28:57 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-datarootdir=@datarootdir@
-datadir=@datadir@
-sysconfdir=@sysconfdir@
- 
-Name: Pidgin (Gaim compatibility)
-Description: Pidgin is a GTK2-based instant messenger application.
-Version: @VERSION@
-Requires: glib-2.0
-Cflags: -I${pc_top_builddir}/${pcfiledir}/libpurple -I${pc_top_builddir}/${pcfiledir}/pidgin
-Libs: ${pc_top_builddir}/${pcfiledir}/libpurple/libpurple.la
--- a/gaim.pc.in	Wed Jun 13 19:28:57 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-datarootdir=@datarootdir@
-datadir=@datadir@
-sysconfdir=@sysconfdir@
-
-Name: Pidgin (Gaim compatibility)
-Description: Pidgin is a GTK2-based instant messenger application.
-Version: @VERSION@
-Requires: glib-2.0
-Cflags: -I${includedir}/libpurple
-Libs: -L${libdir} -lpurple
--- a/libpurple/Makefile.am	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -9,8 +9,8 @@
 		purple-send-async \
 		purple-url-handler \
 		purple.h.in \
-		purple.pc.in \
-		purple-uninstalled.pc.in \
+		purple-3.pc.in \
+		purple-3-uninstalled.pc.in \
 		version.h.in \
 		Makefile.mingw \
 		win32/global.mak \
@@ -30,9 +30,9 @@
 endif
 
 pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = purple.pc
+pkgconfig_DATA = purple-3.pc
 
-SUBDIRS = $(GCONF_DIR) plugins protocols tests . example
+SUBDIRS = $(GCONF_DIR) plugins protocols ciphers . tests example
 
 purple_coresources = \
 	account.c \
@@ -53,6 +53,11 @@
 	idle.c \
 	imgstore.c \
 	log.c \
+	media/backend-fs2.c \
+	media/backend-iface.c \
+	media/candidate.c \
+	media/codec.c \
+	media/enum-types.c \
 	media.c \
 	mediamanager.c \
 	mime.c \
@@ -112,7 +117,6 @@
 	desktopitem.h \
 	eventloop.h \
 	ft.h \
-	gaim-compat.h \
 	idle.h \
 	imgstore.h \
 	log.h \
@@ -155,16 +159,20 @@
 	xmlnode.h \
 	whiteboard.h
 
+purple_mediaheaders = \
+	backend-iface.h \
+	candidate.h \
+	codec.h \
+	enum-types.h
+
 purple_builtheaders = purple.h version.h marshallers.h
 
 marshallers.h: marshallers.list
-	@echo "Generating marshallers.h"
-	$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h
+	$(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h
 
 marshallers.c: marshallers.list marshallers.h
-	@echo "Generating marshallers.c"
-	echo "#include \"marshallers.h\"" > marshallers.c
-	$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c
+	$(AM_V_GEN)echo "#include \"marshallers.h\"" > marshallers.c
+	$(AM_V_at)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c
 
 if ENABLE_DBUS
 
@@ -191,6 +199,7 @@
                 savedstatuses.h smiley.h status.h server.h util.h xmlnode.h prpl.h
 
 purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \
+		$(addprefix $(srcdir)/media/, $(purple_mediaheaders)) \
 		$(purple_builtheaders)
 dbus_build_exported = $(addprefix $(srcdir)/, $(dbus_exported))
 # We should probably make this better
@@ -199,16 +208,16 @@
 	$(srcdir)/protocols/jabber/libxmpp.c
 
 dbus-types.c: dbus-analyze-types.py $(purple_build_coreheaders)
-	cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@
+	$(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DEFINE_TYPE\(%s\) > $@
 
 dbus-types.h: dbus-analyze-types.py $(purple_build_coreheaders)
-	cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DECLARE_TYPE\(%s\) > $@
+	$(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --pattern=PURPLE_DBUS_DECLARE_TYPE\(%s\) > $@
 
 dbus-bindings.c: dbus-analyze-functions.py $(dbus_exported)
-	cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py > $@
+	$(AM_V_GEN)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py > $@
 
 dbus-signals.c: dbus-analyze-signals.py $(dbus_signals)
-	cat $(dbus_signals) | $(PYTHON) $(srcdir)/dbus-analyze-signals.py > $@
+	$(AM_V_GEN)cat $(dbus_signals) | $(PYTHON) $(srcdir)/dbus-analyze-signals.py > $@
 
 dbus-server.$(OBJEXT): dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
 dbus-server.lo: dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
@@ -223,11 +232,11 @@
 libpurple_client_la_LIBADD = $(DBUS_LIBS)
 
 purple-client-bindings.c: dbus-analyze-functions.py $(dbus_exported)
-	cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client > $@
+	$(AM_V_GEN)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client > $@
 
-purple-client-bindings.h: dbus-analyze-types.py dbus-analyze-functions.py $(purple_coreheaders) $(purple_builtheaders) $(dbus_exported)
-	cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --keyword=enum --verbatim > $@
-	cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client --headers >> $@
+purple-client-bindings.h: dbus-analyze-types.py dbus-analyze-functions.py $(purple_coreheaders) $(addprefix media/, $(purple_mediaheaders)) $(purple_builtheaders) $(dbus_exported)
+	$(AM_V_GEN)cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --keyword=enum --verbatim > $@
+	$(AM_V_at)cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client --headers >> $@
 
 $(libpurple_client_la_OBJECTS): purple-client-bindings.h purple-client-bindings.c
 
@@ -273,7 +282,9 @@
 	$(dbus_sources)
 
 noinst_HEADERS= \
-	internal.h 
+	internal.h \
+	media/backend-fs2.h \
+	valgrind.h
 
 libpurpleincludedir=$(includedir)/libpurple
 libpurpleinclude_HEADERS = \
@@ -281,6 +292,10 @@
 	$(purple_builtheaders) \
 	$(dbus_headers)
 
+mediaincludedir=$(includedir)/libpurple/media
+mediainclude_HEADERS = \
+	$(addprefix $(srcdir)/media/, $(purple_mediaheaders))
+
 libpurple_la_DEPENDENCIES = $(STATIC_LINK_LIBS)
 libpurple_la_LDFLAGS = -export-dynamic -version-info $(PURPLE_LT_VERSION_INFO) -no-undefined
 libpurple_la_LIBADD = \
@@ -294,10 +309,10 @@
 	$(GSTREAMER_LIBS) \
 	$(GSTINTERFACES_LIBS) \
 	$(IDN_LIBS) \
+	ciphers/libpurple-ciphers.la \
 	-lm
 
 AM_CPPFLAGS = \
-	-DBR_PTHREADS=0 \
 	-DDATADIR=\"$(datadir)\" \
 	-DLIBDIR=\"$(libdir)/purple-$(PURPLE_MAJOR_VERSION)/\" \
 	-DLOCALEDIR=\"$(datadir)/locale\" \
--- a/libpurple/Makefile.mingw	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/Makefile.mingw	Wed Jun 13 19:30:27 2012 -0400
@@ -8,7 +8,7 @@
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
 TARGET = libpurple
-NEEDED_DLLS = $(LIBXML2_TOP)/bin/libxml2.dll
+NEEDED_DLLS = $(LIBXML2_TOP)/bin/libxml2-2.dll
 
 ##
 ## INCLUDE PATHS
@@ -20,7 +20,7 @@
 			-I$(GTK_TOP)/include \
 			-I$(GTK_TOP)/include/glib-2.0 \
 			-I$(GTK_TOP)/lib/glib-2.0/include \
-			-I$(LIBXML2_TOP)/include
+			-I$(LIBXML2_TOP)/include/libxml2
 
 LIB_PATHS +=		-L$(GTK_TOP)/lib \
 			-L$(LIBXML2_TOP)/lib
@@ -35,6 +35,14 @@
 			buddyicon.c \
 			certificate.c \
 			cipher.c \
+			ciphers/des.c \
+			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 \
@@ -48,8 +56,8 @@
 			idle.c \
 			imgstore.c \
 			log.c \
+			mediamanager.c \
 			media.c \
-			mediamanager.c \
 			mime.c \
 			nat-pmp.c \
 			network.c \
--- a/libpurple/account.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/account.c	Wed Jun 13 19:30:27 2012 -0400
@@ -41,14 +41,6 @@
 #include "util.h"
 #include "xmlnode.h"
 
-typedef struct
-{
-	PurpleConnectionErrorInfo *current_error;
-} PurpleAccountPrivate;
-
-#define PURPLE_ACCOUNT_GET_PRIVATE(account) \
-	((PurpleAccountPrivate *) (account->priv))
-
 /* TODO: Should use PurpleValue instead of this?  What about "ui"? */
 typedef struct
 {
@@ -294,6 +286,7 @@
 			 proxy_type == PURPLE_PROXY_HTTP       ? "http"   :
 			 proxy_type == PURPLE_PROXY_SOCKS4     ? "socks4" :
 			 proxy_type == PURPLE_PROXY_SOCKS5     ? "socks5" :
+			 proxy_type == PURPLE_PROXY_TOR        ? "tor" :
 			 proxy_type == PURPLE_PROXY_USE_ENVVAR ? "envvar" : "unknown"), -1);
 
 	if ((value = purple_proxy_info_get_host(proxy_info)) != NULL)
@@ -360,8 +353,6 @@
 static xmlnode *
 account_to_xmlnode(PurpleAccount *account)
 {
-	PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
-
 	xmlnode *node, *child;
 	const char *tmp;
 	PurplePresence *presence;
@@ -418,7 +409,7 @@
 		xmlnode_insert_child(node, child);
 	}
 
-	child = current_error_to_xmlnode(priv->current_error);
+	child = current_error_to_xmlnode(account->current_error);
 	xmlnode_insert_child(node, child);
 
 	return node;
@@ -508,8 +499,46 @@
 		purple_account_remove_setting(account, "xferjp_host");
 
 	}
-
-	return;
+}
+
+static void
+migrate_icq_server(PurpleAccount *account)
+{
+	/* Migrate the login server setting for ICQ accounts.  See
+	 * 'mtn log --last 1 --no-graph --from b6d7712e90b68610df3bd2d8cbaf46d94c8b3794'
+	 * for details on the change. */
+
+	if(purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) {
+		const char *tmp = purple_account_get_string(account, "server", NULL);
+
+		/* Non-secure server */
+		if(purple_strequal(tmp,	"login.messaging.aol.com") ||
+				purple_strequal(tmp, "login.oscar.aol.com"))
+			purple_account_set_string(account, "server", "login.icq.com");
+
+		/* Secure server */
+		if(purple_strequal(tmp, "slogin.oscar.aol.com"))
+			purple_account_set_string(account, "server", "slogin.icq.com");
+	}
+}
+
+static void
+migrate_xmpp_encryption(PurpleAccount *account)
+{
+	/* When this is removed, nuke the "old_ssl" and "require_tls" settings */
+	if (g_str_equal(purple_account_get_protocol_id(account), "prpl-jabber")) {
+		const char *sec = purple_account_get_string(account, "connection_security", "");
+
+		if (g_str_equal("", sec)) {
+			const char *val = "require_tls";
+			if (purple_account_get_bool(account, "old_ssl", FALSE))
+				val = "old_ssl";
+			else if (!purple_account_get_bool(account, "require_tls", TRUE))
+				val = "opportunistic_tls";
+
+			purple_account_set_string(account, "connection_security", val);
+		}
+	}
 }
 
 static void
@@ -579,6 +608,12 @@
 	/* we do this here because we need access to account settings to determine
 	 * if we can/should migrate an old Yahoo! JAPAN account */
 	migrate_yahoo_japan(account);
+	/* we do this here because we need access to account settings to determine
+	 * if we can/should migrate an ICQ account's server setting */
+	migrate_icq_server(account);
+	/* we do this here because we need to do it before the user views the
+	 * Edit Account dialog. */
+	migrate_xmpp_encryption(account);
 }
 
 static GList *
@@ -702,6 +737,8 @@
 			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_SOCKS4);
 		else if (purple_strequal(data, "socks5"))
 			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_SOCKS5);
+		else if (purple_strequal(data, "tor"))
+			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_TOR);
 		else if (purple_strequal(data, "envvar"))
 			purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_USE_ENVVAR);
 		else
@@ -827,7 +864,7 @@
 		return NULL;
 	}
 
-	ret = purple_account_new(name, _purple_oscar_convert(name, protocol_id)); /* XXX: */
+	ret = purple_account_new(name, protocol_id);
 	g_free(name);
 	g_free(protocol_id);
 
@@ -877,15 +914,6 @@
 		{
 			purple_buddy_icons_set_account_icon(ret, (guchar *)contents, len);
 		}
-		else
-		{
-			/* Try to see if the icon got left behind in the old cache. */
-			g_free(filename);
-			filename = g_build_filename(g_get_home_dir(), ".gaim", "icons", data, NULL);
-			if (g_file_get_contents(filename, &contents, &len, NULL)) {
-				purple_buddy_icons_set_account_icon(ret, (guchar*)contents, len);
-			}
-		}
 
 		g_free(filename);
 		g_free(data);
@@ -958,7 +986,6 @@
 purple_account_new(const char *username, const char *protocol_id)
 {
 	PurpleAccount *account = NULL;
-	PurpleAccountPrivate *priv = NULL;
 	PurplePlugin *prpl = NULL;
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleStatusType *status_type;
@@ -973,8 +1000,6 @@
 
 	account = g_new0(PurpleAccount, 1);
 	PURPLE_DBUS_REGISTER_POINTER(account, PurpleAccount);
-	priv = g_new0(PurpleAccountPrivate, 1);
-	account->priv = priv;
 
 	purple_account_set_username(account, username);
 
@@ -1017,7 +1042,6 @@
 void
 purple_account_destroy(PurpleAccount *account)
 {
-	PurpleAccountPrivate *priv = NULL;
 	GList *l;
 
 	g_return_if_fail(account != NULL);
@@ -1043,17 +1067,32 @@
 	g_hash_table_destroy(account->settings);
 	g_hash_table_destroy(account->ui_settings);
 
+	if (account->proxy_info)
+		purple_proxy_info_destroy(account->proxy_info);
+
 	purple_account_set_status_types(account, NULL);
 
-	purple_presence_destroy(account->presence);
+	if (account->presence)
+		purple_presence_destroy(account->presence);
 
 	if(account->system_log)
 		purple_log_free(account->system_log);
 
-	priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
-	PURPLE_DBUS_UNREGISTER_POINTER(priv->current_error);
-	g_free(priv->current_error);
-	g_free(priv);
+	while (account->deny) {
+		g_free(account->deny->data);
+		account->deny = g_slist_delete_link(account->deny, account->deny);
+	}
+
+	while (account->permit) {
+		g_free(account->permit->data);
+		account->permit = g_slist_delete_link(account->permit, account->permit);
+	}
+
+	PURPLE_DBUS_UNREGISTER_POINTER(account->current_error);
+	if (account->current_error) {
+		g_free(account->current_error->description);
+		g_free(account->current_error);
+	}
 
 	PURPLE_DBUS_UNREGISTER_POINTER(account);
 	g_free(account);
@@ -1080,6 +1119,15 @@
 }
 
 void
+purple_account_register_completed(PurpleAccount *account, gboolean succeeded)
+{
+	g_return_if_fail(account != NULL);
+
+	if (account->registration_cb)
+		(account->registration_cb)(account, succeeded, account->registration_cb_user_data);
+}
+
+void
 purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data)
 {
 	g_return_if_fail(account != NULL);
@@ -1116,7 +1164,7 @@
 static void
 request_password_cancel_cb(PurpleAccount *account, PurpleRequestFields *fields)
 {
-	/* Disable the account as the user has canceled connecting */
+	/* Disable the account as the user has cancelled connecting */
 	purple_account_set_enabled(account, purple_core_get_ui(), FALSE);
 }
 
@@ -1206,11 +1254,14 @@
 purple_account_disconnect(PurpleAccount *account)
 {
 	PurpleConnection *gc;
+	const char *username;
 
 	g_return_if_fail(account != NULL);
 	g_return_if_fail(!purple_account_is_disconnected(account));
 
-	purple_debug_info("account", "Disconnecting account %p\n", account);
+	username = purple_account_get_username(account);
+	purple_debug_info("account", "Disconnecting account %s (%p)\n",
+	                  username ? username : "(null)", account);
 
 	account->disconnecting = TRUE;
 
@@ -1223,6 +1274,14 @@
 	account->disconnecting = FALSE;
 }
 
+gboolean
+purple_account_is_disconnecting(const PurpleAccount *account)
+{
+	g_return_val_if_fail(account != NULL, TRUE);
+	
+	return account->disconnecting;
+}
+
 void
 purple_account_notify_added(PurpleAccount *account, const char *remote_user,
                           const char *id, const char *alias,
@@ -1325,7 +1384,8 @@
 
 	handles = g_list_remove(handles, info);
 
-	info->auth_cb(info->userdata);
+	if (info->auth_cb != NULL)
+		info->auth_cb(info->userdata);
 
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-granted", info->account, info->user);
@@ -1340,7 +1400,8 @@
 
 	handles = g_list_remove(handles, info);
 
-	info->deny_cb(info->userdata);
+	if (info->deny_cb != NULL)
+		info->deny_cb(info->userdata);
 
 	purple_signal_emit(purple_accounts_get_handle(),
 			"account-authorization-denied", info->account, info->user);
@@ -1367,13 +1428,36 @@
 				"account-authorization-requested", account, remote_user));
 
 	if (plugin_return > 0) {
-		auth_cb(user_data);
+		if (auth_cb != NULL)
+			auth_cb(user_data);
 		return NULL;
 	} else if (plugin_return < 0) {
-		deny_cb(user_data);
+		if (deny_cb != NULL)
+			deny_cb(user_data);
 		return NULL;
 	}
 
+	plugin_return = GPOINTER_TO_INT(
+			purple_signal_emit_return_1(
+				purple_accounts_get_handle(),
+				"account-authorization-requested-with-message",
+				account, remote_user, message
+			));
+
+	switch (plugin_return)
+	{
+		case PURPLE_ACCOUNT_RESPONSE_IGNORE:
+			return NULL;
+		case PURPLE_ACCOUNT_RESPONSE_ACCEPT:
+			if (auth_cb != NULL)
+				auth_cb(user_data);
+			return NULL;
+		case PURPLE_ACCOUNT_RESPONSE_DENY:
+			if (deny_cb != NULL)
+				deny_cb(user_data);
+			return NULL;
+	}
+
 	if (ui_ops != NULL && ui_ops->request_authorize != NULL) {
 		info            = g_new0(PurpleAccountRequestInfo, 1);
 		info->type      = PURPLE_ACCOUNT_REQUEST_AUTHORIZATION;
@@ -1524,7 +1608,7 @@
 	purple_request_input(gc, _("Set User Info"), primary, NULL,
 					   purple_account_get_user_info(account),
 					   TRUE, FALSE, ((gc != NULL) &&
-					   (gc->flags & PURPLE_CONNECTION_HTML) ? "html" : NULL),
+					   (purple_connection_get_flags(gc) & PURPLE_CONNECTION_HTML) ? "html" : NULL),
 					   _("Save"), G_CALLBACK(set_user_info_cb),
 					   _("Cancel"), NULL,
 					   account, NULL, NULL,
@@ -1689,6 +1773,14 @@
 }
 
 void
+purple_account_set_privacy_type(PurpleAccount *account, PurplePrivacyType privacy_type)
+{
+	g_return_if_fail(account != NULL);
+
+	account->perm_deny = privacy_type;
+}
+
+void
 purple_account_set_status_types(PurpleAccount *account, GList *status_types)
 {
 	g_return_if_fail(account != NULL);
@@ -1754,6 +1846,106 @@
 	schedule_accounts_save();
 }
 
+struct public_alias_closure
+{
+	PurpleAccount *account;
+	gpointer failure_cb;
+};
+
+static gboolean
+set_public_alias_unsupported(gpointer data)
+{
+	struct public_alias_closure *closure = data;
+	PurpleSetPublicAliasFailureCallback failure_cb = closure->failure_cb;
+
+	failure_cb(closure->account,
+	           _("This protocol does not support setting a public alias."));
+	g_free(closure);
+
+	return FALSE;
+}
+
+void
+purple_account_set_public_alias(PurpleAccount *account,
+		const char *alias, PurpleSetPublicAliasSuccessCallback success_cb,
+		PurpleSetPublicAliasFailureCallback failure_cb)
+{
+	PurpleConnection *gc;
+	PurplePlugin *prpl = NULL;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(purple_account_is_connected(account));
+
+	gc = purple_account_get_connection(account);
+	prpl = purple_connection_get_prpl(gc);
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, set_public_alias))
+		prpl_info->set_public_alias(gc, alias, success_cb, failure_cb);
+	else if (failure_cb) {
+		struct public_alias_closure *closure =
+				g_new0(struct public_alias_closure, 1);
+		closure->account = account;
+		closure->failure_cb = failure_cb;
+		purple_timeout_add(0, set_public_alias_unsupported, closure);
+	}
+}
+
+static gboolean
+get_public_alias_unsupported(gpointer data)
+{
+	struct public_alias_closure *closure = data;
+	PurpleGetPublicAliasFailureCallback failure_cb = closure->failure_cb;
+
+	failure_cb(closure->account,
+	           _("This protocol does not support fetching the public alias."));
+	g_free(closure);
+
+	return FALSE;
+}
+
+void
+purple_account_get_public_alias(PurpleAccount *account,
+		PurpleGetPublicAliasSuccessCallback success_cb,
+		PurpleGetPublicAliasFailureCallback failure_cb)
+{
+	PurpleConnection *gc;
+	PurplePlugin *prpl = NULL;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(purple_account_is_connected(account));
+
+	gc = purple_account_get_connection(account);
+	prpl = purple_connection_get_prpl(gc);
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_public_alias))
+		prpl_info->get_public_alias(gc, success_cb, failure_cb);
+	else if (failure_cb) {
+		struct public_alias_closure *closure =
+				g_new0(struct public_alias_closure, 1);
+		closure->account = account;
+		closure->failure_cb = failure_cb;
+		purple_timeout_add(0, get_public_alias_unsupported, closure);
+	}
+}
+
+gboolean
+purple_account_get_silence_suppression(const PurpleAccount *account)
+{
+	return purple_account_get_bool(account, "silence-suppression", FALSE);
+}
+
+void
+purple_account_set_silence_suppression(PurpleAccount *account, gboolean value)
+{
+	g_return_if_fail(account != NULL);
+
+	purple_account_set_bool(account, "silence-suppression", value);
+}
+
 void
 purple_account_clear_settings(PurpleAccount *account)
 {
@@ -2016,6 +2208,42 @@
 	return account->gc;
 }
 
+const gchar *
+purple_account_get_name_for_display(const PurpleAccount *account)
+{
+	PurpleBuddy *self = NULL;
+	PurpleConnection *gc = NULL;
+	const gchar *name = NULL, *username = NULL, *displayname = NULL;
+
+	name = purple_account_get_alias(account);
+
+	if (name) {
+		return name;
+	}
+
+	username = purple_account_get_username(account);
+	self = purple_find_buddy((PurpleAccount *)account, username);
+
+	if (self) {
+		const gchar *calias= purple_buddy_get_contact_alias(self);
+
+		/* We don't want to return the buddy name if the buddy/contact
+		 * doesn't have an alias set. */
+		if (!purple_strequal(username, calias)) {
+			return calias;
+		}
+	}
+
+	gc = purple_account_get_connection(account);
+	displayname = purple_connection_get_display_name(gc);
+
+	if (displayname) {
+		return displayname;
+	}
+
+	return username;
+}
+
 gboolean
 purple_account_get_remember_password(const PurpleAccount *account)
 {
@@ -2049,6 +2277,14 @@
 	return account->proxy_info;
 }
 
+PurplePrivacyType
+purple_account_get_privacy_type(const PurpleAccount *account)
+{
+	g_return_val_if_fail(account != NULL, PURPLE_PRIVACY_ALLOW_ALL);
+
+	return account->perm_deny;
+}
+
 PurpleStatus *
 purple_account_get_active_status(const PurpleAccount *account)
 {
@@ -2252,6 +2488,24 @@
 	return setting->value.boolean;
 }
 
+gpointer
+purple_account_get_ui_data(const PurpleAccount *account)
+{
+        g_return_val_if_fail(account != NULL, NULL);
+
+        return account->ui_data;
+}
+
+void
+purple_account_set_ui_data(PurpleAccount *account,
+                                 gpointer ui_data)
+{
+        g_return_if_fail(account != NULL);
+
+        account->ui_data = ui_data;
+}
+
+
 PurpleLog *
 purple_account_get_log(PurpleAccount *account, gboolean create)
 {
@@ -2284,31 +2538,37 @@
 }
 
 void
-purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy)
+purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy, const char *message)
+{
+	PurplePluginProtocolInfo *prpl_info = NULL;
+	PurpleConnection *gc;
+	PurplePlugin *prpl = NULL;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(buddy != NULL);
+
+	gc = purple_account_get_connection(account);
+	if (gc != NULL)
+		prpl = purple_connection_get_prpl(gc);
+
+	if (prpl != NULL)
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+	if (prpl_info != NULL) {
+		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy))
+			prpl_info->add_buddy(gc, buddy, purple_buddy_get_group(buddy), message);
+	}
+}
+
+void
+purple_account_add_buddies(PurpleAccount *account, GList *buddies, const char *message)
 {
 	PurplePluginProtocolInfo *prpl_info = NULL;
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
-
-	if (prpl != NULL)
-		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-
-	if (prpl_info != NULL && prpl_info->add_buddy != NULL)
-		prpl_info->add_buddy(gc, buddy, purple_buddy_get_group(buddy));
-}
-
-void
-purple_account_add_buddies(PurpleAccount *account, GList *buddies)
-{
-	PurplePluginProtocolInfo *prpl_info = NULL;
-	PurpleConnection *gc = purple_account_get_connection(account);
-	PurplePlugin *prpl = NULL;
-
-	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2322,13 +2582,13 @@
 			groups = g_list_append(groups, purple_buddy_get_group(buddy));
 		}
 
-		if (prpl_info->add_buddies != NULL)
-			prpl_info->add_buddies(gc, buddies, groups);
-		else if (prpl_info->add_buddy != NULL) {
+		if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddies))
+			prpl_info->add_buddies(gc, buddies, groups, message);
+		else if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, add_buddy)) {
 			GList *curb = buddies, *curg = groups;
 
 			while ((curb != NULL) && (curg != NULL)) {
-				prpl_info->add_buddy(gc, curb->data, curg->data);
+				prpl_info->add_buddy(gc, curb->data, curg->data, message);
 				curb = curb->next;
 				curg = curg->next;
 			}
@@ -2347,7 +2607,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2364,7 +2624,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2392,7 +2652,7 @@
 	PurplePlugin *prpl = NULL;
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2412,7 +2672,7 @@
 	purple_account_set_password(account, new_pw);
 
 	if (gc != NULL)
-	        prpl = purple_connection_get_prpl(gc);
+		prpl = purple_connection_get_prpl(gc);
 
 	if (prpl != NULL)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
@@ -2450,23 +2710,34 @@
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
 	purple_account_clear_current_error(account);
+
+	purple_signal_emit(purple_accounts_get_handle(), "account-signed-on",
+	                   account);
+}
+
+static void
+signed_off_cb(PurpleConnection *gc,
+              gpointer unused)
+{
+	PurpleAccount *account = purple_connection_get_account(gc);
+
+	purple_signal_emit(purple_accounts_get_handle(), "account-signed-off",
+	                   account);
 }
 
 static void
 set_current_error(PurpleAccount *account, PurpleConnectionErrorInfo *new_err)
 {
-	PurpleAccountPrivate *priv;
 	PurpleConnectionErrorInfo *old_err;
 
 	g_return_if_fail(account != NULL);
 
-	priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
-	old_err = priv->current_error;
+	old_err = account->current_error;
 
 	if(new_err == old_err)
 		return;
 
-	priv->current_error = new_err;
+	account->current_error = new_err;
 
 	purple_signal_emit(purple_accounts_get_handle(),
 	                   "account-error-changed",
@@ -2500,13 +2771,15 @@
 	err->description = g_strdup(description);
 
 	set_current_error(account, err);
+
+	purple_signal_emit(purple_accounts_get_handle(), "account-connection-error",
+	                   account, type, description);
 }
 
 const PurpleConnectionErrorInfo *
 purple_account_get_current_error(PurpleAccount *account)
 {
-	PurpleAccountPrivate *priv = PURPLE_ACCOUNT_GET_PRIVATE(account);
-	return priv->current_error;
+	return account->current_error;
 }
 
 void
@@ -2684,11 +2957,12 @@
 	char *who;
 
 	g_return_val_if_fail(name != NULL, NULL);
+	g_return_val_if_fail(protocol_id != NULL, NULL);
 
 	for (l = purple_accounts_get_all(); l != NULL; l = l->next) {
 		account = (PurpleAccount *)l->data;
-		if (protocol_id && !purple_strequal(account->protocol_id, protocol_id))
-		  continue;
+		if (!purple_strequal(account->protocol_id, protocol_id))
+			continue;
 
 		who = g_strdup(purple_normalize(account, name));
 		if (purple_strequal(purple_normalize(account, purple_account_get_username(account)), who)) {
@@ -2820,6 +3094,13 @@
 										PURPLE_SUBTYPE_ACCOUNT),
 						purple_value_new(PURPLE_TYPE_STRING));
 
+	purple_signal_register(handle, "account-authorization-requested-with-message",
+						purple_marshal_INT__POINTER_POINTER_POINTER,
+						purple_value_new(PURPLE_TYPE_INT), 3,
+						purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_ACCOUNT),
+						purple_value_new(PURPLE_TYPE_STRING),
+						purple_value_new(PURPLE_TYPE_STRING));
 	purple_signal_register(handle, "account-authorization-denied",
 						purple_marshal_VOID__POINTER_POINTER, NULL, 2,
 						purple_value_new(PURPLE_TYPE_SUBTYPE,
@@ -2840,8 +3121,27 @@
 	                       purple_value_new(PURPLE_TYPE_POINTER),
 	                       purple_value_new(PURPLE_TYPE_POINTER));
 
+	purple_signal_register(handle, "account-signed-on",
+	                       purple_marshal_VOID__POINTER, NULL, 1,
+	                       purple_value_new(PURPLE_TYPE_SUBTYPE,
+	                                        PURPLE_SUBTYPE_ACCOUNT));
+
+	purple_signal_register(handle, "account-signed-off",
+	                       purple_marshal_VOID__POINTER, NULL, 1,
+	                       purple_value_new(PURPLE_TYPE_SUBTYPE,
+	                                        PURPLE_SUBTYPE_ACCOUNT));
+
+	purple_signal_register(handle, "account-connection-error",
+	                       purple_marshal_VOID__POINTER_INT_POINTER, NULL, 3,
+	                       purple_value_new(PURPLE_TYPE_SUBTYPE,
+	                                        PURPLE_SUBTYPE_ACCOUNT),
+	                       purple_value_new(PURPLE_TYPE_ENUM),
+	                       purple_value_new(PURPLE_TYPE_STRING));
+
 	purple_signal_connect(conn_handle, "signed-on", handle,
 	                      PURPLE_CALLBACK(signed_on_cb), NULL);
+	purple_signal_connect(conn_handle, "signed-off", handle,
+	                      PURPLE_CALLBACK(signed_off_cb), NULL);
 	purple_signal_connect(conn_handle, "connection-error", handle,
 	                      PURPLE_CALLBACK(connection_error_cb), NULL);
 
--- a/libpurple/account.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/account.h	Wed Jun 13 19:30:27 2012 -0400
@@ -39,6 +39,10 @@
 typedef void (*PurpleAccountRequestAuthorizationCb)(void *);
 typedef void (*PurpleAccountRegistrationCb)(PurpleAccount *account, gboolean succeeded, void *user_data);
 typedef void (*PurpleAccountUnregistrationCb)(PurpleAccount *account, gboolean succeeded, void *user_data);
+typedef void (*PurpleSetPublicAliasSuccessCallback)(PurpleAccount *account, const char *new_alias);
+typedef void (*PurpleSetPublicAliasFailureCallback)(PurpleAccount *account, const char *error);
+typedef void (*PurpleGetPublicAliasSuccessCallback)(PurpleAccount *account, const char *alias);
+typedef void (*PurpleGetPublicAliasFailureCallback)(PurpleAccount *account, const char *error);
 
 #include "connection.h"
 #include "log.h"
@@ -55,6 +59,16 @@
 	PURPLE_ACCOUNT_REQUEST_AUTHORIZATION = 0 /* Account authorization request */
 } PurpleAccountRequestType;
 
+/**
+ * Account request response types
+ */
+typedef enum
+{
+	PURPLE_ACCOUNT_RESPONSE_IGNORE = -2,
+	PURPLE_ACCOUNT_RESPONSE_DENY = -1,
+	PURPLE_ACCOUNT_RESPONSE_PASS = 0,
+	PURPLE_ACCOUNT_RESPONSE_ACCEPT = 1
+} PurpleAccountRequestResponse;
 
 /**  Account UI operations, used to notify the user of status changes and when
  *   buddies add this account to their buddy lists.
@@ -139,6 +153,7 @@
 	 * buddies are added to your permit list.  Currently we have to
 	 * iterate through the entire list if we want to check if someone
 	 * is permitted or denied.  We should do this for 3.0.0.
+	 * Or maybe use a GTree.
 	 */
 	GSList *permit;             /**< Permit list.                           */
 	GSList *deny;               /**< Deny list.                             */
@@ -153,12 +168,10 @@
 	PurpleAccountRegistrationCb registration_cb;
 	void *registration_cb_user_data;
 
-	gpointer priv;              /**< Pointer to opaque private data. */
+	PurpleConnectionErrorInfo *current_error;	/**< Errors */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Account API                                                     */
@@ -206,6 +219,15 @@
 void purple_account_register(PurpleAccount *account);
 
 /**
+ * Registration of the account was completed.
+ * Calls the registration call-back set with purple_account_set_register_callback().
+ *
+ * @param account The account being registered.
+ * @param succeeded Was the account registration successful?
+ */
+void purple_account_register_completed(PurpleAccount *account, gboolean succeeded);
+
+/**
  * Unregisters an account (deleting it from the server).
  *
  * @param account The account to unregister.
@@ -222,6 +244,15 @@
 void purple_account_disconnect(PurpleAccount *account);
 
 /**
+ * Indicates if the account is currently being disconnected.
+ *
+ * @param account The account
+ *
+ * @return TRUE if the account is being disconnected.
+ */
+gboolean purple_account_is_disconnecting(const PurpleAccount *account);
+
+/**
  * Notifies the user that the account was added to a remote user's
  * buddy list.
  *
@@ -414,6 +445,14 @@
 void purple_account_set_proxy_info(PurpleAccount *account, PurpleProxyInfo *info);
 
 /**
+ * Sets the account's privacy type.
+ *
+ * @param account      The account.
+ * @param privacy_type The privacy type.
+ */
+void purple_account_set_privacy_type(PurpleAccount *account, PurplePrivacyType privacy_type);
+
+/**
  * Sets the account's status types.
  *
  * @param account      The account.
@@ -452,6 +491,57 @@
 	const char *status_id, gboolean active, GList *attrs);
 
 /**
+ * Set a server-side (public) alias for this account.  The account
+ * must already be connected.
+ *
+ * Currently, the public alias is not stored locally, although this
+ * may change in a later version.
+ *
+ * @param account    The account
+ * @param alias      The new public alias for this account or NULL
+ *                   to unset the alias/nickname (or return it to
+ *                   a protocol-specific "default", like the username)
+ * @param success_cb A callback which will be called if the alias
+ *                   is successfully set on the server (or NULL).
+ * @param failure_cb A callback which will be called if the alias
+ *                   is not successfully set on the server (or NULL).
+ */
+void purple_account_set_public_alias(PurpleAccount *account,
+	const char *alias, PurpleSetPublicAliasSuccessCallback success_cb,
+	PurpleSetPublicAliasFailureCallback failure_cb);
+
+/**
+ * Fetch the server-side (public) alias for this account.  The account
+ * must already be connected.
+ *
+ * @param account    The account
+ * @param success_cb A callback which will be called with the alias
+ * @param failure_cb A callback which will be called if the prpl is
+ *                   unable to retrieve the server-side alias.
+ */
+void purple_account_get_public_alias(PurpleAccount *account,
+	PurpleGetPublicAliasSuccessCallback success_cb,
+	PurpleGetPublicAliasFailureCallback failure_cb);
+
+/**
+ * Return whether silence suppression is used during voice call.
+ *
+ * @param account The account.
+ *
+ * @return @c TRUE if suppression is used, or @c FALSE if not.
+ */
+gboolean purple_account_get_silence_suppression(const PurpleAccount *account);
+
+/**
+ * Sets whether silence suppression is used during voice call.
+ *
+ * @param account The account.
+ * @param value   @c TRUE if suppression should be used.
+ */
+void purple_account_set_silence_suppression(PurpleAccount *account,
+											gboolean value);
+
+/**
  * Clears all protocol-specific settings on an account.
  *
  * @param account The account.
@@ -463,8 +553,6 @@
  *
  * @param account The account.
  * @param setting The setting to remove.
- *
- * @since 2.6.0
  */
 void purple_account_remove_setting(PurpleAccount *account, const char *setting);
 
@@ -531,6 +619,25 @@
 							  const char *name, gboolean value);
 
 /**
+ * Returns the UI data associated with this account.
+ *
+ * @param account The account.
+ *
+ * @return The UI data associated with this object.  This is a
+ *         convenience field provided to the UIs--it is not
+ *         used by the libuprple core.
+ */
+gpointer purple_account_get_ui_data(const PurpleAccount *account);
+
+/**
+ * Set the UI data associated with this account.
+ *
+ * @param account The account.
+ * @param ui_data A pointer to associate with this object.
+ */
+void purple_account_set_ui_data(PurpleAccount *account, gpointer ui_data);
+
+/**
  * Returns whether or not the account is connected.
  *
  * @param account The account.
@@ -630,6 +737,18 @@
 PurpleConnection *purple_account_get_connection(const PurpleAccount *account);
 
 /**
+ * Returns a name for this account appropriate for display to the user. In
+ * order of preference: the account's alias; the contact or buddy alias (if
+ * the account exists on its own buddy list); the connection's display name;
+ * the account's username.
+ *
+ * @param account The account.
+ *
+ * @return The name to display.
+ */
+const gchar *purple_account_get_name_for_display(const PurpleAccount *account);
+
+/**
  * Returns whether or not this account should save its password.
  *
  * @param account The account.
@@ -669,6 +788,15 @@
 PurpleProxyInfo *purple_account_get_proxy_info(const PurpleAccount *account);
 
 /**
+ * Returns the account's privacy type.
+ *
+ * @param account   The account.
+ *
+ * @return The privacy type.
+ */
+PurplePrivacyType purple_account_get_privacy_type(const PurpleAccount *account);
+
+/**
  * Returns the active status for this account.  This looks through
  * the PurplePresence associated with this account and returns the
  * PurpleStatus that has its active flag set to "TRUE."  There can be
@@ -854,15 +982,18 @@
  *
  * @param account The account.
  * @param buddy The buddy to add.
+ * @param message The invite message.  This may be ignored by a prpl.
  */
-void purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy);
+void purple_account_add_buddy(PurpleAccount *account, PurpleBuddy *buddy, const char *message);
+
 /**
  * Adds a list of buddies to the server-side buddy list.
  *
  * @param account The account.
  * @param buddies The list of PurpleBlistNodes representing the buddies to add.
+ * @param message The invite message.  This may be ignored by a prpl.
  */
-void purple_account_add_buddies(PurpleAccount *account, GList *buddies);
+void purple_account_add_buddies(PurpleAccount *account, GList *buddies, const char *message);
 
 /**
  * Removes a buddy from the server-side buddy list.
@@ -1058,8 +1189,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_ACCOUNT_H_ */
--- a/libpurple/accountopt.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/accountopt.c	Wed Jun 13 19:30:27 2012 -0400
@@ -28,6 +28,51 @@
 #include "accountopt.h"
 #include "util.h"
 
+/**
+ * An option for an account.
+ *
+ * This is set by protocol plugins, and appears in the account settings
+ * dialogs.
+ */
+struct _PurpleAccountOption
+{
+	PurplePrefType type;      /**< The type of value.                     */
+
+	char *text;             /**< The text that will appear to the user. */
+	char *pref_name;        /**< The name of the associated preference. */
+
+	union
+	{
+		gboolean boolean;   /**< The default boolean value.             */
+		int integer;        /**< The default integer value.             */
+		char *string;       /**< The default string value.              */
+		GList *list;        /**< The default list value.                */
+
+	} default_value;
+
+	gboolean masked;        /**< Whether the value entered should be
+	                         *   obscured from view (for passwords and
+	                         *   similar options)
+	                         */
+};
+
+/**
+ * A username split.
+ *
+ * This is used by some protocols to separate the fields of the username
+ * into more human-readable components.
+ */
+struct _PurpleAccountUserSplit
+{
+	char *text;             /**< The text that will appear to the user. */
+	char *default_value;    /**< The default value.                     */
+	char  field_sep;        /**< The field separator.                   */
+	gboolean reverse;       /**< TRUE if the separator should be found
+							  starting a the end of the string, FALSE
+							  otherwise                                 */
+};
+
+
 PurpleAccountOption *
 purple_account_option_new(PurplePrefType type, const char *text,
 						const char *pref_name)
@@ -111,6 +156,16 @@
 	return option;
 }
 
+static void
+purple_account_option_list_free(gpointer data, gpointer user_data)
+{
+	PurpleKeyValuePair *kvp = data;
+
+	g_free(kvp->value);
+	g_free(kvp->key);
+	g_free(kvp);
+}
+
 void
 purple_account_option_destroy(PurpleAccountOption *option)
 {
@@ -127,7 +182,7 @@
 	{
 		if (option->default_value.list != NULL)
 		{
-			g_list_foreach(option->default_value.list, (GFunc)g_free, NULL);
+			g_list_foreach(option->default_value.list, purple_account_option_list_free, NULL);
 			g_list_free(option->default_value.list);
 		}
 	}
@@ -183,7 +238,7 @@
 
 	if (option->default_value.list != NULL)
 	{
-		g_list_foreach(option->default_value.list, (GFunc)g_free, NULL);
+		g_list_foreach(option->default_value.list, purple_account_option_list_free, NULL);
 		g_list_free(option->default_value.list);
 	}
 
--- a/libpurple/accountopt.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/accountopt.h	Wed Jun 13 19:30:27 2012 -0400
@@ -28,54 +28,16 @@
 
 #include "prefs.h"
 
-/**
- * An option for an account.
- *
- * This is set by protocol plugins, and appears in the account settings
- * dialogs.
- */
-typedef struct
-{
-	PurplePrefType type;      /**< The type of value.                     */
-
-	char *text;             /**< The text that will appear to the user. */
-	char *pref_name;        /**< The name of the associated preference. */
-
-	union
-	{
-		gboolean boolean;   /**< The default boolean value.             */
-		int integer;        /**< The default integer value.             */
-		char *string;       /**< The default string value.              */
-		GList *list;        /**< The default list value.                */
-
-	} default_value;
+/**************************************************************************/
+/** Data Structures                                                       */
+/**************************************************************************/
 
-	gboolean masked;        /**< Whether the value entered should be
-	                         *   obscured from view (for passwords and
-	                         *   similar options)
-	                         */
-} PurpleAccountOption;
+/** @copydoc _PurpleAccountOption */
+typedef struct _PurpleAccountOption		PurpleAccountOption;
+/** @copydoc _PurpleAccountUserSplit */
+typedef struct _PurpleAccountUserSplit	PurpleAccountUserSplit;
 
-/**
- * A username split.
- *
- * This is used by some protocols to separate the fields of the username
- * into more human-readable components.
- */
-typedef struct
-{
-	char *text;             /**< The text that will appear to the user. */
-	char *default_value;    /**< The default value.                     */
-	char  field_sep;        /**< The field separator.                   */
-	gboolean reverse;       /**< TRUE if the separator should be found
-							  starting a the end of the string, FALSE
-							  otherwise                                 */
-
-} PurpleAccountUserSplit;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Account Option API                                              */
@@ -388,8 +350,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_ACCOUNTOPT_H_ */
--- a/libpurple/blist.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/blist.c	Wed Jun 13 19:30:27 2012 -0400
@@ -28,6 +28,7 @@
 #include "dbus-maybe.h"
 #include "debug.h"
 #include "notify.h"
+#include "pounce.h"
 #include "prefs.h"
 #include "privacy.h"
 #include "prpl.h"
@@ -313,7 +314,7 @@
 	node = xmlnode_new("account");
 	xmlnode_set_attrib(node, "proto", purple_account_get_protocol_id(account));
 	xmlnode_set_attrib(node, "name", purple_account_get_username(account));
-	g_snprintf(buf, sizeof(buf), "%d", account->perm_deny);
+	g_snprintf(buf, sizeof(buf), "%d", purple_account_get_privacy_type(account));
 	xmlnode_set_attrib(node, "mode", buf);
 
 	for (cur = account->permit; cur; cur = cur->next)
@@ -460,19 +461,16 @@
 	PurpleAccount *account;
 	PurpleBuddy *buddy;
 	char *name = NULL, *alias = NULL;
-	const char *acct_name, *proto, *protocol;
+	const char *acct_name, *proto;
 	xmlnode *x;
 
 	acct_name = xmlnode_get_attrib(bnode, "account");
-	protocol = xmlnode_get_attrib(bnode, "protocol");
-	protocol = _purple_oscar_convert(acct_name, protocol); /* XXX: Remove */
 	proto = xmlnode_get_attrib(bnode, "proto");
-	proto = _purple_oscar_convert(acct_name, proto); /* XXX: Remove */
-
-	if (!acct_name || (!proto && !protocol))
+
+	if (!acct_name || !proto)
 		return;
 
-	account = purple_accounts_find(acct_name, proto ? proto : protocol);
+	account = purple_accounts_find(acct_name, proto);
 
 	if (!account)
 		return;
@@ -531,19 +529,18 @@
 {
 	PurpleChat *chat;
 	PurpleAccount *account;
-	const char *acct_name, *proto, *protocol;
+	const char *acct_name, *proto;
 	xmlnode *x;
 	char *alias = NULL;
 	GHashTable *components;
 
 	acct_name = xmlnode_get_attrib(cnode, "account");
-	protocol = xmlnode_get_attrib(cnode, "protocol");
 	proto = xmlnode_get_attrib(cnode, "proto");
 
-	if (!acct_name || (!proto && !protocol))
+	if (!acct_name || !proto)
 		return;
 
-	account = purple_accounts_find(acct_name, proto ? proto : protocol);
+	account = purple_accounts_find(acct_name, proto);
 
 	if (!account)
 		return;
@@ -629,23 +626,22 @@
 			xmlnode *x;
 			PurpleAccount *account;
 			int imode;
-			const char *acct_name, *proto, *mode, *protocol;
+			const char *acct_name, *proto, *mode;
 
 			acct_name = xmlnode_get_attrib(anode, "name");
-			protocol = xmlnode_get_attrib(anode, "protocol");
 			proto = xmlnode_get_attrib(anode, "proto");
 			mode = xmlnode_get_attrib(anode, "mode");
 
-			if (!acct_name || (!proto && !protocol) || !mode)
+			if (!acct_name || !proto || !mode)
 				continue;
 
-			account = purple_accounts_find(acct_name, proto ? proto : protocol);
+			account = purple_accounts_find(acct_name, proto);
 
 			if (!account)
 				continue;
 
 			imode = atoi(mode);
-			account->perm_deny = (imode != 0 ? imode : PURPLE_PRIVACY_ALLOW_ALL);
+			purple_account_set_privacy_type(account, (imode != 0 ? imode : PURPLE_PRIVACY_ALLOW_ALL));
 
 			for (x = anode->child; x; x = x->next) {
 				char *name;
@@ -960,12 +956,6 @@
 		ops->update(purplebuddylist, node);
 }
 
-void
-purple_blist_update_buddy_icon(PurpleBuddy *buddy)
-{
-	purple_blist_update_node_icon((PurpleBlistNode *)buddy);
-}
-
 /*
  * TODO: Maybe remove the call to this from server.c and call it
  * from oscar.c and toc.c instead?
@@ -1118,7 +1108,7 @@
 	old_alias = buddy->alias;
 
 	if ((new_alias != NULL) && (*new_alias != '\0'))
-		buddy->alias = g_strdup(alias);
+		buddy->alias = new_alias;
 	else {
 		buddy->alias = NULL;
 		g_free(new_alias); /* could be "\0" */
@@ -1317,7 +1307,7 @@
 
 				purple_account_remove_buddies(account, buddies, groups);
 				g_list_free(groups);
-				purple_account_add_buddies(account, buddies);
+				purple_account_add_buddies(account, buddies, NULL);
 			}
 
 			g_list_free(buddies);
@@ -1739,9 +1729,12 @@
 	g_free(contact);
 }
 
-void purple_contact_set_alias(PurpleContact *contact, const char *alias)
+PurpleGroup *
+purple_contact_get_group(const PurpleContact *contact)
 {
-	purple_blist_alias_contact(contact,alias);
+	g_return_val_if_fail(contact, NULL);
+
+	return (PurpleGroup *)(((PurpleBlistNode *)contact)->parent);
 }
 
 const char *purple_contact_get_alias(PurpleContact* contact)
@@ -1781,6 +1774,13 @@
 	contact->priority_valid = FALSE;
 }
 
+int purple_contact_get_contact_size(PurpleContact *contact, gboolean offline)   
+{
+	g_return_val_if_fail(contact != NULL, 0);
+
+	return offline ? contact->totalsize : contact->currentsize;
+}   
+
 PurpleGroup *purple_group_new(const char *name)
 {
 	PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
@@ -2009,18 +2009,14 @@
 
 	ops = purple_blist_get_ui_ops();
 
-	if (!purplebuddylist->root) {
-		purplebuddylist->root = gnode;
-
-		key = g_utf8_collate_key(group->name, -1);
-		g_hash_table_insert(groups_cache, key, group);
-		return;
+	/* if we're moving to overtop of ourselves, do nothing */
+	if (gnode == node) {
+		if (!purplebuddylist->root)
+			node = NULL;
+		else
+			return;
 	}
 
-	/* if we're moving to overtop of ourselves, do nothing */
-	if (gnode == node)
-		return;
-
 	if (purple_find_group(group->name)) {
 		/* This is just being moved */
 
@@ -2180,6 +2176,9 @@
 	if (ops && ops->remove_node)
 		ops->remove_node(node);
 
+	/* Remove this buddy's pounces */
+	purple_pounce_destroy_all_by_buddy(buddy);
+
 	/* Signal that the buddy has been removed before freeing the memory for it */
 	purple_signal_emit(purple_blist_get_handle(), "buddy-removed", buddy);
 
@@ -2372,26 +2371,6 @@
 	return NULL;
 }
 
-const char *purple_buddy_get_local_alias(PurpleBuddy *buddy)
-{
-	PurpleContact *c;
-
-	g_return_val_if_fail(buddy != NULL, NULL);
-
-	/* Search for an alias for the buddy. In order of precedence: */
-	/* The buddy alias */
-	if (buddy->alias != NULL)
-		return buddy->alias;
-
-	/* The contact alias */
-	c = purple_buddy_get_contact(buddy);
-	if ((c != NULL) && (c->alias != NULL))
-		return c->alias;
-
-	/* The buddy's user name (i.e. no alias) */
-	return buddy->name;
-}
-
 const char *purple_chat_get_name(PurpleChat *chat)
 {
 	char *ret = NULL;
@@ -2432,6 +2411,9 @@
 	hb.name = (gchar *)purple_normalize(account, name);
 
 	for (group = purplebuddylist->root; group; group = group->next) {
+		if (!group->child)
+			continue;
+
 		hb.group = group;
 		if ((buddy = g_hash_table_lookup(purplebuddylist->buddies, &hb))) {
 			return buddy;
@@ -2481,6 +2463,9 @@
 		hb.account = account;
 
 		for (node = purplebuddylist->root; node != NULL; node = node->next) {
+			if (!node->child)
+				continue;
+
 			hb.group = node;
 			if ((buddy = g_hash_table_lookup(purplebuddylist->buddies, &hb)) != NULL)
 				ret = g_slist_prepend(ret, buddy);
@@ -2603,6 +2588,18 @@
 	return buddy->presence;
 }
 
+PurpleMediaCaps purple_buddy_get_media_caps(const PurpleBuddy *buddy)
+{
+	g_return_val_if_fail(buddy != NULL, 0);
+	return buddy->media_caps;
+}
+
+void purple_buddy_set_media_caps(PurpleBuddy *buddy, PurpleMediaCaps media_caps)
+{
+	g_return_if_fail(buddy != NULL);
+	buddy->media_caps = media_caps;
+}
+
 PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy)
 {
 	g_return_val_if_fail(buddy != NULL, NULL);
@@ -2911,6 +2908,17 @@
 	return node->type;
 }
 
+gboolean
+purple_blist_node_has_setting(PurpleBlistNode* node, const char *key)
+{
+	g_return_val_if_fail(node != NULL, FALSE);
+	g_return_val_if_fail(node->settings != NULL, FALSE);
+	g_return_val_if_fail(key != NULL, FALSE);
+
+	/* Boxed type, so it won't ever be NULL, so no need for _extended */
+	return (g_hash_table_lookup(node->settings, key) != NULL);
+}
+
 void
 purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
 {
@@ -3185,6 +3193,13 @@
 										PURPLE_SUBTYPE_BLIST_NODE),
 						 purple_value_new(PURPLE_TYPE_STRING));
 
+	purple_signal_register(handle, "buddy-caps-changed",
+			purple_marshal_VOID__POINTER_INT_INT, NULL,
+			3, purple_value_new(PURPLE_TYPE_SUBTYPE,
+				PURPLE_SUBTYPE_BLIST_BUDDY),
+			purple_value_new(PURPLE_TYPE_INT),
+			purple_value_new(PURPLE_TYPE_INT));
+
 	purple_signal_connect(purple_accounts_get_handle(), "account-created",
 			handle,
 			PURPLE_CALLBACK(purple_blist_buddies_cache_add_account),
--- a/libpurple/blist.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/blist.h	Wed Jun 13 19:30:27 2012 -0400
@@ -75,9 +75,6 @@
 
 } PurpleBlistNodeFlags;
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_BLIST_NODE(obj) ((PurpleBlistNode *)(obj))
 
 #define PURPLE_BLIST_NODE_HAS_FLAG(b, f) (purple_blist_node_get_flags((PurpleBlistNode*)(b)) & (f))
@@ -86,28 +83,17 @@
 #define PURPLE_BLIST_NODE_NAME(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_CHAT_NODE  ? purple_chat_get_name((PurpleChat*)n) :        \
 				     purple_blist_node_get_type(n) == PURPLE_BLIST_BUDDY_NODE ? purple_buddy_get_name((PurpleBuddy*)n) : NULL)
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_GROUP(obj) ((PurpleGroup *)(obj))
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_CONTACT(obj) ((PurpleContact *)(obj))
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_BUDDY(obj) ((PurpleBuddy *)(obj))
 
-/**
- * @since 2.6.0
- */
 #define PURPLE_CHAT(obj) ((PurpleChat *)(obj))
 
 #include "account.h"
 #include "buddyicon.h"
+#include "media.h"
 #include "status.h"
 
 /**************************************************************************/
@@ -143,6 +129,7 @@
 	PurpleBuddyIcon *icon;                    /**< The buddy icon. */
 	PurpleAccount *account;					/**< the account this buddy belongs to */
 	PurplePresence *presence;
+	PurpleMediaCaps media_caps;		/**< The media capabilities of the buddy. */
 };
 
 /**
@@ -223,9 +210,7 @@
 	 * be set to a fallback function that saves data to blist.xml like in
 	 * previous libpurple versions.
 	 *
-	 * @attrib node    The node which has been modified.
-	 *
-	 * @since 2.6.0.
+	 * @param node    The node which has been modified.
 	 */
 	void (*save_node)(PurpleBlistNode *node);
 
@@ -238,8 +223,7 @@
 	 * be set to a fallback function that saves data to blist.xml like in
 	 * previous libpurple versions.
 	 *
-	 * @attrib node  The node which has been modified.
-	 * @since 2.6.0.
+	 * @param node  The node which has been modified.
 	 */
 	void (*remove_node)(PurpleBlistNode *node);
 
@@ -252,18 +236,15 @@
 	 * be set to a fallback function that saves data to blist.xml like in
 	 * previous libpurple versions.
 	 *
-	 * @attrib account  The account whose data to save. If NULL, save all data
+	 * @param account  The account whose data to save. If NULL, save all data
 	 *                  for all accounts.
-	 * @since 2.6.0.
 	 */
 	void (*save_account)(PurpleAccount *account);
 
 	void (*_purple_reserved1)(void);
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Buddy List API                                                  */
@@ -309,7 +290,6 @@
  *         freeing the list.
  *
  * @see purple_find_buddies
- * @since 2.6.0
  */
 GSList *purple_blist_get_buddies(void);
 
@@ -317,8 +297,6 @@
  * Returns the UI data for the list.
  *
  * @return The UI data for the list.
- *
- * @since 2.6.0
  */
 gpointer purple_blist_get_ui_data(void);
 
@@ -326,8 +304,6 @@
  * Sets the UI data for the list.
  *
  * @param ui_data The UI data for the list.
- *
- * @since 2.6.0
  */
 void purple_blist_set_ui_data(gpointer ui_data);
 
@@ -350,7 +326,7 @@
  *
  * @param node A node.
  * @return  The parent node.
- * @since 2.4.0
+ *
  * @see purple_blist_node_get_first_child
  * @see purple_blist_node_get_sibling_next
  * @see purple_blist_node_get_sibling_prev
@@ -363,7 +339,7 @@
  *
  * @param node A node.
  * @return  The child node.
- * @since 2.4.0
+ *
  * @see purple_blist_node_get_parent
  * @see purple_blist_node_get_sibling_next
  * @see purple_blist_node_get_sibling_prev
@@ -376,7 +352,7 @@
  *
  * @param node A node.
  * @return  The sibling node.
- * @since 2.4.0
+ *
  * @see purple_blist_node_get_parent
  * @see purple_blist_node_get_first_child
  * @see purple_blist_node_get_sibling_prev
@@ -389,7 +365,7 @@
  *
  * @param node A node.
  * @return  The sibling node.
- * @since 2.4.0
+ *
  * @see purple_blist_node_get_parent
  * @see purple_blist_node_get_first_child
  * @see purple_blist_node_get_sibling_next
@@ -402,7 +378,6 @@
  *
  * @param node The node.
  * @return The UI data.
- * @since 2.6.0
  */
 gpointer purple_blist_node_get_ui_data(const PurpleBlistNode *node);
 
@@ -411,8 +386,6 @@
  *
  * @param node The node.
  * @param ui_data The UI data.
- *
- * @since 2.6.0
  */
 void purple_blist_node_set_ui_data(PurpleBlistNode *node, gpointer ui_data);
 
@@ -452,21 +425,9 @@
  * Updates a node's custom icon.
  *
  * @param node  The PurpleBlistNode whose custom icon has changed.
- *
- * @since 2.5.0
  */
 void purple_blist_update_node_icon(PurpleBlistNode *node);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
-/**
- * Updates a buddy's icon.
- *
- * @param buddy  The buddy whose buddy icon has changed
- * @deprecated Use purple_blist_update_node_icon() instead.
- */
-void purple_blist_update_buddy_icon(PurpleBuddy *buddy);
-#endif
-
 /**
  * Renames a buddy in the buddy list.
  *
@@ -621,7 +582,6 @@
  * @return      The protocol data.
  *
  * @see purple_buddy_set_protocol_data()
- * @since 2.6.0
  */
 gpointer purple_buddy_get_protocol_data(const PurpleBuddy *buddy);
 
@@ -634,7 +594,6 @@
  * @param data  The data.
  *
  * @see purple_buddy_get_protocol_data()
- * @since 2.6.0
  */
 void purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data);
 
@@ -657,6 +616,22 @@
 PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy);
 
 /**
+ * Gets the media caps from a buddy.
+ *
+ * @param buddy The buddy.
+ * @return      The media caps.
+ */
+PurpleMediaCaps purple_buddy_get_media_caps(const PurpleBuddy *buddy);
+
+/**
+ * Sets the media caps for a buddy.
+ *
+ * @param buddy      The PurpleBuddy.
+ * @param media_caps The PurpleMediaCaps.
+ */
+void purple_buddy_set_media_caps(PurpleBuddy *buddy, PurpleMediaCaps media_caps);
+
+/**
  * Adds a new buddy to the buddy list.
  *
  * The buddy will be inserted right after node or prepended to the
@@ -715,6 +690,14 @@
 void purple_contact_destroy(PurpleContact *contact);
 
 /**
+ * Gets the PurpleGroup from a PurpleContact
+ *
+ * @param contact  The contact
+ * @return         The group
+ */
+PurpleGroup *purple_contact_get_group(const PurpleContact *contact);
+
+/**
  * Adds a new contact to the buddy list.
  *
  * The new contact will be inserted after insert or prepended to the list if
@@ -744,18 +727,6 @@
  */
 PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
-/**
- * Sets the alias for a contact.
- *
- * @param contact  The contact
- * @param alias    The alias to set, or NULL to unset
- *
- * @deprecated Use purple_blist_alias_contact() instead.
- */
-void purple_contact_set_alias(PurpleContact *contact, const char *alias);
-#endif
-
 /**
  * Gets the alias for a contact.
  *
@@ -783,6 +754,15 @@
 void purple_contact_invalidate_priority_buddy(PurpleContact *contact);
 
 /**
+ * Determines the total size of a contact.
+ *
+ * @param contact	The contact
+ * @param offline	Count buddies in offline accounts
+ * @return The number of buddies in the contact
+ */
+int purple_contact_get_contact_size(PurpleContact *contact, gboolean offline);
+
+/**
  * Removes a buddy from the buddy list and frees the memory allocated to it.
  * This doesn't actually try to remove the buddy from the server list.
  *
@@ -846,19 +826,6 @@
  */
 const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BLIST_C_)
-/**
- * Returns the correct alias for this user, ignoring server aliases.  Used
- * when a user-recognizable name is required.  In order: buddy's alias; buddy's
- * contact alias; buddy's user name.
- *
- * @param buddy  The buddy whose alias will be returned.
- * @return       The appropriate name or alias.
- * @deprecated   Try purple_buddy_get_alias(), if server aliases are okay.
- */
-const char *purple_buddy_get_local_alias(PurpleBuddy *buddy);
-#endif
-
 /**
  * Returns the correct name to display for a buddy. In order of precedence:
  * the buddy's alias; the buddy's server alias; the buddy's contact alias;
@@ -874,8 +841,6 @@
  *
  * @param buddy  The buddy
  * @return       The local alias for the buddy
- *
- * @since 2.6.0
  */
 const char *purple_buddy_get_local_buddy_alias(PurpleBuddy *buddy);
 
@@ -951,8 +916,6 @@
  * @param chat  The chat.
  *
  * @return  The account the chat belongs to.
- *
- * @since 2.4.0
  */
 PurpleAccount *purple_chat_get_account(PurpleChat *chat);
 
@@ -962,8 +925,6 @@
  * @param chat  The chat.
  *
  * @constreturn  The hashtable.
- *
- * @since 2.4.0
  */
 GHashTable *purple_chat_get_components(PurpleChat *chat);
 
@@ -1091,6 +1052,16 @@
 void purple_blist_request_add_group(void);
 
 /**
+ * Checks whether a named setting exists for a node in the buddy list
+ *
+ * @param node  The node to check from which to check settings
+ * @param key   The identifier of the data
+ *
+ * @return TRUE if a value exists, or FALSE if there is no setting
+ */
+gboolean purple_blist_node_has_setting(PurpleBlistNode *node, const char *key);
+
+/**
  * Associates a boolean with a node in the buddy list
  *
  * @param node  The node to associate the data with
@@ -1181,8 +1152,6 @@
  * @param node The node.
  *
  * @return The type of the node.
- *
- * @since 2.1.0
  */
 PurpleBlistNodeType purple_blist_node_get_type(PurpleBlistNode *node);
 
@@ -1241,8 +1210,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_BLIST_H_ */
--- a/libpurple/buddyicon.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/buddyicon.c	Wed Jun 13 19:30:27 2012 -0400
@@ -105,9 +105,6 @@
 /** "Should icons be cached to disk?" */
 static gboolean    icon_caching  = TRUE;
 
-/* For ~/.gaim to ~/.purple migration. */
-static char *old_icons_dir = NULL;
-
 static void delete_buddy_icon_settings(PurpleBlistNode *node, const char *setting_name);
 
 /*
@@ -716,9 +713,8 @@
 	if (read_icon_file(path, &data, &len))
 	{
 		g_free(path);
-		img = purple_buddy_icon_data_new(data, len, account_icon_file);
-		g_hash_table_insert(pointer_icon_cache, account, img);
-		return img;
+		img = purple_buddy_icons_set_account_icon(account, data, len);
+		return purple_imgstore_ref(img);
 	}
 	g_free(path);
 
@@ -760,7 +756,7 @@
 	else
 		g_hash_table_remove(pointer_icon_cache, account);
 
-	if (purple_account_is_connected(account))
+	if (!purple_account_is_disconnected(account))
 	{
 		PurpleConnection *gc;
 		PurplePluginProtocolInfo *prpl_info;
@@ -841,9 +837,8 @@
 	if (read_icon_file(path, &data, &len))
 	{
 		g_free(path);
-		img = purple_buddy_icon_data_new(data, len, custom_icon_file);
-		g_hash_table_insert(pointer_icon_cache, node, img);
-		return img;
+		img = purple_buddy_icons_node_set_custom_icon(node, data, len);
+		return purple_imgstore_ref(img);
 	}
 	g_free(path);
 
@@ -960,31 +955,6 @@
 	return purple_buddy_icons_node_set_custom_icon(node, data, len);
 }
 
-gboolean
-purple_buddy_icons_has_custom_icon(PurpleContact *contact)
-{
-	return purple_buddy_icons_node_has_custom_icon((PurpleBlistNode*)contact);
-}
-
-PurpleStoredImage *
-purple_buddy_icons_find_custom_icon(PurpleContact *contact)
-{
-	return purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)contact);
-}
-
-PurpleStoredImage *
-purple_buddy_icons_set_custom_icon(PurpleContact *contact, guchar *icon_data,
-                                   size_t icon_len)
-{
-	return purple_buddy_icons_node_set_custom_icon((PurpleBlistNode*)contact, icon_data, icon_len);
-}
-
-void
-_purple_buddy_icon_set_old_icons_dir(const char *dirname)
-{
-	old_icons_dir = g_strdup(dirname);
-}
-
 static void
 delete_buddy_icon_settings(PurpleBlistNode *node, const char *setting_name)
 {
@@ -997,133 +967,6 @@
 	}
 }
 
-static void
-migrate_buddy_icon(PurpleBlistNode *node, const char *setting_name,
-                   const char *dirname, const char *filename)
-{
-	char *path;
-
-	if (filename[0] != '/')
-	{
-		path = g_build_filename(dirname, filename, NULL);
-		if (g_file_test(path, G_FILE_TEST_EXISTS))
-		{
-			g_free(path);
-			return;
-		}
-		g_free(path);
-
-		path = g_build_filename(old_icons_dir, filename, NULL);
-	}
-	else
-		path = g_strdup(filename);
-
-	if (g_file_test(path, G_FILE_TEST_EXISTS))
-	{
-		guchar *icon_data;
-		size_t icon_len;
-		FILE *file;
-		char *new_filename;
-
-		if (!read_icon_file(path, &icon_data, &icon_len))
-		{
-			g_free(path);
-			delete_buddy_icon_settings(node, setting_name);
-			return;
-		}
-
-		if (icon_data == NULL || icon_len <= 0)
-		{
-			/* This really applies to the icon_len check.
-			 * icon_data should never be NULL if
-			 * read_icon_file() returns TRUE. */
-			purple_debug_error("buddyicon", "Empty buddy icon file: %s\n", path);
-			delete_buddy_icon_settings(node, setting_name);
-			g_free(path);
-			return;
-		}
-
-		g_free(path);
-
-		new_filename = purple_util_get_image_filename(icon_data, icon_len);
-		if (new_filename == NULL)
-		{
-			purple_debug_error("buddyicon",
-				"New icon filename is NULL. This should never happen! "
-				"The old filename was: %s\n", path);
-			delete_buddy_icon_settings(node, setting_name);
-			g_return_if_reached();
-		}
-
-		path = g_build_filename(dirname, new_filename, NULL);
-		if ((file = g_fopen(path, "wb")) != NULL)
-		{
-			if (!fwrite(icon_data, icon_len, 1, file))
-			{
-				purple_debug_error("buddyicon", "Error writing %s: %s\n",
-				                   path, g_strerror(errno));
-			}
-			else
-				purple_debug_info("buddyicon", "Wrote migrated cache file: %s\n", path);
-
-			fclose(file);
-		}
-		else
-		{
-			purple_debug_error("buddyicon", "Unable to create file %s: %s\n",
-			                   path, g_strerror(errno));
-			g_free(new_filename);
-			g_free(path);
-
-			delete_buddy_icon_settings(node, setting_name);
-			return;
-		}
-		g_free(path);
-
-		purple_blist_node_set_string(node,
-		                             setting_name,
-		                             new_filename);
-		ref_filename(new_filename);
-
-		g_free(new_filename);
-
-		if (purple_strequal(setting_name, "buddy_icon"))
-		{
-			const char *hash;
-
-			hash = purple_blist_node_get_string(node, "avatar_hash");
-			if (hash != NULL)
-			{
-				purple_blist_node_set_string(node, "icon_checksum", hash);
-				purple_blist_node_remove_setting(node, "avatar_hash");
-			}
-			else
-			{
-				PurpleAccount *account = purple_buddy_get_account((PurpleBuddy *)node);
-				const char *prpl_id = purple_account_get_protocol_id(account);
-
-				if (g_str_equal(prpl_id, "prpl-yahoo") || g_str_equal(prpl_id, "prpl-yahoojp"))
-				{
-					int checksum = purple_blist_node_get_int(node, "icon_checksum");
-					if (checksum != 0)
-					{
-						char *checksum_str = g_strdup_printf("%i", checksum);
-						purple_blist_node_remove_setting(node, "icon_checksum");
-						purple_blist_node_set_string(node, "icon_checksum", checksum_str);
-						g_free(checksum_str);
-					}
-				}
-			}
-		}
-	}
-	else
-	{
-		purple_debug_error("buddyicon", "Old icon file doesn't exist: %s\n", path);
-		delete_buddy_icon_settings(node, setting_name);
-		g_free(path);
-	}
-}
-
 void
 _purple_buddy_icons_account_loaded_cb()
 {
@@ -1155,22 +998,6 @@
 	PurpleBlistNode *node = purple_blist_get_root();
 	const char *dirname = purple_buddy_icons_get_cache_dir();
 
-	/* Doing this once here saves having to check it inside a loop. */
-	if (old_icons_dir != NULL)
-	{
-		if (!g_file_test(dirname, G_FILE_TEST_IS_DIR))
-		{
-			purple_debug_info("buddyicon", "Creating icon cache directory.\n");
-
-			if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
-			{
-				purple_debug_error("buddyicon",
-				                   "Unable to create directory %s: %s\n",
-				                   dirname, g_strerror(errno));
-			}
-		}
-	}
-
 	while (node != NULL)
 	{
 		if (PURPLE_BLIST_NODE_IS_BUDDY(node))
@@ -1180,26 +1007,17 @@
 			filename = purple_blist_node_get_string(node, "buddy_icon");
 			if (filename != NULL)
 			{
-				if (old_icons_dir != NULL)
+				char *path = g_build_filename(dirname, filename, NULL);
+				if (!g_file_test(path, G_FILE_TEST_EXISTS))
 				{
-					migrate_buddy_icon(node,
-					                   "buddy_icon",
-					                   dirname, filename);
+					purple_blist_node_remove_setting(node,
+					                                 "buddy_icon");
+					purple_blist_node_remove_setting(node,
+					                                 "icon_checksum");
 				}
 				else
-				{
-					char *path = g_build_filename(dirname, filename, NULL);
-					if (!g_file_test(path, G_FILE_TEST_EXISTS))
-					{
-						purple_blist_node_remove_setting(node,
-						                                 "buddy_icon");
-						purple_blist_node_remove_setting(node,
-						                                 "icon_checksum");
-					}
-					else
-						ref_filename(filename);
-					g_free(path);
-				}
+					ref_filename(filename);
+				g_free(path);
 			}
 		}
 		else if (PURPLE_BLIST_NODE_IS_CONTACT(node) ||
@@ -1211,24 +1029,15 @@
 			filename = purple_blist_node_get_string(node, "custom_buddy_icon");
 			if (filename != NULL)
 			{
-				if (old_icons_dir != NULL)
+				char *path = g_build_filename(dirname, filename, NULL);
+				if (!g_file_test(path, G_FILE_TEST_EXISTS))
 				{
-					migrate_buddy_icon(node,
-					                   "custom_buddy_icon",
-					                   dirname, filename);
+					purple_blist_node_remove_setting(node,
+					                                 "custom_buddy_icon");
 				}
 				else
-				{
-					char *path = g_build_filename(dirname, filename, NULL);
-					if (!g_file_test(path, G_FILE_TEST_EXISTS))
-					{
-						purple_blist_node_remove_setting(node,
-						                                 "custom_buddy_icon");
-					}
-					else
-						ref_filename(filename);
-					g_free(path);
-				}
+					ref_filename(filename);
+				g_free(path);
 			}
 		}
 		node = purple_blist_node_next(node, TRUE);
@@ -1300,8 +1109,9 @@
 	g_hash_table_destroy(icon_data_cache);
 	g_hash_table_destroy(icon_file_cache);
 	g_hash_table_destroy(pointer_icon_cache);
-	g_free(old_icons_dir);
 	g_free(cache_dir);
+
+	cache_dir = NULL;
 }
 
 void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int *height)
--- a/libpurple/buddyicon.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/buddyicon.h	Wed Jun 13 19:30:27 2012 -0400
@@ -39,10 +39,7 @@
 #include "prpl.h"
 #include "util.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Buddy Icon API                                                  */
@@ -275,7 +272,6 @@
  * @param node The blist node.
  *
  * @return A boolean indicating if @a node has a custom buddy icon.
- * @since 2.5.0
  */
 gboolean
 purple_buddy_icons_node_has_custom_icon(PurpleBlistNode *node);
@@ -293,7 +289,6 @@
  * @param node The node.
  *
  * @return The custom buddy icon.
- * @since 2.5.0
  */
 PurpleStoredImage *
 purple_buddy_icons_node_find_custom_icon(PurpleBlistNode *node);
@@ -311,7 +306,6 @@
  *
  * @return The icon that was set. The caller does NOT own a reference to this,
  *         and must call purple_imgstore_ref() if it wants one.
- * @since 2.5.0
  */
 PurpleStoredImage *
 purple_buddy_icons_node_set_custom_icon(PurpleBlistNode *node,
@@ -329,45 +323,11 @@
  *
  * @return The icon that was set. The caller does NOT own a reference to this,
  *         and must call purple_imgstore_ref() if it wants one.
- * @since 2.5.0
  */
 PurpleStoredImage *
 purple_buddy_icons_node_set_custom_icon_from_file(PurpleBlistNode *node,
                                                   const gchar *filename);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_BUDDYICON_C_)
-/**
- * PurpleContact version of purple_buddy_icons_node_has_custom_icon.
- *
- * @copydoc purple_buddy_icons_node_has_custom_icon()
- *
- * @deprecated Use purple_buddy_icons_node_has_custom_icon instead.
- */
-gboolean
-purple_buddy_icons_has_custom_icon(PurpleContact *contact);
-
-/**
- * PurpleContact version of purple_buddy_icons_node_find_custom_icon.
- *
- * @copydoc purple_buddy_icons_node_find_custom_icon()
- *
- * @deprecated Use purple_buddy_icons_node_find_custom_icon instead.
- */
-PurpleStoredImage *
-purple_buddy_icons_find_custom_icon(PurpleContact *contact);
-
-/**
- * PurpleContact version of purple_buddy_icons_node_set_custom_icon.
- *
- * @copydoc purple_buddy_icons_node_set_custom_icon()
- *
- * @deprecated Use purple_buddy_icons_node_set_custom_icon instead.
- */
-PurpleStoredImage *
-purple_buddy_icons_set_custom_icon(PurpleContact *contact,
-                                   guchar *icon_data, size_t icon_len);
-#endif
-
 /**
  * Sets whether or not buddy icon caching is enabled.
  *
@@ -434,8 +394,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_BUDDYICON_H_ */
--- a/libpurple/certificate.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/certificate.c	Wed Jun 13 19:30:27 2012 -0400
@@ -26,8 +26,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
-#include <glib.h>
-
 #include "internal.h"
 #include "certificate.h"
 #include "dbus-maybe.h"
@@ -43,6 +41,93 @@
 /** 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)
+{
+	switch (flag) {
+		case PURPLE_CERTIFICATE_SELF_SIGNED:
+			return _("The certificate is self-signed and cannot be "
+			         "automatically checked.");
+			break;
+		case PURPLE_CERTIFICATE_CA_UNKNOWN:
+			return _("The certificate is not trusted because no certificate "
+			         "that can verify it is currently trusted.");
+			break;
+		case PURPLE_CERTIFICATE_NOT_ACTIVATED:
+			return _("The certificate is not valid yet.  Check that your "
+			         "computer's date and time are accurate.");
+			break;
+		case PURPLE_CERTIFICATE_EXPIRED:
+			return _("The certificate has expired and should not be "
+			         "considered valid.  Check that your computer's date "
+			         "and time are accurate.");
+			break;
+		case PURPLE_CERTIFICATE_NAME_MISMATCH:
+			/* Translators: "domain" refers to a DNS domain (e.g. talk.google.com) */
+			return _("The certificate presented is not issued to this domain.");
+			break;
+		case PURPLE_CERTIFICATE_NO_CA_POOL:
+			return _("You have no database of root certificates, so "
+			         "this certificate cannot be validated.");
+			break;
+		case PURPLE_CERTIFICATE_INVALID_CHAIN:
+			return _("The certificate chain presented is invalid.");
+			break;
+		case PURPLE_CERTIFICATE_REVOKED:
+			return _("The certificate has been revoked.");
+			break;
+		case PURPLE_CERTIFICATE_UNKNOWN_ERROR:
+		default:
+			return _("An unknown certificate error occurred.");
+			break;
+	}
+}
+
 void
 purple_certificate_verify (PurpleCertificateVerifier *verifier,
 			   const gchar *subject_name, GList *cert_chain,
@@ -190,7 +275,7 @@
 }
 
 gboolean
-purple_certificate_check_signature_chain_with_failing(GList *chain,
+purple_certificate_check_signature_chain(GList *chain,
                                                       PurpleCertificate **failing)
 {
 	GList *cur;
@@ -229,7 +314,7 @@
 		uid = purple_certificate_get_unique_id(issuer);
 
 		ret = purple_certificate_get_times(issuer, &activation, &expiration);
-		if (!ret || now < activation || now > expiration) { 
+		if (!ret || now < activation || now > expiration) {
 			if (!ret)
 				purple_debug_error("certificate",
 						"...Failed to get validity times for certificate %s\n"
@@ -278,12 +363,6 @@
 	return TRUE;
 }
 
-gboolean
-purple_certificate_check_signature_chain(GList *chain)
-{
-	return purple_certificate_check_signature_chain_with_failing(chain, NULL);
-}
-
 PurpleCertificate *
 purple_certificate_import(PurpleCertificateScheme *scheme, const gchar *filename)
 {
@@ -294,6 +373,16 @@
 	return (scheme->import_certificate)(filename);
 }
 
+GSList *
+purple_certificates_import(PurpleCertificateScheme *scheme, const gchar *filename)
+{
+	g_return_val_if_fail(scheme, NULL);
+	g_return_val_if_fail(scheme->import_certificates, NULL);
+	g_return_val_if_fail(filename, NULL);
+
+	return (scheme->import_certificates)(filename);
+}
+
 gboolean
 purple_certificate_export(const gchar *filename, PurpleCertificate *crt)
 {
@@ -318,7 +407,7 @@
 	return (array1->len == array2->len) &&
 		(0 == memcmp(array1->data, array2->data, array1->len));
 }
-	
+
 GByteArray *
 purple_certificate_get_fingerprint_sha1(PurpleCertificate *crt)
 {
@@ -386,7 +475,6 @@
 
 	scheme = crt->scheme;
 
-	/* TODO: Instead of failing, maybe use get_subject_name and strcmp? */
 	g_return_val_if_fail(scheme->check_subject_name, FALSE);
 
 	return (scheme->check_subject_name)(crt, name);
@@ -411,6 +499,24 @@
 	return (scheme->get_times)(crt, activation, expiration);
 }
 
+GByteArray *
+purple_certificate_get_der_data(PurpleCertificate *crt)
+{
+	PurpleCertificateScheme *scheme;
+	GByteArray *data;
+
+	g_return_val_if_fail(crt, NULL);
+	g_return_val_if_fail(crt->scheme, NULL);
+
+	scheme = crt->scheme;
+
+	g_return_val_if_fail(scheme->get_der_data, NULL);
+
+	data = (scheme->get_der_data)(crt);
+
+	return data;
+}
+
 gchar *
 purple_certificate_pool_mkpath(PurpleCertificatePool *pool, const gchar *id)
 {
@@ -622,6 +728,7 @@
 		x509_singleuse_verify_cb );
 
 	/* Cleanup */
+	g_free(cn);
 	g_free(primary);
 	g_free(secondary);
 	g_free(sha_asc);
@@ -718,8 +825,9 @@
 	PurpleCertificateScheme *x509;
 	GDir *certdir;
 	const gchar *entry;
-	GPatternSpec *pempat;
+	GPatternSpec *pempat, *crtpat;
 	GList *iter = NULL;
+	GSList *crts = NULL;
 
 	if (x509_ca_initialized) return TRUE;
 
@@ -735,6 +843,7 @@
 
 	/* Use a glob to only read .pem files */
 	pempat = g_pattern_spec_new("*.pem");
+	crtpat = g_pattern_spec_new("*.crt");
 
 	/* Populate the certificates pool from the search path(s) */
 	for (iter = x509_ca_paths; iter; iter = iter->next) {
@@ -748,32 +857,40 @@
 			gchar *fullpath;
 			PurpleCertificate *crt;
 
-			if ( !g_pattern_match_string(pempat, entry) ) {
+			if (!g_pattern_match_string(pempat, entry) && !g_pattern_match_string(crtpat, entry)) {
 				continue;
 			}
 
 			fullpath = g_build_filename(iter->data, entry, NULL);
 
 			/* TODO: Respond to a failure in the following? */
-			crt = purple_certificate_import(x509, fullpath);
+			crts = purple_certificates_import(x509, fullpath);
 
-			if (x509_ca_quiet_put_cert(crt)) {
-				purple_debug_info("certificate/x509/ca",
-						  "Loaded %s\n",
-						  fullpath);
-			} else {
-				purple_debug_error("certificate/x509/ca",
-						  "Failed to load %s\n",
-						  fullpath);
+			while (crts && crts->data) {
+				crt = crts->data;
+				if (x509_ca_quiet_put_cert(crt)) {
+					gchar *name;
+					name = purple_certificate_get_subject_name(crt);
+					purple_debug_info("certificate/x509/ca",
+							  "Loaded %s from %s\n",
+							  name ? name : "(unknown)", fullpath);
+					g_free(name);
+				} else {
+					purple_debug_error("certificate/x509/ca",
+							  "Failed to load certificate from %s\n",
+							  fullpath);
+				}
+				purple_certificate_destroy(crt);
+				crts = g_slist_delete_link(crts, crts);
 			}
 
-			purple_certificate_destroy(crt);
 			g_free(fullpath);
 		}
 		g_dir_close(certdir);
 	}
 
 	g_pattern_spec_free(pempat);
+	g_pattern_spec_free(crtpat);
 
 	purple_debug_info("certificate/x509/ca",
 			  "Lazy init completed.\n");
@@ -792,7 +909,6 @@
 #else
 # ifdef SSL_CERTIFICATES_DIR
 		x509_ca_paths = g_list_append(NULL, g_strdup(SSL_CERTIFICATES_DIR));
-# else
 # endif
 		x509_ca_paths = g_list_append(x509_ca_paths,
 			g_build_filename(DATADIR, "purple", "ca-certs", NULL));
@@ -843,6 +959,22 @@
 	return NULL;
 }
 
+static GSList *
+x509_ca_locate_certs(GList *lst, const gchar *dn)
+{
+	GList *cur;
+	GSList *crts = NULL;
+
+	for (cur = lst; cur; cur = cur->next) {
+		x509_ca_element *el = cur->data;
+		if (purple_strequal(dn, el->dn)) {
+			crts = g_slist_prepend(crts, el);
+		}
+	}
+	return crts;
+}
+
+
 static gboolean
 x509_ca_cert_in_pool(const gchar *id)
 {
@@ -881,6 +1013,31 @@
 	return crt;
 }
 
+static GSList *
+x509_ca_get_certs(const gchar *id)
+{
+	GSList *crts = NULL, *els = NULL;
+
+	g_return_val_if_fail(x509_ca_lazy_init(), NULL);
+	g_return_val_if_fail(id, NULL);
+
+	/* Search the memory-cached pool */
+	els = x509_ca_locate_certs(x509_ca_certs, id);
+
+	if (els != NULL) {
+		GSList *cur;
+		/* Make a copy of the memcached ones for the function caller
+		   to play with */
+		for (cur = els; cur; cur = cur->next) {
+			x509_ca_element *el = cur->data;
+			crts = g_slist_prepend(crts, purple_certificate_copy(el->crt));
+		}
+		g_slist_free(els);
+	}
+
+	return crts;
+}
+
 static gboolean
 x509_ca_put_cert(const gchar *id, PurpleCertificate *crt)
 {
@@ -1266,10 +1423,105 @@
 }
 
 static void
-x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq);
+x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
+                             PurpleCertificateInvalidityFlags flags);
 
 static void
-x509_tls_cached_cert_in_cache(PurpleCertificateVerificationRequest *vrq)
+x509_tls_cached_complete(PurpleCertificateVerificationRequest *vrq,
+                         PurpleCertificateInvalidityFlags flags)
+{
+	PurpleCertificatePool *tls_peers;
+	PurpleCertificate *peer_crt = vrq->cert_chain->data;
+
+	if (flags & PURPLE_CERTIFICATE_FATALS_MASK) {
+		/* TODO: Also print any other warnings? */
+		const gchar *error;
+		gchar *tmp, *secondary;
+
+		if (flags & PURPLE_CERTIFICATE_INVALID_CHAIN)
+			error = invalidity_reason_to_string(PURPLE_CERTIFICATE_INVALID_CHAIN);
+		else if (flags & PURPLE_CERTIFICATE_REVOKED)
+			error = invalidity_reason_to_string(PURPLE_CERTIFICATE_REVOKED);
+		else
+			error = invalidity_reason_to_string(PURPLE_CERTIFICATE_UNKNOWN_ERROR);
+
+		tmp = g_strdup_printf(_("The certificate for %s could not be validated."),
+					vrq->subject_name);
+		secondary = g_strconcat(tmp, " ", error, NULL);
+		g_free(tmp);
+
+		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);
+		return;
+	} else if (flags & PURPLE_CERTIFICATE_NON_FATALS_MASK) {
+		/* Non-fatal error. Prompt the user. */
+		gchar *tmp;
+		GString *errors;
+		guint32 i = 1;
+
+		tmp = g_strdup_printf(_("The certificate for %s could not be validated."),
+					vrq->subject_name);
+		errors = g_string_new(tmp);
+		g_free(tmp);
+
+		errors = g_string_append_c(errors, '\n');
+
+		/* Special case a name mismatch because we want to display the two names... */
+		if (flags & PURPLE_CERTIFICATE_NAME_MISMATCH) {
+			gchar *sn = purple_certificate_get_subject_name(peer_crt);
+
+			if (sn) {
+				g_string_append_printf(errors, _("The certificate claims to be "
+							"from \"%s\" instead. This could mean that you are "
+							"not connecting to the service you believe you are."),
+							sn);
+				g_free(sn);
+
+				flags &= ~PURPLE_CERTIFICATE_NAME_MISMATCH;
+			}
+		}
+
+		while (i != PURPLE_CERTIFICATE_LAST) {
+			if (flags & i) {
+				errors = g_string_append_c(errors, '\n');
+				g_string_append(errors, invalidity_reason_to_string(i));
+			}
+
+			i <<= 1;
+		}
+
+		x509_tls_cached_user_auth(vrq, errors->str);
+		g_string_free(errors, TRUE);
+		return;
+	}
+
+	/* If we reach this point, the certificate is good. */
+
+	/* Look up the local cache and store it there for future use */
+	tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name,
+						 "tls_peers");
+	if (tls_peers) {
+		if (!purple_certificate_pool_store(tls_peers,vrq->subject_name,
+		                                   peer_crt)) {
+			purple_debug_error("certificate/x509/tls_cached",
+			                   "FAILED to cache peer certificate\n");
+		}
+	} else {
+		purple_debug_error("certificate/x509/tls_cached",
+		                   "Unable to locate tls_peers certificate cache.\n");
+	}
+
+	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID);
+}
+
+static void
+x509_tls_cached_cert_in_cache(PurpleCertificateVerificationRequest *vrq,
+                              PurpleCertificateInvalidityFlags flags)
 {
 	/* TODO: Looking this up by name over and over is expensive.
 	   Fix, please! */
@@ -1292,7 +1544,7 @@
 				   "Lookup failed on cached certificate!\n"
 				   "Falling back to full verification.\n");
 		/* vrq now becomes the problem of unknown_peer */
-		x509_tls_cached_unknown_peer(vrq);
+		x509_tls_cached_unknown_peer(vrq, flags);
 		return;
 	}
 
@@ -1303,14 +1555,12 @@
 	if (!memcmp(peer_fpr->data, cached_fpr->data, peer_fpr->len)) {
 		purple_debug_info("certificate/x509/tls_cached",
 				  "Peer cert matched cached\n");
-		/* vrq is now finished */
-		purple_certificate_verify_complete(vrq,
-						   PURPLE_CERTIFICATE_VALID);
+		x509_tls_cached_complete(vrq, flags);
 	} else {
 		purple_debug_error("certificate/x509/tls_cached",
 				  "Peer cert did NOT match cached\n");
 		/* vrq now becomes the problem of the user */
-		x509_tls_cached_unknown_peer(vrq);
+		x509_tls_cached_unknown_peer(vrq, flags);
 	}
 
 	purple_certificate_destroy(cached_crt);
@@ -1318,53 +1568,76 @@
 	g_byte_array_free(cached_fpr, TRUE);
 }
 
+/*
+ * This is called from two points in x509_tls_cached_unknown_peer below
+ * once we've verified the signature chain is valid. Now we need to verify
+ * the subject name of the certificate.
+ */
+static void
+x509_tls_cached_check_subject_name(PurpleCertificateVerificationRequest *vrq,
+                                   PurpleCertificateInvalidityFlags flags)
+{
+	PurpleCertificate *peer_crt;
+	GList *chain = vrq->cert_chain;
+
+	peer_crt = (PurpleCertificate *) chain->data;
+
+	/* Last, check that the hostname matches */
+	if ( ! purple_certificate_check_subject_name(peer_crt,
+						     vrq->subject_name) ) {
+		gchar *sn = purple_certificate_get_subject_name(peer_crt);
+
+		flags |= PURPLE_CERTIFICATE_NAME_MISMATCH;
+		purple_debug_error("certificate/x509/tls_cached",
+				  "Name mismatch: Certificate given for %s "
+				  "has a name of %s\n",
+				  vrq->subject_name, sn);
+		g_free(sn);
+	}
+
+	x509_tls_cached_complete(vrq, flags);
+}
+
 /* For when we've never communicated with this party before */
 /* TODO: Need ways to specify possibly multiple problems with a cert, or at
-   least  reprioritize them. For example, maybe the signature ought to be
-   checked BEFORE the hostname checking?
-   Stu thinks we should check the signature before the name, so we do now.
-   The above TODO still stands. */
+   least  reprioritize them.
+ */
 static void
-x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq)
+x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
+                             PurpleCertificateInvalidityFlags flags)
 {
-	PurpleCertificatePool *ca, *tls_peers;
+	PurpleCertificatePool *ca;
 	PurpleCertificate *peer_crt;
+	PurpleCertificate *ca_crt, *end_crt;
 	PurpleCertificate *failing_crt;
 	GList *chain = vrq->cert_chain;
-	gboolean chain_validated = FALSE;
+	GSList *ca_crts, *cur;
+	GByteArray *last_fpr, *ca_fpr;
+	gboolean valid = FALSE;
+	gchar *ca_id, *ca2_id;
 
 	peer_crt = (PurpleCertificate *) chain->data;
 
 	/* TODO: Figure out a way to check for a bad signature, as opposed to
 	   "not self-signed" */
 	if ( purple_certificate_signed_by(peer_crt, peer_crt) ) {
-		gchar *msg;
+		flags |= PURPLE_CERTIFICATE_SELF_SIGNED;
 
 		purple_debug_info("certificate/x509/tls_cached",
 				  "Certificate for %s is self-signed.\n",
 				  vrq->subject_name);
 
-		/* Prompt the user to authenticate the certificate */
-		/* vrq will be completed by user_auth */
-		msg = g_strdup_printf(_("The certificate presented by \"%s\" "
-					"is self-signed. It cannot be "
-					"automatically checked."),
-				      vrq->subject_name);
-
-		x509_tls_cached_user_auth(vrq,msg);
-
-		g_free(msg);
+		x509_tls_cached_check_subject_name(vrq, flags);
 		return;
 	} /* if (self signed) */
 
-	/* Next, attempt to verify the last certificate against a CA */
 	ca = purple_certificate_find_pool(x509_tls_cached.scheme_name, "ca");
 
 	/* Next, check that the certificate chain is valid */
-	if (purple_certificate_check_signature_chain_with_failing(chain,
-	                                                          &failing_crt))
-		chain_validated = TRUE;
-	else {
+	if (!purple_certificate_check_signature_chain(chain,
+				&failing_crt))
+	{
+		gboolean chain_validated = FALSE;
 		/*
 		 * Check if the failing certificate is in the CA store. If it is, then
 		 * consider this fully validated. This works around issues with some
@@ -1399,178 +1672,96 @@
 		 * If we get here, either the cert matched the stuff right above
 		 * or it didn't, in which case we give up and complain to the user.
 		 */
-		if (!chain_validated) {
+		if (!chain_validated)
 			/* TODO: Tell the user where the chain broke? */
-			/* TODO: This error will hopelessly confuse any
-			   non-elite user. */
-			gchar *secondary;
-
-			secondary = g_strdup_printf(_("The certificate chain presented"
-						      " for %s is not valid."),
-						    vrq->subject_name);
+			flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
 
-			/* TODO: Make this error either block the ensuing SSL
-			   connection error until the user dismisses this one, or
-			   stifle it. */
-			purple_notify_error(NULL, /* TODO: Probably wrong. */
-					    _("SSL Certificate Error"),
-					    _("Invalid certificate chain"),
-					    secondary );
-			g_free(secondary);
-
-			/* Okay, we're done here */
-			purple_certificate_verify_complete(vrq,
-							   PURPLE_CERTIFICATE_INVALID);
-			return;
-		}
+		x509_tls_cached_check_subject_name(vrq, flags);
+		return;
 	} /* if (signature chain not good) */
 
+	/* Next, attempt to verify the last certificate is signed by a trusted
+	 * CA, or is a trusted CA (based on fingerprint).
+	 */
 	/* If, for whatever reason, there is no Certificate Authority pool
-	   loaded, we will simply present it to the user for checking. */
+	   loaded, we'll verify the subject name and then warn about thsi. */
 	if ( !ca ) {
 		purple_debug_error("certificate/x509/tls_cached",
 				   "No X.509 Certificate Authority pool "
 				   "could be found!\n");
 
-		/* vrq will be completed by user_auth */
-		x509_tls_cached_user_auth(vrq,_("You have no database of root "
-						"certificates, so this "
-						"certificate cannot be "
-						"validated."));
+		flags |= PURPLE_CERTIFICATE_NO_CA_POOL;
+
+		x509_tls_cached_check_subject_name(vrq, flags);
 		return;
 	}
 
-	if (!chain_validated) {
-		GByteArray *last_fpr, *ca_fpr;
-		PurpleCertificate *ca_crt, *end_crt;
-		gchar *ca_id;
+	end_crt = g_list_last(chain)->data;
 
-		end_crt = g_list_last(chain)->data;
+	/* Attempt to look up the last certificate, and the last certificate's
+	 * issuer. 
+	 */
+	ca_id  = purple_certificate_get_issuer_unique_id(end_crt);
+	ca2_id = purple_certificate_get_unique_id(end_crt);
+	purple_debug_info("certificate/x509/tls_cached",
+			  "Checking for a CA with DN=%s\n",
+			  ca_id);
+	purple_debug_info("certificate/x509/tls_cached",
+			  "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;
 
-		/* Attempt to look up the last certificate's issuer */
-		ca_id = purple_certificate_get_issuer_unique_id(end_crt);
-		purple_debug_info("certificate/x509/tls_cached",
-				  "Checking for a CA with DN=%s\n",
-				  ca_id);
-		ca_crt = purple_certificate_pool_retrieve(ca, ca_id);
-		if ( NULL == ca_crt ) {
-			purple_debug_warning("certificate/x509/tls_cached",
-					  "Certificate Authority with DN='%s' not "
-					  "found. I'll prompt the user, I guess.\n",
-					  ca_id);
-			g_free(ca_id);
-			/* vrq will be completed by user_auth */
-			x509_tls_cached_user_auth(vrq,_("The root certificate this "
-							"one claims to be issued by "
-							"is unknown to Pidgin."));
-			return;
-		}
+		purple_debug_warning("certificate/x509/tls_cached",
+				  "No Certificate Authorities with either DN "
+				  "found. I'll prompt the user, I guess.\n");
+
+		x509_tls_cached_check_subject_name(vrq, flags);
+		return;
+	}
 
-		g_free(ca_id);
+	/*
+	 * Check the fingerprints; if they match, then this certificate *is* one
+	 * of the designated "trusted roots", and we don't need to verify the
+	 * signature. This is good because some of the older roots are self-signed
+	 * with bad hash algorithms that we don't want to allow in any other
+	 * circumstances (one of Verisign's root CAs is self-signed with MD2).
+	 *
+	 * If the fingerprints don't match, we'll fall back to checking the
+	 * signature.
+	 */
+	last_fpr = purple_certificate_get_fingerprint_sha1(end_crt);
+	for (cur = ca_crts; cur; cur = cur->next) {
+		ca_crt = cur->data;
+		ca_fpr = purple_certificate_get_fingerprint_sha1(ca_crt);
 
-		/*
-		 * Check the fingerprints; if they match, then this certificate *is* one
-		 * of the designated "trusted roots", and we don't need to verify the
-		 * signature. This is good because some of the older roots are self-signed
-		 * with bad hash algorithms that we don't want to allow in any other
-		 * circumstances (one of Verisign's root CAs is self-signed with MD2).
-		 *
-		 * If the fingerprints don't match, we'll fall back to checking the
-		 * signature.
-		 *
-		 * GnuTLS doesn't seem to include the final root in the verification
-		 * list, so this check will never succeed.  NSS *does* include it in
-		 * the list, so here we are.
-		 */
-		last_fpr = purple_certificate_get_fingerprint_sha1(end_crt);
-		ca_fpr   = purple_certificate_get_fingerprint_sha1(ca_crt);
-
-		if ( !byte_arrays_equal(last_fpr, ca_fpr) &&
-				!purple_certificate_signed_by(end_crt, ca_crt) )
+		if ( byte_arrays_equal(last_fpr, ca_fpr) ||
+				purple_certificate_signed_by(end_crt, ca_crt) )
 		{
 			/* TODO: If signed_by ever returns a reason, maybe mention
 			   that, too. */
 			/* TODO: Also mention the CA involved. While I could do this
 			   now, a full DN is a little much with which to assault the
 			   user's poor, leaky eyes. */
-			/* TODO: This error message makes my eyes cross, and I wrote it */
-			gchar * secondary =
-				g_strdup_printf(_("The certificate chain presented by "
-						  "%s does not have a valid digital "
-						  "signature from the Certificate "
-						  "Authority from which it claims to "
-						  "have a signature."),
-						vrq->subject_name);
-
-			purple_notify_error(NULL, /* TODO: Probably wrong */
-					    _("SSL Certificate Error"),
-					    _("Invalid certificate authority"
-					      " signature"),
-					    secondary);
-			g_free(secondary);
-
-			/* Signal "bad cert" */
-			purple_certificate_verify_complete(vrq,
-							   PURPLE_CERTIFICATE_INVALID);
-
-			purple_certificate_destroy(ca_crt);
+			valid = TRUE;
 			g_byte_array_free(ca_fpr, TRUE);
-			g_byte_array_free(last_fpr, TRUE);
-			return;
-		} /* if (CA signature not good) */
+			break;
+		}
 
 		g_byte_array_free(ca_fpr, TRUE);
-		g_byte_array_free(last_fpr, TRUE);
 	}
 
-	/* Last, check that the hostname matches */
-	if ( ! purple_certificate_check_subject_name(peer_crt,
-						     vrq->subject_name) ) {
-		gchar *sn = purple_certificate_get_subject_name(peer_crt);
-		gchar *msg;
-
-		purple_debug_error("certificate/x509/tls_cached",
-				  "Name mismatch: Certificate given for %s "
-				  "has a name of %s\n",
-				  vrq->subject_name, sn);
-
-		/* Prompt the user to authenticate the certificate */
-		/* TODO: Provide the user with more guidance about why he is
-		   being prompted */
-		/* vrq will be completed by user_auth */
-		msg = g_strdup_printf(_("The certificate presented by \"%s\" "
-					"claims to be from \"%s\" instead.  "
-					"This could mean that you are not "
-					"connecting to the service you "
-					"believe you are."),
-				      vrq->subject_name, sn);
-
-		x509_tls_cached_user_auth(vrq,msg);
+	if (valid == FALSE)
+		flags |= PURPLE_CERTIFICATE_INVALID_CHAIN;
 
-		g_free(sn);
-		g_free(msg);
-		return;
-	} /* if (name mismatch) */
-
-	/* If we reach this point, the certificate is good. */
-	/* Look up the local cache and store it there for future use */
-	tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name,
-						 "tls_peers");
+	g_slist_foreach(ca_crts, (GFunc)purple_certificate_destroy, NULL);
+	g_slist_free(ca_crts);
+	g_byte_array_free(last_fpr, TRUE);
 
-	if (tls_peers) {
-		if (!purple_certificate_pool_store(tls_peers,vrq->subject_name,
-						   peer_crt) ) {
-			purple_debug_error("certificate/x509/tls_cached",
-					   "FAILED to cache peer certificate\n");
-		}
-	} else {
-		purple_debug_error("certificate/x509/tls_cached",
-				   "Unable to locate tls_peers certificate "
-				   "cache.\n");
-	}
-
-	/* Whew! Done! */
-	purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID);
+	x509_tls_cached_check_subject_name(vrq, flags);
 }
 
 static void
@@ -1579,6 +1770,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;
 	gboolean ret;
 
 	g_return_if_fail(vrq);
@@ -1594,37 +1786,21 @@
 	now = time(NULL);
 	ret = purple_certificate_get_times(vrq->cert_chain->data, &activation,
 	                                   &expiration);
-	if (!ret || now > expiration || now < activation) {
-		gchar *secondary;
-
-		if (!ret)
-			purple_debug_error("certificate/x509/tls_cached",
-					"Failed to get validity times for certificate %s\n",
-					vrq->subject_name);
-		else if (now > expiration)
-			purple_debug_error("certificate/x509/tls_cached",
-					"Certificate %s expired at %s\n",
-					vrq->subject_name, ctime(&expiration));
-		else
-			purple_debug_error("certificate/x509/tls_cached",
-					"Certificate %s is not yet valid, will be at %s\n",
-					vrq->subject_name, ctime(&activation));
-
-		/* FIXME 2.6.1 */
-		secondary = g_strdup_printf(_("The certificate chain presented"
-					" for %s is not valid."),
-					vrq->subject_name);
-
-		purple_notify_error(NULL, /* TODO: Probably wrong. */
-					_("SSL Certificate Error"),
-					_("Invalid certificate chain"),
-					secondary );
-		g_free(secondary);
-
-		/* Okay, we're done here */
-		purple_certificate_verify_complete(vrq,
-						    PURPLE_CERTIFICATE_INVALID);
-		return;
+	if (!ret) {
+		flags |= PURPLE_CERTIFICATE_EXPIRED | PURPLE_CERTIFICATE_NOT_ACTIVATED;
+		purple_debug_error("certificate/x509/tls_cached",
+				"Failed to get validity times for certificate %s\n",
+				vrq->subject_name);
+	} else if (now > expiration) {
+		flags |= PURPLE_CERTIFICATE_EXPIRED;
+		purple_debug_error("certificate/x509/tls_cached",
+				"Certificate %s expired at %s\n",
+				vrq->subject_name, ctime(&expiration));
+	} else if (now < activation) {
+		flags |= PURPLE_CERTIFICATE_NOT_ACTIVATED;
+		purple_debug_error("certificate/x509/tls_cached",
+				"Certificate %s is not yet valid, will be at %s\n",
+				vrq->subject_name, ctime(&activation));
 	}
 
 	tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name,tls_peers_name);
@@ -1634,9 +1810,8 @@
 				   "Couldn't find local peers cache %s\n",
 				   tls_peers_name);
 
-
 		/* vrq now becomes the problem of unknown_peer */
-		x509_tls_cached_unknown_peer(vrq);
+		x509_tls_cached_unknown_peer(vrq, flags);
 		return;
 	}
 
@@ -1647,12 +1822,12 @@
 		purple_debug_info("certificate/x509/tls_cached",
 				  "...Found cached cert\n");
 		/* vrq is now the responsibility of cert_in_cache */
-		x509_tls_cached_cert_in_cache(vrq);
+		x509_tls_cached_cert_in_cache(vrq, flags);
 	} else {
 		purple_debug_warning("certificate/x509/tls_cached",
 				  "...Not in cache\n");
 		/* vrq now becomes the problem of unknown_peer */
-		x509_tls_cached_unknown_peer(vrq);
+		x509_tls_cached_unknown_peer(vrq, flags);
 	}
 }
 
--- a/libpurple/certificate.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/certificate.h	Wed Jun 13 19:30:27 2012 -0400
@@ -2,7 +2,6 @@
  * @file certificate.h Public-Key Certificate API
  * @ingroup core
  * @see @ref certificate-signals
- * @since 2.2.0
  */
 
 /*
@@ -35,11 +34,6 @@
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
 typedef enum
 {
 	PURPLE_CERTIFICATE_INVALID = 0,
@@ -250,10 +244,25 @@
 	/** Retrieve the certificate activation/expiration times */
 	gboolean (* get_times)(PurpleCertificate *crt, time_t *activation, time_t *expiration);
 
+	/** Imports certificates from a file
+	 *
+	 *  @param filename   File to import the certificates from
+	 *  @return           GSList of pointers to the newly allocated Certificate structs
+	 *                    or NULL on failure.
+	 */
+	GSList * (* import_certificates)(const gchar * filename);
+
+	/**
+	 * Retrieves the certificate data in DER form
+	 *
+	 * @param crt   Certificate instance
+	 * @return Binary DER representation of certificate - must be freed using
+	 *         g_byte_array_free()
+	 */
+	GByteArray * (* get_der_data)(PurpleCertificate *crt);
+
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
 };
 
 /** A set of operations used to provide logic for verifying a Certificate's
@@ -343,6 +352,8 @@
 	gpointer cb_data;
 };
 
+G_BEGIN_DECLS
+
 /*****************************************************************************/
 /** @name Certificate Verification Functions                                 */
 /*****************************************************************************/
@@ -455,33 +466,12 @@
  *                   chain fails to validate, this will be set to the
  *                   certificate whose signature could not be validated.
  * @return TRUE if the chain is valid. See description.
- *
- * @since 2.6.0
- * @deprecated  This function will become
- *              purple_certificate_check_signature_chain in 3.0.0
  */
 gboolean
-purple_certificate_check_signature_chain_with_failing(GList *chain,
+purple_certificate_check_signature_chain(GList *chain,
 		PurpleCertificate **failing);
 
 /**
- * Check that a certificate chain is valid
- *
- * Uses purple_certificate_signed_by() to verify that each PurpleCertificate
- * in the chain carries a valid signature from the next. A single-certificate
- * chain is considered to be valid.
- *
- * @param chain      List of PurpleCertificate instances comprising the chain,
- *                   in the order certificate, issuer, issuer's issuer, etc.
- * @return TRUE if the chain is valid. See description.
- * @todo Specify which certificate in the chain caused a failure
- * @deprecated  This function will be removed in 3.0.0 and replaced with
- *              purple_certificate_check_signature_chain_with_failing
- */
-gboolean
-purple_certificate_check_signature_chain(GList *chain);
-
-/**
  * Imports a PurpleCertificate from a file
  *
  * @param scheme      Scheme to import under
@@ -492,6 +482,16 @@
 purple_certificate_import(PurpleCertificateScheme *scheme, const gchar *filename);
 
 /**
+ * Imports a list of PurpleCertificates from a file
+ *
+ * @param scheme      Scheme to import under
+ * @param filename    File path to import from
+ * @return Pointer to a GSList of new PurpleCertificates, or NULL on failure
+ */
+GSList *
+purple_certificates_import(PurpleCertificateScheme *scheme, const gchar *filename);
+
+/**
  * Exports a PurpleCertificate to a file
  *
  * @param filename    File to export the certificate to
@@ -566,6 +566,17 @@
 gboolean
 purple_certificate_get_times(PurpleCertificate *crt, time_t *activation, time_t *expiration);
 
+/**
+ * Retrieves the certificate data in DER form.
+ *
+ * @param crt Certificate instance
+ *
+ * @return Binary DER representation of the certificate - must be freed using
+ *         g_byte_array_free().
+ */
+GByteArray *
+purple_certificate_get_der_data(PurpleCertificate *crt);
+
 /*@}*/
 
 /*****************************************************************************/
@@ -819,8 +830,6 @@
  */
 void purple_certificate_add_ca_search_path(const char *path);
 
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
 
 #endif /* _PURPLE_CERTIFICATE_H */
--- a/libpurple/cipher.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/cipher.c	Wed Jun 13 19:30:27 2012 -0400
@@ -5,23 +5,6 @@
  * 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>
- *
- * Original md4 taken from linux kernel
- * MD4 Message Digest Algorithm (RFC1320).
- *
- * Implementation derived from Andrew Tridgell and Steve French's
- * CIFS MD4 implementation, and the cryptoapi implementation
- * originally based on the public domain implementation written
- * by Colin Plumb in 1993.
- *
- * Copyright (c) Andrew Tridgell 1997-1998.
- * Modified by Steve French (sfrench@us.ibm.com) 2002
- * Copyright (c) Cryptoapi developers.
- * Copyright (c) 2002 David S. Miller (davem@redhat.com)
- * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
- *
  * Original des taken from gpg
  *
  * des.c - DES and Triple-DES encryption/decryption Algorithm
@@ -50,10 +33,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-#include <glib.h>
-#include <string.h>
-#include <stdio.h>
-
 #include "internal.h"
 #include "cipher.h"
 #include "dbus-maybe.h"
@@ -61,2454 +40,6 @@
 #include "signals.h"
 #include "value.h"
 
-#if GLIB_CHECK_VERSION(2,16,0)
-static void
-purple_g_checksum_init(PurpleCipherContext *context, GChecksumType type)
-{
-	GChecksum *checksum;
-
-	checksum = g_checksum_new(type);
-	purple_cipher_context_set_data(context, checksum);
-}
-
-static void
-purple_g_checksum_reset(PurpleCipherContext *context, GChecksumType type)
-{
-	GChecksum *checksum;
-
-	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
-purple_g_checksum_uninit(PurpleCipherContext *context)
-{
-	GChecksum *checksum;
-
-	checksum = purple_cipher_context_get_data(context);
-	g_return_if_fail(checksum != NULL);
-
-	g_checksum_free(checksum);
-}
-
-static void
-purple_g_checksum_append(PurpleCipherContext *context, const guchar *data,
-                         gsize len)
-{
-	GChecksum *checksum;
-
-	checksum = purple_cipher_context_get_data(context);
-	g_return_if_fail(checksum != NULL);
-
-	while (len >= G_MAXSSIZE) {
-		g_checksum_update(checksum, data, G_MAXSSIZE);
-		len -= G_MAXSSIZE;
-		data += G_MAXSSIZE;
-	}
-
-	if (len)
-		g_checksum_update(checksum, data, len);
-}
-
-static gboolean
-purple_g_checksum_digest(PurpleCipherContext *context, GChecksumType type,
-                         gsize len, guchar *digest, gsize *out_len)
-{
-	GChecksum *checksum;
-	const gssize required_length = g_checksum_type_get_length(type);
-
-	checksum = purple_cipher_context_get_data(context);
-
-	g_return_val_if_fail(len >= required_length, FALSE);
-	g_return_val_if_fail(checksum != NULL, FALSE);
-
-	g_checksum_get_digest(checksum, digest, &len);
-
-	purple_cipher_context_reset(context, NULL);
-
-	if (out_len)
-		*out_len = len;
-
-	return TRUE;
-}
-#endif
-
-
-/*******************************************************************************
- * MD5
- ******************************************************************************/
-#define MD5_HMAC_BLOCK_SIZE	64
-
-static size_t
-md5_get_block_size(PurpleCipherContext *context)
-{
-	/* This does not change (in this case) */
-	return MD5_HMAC_BLOCK_SIZE;
-}
-
-#if GLIB_CHECK_VERSION(2,16,0)
-
-static void
-md5_init(PurpleCipherContext *context, void *extra)
-{
-	purple_g_checksum_init(context, G_CHECKSUM_MD5);
-}
-
-static void
-md5_reset(PurpleCipherContext *context, void *extra)
-{
-	purple_g_checksum_reset(context, G_CHECKSUM_MD5);
-}
-
-static gboolean
-md5_digest(PurpleCipherContext *context, gsize in_len, guchar digest[16],
-           size_t *out_len)
-{
-	return purple_g_checksum_digest(context, G_CHECKSUM_MD5, in_len,
-	                                digest, out_len);
-}
-
-static PurpleCipherOps MD5Ops = {
-	NULL,			/* Set Option		*/
-	NULL,			/* Get Option		*/
-	md5_init,		/* init				*/
-	md5_reset,		/* reset			*/
-	purple_g_checksum_uninit,	/* uninit			*/
-	NULL,			/* set iv			*/
-	purple_g_checksum_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 */
-};
-
-#else /* GLIB_CHECK_VERSION(2,16,0) */
-
-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 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 */
-};
-
-#endif /* GLIB_CHECK_VERSION(2,16,0) */
-
-/*******************************************************************************
- * MD4
- ******************************************************************************/
-#define MD4_DIGEST_SIZE		16
-#define MD4_HMAC_BLOCK_SIZE	64
-#define MD4_BLOCK_WORDS		16
-#define MD4_HASH_WORDS		4
-
-
-
-struct MD4_Context {
-	guint32 hash[MD4_HASH_WORDS];
-	guint32 block[MD4_BLOCK_WORDS];
-	guint64 byte_count;
-};
-
-static inline guint32 lshift(guint32 x, unsigned int s)
-{
-	x &= 0xFFFFFFFF;
-	return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s));
-}
-
-static inline guint32 F(guint32 x, guint32 y, guint32 z)
-{
-	return (x & y) | ((~x) & z);
-}
-
-static inline guint32 G(guint32 x, guint32 y, guint32 z)
-{
-	return (x & y) | (x & z) | (y & z);
-}
-
-static inline guint32 H(guint32 x, guint32 y, guint32 z)
-{
-	return x ^ y ^ z;
-}
-
-#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s))
-#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (guint32)0x5A827999,s))
-#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (guint32)0x6ED9EBA1,s))
-
-static inline void le32_to_cpu_array(guint32 *buf, unsigned int words)
-{
-	while (words--) {
-		*buf=GUINT_FROM_LE(*buf);
-		buf++;
-	}
-}
-
-static inline void cpu_to_le32_array(guint32 *buf, unsigned int words)
-{
-	while (words--) {
-		*buf=GUINT_TO_LE(*buf);
-		buf++;
-	}
-}
-
-static void md4_transform(guint32 *hash, guint32 const *in)
-{
-	guint32 a, b, c, d;
-
-	a = hash[0];
-	b = hash[1];
-	c = hash[2];
-	d = hash[3];
-
-	ROUND1(a, b, c, d, in[0], 3);
-	ROUND1(d, a, b, c, in[1], 7);
-	ROUND1(c, d, a, b, in[2], 11);
-	ROUND1(b, c, d, a, in[3], 19);
-	ROUND1(a, b, c, d, in[4], 3);
-	ROUND1(d, a, b, c, in[5], 7);
-	ROUND1(c, d, a, b, in[6], 11);
-	ROUND1(b, c, d, a, in[7], 19);
-	ROUND1(a, b, c, d, in[8], 3);
-	ROUND1(d, a, b, c, in[9], 7);
-	ROUND1(c, d, a, b, in[10], 11);
-	ROUND1(b, c, d, a, in[11], 19);
-	ROUND1(a, b, c, d, in[12], 3);
-	ROUND1(d, a, b, c, in[13], 7);
-	ROUND1(c, d, a, b, in[14], 11);
-	ROUND1(b, c, d, a, in[15], 19);
-
-	ROUND2(a, b, c, d,in[ 0], 3);
-	ROUND2(d, a, b, c, in[4], 5);
-	ROUND2(c, d, a, b, in[8], 9);
-	ROUND2(b, c, d, a, in[12], 13);
-	ROUND2(a, b, c, d, in[1], 3);
-	ROUND2(d, a, b, c, in[5], 5);
-	ROUND2(c, d, a, b, in[9], 9);
-	ROUND2(b, c, d, a, in[13], 13);
-	ROUND2(a, b, c, d, in[2], 3);
-	ROUND2(d, a, b, c, in[6], 5);
-	ROUND2(c, d, a, b, in[10], 9);
-	ROUND2(b, c, d, a, in[14], 13);
-	ROUND2(a, b, c, d, in[3], 3);
-	ROUND2(d, a, b, c, in[7], 5);
-	ROUND2(c, d, a, b, in[11], 9);
-	ROUND2(b, c, d, a, in[15], 13);
-
-	ROUND3(a, b, c, d,in[ 0], 3);
-	ROUND3(d, a, b, c, in[8], 9);
-	ROUND3(c, d, a, b, in[4], 11);
-	ROUND3(b, c, d, a, in[12], 15);
-	ROUND3(a, b, c, d, in[2], 3);
-	ROUND3(d, a, b, c, in[10], 9);
-	ROUND3(c, d, a, b, in[6], 11);
-	ROUND3(b, c, d, a, in[14], 15);
-	ROUND3(a, b, c, d, in[1], 3);
-	ROUND3(d, a, b, c, in[9], 9);
-	ROUND3(c, d, a, b, in[5], 11);
-	ROUND3(b, c, d, a, in[13], 15);
-	ROUND3(a, b, c, d, in[3], 3);
-	ROUND3(d, a, b, c, in[11], 9);
-	ROUND3(c, d, a, b, in[7], 11);
-	ROUND3(b, c, d, a, in[15], 15);
-
-	hash[0] += a;
-	hash[1] += b;
-	hash[2] += c;
-	hash[3] += d;
-}
-
-static inline void md4_transform_helper(struct MD4_Context *ctx)
-{
-	le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(guint32));
-	md4_transform(ctx->hash, ctx->block);
-}
-
-static void
-md4_init(PurpleCipherContext *context, gpointer extra) {
-	struct MD4_Context *mctx;
-	mctx = g_new0(struct MD4_Context, 1);
-	purple_cipher_context_set_data(context, mctx);
-	purple_cipher_context_reset(context, extra);
-
-	mctx->hash[0] = 0x67452301;
-	mctx->hash[1] = 0xefcdab89;
-	mctx->hash[2] = 0x98badcfe;
-	mctx->hash[3] = 0x10325476;
-	mctx->byte_count = 0;
-}
-
-static void
-md4_reset(PurpleCipherContext *context, gpointer extra) {
-	struct MD4_Context *mctx;
-
-	mctx = purple_cipher_context_get_data(context);
-
-	mctx->hash[0] = 0x67452301;
-	mctx->hash[1] = 0xefcdab89;
-	mctx->hash[2] = 0x98badcfe;
-	mctx->hash[3] = 0x10325476;
-	mctx->byte_count = 0;
-}
-
-static void
-md4_append(PurpleCipherContext *context, const guchar *data, size_t len)
-{
-	struct MD4_Context *mctx = purple_cipher_context_get_data(context);
-	const guint32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
-
-	mctx->byte_count += len;
-
-	if (avail > len) {
-		memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
-				data, len);
-		return;
-	}
-
-	memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
-			data, avail);
-
-	md4_transform_helper(mctx);
-	data += avail;
-	len -= avail;
-
-	while (len >= sizeof(mctx->block)) {
-		memcpy(mctx->block, data, sizeof(mctx->block));
-		md4_transform_helper(mctx);
-		data += sizeof(mctx->block);
-		len -= sizeof(mctx->block);
-	}
-
-	memcpy(mctx->block, data, len);
-}
-
-static gboolean
-md4_digest(PurpleCipherContext *context, size_t in_len, guchar *out,
-		                   size_t *out_len)
-{
-	struct MD4_Context *mctx = purple_cipher_context_get_data(context);
-	const unsigned int offset = mctx->byte_count & 0x3f;
-	char *p = (char *)mctx->block + offset;
-	int padding = 56 - (offset + 1);
-
-
-	if(in_len<16) return FALSE;
-	if(out_len) *out_len = 16;
-	*p++ = 0x80;
-	if (padding < 0) {
-		memset(p, 0x00, padding + sizeof (guint64));
-		md4_transform_helper(mctx);
-		p = (char *)mctx->block;
-		padding = 56;
-	}
-
-	memset(p, 0, padding);
-	mctx->block[14] = mctx->byte_count << 3;
-	mctx->block[15] = mctx->byte_count >> 29;
-	le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
-				sizeof(guint64)) / sizeof(guint32));
-	md4_transform(mctx->hash, mctx->block);
-	cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(guint32));
-	memcpy(out, mctx->hash, sizeof(mctx->hash));
-	memset(mctx, 0, sizeof(*mctx));
-	return TRUE;
-}
-
-static void
-md4_uninit(PurpleCipherContext *context) {
-	struct MD4_Context *md4_context;
-
-	purple_cipher_context_reset(context, NULL);
-
-	md4_context = purple_cipher_context_get_data(context);
-	memset(md4_context, 0, sizeof(md4_context));
-
-	g_free(md4_context);
-	md4_context = NULL;
-}
-
-static size_t
-md4_get_block_size(PurpleCipherContext *context)
-{
-	/* This does not change (in this case) */
-	return MD4_HMAC_BLOCK_SIZE;
-}
-
-static PurpleCipherOps MD4Ops = {
-	NULL,                   /* Set option */
-	NULL,                   /* Get option */
-	md4_init,               /* init */
-	md4_reset,              /* reset */
-	md4_uninit,             /* uninit */
-	NULL,                   /* set iv */
-	md4_append,             /* append */
-	md4_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 */
-	md4_get_block_size,     /* get block size */
-	NULL                    /* set key with len */
-};
-
-/*******************************************************************************
- * HMAC
- ******************************************************************************/
-
-struct HMAC_Context {
-	PurpleCipherContext *hash;
-	char *name;
-	int blocksize;
-	guchar *opad;
-};
-
-static void
-hmac_init(PurpleCipherContext *context, gpointer extra)
-{
-	struct HMAC_Context *hctx;
-	hctx = g_new0(struct HMAC_Context, 1);
-	purple_cipher_context_set_data(context, hctx);
-	purple_cipher_context_reset(context, extra);
-}
-
-static void
-hmac_reset(PurpleCipherContext *context, gpointer extra)
-{
-	struct HMAC_Context *hctx;
-
-	hctx = purple_cipher_context_get_data(context);
-
-	g_free(hctx->name);
-	hctx->name = NULL;
-	if (hctx->hash)
-		purple_cipher_context_destroy(hctx->hash);
-	hctx->hash = NULL;
-	hctx->blocksize = 0;
-	g_free(hctx->opad);
-	hctx->opad = NULL;
-}
-
-static void
-hmac_set_opt(PurpleCipherContext *context, const gchar *name, void *value)
-{
-	struct HMAC_Context *hctx;
-
-	hctx = purple_cipher_context_get_data(context);
-
-	if (purple_strequal(name, "hash")) {
-		g_free(hctx->name);
-		if (hctx->hash)
-			purple_cipher_context_destroy(hctx->hash);
-		hctx->name = g_strdup((char*)value);
-		hctx->hash = purple_cipher_context_new_by_name((char *)value, NULL);
-		hctx->blocksize = purple_cipher_context_get_block_size(hctx->hash);
-	}
-}
-
-static void *
-hmac_get_opt(PurpleCipherContext *context, const gchar *name)
-{
-	struct HMAC_Context *hctx;
-
-	hctx = purple_cipher_context_get_data(context);
-
-	if (purple_strequal(name, "hash")) {
-		return hctx->name;
-	}
-
-	return NULL;
-}
-
-static void
-hmac_append(PurpleCipherContext *context, const guchar *data, size_t len)
-{
-	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
-
-	g_return_if_fail(hctx->hash != NULL);
-
-	purple_cipher_context_append(hctx->hash, data, len);
-}
-
-static gboolean
-hmac_digest(PurpleCipherContext *context, size_t in_len, guchar *out, size_t *out_len)
-{
-	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
-	PurpleCipherContext *hash = hctx->hash;
-	guchar *inner_hash;
-	size_t hash_len;
-	gboolean result;
-
-	g_return_val_if_fail(hash != NULL, FALSE);
-
-	inner_hash = g_malloc(100); /* TODO: Should be enough for now... */
-	result = purple_cipher_context_digest(hash, 100, inner_hash, &hash_len);
-
-	purple_cipher_context_reset(hash, NULL);
-
-	purple_cipher_context_append(hash, hctx->opad, hctx->blocksize);
-	purple_cipher_context_append(hash, inner_hash, hash_len);
-
-	g_free(inner_hash);
-
-	result = result && purple_cipher_context_digest(hash, in_len, out, out_len);
-
-	return result;
-}
-
-static void
-hmac_uninit(PurpleCipherContext *context)
-{
-	struct HMAC_Context *hctx;
-
-	purple_cipher_context_reset(context, NULL);
-
-	hctx = purple_cipher_context_get_data(context);
-
-	g_free(hctx);
-}
-
-static void
-hmac_set_key_with_len(PurpleCipherContext *context, const guchar * key, size_t key_len)
-{
-	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
-	int blocksize, i;
-	guchar *ipad;
-	guchar *full_key;
-
-	g_return_if_fail(hctx->hash != NULL);
-
-	g_free(hctx->opad);
-
-	blocksize = hctx->blocksize;
-	ipad = g_malloc(blocksize);
-	hctx->opad = g_malloc(blocksize);
-
-	if (key_len > blocksize) {
-		purple_cipher_context_reset(hctx->hash, NULL);
-		purple_cipher_context_append(hctx->hash, key, key_len);
-		full_key = g_malloc(100); /* TODO: Should be enough for now... */
-		purple_cipher_context_digest(hctx->hash, 100, full_key, &key_len);
-	} else
-		full_key = g_memdup(key, key_len);
-
-    if (key_len < blocksize) {
-    	full_key = g_realloc(full_key, blocksize);
-    	memset(full_key + key_len, 0, blocksize - key_len);
-    }
-
-	for(i = 0; i < blocksize; i++) {
-		ipad[i] = 0x36 ^ full_key[i];
-		hctx->opad[i] = 0x5c ^ full_key[i];
-	}
-
-	g_free(full_key);
-
-	purple_cipher_context_reset(hctx->hash, NULL);
-	purple_cipher_context_append(hctx->hash, ipad, blocksize);
-	g_free(ipad);
-}
-
-static void
-hmac_set_key(PurpleCipherContext *context, const guchar * key)
-{
-	hmac_set_key_with_len(context, key, strlen((char *)key));
-}
-
-static size_t
-hmac_get_block_size(PurpleCipherContext *context)
-{
-	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
-
-	return hctx->blocksize;
-}
-
-static PurpleCipherOps HMACOps = {
-	hmac_set_opt,           /* Set option */
-	hmac_get_opt,           /* Get option */
-	hmac_init,               /* init */
-	hmac_reset,              /* reset */
-	hmac_uninit,             /* uninit */
-	NULL,                   /* set iv */
-	hmac_append,             /* append */
-	hmac_digest,             /* digest */
-	NULL,                   /* encrypt */
-	NULL,                   /* decrypt */
-	NULL,                   /* set salt */
-	NULL,                   /* get salt size */
-	hmac_set_key,           /* set key */
-	NULL,                   /* get key size */
-	NULL,                   /* set batch mode */
-	NULL,                   /* get batch mode */
-	hmac_get_block_size,    /* get block size */
-	hmac_set_key_with_len   /* set key with len */
-};
-
-/******************************************************************************
- * DES
- *****************************************************************************/
-
-typedef struct _des_ctx
-{
-	guint32 encrypt_subkeys[32];
-	guint32 decrypt_subkeys[32];
-} des_ctx[1];
-
-/*
- *  The s-box values are permuted according to the 'primitive function P'
- */
-static const guint32 sbox1[64] =
-{
-	0x00808200, 0x00000000, 0x00008000, 0x00808202, 0x00808002, 0x00008202, 0x00000002, 0x00008000,
-	0x00000200, 0x00808200, 0x00808202, 0x00000200, 0x00800202, 0x00808002, 0x00800000, 0x00000002,
-	0x00000202, 0x00800200, 0x00800200, 0x00008200, 0x00008200, 0x00808000, 0x00808000, 0x00800202,
-	0x00008002, 0x00800002, 0x00800002, 0x00008002, 0x00000000, 0x00000202, 0x00008202, 0x00800000,
-	0x00008000, 0x00808202, 0x00000002, 0x00808000, 0x00808200, 0x00800000, 0x00800000, 0x00000200,
-	0x00808002, 0x00008000, 0x00008200, 0x00800002, 0x00000200, 0x00000002, 0x00800202, 0x00008202,
-	0x00808202, 0x00008002, 0x00808000, 0x00800202, 0x00800002, 0x00000202, 0x00008202, 0x00808200,
-	0x00000202, 0x00800200, 0x00800200, 0x00000000, 0x00008002, 0x00008200, 0x00000000, 0x00808002
-};
-
-static const guint32 sbox2[64] =
-{
-	0x40084010, 0x40004000, 0x00004000, 0x00084010, 0x00080000, 0x00000010, 0x40080010, 0x40004010,
-	0x40000010, 0x40084010, 0x40084000, 0x40000000, 0x40004000, 0x00080000, 0x00000010, 0x40080010,
-	0x00084000, 0x00080010, 0x40004010, 0x00000000, 0x40000000, 0x00004000, 0x00084010, 0x40080000,
-	0x00080010, 0x40000010, 0x00000000, 0x00084000, 0x00004010, 0x40084000, 0x40080000, 0x00004010,
-	0x00000000, 0x00084010, 0x40080010, 0x00080000, 0x40004010, 0x40080000, 0x40084000, 0x00004000,
-	0x40080000, 0x40004000, 0x00000010, 0x40084010, 0x00084010, 0x00000010, 0x00004000, 0x40000000,
-	0x00004010, 0x40084000, 0x00080000, 0x40000010, 0x00080010, 0x40004010, 0x40000010, 0x00080010,
-	0x00084000, 0x00000000, 0x40004000, 0x00004010, 0x40000000, 0x40080010, 0x40084010, 0x00084000
-};
-
-static const guint32 sbox3[64] =
-{
-	0x00000104, 0x04010100, 0x00000000, 0x04010004, 0x04000100, 0x00000000, 0x00010104, 0x04000100,
-	0x00010004, 0x04000004, 0x04000004, 0x00010000, 0x04010104, 0x00010004, 0x04010000, 0x00000104,
-	0x04000000, 0x00000004, 0x04010100, 0x00000100, 0x00010100, 0x04010000, 0x04010004, 0x00010104,
-	0x04000104, 0x00010100, 0x00010000, 0x04000104, 0x00000004, 0x04010104, 0x00000100, 0x04000000,
-	0x04010100, 0x04000000, 0x00010004, 0x00000104, 0x00010000, 0x04010100, 0x04000100, 0x00000000,
-	0x00000100, 0x00010004, 0x04010104, 0x04000100, 0x04000004, 0x00000100, 0x00000000, 0x04010004,
-	0x04000104, 0x00010000, 0x04000000, 0x04010104, 0x00000004, 0x00010104, 0x00010100, 0x04000004,
-	0x04010000, 0x04000104, 0x00000104, 0x04010000, 0x00010104, 0x00000004, 0x04010004, 0x00010100
-};
-
-static const guint32 sbox4[64] =
-{
-	0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x00401040, 0x80400040, 0x80400000, 0x80001000,
-	0x00000000, 0x00401000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00400040, 0x80400000,
-	0x80000000, 0x00001000, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x80001000, 0x00001040,
-	0x80400040, 0x80000000, 0x00001040, 0x00400040, 0x00001000, 0x00401040, 0x80401040, 0x80000040,
-	0x00400040, 0x80400000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00000000, 0x00401000,
-	0x00001040, 0x00400040, 0x80400040, 0x80000000, 0x80401000, 0x80001040, 0x80001040, 0x00000040,
-	0x80401040, 0x80000040, 0x80000000, 0x00001000, 0x80400000, 0x80001000, 0x00401040, 0x80400040,
-	0x80001000, 0x00001040, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x00001000, 0x00401040
-};
-
-static const guint32 sbox5[64] =
-{
-	0x00000080, 0x01040080, 0x01040000, 0x21000080, 0x00040000, 0x00000080, 0x20000000, 0x01040000,
-	0x20040080, 0x00040000, 0x01000080, 0x20040080, 0x21000080, 0x21040000, 0x00040080, 0x20000000,
-	0x01000000, 0x20040000, 0x20040000, 0x00000000, 0x20000080, 0x21040080, 0x21040080, 0x01000080,
-	0x21040000, 0x20000080, 0x00000000, 0x21000000, 0x01040080, 0x01000000, 0x21000000, 0x00040080,
-	0x00040000, 0x21000080, 0x00000080, 0x01000000, 0x20000000, 0x01040000, 0x21000080, 0x20040080,
-	0x01000080, 0x20000000, 0x21040000, 0x01040080, 0x20040080, 0x00000080, 0x01000000, 0x21040000,
-	0x21040080, 0x00040080, 0x21000000, 0x21040080, 0x01040000, 0x00000000, 0x20040000, 0x21000000,
-	0x00040080, 0x01000080, 0x20000080, 0x00040000, 0x00000000, 0x20040000, 0x01040080, 0x20000080
-};
-
-static const guint32 sbox6[64] =
-{
-	0x10000008, 0x10200000, 0x00002000, 0x10202008, 0x10200000, 0x00000008, 0x10202008, 0x00200000,
-	0x10002000, 0x00202008, 0x00200000, 0x10000008, 0x00200008, 0x10002000, 0x10000000, 0x00002008,
-	0x00000000, 0x00200008, 0x10002008, 0x00002000, 0x00202000, 0x10002008, 0x00000008, 0x10200008,
-	0x10200008, 0x00000000, 0x00202008, 0x10202000, 0x00002008, 0x00202000, 0x10202000, 0x10000000,
-	0x10002000, 0x00000008, 0x10200008, 0x00202000, 0x10202008, 0x00200000, 0x00002008, 0x10000008,
-	0x00200000, 0x10002000, 0x10000000, 0x00002008, 0x10000008, 0x10202008, 0x00202000, 0x10200000,
-	0x00202008, 0x10202000, 0x00000000, 0x10200008, 0x00000008, 0x00002000, 0x10200000, 0x00202008,
-	0x00002000, 0x00200008, 0x10002008, 0x00000000, 0x10202000, 0x10000000, 0x00200008, 0x10002008
-};
-
-static const guint32 sbox7[64] =
-{
-	0x00100000, 0x02100001, 0x02000401, 0x00000000, 0x00000400, 0x02000401, 0x00100401, 0x02100400,
-	0x02100401, 0x00100000, 0x00000000, 0x02000001, 0x00000001, 0x02000000, 0x02100001, 0x00000401,
-	0x02000400, 0x00100401, 0x00100001, 0x02000400, 0x02000001, 0x02100000, 0x02100400, 0x00100001,
-	0x02100000, 0x00000400, 0x00000401, 0x02100401, 0x00100400, 0x00000001, 0x02000000, 0x00100400,
-	0x02000000, 0x00100400, 0x00100000, 0x02000401, 0x02000401, 0x02100001, 0x02100001, 0x00000001,
-	0x00100001, 0x02000000, 0x02000400, 0x00100000, 0x02100400, 0x00000401, 0x00100401, 0x02100400,
-	0x00000401, 0x02000001, 0x02100401, 0x02100000, 0x00100400, 0x00000000, 0x00000001, 0x02100401,
-	0x00000000, 0x00100401, 0x02100000, 0x00000400, 0x02000001, 0x02000400, 0x00000400, 0x00100001
-};
-
-static const guint32 sbox8[64] =
-{
-	0x08000820, 0x00000800, 0x00020000, 0x08020820, 0x08000000, 0x08000820, 0x00000020, 0x08000000,
-	0x00020020, 0x08020000, 0x08020820, 0x00020800, 0x08020800, 0x00020820, 0x00000800, 0x00000020,
-	0x08020000, 0x08000020, 0x08000800, 0x00000820, 0x00020800, 0x00020020, 0x08020020, 0x08020800,
-	0x00000820, 0x00000000, 0x00000000, 0x08020020, 0x08000020, 0x08000800, 0x00020820, 0x00020000,
-	0x00020820, 0x00020000, 0x08020800, 0x00000800, 0x00000020, 0x08020020, 0x00000800, 0x00020820,
-	0x08000800, 0x00000020, 0x08000020, 0x08020000, 0x08020020, 0x08000000, 0x00020000, 0x08000820,
-	0x00000000, 0x08020820, 0x00020020, 0x08000020, 0x08020000, 0x08000800, 0x08000820, 0x00000000,
-	0x08020820, 0x00020800, 0x00020800, 0x00000820, 0x00000820, 0x00020020, 0x08000000, 0x08020800
-};
-
-
-
-/*
- *  * These two tables are part of the 'permuted choice 1' function.
- *   * In this implementation several speed improvements are done.
- *    */
-static const guint32 leftkey_swap[16] =
-{
-	0x00000000, 0x00000001, 0x00000100, 0x00000101,
-	0x00010000, 0x00010001, 0x00010100, 0x00010101,
-	0x01000000, 0x01000001, 0x01000100, 0x01000101,
-	0x01010000, 0x01010001, 0x01010100, 0x01010101
-};
-
-static const guint32 rightkey_swap[16] =
-{
-	0x00000000, 0x01000000, 0x00010000, 0x01010000,
-	0x00000100, 0x01000100, 0x00010100, 0x01010100,
-	0x00000001, 0x01000001, 0x00010001, 0x01010001,
-	0x00000101, 0x01000101, 0x00010101, 0x01010101,
-};
-
-
-
-/*
- *  Numbers of left shifts per round for encryption subkey schedule
- *  To calculate the decryption key scheduling we just reverse the
- *  ordering of the subkeys so we can omit the table for decryption
- *  subkey schedule.
- */
-static const guint8 encrypt_rotate_tab[16] =
-{
-	1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
-};
-
-/*
- *  Macro to swap bits across two words
- **/
-#define DO_PERMUTATION(a, temp, b, offset, mask)	\
-	temp = ((a>>offset) ^ b) & mask;			\
-b ^= temp;						\
-a ^= temp<<offset;
-
-
-/*
- *  This performs the 'initial permutation' for the data to be encrypted or decrypted
- **/
-#define INITIAL_PERMUTATION(left, temp, right)		\
-	DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f)	\
-DO_PERMUTATION(left, temp, right, 16, 0x0000ffff)	\
-DO_PERMUTATION(right, temp, left, 2, 0x33333333)	\
-DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff)	\
-DO_PERMUTATION(left, temp, right, 1, 0x55555555)
-
-
-/*
- * The 'inverse initial permutation'
- **/
-#define FINAL_PERMUTATION(left, temp, right)		\
-	DO_PERMUTATION(left, temp, right, 1, 0x55555555)	\
-DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff)	\
-DO_PERMUTATION(right, temp, left, 2, 0x33333333)	\
-DO_PERMUTATION(left, temp, right, 16, 0x0000ffff)	\
-DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f)
-
-
-/*
- * A full DES round including 'expansion function', 'sbox substitution'
- * and 'primitive function P' but without swapping the left and right word.
- **/
-#define DES_ROUND(from, to, work, subkey)		\
-	work = ((from<<1) | (from>>31)) ^ *subkey++;	\
-to ^= sbox8[  work	    & 0x3f ];			\
-to ^= sbox6[ (work>>8)  & 0x3f ];			\
-to ^= sbox4[ (work>>16) & 0x3f ];			\
-to ^= sbox2[ (work>>24) & 0x3f ];			\
-work = ((from>>3) | (from<<29)) ^ *subkey++;	\
-to ^= sbox7[  work	    & 0x3f ];			\
-to ^= sbox5[ (work>>8)  & 0x3f ];			\
-to ^= sbox3[ (work>>16) & 0x3f ];			\
-to ^= sbox1[ (work>>24) & 0x3f ];
-
-
-/*
- * Macros to convert 8 bytes from/to 32bit words
- **/
-#define READ_64BIT_DATA(data, left, right)					\
-	left  = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];	\
-right = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
-
-#define WRITE_64BIT_DATA(data, left, right)					\
-	data[0] = (left >> 24) &0xff; data[1] = (left >> 16) &0xff; 		\
-data[2] = (left >> 8) &0xff; data[3] = left &0xff;				\
-data[4] = (right >> 24) &0xff; data[5] = (right >> 16) &0xff;		\
-data[6] = (right >> 8) &0xff; data[7] = right &0xff;
-
-
-
-
-
-
-/*
- * des_key_schedule():	  Calculate 16 subkeys pairs (even/odd) for
- *			  16 encryption rounds.
- *			  To calculate subkeys for decryption the caller
- *    			  have to reorder the generated subkeys.
- *
- *        rawkey:	    8 Bytes of key data
- *        subkey:	    Array of at least 32 guint32s. Will be filled
- *    		    with calculated subkeys.
- *
- **/
-static void
-des_key_schedule (const guint8 * rawkey, guint32 * subkey)
-{
-	guint32 left, right, work;
-	int round;
-
-	READ_64BIT_DATA (rawkey, left, right)
-
-		DO_PERMUTATION (right, work, left, 4, 0x0f0f0f0f)
-		DO_PERMUTATION (right, work, left, 0, 0x10101010)
-
-		left = (leftkey_swap[(left >> 0) & 0xf] << 3) | (leftkey_swap[(left >> 8) & 0xf] << 2)
-		| (leftkey_swap[(left >> 16) & 0xf] << 1) | (leftkey_swap[(left >> 24) & 0xf])
-		| (leftkey_swap[(left >> 5) & 0xf] << 7) | (leftkey_swap[(left >> 13) & 0xf] << 6)
-		| (leftkey_swap[(left >> 21) & 0xf] << 5) | (leftkey_swap[(left >> 29) & 0xf] << 4);
-
-	left &= 0x0fffffff;
-
-	right = (rightkey_swap[(right >> 1) & 0xf] << 3) | (rightkey_swap[(right >> 9) & 0xf] << 2)
-		| (rightkey_swap[(right >> 17) & 0xf] << 1) | (rightkey_swap[(right >> 25) & 0xf])
-		| (rightkey_swap[(right >> 4) & 0xf] << 7) | (rightkey_swap[(right >> 12) & 0xf] << 6)
-		| (rightkey_swap[(right >> 20) & 0xf] << 5) | (rightkey_swap[(right >> 28) & 0xf] << 4);
-
-	right &= 0x0fffffff;
-
-	for (round = 0; round < 16; ++round)
-	{
-		left = ((left << encrypt_rotate_tab[round]) | (left >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff;
-		right = ((right << encrypt_rotate_tab[round]) | (right >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff;
-
-		*subkey++ = ((left << 4) & 0x24000000)
-			| ((left << 28) & 0x10000000)
-			| ((left << 14) & 0x08000000)
-			| ((left << 18) & 0x02080000)
-			| ((left << 6) & 0x01000000)
-			| ((left << 9) & 0x00200000)
-			| ((left >> 1) & 0x00100000)
-			| ((left << 10) & 0x00040000)
-			| ((left << 2) & 0x00020000)
-			| ((left >> 10) & 0x00010000)
-			| ((right >> 13) & 0x00002000)
-			| ((right >> 4) & 0x00001000)
-			| ((right << 6) & 0x00000800)
-			| ((right >> 1) & 0x00000400)
-			| ((right >> 14) & 0x00000200)
-			| (right & 0x00000100)
-			| ((right >> 5) & 0x00000020)
-			| ((right >> 10) & 0x00000010)
-			| ((right >> 3) & 0x00000008)
-			| ((right >> 18) & 0x00000004)
-			| ((right >> 26) & 0x00000002)
-			| ((right >> 24) & 0x00000001);
-
-		*subkey++ = ((left << 15) & 0x20000000)
-			| ((left << 17) & 0x10000000)
-			| ((left << 10) & 0x08000000)
-			| ((left << 22) & 0x04000000)
-			| ((left >> 2) & 0x02000000)
-			| ((left << 1) & 0x01000000)
-			| ((left << 16) & 0x00200000)
-			| ((left << 11) & 0x00100000)
-			| ((left << 3) & 0x00080000)
-			| ((left >> 6) & 0x00040000)
-			| ((left << 15) & 0x00020000)
-			| ((left >> 4) & 0x00010000)
-			| ((right >> 2) & 0x00002000)
-			| ((right << 8) & 0x00001000)
-			| ((right >> 14) & 0x00000808)
-			| ((right >> 9) & 0x00000400)
-			| ((right) & 0x00000200)
-			| ((right << 7) & 0x00000100)
-			| ((right >> 7) & 0x00000020)
-			| ((right >> 3) & 0x00000011)
-			| ((right << 2) & 0x00000004)
-			| ((right >> 21) & 0x00000002);
-	}
-}
-
-
-
-/*
- *  Fill a DES context with subkeys calculated from a 64bit key.
- *  Does not check parity bits, but simply ignore them.
- *  Does not check for weak keys.
- **/
-static void
-des_set_key (PurpleCipherContext *context, const guchar * key)
-{
-	struct _des_ctx *ctx = purple_cipher_context_get_data(context);
-	int i;
-
-	des_key_schedule (key, ctx->encrypt_subkeys);
-
-	for(i=0; i<32; i+=2)
-	{
-		ctx->decrypt_subkeys[i]	= ctx->encrypt_subkeys[30-i];
-		ctx->decrypt_subkeys[i+1] = ctx->encrypt_subkeys[31-i];
-	}
-}
-
-
-
-/*
- *  Electronic Codebook Mode DES encryption/decryption of data according
- *  to 'mode'.
- **/
-static int
-des_ecb_crypt (struct _des_ctx *ctx, const guint8 * from, guint8 * to, int mode)
-{
-	guint32 left, right, work;
-	guint32 *keys;
-
-	keys = mode ? ctx->decrypt_subkeys : ctx->encrypt_subkeys;
-
-	READ_64BIT_DATA (from, left, right)
-		INITIAL_PERMUTATION (left, work, right)
-
-		DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
-		DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
-		DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
-		DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
-		DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
-		DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
-		DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
-		DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
-
-		FINAL_PERMUTATION (right, work, left)
-		WRITE_64BIT_DATA (to, right, left)
-
-		return 0;
-}
-
-static gint
-des_encrypt(PurpleCipherContext *context, const guchar data[],
-	    size_t len, guchar output[], size_t *outlen) {
-	int offset = 0;
-	int i = 0;
-	int tmp;
-	guint8 buf[8] = {0,0,0,0,0,0,0,0};
-	while(offset+8<=len) {
-		des_ecb_crypt(purple_cipher_context_get_data(context),
-				data+offset,
-				output+offset,
-				0);
-		offset+=8;
-	}
-	*outlen = len;
-	if(offset<len) {
-		*outlen += len - offset;
-		tmp = offset;
-		while(tmp<len) {
-			buf[i++] = data[tmp];
-			tmp++;
-		}
-		des_ecb_crypt(purple_cipher_context_get_data(context),
-				buf,
-				output+offset,
-				0);
-	}
-	return 0;
-}
-
-static gint
-des_decrypt(PurpleCipherContext *context, const guchar data[],
-	    size_t len, guchar output[], size_t *outlen) {
-	int offset = 0;
-	int i = 0;
-	int tmp;
-	guint8 buf[8] = {0,0,0,0,0,0,0,0};
-	while(offset+8<=len) {
-		des_ecb_crypt(purple_cipher_context_get_data(context),
-				data+offset,
-				output+offset,
-				1);
-		offset+=8;
-	}
-	*outlen = len;
-	if(offset<len) {
-		*outlen += len - offset;
-		tmp = offset;
-		while(tmp<len) {
-			buf[i++] = data[tmp];
-			tmp++;
-		}
-		des_ecb_crypt(purple_cipher_context_get_data(context),
-				buf,
-				output+offset,
-				1);
-	}
-	return 0;
-}
-
-static void
-des_init(PurpleCipherContext *context, gpointer extra) {
-	struct _des_ctx *mctx;
-	mctx = g_new0(struct _des_ctx, 1);
-	purple_cipher_context_set_data(context, mctx);
-}
-
-static void
-des_uninit(PurpleCipherContext *context) {
-	struct _des_ctx *des_context;
-
-	des_context = purple_cipher_context_get_data(context);
-	memset(des_context, 0, sizeof(des_context));
-
-	g_free(des_context);
-	des_context = NULL;
-}
-
-static PurpleCipherOps DESOps = {
-	NULL,              /* Set option */
-	NULL,              /* Get option */
-	des_init,          /* init */
- 	NULL,              /* reset */
-	des_uninit,        /* uninit */
-	NULL,              /* set iv */
-	NULL,              /* append */
-	NULL,              /* digest */
-	des_encrypt,       /* encrypt */
-	des_decrypt,       /* decrypt */
-	NULL,              /* set salt */
-	NULL,              /* get salt size */
-	des_set_key,       /* set key */
-	NULL,              /* get key size */
-	NULL,              /* set batch mode */
-	NULL,              /* get batch mode */
-	NULL,              /* get block size */
-	NULL               /* set key with len */
-};
-
-/******************************************************************************
- * Triple-DES
- *****************************************************************************/
-
-typedef struct _des3_ctx
-{
-	PurpleCipherBatchMode mode;
-	guchar iv[8];
-	/* First key for encryption */
-	struct _des_ctx key1;
-	/* Second key for decryption */
-	struct _des_ctx key2;
-	/* Third key for encryption */
-	struct _des_ctx key3;
-} des3_ctx[1];
-
-/*
- *  Fill a DES3 context with subkeys calculated from 3 64bit key.
- *  Does not check parity bits, but simply ignore them.
- *  Does not check for weak keys.
- **/
-static void
-des3_set_key(PurpleCipherContext *context, const guchar * key)
-{
-	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
-	int i;
-
-	des_key_schedule (key +  0, ctx->key1.encrypt_subkeys);
-	des_key_schedule (key +  8, ctx->key2.encrypt_subkeys);
-	des_key_schedule (key + 16, ctx->key3.encrypt_subkeys);
-
-	for (i = 0; i < 32; i += 2)
-	{
-		ctx->key1.decrypt_subkeys[i]	= ctx->key1.encrypt_subkeys[30-i];
-		ctx->key1.decrypt_subkeys[i+1]	= ctx->key1.encrypt_subkeys[31-i];
-		ctx->key2.decrypt_subkeys[i]	= ctx->key2.encrypt_subkeys[30-i];
-		ctx->key2.decrypt_subkeys[i+1]	= ctx->key2.encrypt_subkeys[31-i];
-		ctx->key3.decrypt_subkeys[i]	= ctx->key3.encrypt_subkeys[30-i];
-		ctx->key3.decrypt_subkeys[i+1]	= ctx->key3.encrypt_subkeys[31-i];
-	}
-}
-
-static gint
-des3_ecb_encrypt(struct _des3_ctx *ctx, const guchar data[],
-                 size_t len, guchar output[], size_t *outlen)
-{
-	int offset = 0;
-	int i = 0;
-	int tmp;
-	guint8 buf[8] = {0,0,0,0,0,0,0,0};
-	while (offset + 8 <= len) {
-		des_ecb_crypt(&ctx->key1,
-		              data+offset,
-		              output+offset,
-		              0);
-		des_ecb_crypt(&ctx->key2,
-		              output+offset,
-		              buf,
-		              1);
-		des_ecb_crypt(&ctx->key3,
-		              buf,
-		              output+offset,
-		              0);
-		offset += 8;
-	}
-	*outlen = len;
-	if (offset < len) {
-		*outlen += len - offset;
-		tmp = offset;
-		memset(buf, 0, 8);
-		while (tmp < len) {
-			buf[i++] = data[tmp];
-			tmp++;
-		}
-		des_ecb_crypt(&ctx->key1,
-		              buf,
-		              output+offset,
-		              0);
-		des_ecb_crypt(&ctx->key2,
-		              output+offset,
-		              buf,
-		              1);
-		des_ecb_crypt(&ctx->key3,
-		              buf,
-		              output+offset,
-		              0);
-	}
-	return 0;
-}
-
-static gint
-des3_cbc_encrypt(struct _des3_ctx *ctx, const guchar data[],
-                 size_t len, guchar output[], size_t *outlen)
-{
-	int offset = 0;
-	int i = 0;
-	int tmp;
-	guint8 buf[8];
-	memcpy(buf, ctx->iv, 8);
-	while (offset + 8 <= len) {
-		for (i = 0; i < 8; i++)
-			buf[i] ^= data[offset + i];
-		des_ecb_crypt(&ctx->key1,
-		              buf,
-		              output+offset,
-		              0);
-		des_ecb_crypt(&ctx->key2,
-		              output+offset,
-		              buf,
-		              1);
-		des_ecb_crypt(&ctx->key3,
-		              buf,
-		              output+offset,
-		              0);
-		memcpy(buf, output+offset, 8);
-		offset += 8;
-	}
-	*outlen = len;
-	if (offset < len) {
-		*outlen += len - offset;
-		tmp = offset;
-		i = 0;
-		while (tmp < len) {
-			buf[i++] ^= data[tmp];
-			tmp++;
-		}
-		des_ecb_crypt(&ctx->key1,
-		              buf,
-		              output+offset,
-		              0);
-		des_ecb_crypt(&ctx->key2,
-		              output+offset,
-		              buf,
-		              1);
-		des_ecb_crypt(&ctx->key3,
-		              buf,
-		              output+offset,
-		              0);
-	}
-	return 0;
-}
-
-static gint
-des3_encrypt(PurpleCipherContext *context, const guchar data[],
-             size_t len, guchar output[], size_t *outlen)
-{
-	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
-
-	if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
-		return des3_ecb_encrypt(ctx, data, len, output, outlen);
-	} else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
-		return des3_cbc_encrypt(ctx, data, len, output, outlen);
-	} else {
-		g_return_val_if_reached(0);
-	}
-
-	return 0;
-}
-
-static gint
-des3_ecb_decrypt(struct _des3_ctx *ctx, const guchar data[],
-                 size_t len, guchar output[], size_t *outlen)
-{
-	int offset = 0;
-	int i = 0;
-	int tmp;
-	guint8 buf[8] = {0,0,0,0,0,0,0,0};
-	while (offset + 8 <= len) {
-		/* NOTE: Apply key in reverse */
-		des_ecb_crypt(&ctx->key3,
-		              data+offset,
-		              output+offset,
-		              1);
-		des_ecb_crypt(&ctx->key2,
-		              output+offset,
-		              buf,
-		              0);
-		des_ecb_crypt(&ctx->key1,
-		              buf,
-		              output+offset,
-		              1);
-		offset+=8;
-	}
-	*outlen = len;
-	if (offset < len) {
-		*outlen += len - offset;
-		tmp = offset;
-		memset(buf, 0, 8);
-		while (tmp < len) {
-			buf[i++] = data[tmp];
-			tmp++;
-		}
-		des_ecb_crypt(&ctx->key3,
-		              buf,
-		              output+offset,
-		              1);
-		des_ecb_crypt(&ctx->key2,
-		              output+offset,
-		              buf,
-		              0);
-		des_ecb_crypt(&ctx->key1,
-		              buf,
-		              output+offset,
-		              1);
-	}
-	return 0;
-}
-
-static gint
-des3_cbc_decrypt(struct _des3_ctx *ctx, const guchar data[],
-                 size_t len, guchar output[], size_t *outlen)
-{
-	int offset = 0;
-	int i = 0;
-	int tmp;
-	guint8 buf[8] = {0,0,0,0,0,0,0,0};
-	guint8 link[8];
-	memcpy(link, ctx->iv, 8);
-	while (offset + 8 <= len) {
-		des_ecb_crypt(&ctx->key3,
-		              data+offset,
-		              output+offset,
-		              1);
-		des_ecb_crypt(&ctx->key2,
-		              output+offset,
-		              buf,
-		              0);
-		des_ecb_crypt(&ctx->key1,
-		              buf,
-		              output+offset,
-		              1);
-		for (i = 0; i < 8; i++)
-			output[offset + i] ^= link[i];
-		memcpy(link, data + offset, 8);
-		offset+=8;
-	}
-	*outlen = len;
-	if(offset<len) {
-		*outlen += len - offset;
-		tmp = offset;
-		memset(buf, 0, 8);
-		i = 0;
-		while(tmp<len) {
-			buf[i++] = data[tmp];
-			tmp++;
-		}
-		des_ecb_crypt(&ctx->key3,
-		              buf,
-		              output+offset,
-		              1);
-		des_ecb_crypt(&ctx->key2,
-		              output+offset,
-		              buf,
-		              0);
-		des_ecb_crypt(&ctx->key1,
-		              buf,
-		              output+offset,
-		              1);
-		for (i = 0; i < 8; i++)
-			output[offset + i] ^= link[i];
-	}
-	return 0;
-}
-
-static gint
-des3_decrypt(PurpleCipherContext *context, const guchar data[],
-             size_t len, guchar output[], size_t *outlen)
-{
-	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
-
-	if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
-		return des3_ecb_decrypt(ctx, data, len, output, outlen);
-	} else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
-		return des3_cbc_decrypt(ctx, data, len, output, outlen);
-	} else {
-		g_return_val_if_reached(0);
-	}
-
-	return 0;
-}
-
-static void
-des3_set_batch(PurpleCipherContext *context, PurpleCipherBatchMode mode)
-{
-	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
-
-	ctx->mode = mode;
-}
-
-static PurpleCipherBatchMode
-des3_get_batch(PurpleCipherContext *context)
-{
-	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
-
-	return ctx->mode;
-}
-
-static void
-des3_set_iv(PurpleCipherContext *context, guchar *iv, size_t len)
-{
-	struct _des3_ctx *ctx;
-
-	g_return_if_fail(len == 8);
-
-	ctx = purple_cipher_context_get_data(context);
-
-	memcpy(ctx->iv, iv, len);
-}
-
-static void
-des3_init(PurpleCipherContext *context, gpointer extra)
-{
-	struct _des3_ctx *mctx;
-	mctx = g_new0(struct _des3_ctx, 1);
-	purple_cipher_context_set_data(context, mctx);
-}
-
-static void
-des3_uninit(PurpleCipherContext *context)
-{
-	struct _des3_ctx *des3_context;
-
-	des3_context = purple_cipher_context_get_data(context);
-	memset(des3_context, 0, sizeof(des3_context));
-
-	g_free(des3_context);
-	des3_context = NULL;
-}
-
-static PurpleCipherOps DES3Ops = {
-	NULL,              /* Set option */
-	NULL,              /* Get option */
-	des3_init,         /* init */
-	NULL,              /* reset */
-	des3_uninit,       /* uninit */
-	des3_set_iv,       /* set iv */
-	NULL,              /* append */
-	NULL,              /* digest */
-	des3_encrypt,      /* encrypt */
-	des3_decrypt,      /* decrypt */
-	NULL,              /* set salt */
-	NULL,              /* get salt size */
-	des3_set_key,      /* set key */
-	NULL,              /* get key size */
-	des3_set_batch,    /* set batch mode */
-	des3_get_batch,    /* get batch mode */
-	NULL,              /* get block size */
-	NULL               /* set key with len */
-};
-
-/*******************************************************************************
- * SHA-1
- ******************************************************************************/
-#define SHA1_HMAC_BLOCK_SIZE	64
-
-static size_t
-sha1_get_block_size(PurpleCipherContext *context)
-{
-	/* This does not change (in this case) */
-	return SHA1_HMAC_BLOCK_SIZE;
-}
-
-
-#if GLIB_CHECK_VERSION(2,16,0)
-
-static void
-sha1_init(PurpleCipherContext *context, void *extra)
-{
-	purple_g_checksum_init(context, G_CHECKSUM_SHA1);
-}
-
-static void
-sha1_reset(PurpleCipherContext *context, void *extra)
-{
-	purple_g_checksum_reset(context, G_CHECKSUM_SHA1);
-}
-
-static gboolean
-sha1_digest(PurpleCipherContext *context, gsize in_len, guchar digest[20],
-            gsize *out_len)
-{
-	return purple_g_checksum_digest(context, G_CHECKSUM_SHA1, in_len,
-	                                digest, out_len);
-}
-
-static PurpleCipherOps SHA1Ops = {
-	NULL,			/* Set Option		*/
-	NULL,			/* Get Option		*/
-	sha1_init,		/* init				*/
-	sha1_reset,		/* reset			*/
-	purple_g_checksum_uninit,	/* uninit			*/
-	NULL,			/* set iv			*/
-	purple_g_checksum_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 */
-};
-
-#else /* 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 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 */
-};
-
-#endif /* GLIB_CHECK_VERSION(2,16,0) */
-
-/*******************************************************************************
- * SHA-256
- ******************************************************************************/
-#define SHA256_HMAC_BLOCK_SIZE	64
-
-static size_t
-sha256_get_block_size(PurpleCipherContext *context)
-{
-	/* This does not change (in this case) */
-	return SHA256_HMAC_BLOCK_SIZE;
-}
-
-#if GLIB_CHECK_VERSION(2,16,0)
-
-static void
-sha256_init(PurpleCipherContext *context, void *extra)
-{
-	purple_g_checksum_init(context, G_CHECKSUM_SHA256);
-}
-
-static void
-sha256_reset(PurpleCipherContext *context, void *extra)
-{
-	purple_g_checksum_reset(context, G_CHECKSUM_SHA256);
-}
-
-static gboolean
-sha256_digest(PurpleCipherContext *context, gsize in_len, guchar digest[20],
-            gsize *out_len)
-{
-	return purple_g_checksum_digest(context, G_CHECKSUM_SHA256, in_len,
-	                                digest, out_len);
-}
-
-static PurpleCipherOps SHA256Ops = {
-	NULL,			/* Set Option		*/
-	NULL,			/* Get Option		*/
-	sha256_init,	/* init				*/
-	sha256_reset,	/* reset			*/
-	purple_g_checksum_uninit,	/* uninit			*/
-	NULL,			/* set iv			*/
-	purple_g_checksum_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 */
-};
-
-#else /* GLIB_CHECK_VERSION(2,16,0) */
-
-#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 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 */
-};
-
-#endif /* GLIB_CHECK_VERSION(2,16,0) */
-
-/*******************************************************************************
- * RC4
- ******************************************************************************/
-
-struct RC4Context {
-  guchar state[256];
-  guchar x;
-  guchar y;
-  gint key_len;
-};
-
-static void
-rc4_init(PurpleCipherContext *context, void *extra) {
-	struct RC4Context *rc4_ctx;
-	rc4_ctx = g_new0(struct RC4Context, 1);
-	purple_cipher_context_set_data(context, rc4_ctx);
-	purple_cipher_context_reset(context, extra);
-}
-
-
-static void
-rc4_reset(PurpleCipherContext *context, void *extra) {
-	struct RC4Context *rc4_ctx;
-	guint i;
-
-	rc4_ctx = purple_cipher_context_get_data(context);
-
-	g_return_if_fail(rc4_ctx);
-
-	for(i = 0; i < 256; i++)
-		rc4_ctx->state[i] = i;
-	rc4_ctx->x = 0;
-	rc4_ctx->y = 0;
-
-	/* default is 5 bytes (40bit key) */
-	rc4_ctx->key_len = 5;
-
-}
-
-static void
-rc4_uninit(PurpleCipherContext *context) {
-	struct RC4Context *rc4_ctx;
-
-	rc4_ctx = purple_cipher_context_get_data(context);
-	memset(rc4_ctx, 0, sizeof(rc4_ctx));
-
-	g_free(rc4_ctx);
-	rc4_ctx = NULL;
-}
-
-
-
-static void
-rc4_set_key (PurpleCipherContext *context, const guchar * key) {
-	struct RC4Context *ctx;
-	guchar *state;
-	guchar temp_swap;
-	guchar x, y;
-	guint i;
-
-	ctx = purple_cipher_context_get_data(context);
-
-	x = 0;
-	y = 0;
-	state = &ctx->state[0];
-	for(i = 0; i < 256; i++)
-	{
-		y = (key[x] + state[i] + y) % 256;
-		temp_swap = state[i];
-		state[i] = state[y];
-		state[y] = temp_swap;
-		x = (x + 1) % ctx->key_len;
-	}
-}
-
-static void
-rc4_set_opt(PurpleCipherContext *context, const gchar *name, void *value) {
-	struct RC4Context *ctx;
-
-	ctx = purple_cipher_context_get_data(context);
-
-	if(purple_strequal(name, "key_len")) {
-		ctx->key_len = GPOINTER_TO_INT(value);
-	}
-}
-
-static size_t
-rc4_get_key_size (PurpleCipherContext *context)
-{
-	struct RC4Context *ctx;
-
-	g_return_val_if_fail(context, -1);
-
-	ctx = purple_cipher_context_get_data(context);
-
-	g_return_val_if_fail(ctx, -1);
-
-	return ctx->key_len;
-}
-
-static void *
-rc4_get_opt(PurpleCipherContext *context, const gchar *name) {
-	struct RC4Context *ctx;
-
-	ctx = purple_cipher_context_get_data(context);
-
-	if(purple_strequal(name, "key_len")) {
-		return GINT_TO_POINTER(ctx->key_len);
-	}
-
-	return NULL;
-}
-
-static gint
-rc4_encrypt(PurpleCipherContext *context, const guchar data[],
-	    size_t len, guchar output[], size_t *outlen) {
-	struct RC4Context *ctx;
-	guchar temp_swap;
-	guchar x, y, z;
-	guchar *state;
-	guint i;
-
-	ctx = purple_cipher_context_get_data(context);
-
-	x = ctx->x;
-	y = ctx->y;
-	state = &ctx->state[0];
-
-	for(i = 0; i < len; i++)
-	{
-		x = (x + 1) % 256;
-		y = (state[x] + y) % 256;
-		temp_swap = state[x];
-		state[x] = state[y];
-		state[y] = temp_swap;
-		z = state[x] + (state[y]) % 256;
-		output[i] = data[i] ^ state[z];
-	}
-	ctx->x = x;
-	ctx->y = y;
-	if(outlen)
-		*outlen = len;
-
-	return 0;
-}
-
-static PurpleCipherOps RC4Ops = {
-	rc4_set_opt,   /* Set Option    */
-	rc4_get_opt,   /* Get Option    */
-	rc4_init,      /* init          */
-	rc4_reset,     /* reset         */
-	rc4_uninit,    /* uninit        */
-	NULL,          /* set iv        */
-	NULL,          /* append        */
-	NULL,          /* digest        */
-	rc4_encrypt,   /* encrypt       */
-	NULL,          /* decrypt       */
-	NULL,          /* set salt      */
-	NULL,          /* get salt size */
-	rc4_set_key,   /* set key       */
-	rc4_get_key_size, /* get key size  */
-	NULL,          /* set batch mode */
-	NULL,          /* get batch mode */
-	NULL,          /* get block size */
-	NULL           /* set key with len */
-};
-
 /*******************************************************************************
  * Structs
  ******************************************************************************/
@@ -2692,6 +223,20 @@
 	return &handle;
 }
 
+/* These are implemented in the purple-ciphers sublibrary built in the ciphers
+ * directory.  We could put a header file in there, but it's less hassle for
+ * the developer to just add it here since they have to register it here as
+ * well.
+ */
+PurpleCipherOps *purple_des_cipher_get_ops();
+PurpleCipherOps *purple_des3_cipher_get_ops();
+PurpleCipherOps *purple_hmac_cipher_get_ops();
+PurpleCipherOps *purple_md4_cipher_get_ops();
+PurpleCipherOps *purple_md5_cipher_get_ops();
+PurpleCipherOps *purple_rc4_cipher_get_ops();
+PurpleCipherOps *purple_sha1_cipher_get_ops();
+PurpleCipherOps *purple_sha256_cipher_get_ops();
+
 void
 purple_ciphers_init() {
 	gpointer handle;
@@ -2707,14 +252,14 @@
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
 										PURPLE_SUBTYPE_CIPHER));
 
-	purple_ciphers_register_cipher("md5", &MD5Ops);
-	purple_ciphers_register_cipher("sha1", &SHA1Ops);
-	purple_ciphers_register_cipher("sha256", &SHA256Ops);
-	purple_ciphers_register_cipher("md4", &MD4Ops);
-	purple_ciphers_register_cipher("hmac", &HMACOps);
-	purple_ciphers_register_cipher("des", &DESOps);
-	purple_ciphers_register_cipher("des3", &DES3Ops);
-	purple_ciphers_register_cipher("rc4", &RC4Ops);
+	purple_ciphers_register_cipher("md5", purple_md5_cipher_get_ops());
+	purple_ciphers_register_cipher("sha1", purple_sha1_cipher_get_ops());
+	purple_ciphers_register_cipher("sha256", purple_sha256_cipher_get_ops());
+	purple_ciphers_register_cipher("md4", purple_md4_cipher_get_ops());
+	purple_ciphers_register_cipher("hmac", purple_hmac_cipher_get_ops());
+	purple_ciphers_register_cipher("des", purple_des_cipher_get_ops());
+	purple_ciphers_register_cipher("des3", purple_des3_cipher_get_ops());
+	purple_ciphers_register_cipher("rc4", purple_rc4_cipher_get_ops());
 }
 
 void
@@ -2727,14 +272,13 @@
 
 		cipher = PURPLE_CIPHER(l->data);
 		purple_ciphers_unregister_cipher(cipher);
-
-		ciphers = g_list_remove(ciphers, cipher);
 	}
 
 	g_list_free(ciphers);
 
 	purple_signals_unregister_by_instance(purple_ciphers_get_handle());
 }
+
 /******************************************************************************
  * PurpleCipherContext API
  *****************************************************************************/
@@ -2834,7 +378,7 @@
 	if(cipher->ops && cipher->ops->uninit)
 		cipher->ops->uninit(context);
 
-	memset(context, 0, sizeof(context));
+	memset(context, 0, sizeof(*context));
 	g_free(context);
 	context = NULL;
 }
--- a/libpurple/cipher.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/cipher.h	Wed Jun 13 19:30:27 2012 -0400
@@ -28,6 +28,7 @@
 #define PURPLE_CIPHER_H
 
 #include <glib.h>
+#include <string.h>
 
 #define PURPLE_CIPHER(obj)			((PurpleCipher *)(obj))			/**< PurpleCipher typecast helper			*/
 #define PURPLE_CIPHER_OPS(obj)		((PurpleCipherOps *)(obj))		/**< PurpleCipherInfo typecase helper		*/
@@ -40,7 +41,7 @@
 /**
  * Modes for batch encrypters
  */
-typedef enum _PurpleCipherBatchMode {
+typedef enum {
 	PURPLE_CIPHER_BATCH_MODE_ECB,
 	PURPLE_CIPHER_BATCH_MODE_CBC
 } PurpleCipherBatchMode;
@@ -48,7 +49,7 @@
 /**
  * The operation flags for a cipher
  */
-typedef enum _PurpleCipherCaps {
+typedef enum {
 	PURPLE_CIPHER_CAPS_SET_OPT          = 1 << 1,   /**< Set option flag	*/
 	PURPLE_CIPHER_CAPS_GET_OPT          = 1 << 2,   /**< Get option flag	*/
 	PURPLE_CIPHER_CAPS_INIT             = 1 << 3,   /**< Init flag			*/
@@ -129,9 +130,7 @@
 	void (*set_key_with_len)(PurpleCipherContext *context, const guchar *key, size_t len);
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
+G_BEGIN_DECLS
 
 /*****************************************************************************/
 /** @name PurpleCipher API													 */
@@ -498,8 +497,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
+G_END_DECLS
 
 #endif /* PURPLE_CIPHER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/ciphers/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,17 @@
+noinst_LTLIBRARIES=libpurple-ciphers.la
+
+libpurple_ciphers_la_SOURCES=\
+	des.c \
+	gchecksum.c \
+	hmac.c \
+	md4.c \
+	md5.c \
+	rc4.c \
+	sha1.c \
+	sha256.c
+
+INCLUDES = -I$(top_srcdir)/libpurple
+
+AM_CPPFLAGS = \
+	$(GLIB_CFLAGS)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/ciphers/des.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,846 @@
+/*
+ * 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 des taken from gpg
+ *
+ * des.c - DES and Triple-DES encryption/decryption Algorithm
+ *  Copyright (C) 1998 Free Software Foundation, Inc.
+ *
+ *  Please see below for more legal information!
+ *
+ *   According to the definition of DES in FIPS PUB 46-2 from December 1993.
+ *   For a description of triple encryption, see:
+ *     Bruce Schneier: Applied Cryptography. Second Edition.
+ *     John Wiley & Sons, 1996. ISBN 0-471-12845-7. Pages 358 ff.
+ *
+ *   This file is part of GnuPG.
+ *
+ * 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>
+
+/******************************************************************************
+ * DES
+ *****************************************************************************/
+typedef struct _des_ctx
+{
+	guint32 encrypt_subkeys[32];
+	guint32 decrypt_subkeys[32];
+} des_ctx[1];
+
+/*
+ *  The s-box values are permuted according to the 'primitive function P'
+ */
+static const guint32 sbox1[64] =
+{
+	0x00808200, 0x00000000, 0x00008000, 0x00808202, 0x00808002, 0x00008202, 0x00000002, 0x00008000,
+	0x00000200, 0x00808200, 0x00808202, 0x00000200, 0x00800202, 0x00808002, 0x00800000, 0x00000002,
+	0x00000202, 0x00800200, 0x00800200, 0x00008200, 0x00008200, 0x00808000, 0x00808000, 0x00800202,
+	0x00008002, 0x00800002, 0x00800002, 0x00008002, 0x00000000, 0x00000202, 0x00008202, 0x00800000,
+	0x00008000, 0x00808202, 0x00000002, 0x00808000, 0x00808200, 0x00800000, 0x00800000, 0x00000200,
+	0x00808002, 0x00008000, 0x00008200, 0x00800002, 0x00000200, 0x00000002, 0x00800202, 0x00008202,
+	0x00808202, 0x00008002, 0x00808000, 0x00800202, 0x00800002, 0x00000202, 0x00008202, 0x00808200,
+	0x00000202, 0x00800200, 0x00800200, 0x00000000, 0x00008002, 0x00008200, 0x00000000, 0x00808002
+};
+
+static const guint32 sbox2[64] =
+{
+	0x40084010, 0x40004000, 0x00004000, 0x00084010, 0x00080000, 0x00000010, 0x40080010, 0x40004010,
+	0x40000010, 0x40084010, 0x40084000, 0x40000000, 0x40004000, 0x00080000, 0x00000010, 0x40080010,
+	0x00084000, 0x00080010, 0x40004010, 0x00000000, 0x40000000, 0x00004000, 0x00084010, 0x40080000,
+	0x00080010, 0x40000010, 0x00000000, 0x00084000, 0x00004010, 0x40084000, 0x40080000, 0x00004010,
+	0x00000000, 0x00084010, 0x40080010, 0x00080000, 0x40004010, 0x40080000, 0x40084000, 0x00004000,
+	0x40080000, 0x40004000, 0x00000010, 0x40084010, 0x00084010, 0x00000010, 0x00004000, 0x40000000,
+	0x00004010, 0x40084000, 0x00080000, 0x40000010, 0x00080010, 0x40004010, 0x40000010, 0x00080010,
+	0x00084000, 0x00000000, 0x40004000, 0x00004010, 0x40000000, 0x40080010, 0x40084010, 0x00084000
+};
+
+static const guint32 sbox3[64] =
+{
+	0x00000104, 0x04010100, 0x00000000, 0x04010004, 0x04000100, 0x00000000, 0x00010104, 0x04000100,
+	0x00010004, 0x04000004, 0x04000004, 0x00010000, 0x04010104, 0x00010004, 0x04010000, 0x00000104,
+	0x04000000, 0x00000004, 0x04010100, 0x00000100, 0x00010100, 0x04010000, 0x04010004, 0x00010104,
+	0x04000104, 0x00010100, 0x00010000, 0x04000104, 0x00000004, 0x04010104, 0x00000100, 0x04000000,
+	0x04010100, 0x04000000, 0x00010004, 0x00000104, 0x00010000, 0x04010100, 0x04000100, 0x00000000,
+	0x00000100, 0x00010004, 0x04010104, 0x04000100, 0x04000004, 0x00000100, 0x00000000, 0x04010004,
+	0x04000104, 0x00010000, 0x04000000, 0x04010104, 0x00000004, 0x00010104, 0x00010100, 0x04000004,
+	0x04010000, 0x04000104, 0x00000104, 0x04010000, 0x00010104, 0x00000004, 0x04010004, 0x00010100
+};
+
+static const guint32 sbox4[64] =
+{
+	0x80401000, 0x80001040, 0x80001040, 0x00000040, 0x00401040, 0x80400040, 0x80400000, 0x80001000,
+	0x00000000, 0x00401000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00400040, 0x80400000,
+	0x80000000, 0x00001000, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x80001000, 0x00001040,
+	0x80400040, 0x80000000, 0x00001040, 0x00400040, 0x00001000, 0x00401040, 0x80401040, 0x80000040,
+	0x00400040, 0x80400000, 0x00401000, 0x80401040, 0x80000040, 0x00000000, 0x00000000, 0x00401000,
+	0x00001040, 0x00400040, 0x80400040, 0x80000000, 0x80401000, 0x80001040, 0x80001040, 0x00000040,
+	0x80401040, 0x80000040, 0x80000000, 0x00001000, 0x80400000, 0x80001000, 0x00401040, 0x80400040,
+	0x80001000, 0x00001040, 0x00400000, 0x80401000, 0x00000040, 0x00400000, 0x00001000, 0x00401040
+};
+
+static const guint32 sbox5[64] =
+{
+	0x00000080, 0x01040080, 0x01040000, 0x21000080, 0x00040000, 0x00000080, 0x20000000, 0x01040000,
+	0x20040080, 0x00040000, 0x01000080, 0x20040080, 0x21000080, 0x21040000, 0x00040080, 0x20000000,
+	0x01000000, 0x20040000, 0x20040000, 0x00000000, 0x20000080, 0x21040080, 0x21040080, 0x01000080,
+	0x21040000, 0x20000080, 0x00000000, 0x21000000, 0x01040080, 0x01000000, 0x21000000, 0x00040080,
+	0x00040000, 0x21000080, 0x00000080, 0x01000000, 0x20000000, 0x01040000, 0x21000080, 0x20040080,
+	0x01000080, 0x20000000, 0x21040000, 0x01040080, 0x20040080, 0x00000080, 0x01000000, 0x21040000,
+	0x21040080, 0x00040080, 0x21000000, 0x21040080, 0x01040000, 0x00000000, 0x20040000, 0x21000000,
+	0x00040080, 0x01000080, 0x20000080, 0x00040000, 0x00000000, 0x20040000, 0x01040080, 0x20000080
+};
+
+static const guint32 sbox6[64] =
+{
+	0x10000008, 0x10200000, 0x00002000, 0x10202008, 0x10200000, 0x00000008, 0x10202008, 0x00200000,
+	0x10002000, 0x00202008, 0x00200000, 0x10000008, 0x00200008, 0x10002000, 0x10000000, 0x00002008,
+	0x00000000, 0x00200008, 0x10002008, 0x00002000, 0x00202000, 0x10002008, 0x00000008, 0x10200008,
+	0x10200008, 0x00000000, 0x00202008, 0x10202000, 0x00002008, 0x00202000, 0x10202000, 0x10000000,
+	0x10002000, 0x00000008, 0x10200008, 0x00202000, 0x10202008, 0x00200000, 0x00002008, 0x10000008,
+	0x00200000, 0x10002000, 0x10000000, 0x00002008, 0x10000008, 0x10202008, 0x00202000, 0x10200000,
+	0x00202008, 0x10202000, 0x00000000, 0x10200008, 0x00000008, 0x00002000, 0x10200000, 0x00202008,
+	0x00002000, 0x00200008, 0x10002008, 0x00000000, 0x10202000, 0x10000000, 0x00200008, 0x10002008
+};
+
+static const guint32 sbox7[64] =
+{
+	0x00100000, 0x02100001, 0x02000401, 0x00000000, 0x00000400, 0x02000401, 0x00100401, 0x02100400,
+	0x02100401, 0x00100000, 0x00000000, 0x02000001, 0x00000001, 0x02000000, 0x02100001, 0x00000401,
+	0x02000400, 0x00100401, 0x00100001, 0x02000400, 0x02000001, 0x02100000, 0x02100400, 0x00100001,
+	0x02100000, 0x00000400, 0x00000401, 0x02100401, 0x00100400, 0x00000001, 0x02000000, 0x00100400,
+	0x02000000, 0x00100400, 0x00100000, 0x02000401, 0x02000401, 0x02100001, 0x02100001, 0x00000001,
+	0x00100001, 0x02000000, 0x02000400, 0x00100000, 0x02100400, 0x00000401, 0x00100401, 0x02100400,
+	0x00000401, 0x02000001, 0x02100401, 0x02100000, 0x00100400, 0x00000000, 0x00000001, 0x02100401,
+	0x00000000, 0x00100401, 0x02100000, 0x00000400, 0x02000001, 0x02000400, 0x00000400, 0x00100001
+};
+
+static const guint32 sbox8[64] =
+{
+	0x08000820, 0x00000800, 0x00020000, 0x08020820, 0x08000000, 0x08000820, 0x00000020, 0x08000000,
+	0x00020020, 0x08020000, 0x08020820, 0x00020800, 0x08020800, 0x00020820, 0x00000800, 0x00000020,
+	0x08020000, 0x08000020, 0x08000800, 0x00000820, 0x00020800, 0x00020020, 0x08020020, 0x08020800,
+	0x00000820, 0x00000000, 0x00000000, 0x08020020, 0x08000020, 0x08000800, 0x00020820, 0x00020000,
+	0x00020820, 0x00020000, 0x08020800, 0x00000800, 0x00000020, 0x08020020, 0x00000800, 0x00020820,
+	0x08000800, 0x00000020, 0x08000020, 0x08020000, 0x08020020, 0x08000000, 0x00020000, 0x08000820,
+	0x00000000, 0x08020820, 0x00020020, 0x08000020, 0x08020000, 0x08000800, 0x08000820, 0x00000000,
+	0x08020820, 0x00020800, 0x00020800, 0x00000820, 0x00000820, 0x00020020, 0x08000000, 0x08020800
+};
+
+
+/*
+ *  * These two tables are part of the 'permuted choice 1' function.
+ *   * In this implementation several speed improvements are done.
+ *    */
+static const guint32 leftkey_swap[16] =
+{
+	0x00000000, 0x00000001, 0x00000100, 0x00000101,
+	0x00010000, 0x00010001, 0x00010100, 0x00010101,
+	0x01000000, 0x01000001, 0x01000100, 0x01000101,
+	0x01010000, 0x01010001, 0x01010100, 0x01010101
+};
+
+static const guint32 rightkey_swap[16] =
+{
+	0x00000000, 0x01000000, 0x00010000, 0x01010000,
+	0x00000100, 0x01000100, 0x00010100, 0x01010100,
+	0x00000001, 0x01000001, 0x00010001, 0x01010001,
+	0x00000101, 0x01000101, 0x00010101, 0x01010101,
+};
+
+
+/*
+ *  Numbers of left shifts per round for encryption subkey schedule
+ *  To calculate the decryption key scheduling we just reverse the
+ *  ordering of the subkeys so we can omit the table for decryption
+ *  subkey schedule.
+ */
+static const guint8 encrypt_rotate_tab[16] =
+{
+	1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+/*
+ *  Macro to swap bits across two words
+ **/
+#define DO_PERMUTATION(a, temp, b, offset, mask)    \
+	temp = ((a>>offset) ^ b) & mask;            \
+	b ^= temp;                      \
+	a ^= temp<<offset;
+
+
+/*
+ *  This performs the 'initial permutation' for the data to be encrypted or decrypted
+ **/
+#define INITIAL_PERMUTATION(left, temp, right)      \
+	DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f)    \
+	DO_PERMUTATION(left, temp, right, 16, 0x0000ffff)   \
+	DO_PERMUTATION(right, temp, left, 2, 0x33333333)    \
+	DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff)    \
+	DO_PERMUTATION(left, temp, right, 1, 0x55555555)
+
+
+/*
+ * The 'inverse initial permutation'
+ **/
+#define FINAL_PERMUTATION(left, temp, right)        \
+	DO_PERMUTATION(left, temp, right, 1, 0x55555555)    \
+	DO_PERMUTATION(right, temp, left, 8, 0x00ff00ff)    \
+	DO_PERMUTATION(right, temp, left, 2, 0x33333333)    \
+	DO_PERMUTATION(left, temp, right, 16, 0x0000ffff)   \
+	DO_PERMUTATION(left, temp, right, 4, 0x0f0f0f0f)
+
+
+/*
+ * A full DES round including 'expansion function', 'sbox substitution'
+ * and 'primitive function P' but without swapping the left and right word.
+ **/
+#define DES_ROUND(from, to, work, subkey)       \
+	work = ((from<<1) | (from>>31)) ^ *subkey++;    \
+	to ^= sbox8[  work      & 0x3f ];           \
+	to ^= sbox6[ (work>>8)  & 0x3f ];           \
+	to ^= sbox4[ (work>>16) & 0x3f ];           \
+	to ^= sbox2[ (work>>24) & 0x3f ];           \
+	work = ((from>>3) | (from<<29)) ^ *subkey++;    \
+	to ^= sbox7[  work      & 0x3f ];           \
+	to ^= sbox5[ (work>>8)  & 0x3f ];           \
+	to ^= sbox3[ (work>>16) & 0x3f ];           \
+	to ^= sbox1[ (work>>24) & 0x3f ];
+
+
+/*
+ * Macros to convert 8 bytes from/to 32bit words
+ **/
+#define READ_64BIT_DATA(data, left, right)                  \
+	left  = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];   \
+	right = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
+
+#define WRITE_64BIT_DATA(data, left, right)                 \
+	data[0] = (left >> 24) &0xff; data[1] = (left >> 16) &0xff;         \
+	data[2] = (left >> 8) &0xff; data[3] = left &0xff;              \
+	data[4] = (right >> 24) &0xff; data[5] = (right >> 16) &0xff;       \
+	data[6] = (right >> 8) &0xff; data[7] = right &0xff;
+
+
+/*
+ * des_key_schedule():    Calculate 16 subkeys pairs (even/odd) for
+ *            16 encryption rounds.
+ *            To calculate subkeys for decryption the caller
+ *                have to reorder the generated subkeys.
+ *
+ *        rawkey:       8 Bytes of key data
+ *        subkey:       Array of at least 32 guint32s. Will be filled
+ *              with calculated subkeys.
+ *
+ **/
+static void
+des_key_schedule (const guint8 * rawkey, guint32 * subkey)
+{
+	guint32 left, right, work;
+	int round;
+
+	READ_64BIT_DATA (rawkey, left, right)
+
+	DO_PERMUTATION (right, work, left, 4, 0x0f0f0f0f)
+	DO_PERMUTATION (right, work, left, 0, 0x10101010)
+
+	left = (leftkey_swap[(left >> 0) & 0xf] << 3) | (leftkey_swap[(left >> 8) & 0xf] << 2)
+	| (leftkey_swap[(left >> 16) & 0xf] << 1) | (leftkey_swap[(left >> 24) & 0xf])
+	| (leftkey_swap[(left >> 5) & 0xf] << 7) | (leftkey_swap[(left >> 13) & 0xf] << 6)
+	| (leftkey_swap[(left >> 21) & 0xf] << 5) | (leftkey_swap[(left >> 29) & 0xf] << 4);
+
+	left &= 0x0fffffff;
+
+	right = (rightkey_swap[(right >> 1) & 0xf] << 3) | (rightkey_swap[(right >> 9) & 0xf] << 2)
+		| (rightkey_swap[(right >> 17) & 0xf] << 1) | (rightkey_swap[(right >> 25) & 0xf])
+		| (rightkey_swap[(right >> 4) & 0xf] << 7) | (rightkey_swap[(right >> 12) & 0xf] << 6)
+		| (rightkey_swap[(right >> 20) & 0xf] << 5) | (rightkey_swap[(right >> 28) & 0xf] << 4);
+
+	right &= 0x0fffffff;
+
+	for (round = 0; round < 16; ++round)
+	{
+        left = ((left << encrypt_rotate_tab[round]) | (left >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff;
+        right = ((right << encrypt_rotate_tab[round]) | (right >> (28 - encrypt_rotate_tab[round]))) & 0x0fffffff;
+
+		*subkey++ = ((left << 4) & 0x24000000)
+			| ((left << 28) & 0x10000000)
+			| ((left << 14) & 0x08000000)
+			| ((left << 18) & 0x02080000)
+			| ((left << 6) & 0x01000000)
+			| ((left << 9) & 0x00200000)
+			| ((left >> 1) & 0x00100000)
+			| ((left << 10) & 0x00040000)
+			| ((left << 2) & 0x00020000)
+			| ((left >> 10) & 0x00010000)
+			| ((right >> 13) & 0x00002000)
+			| ((right >> 4) & 0x00001000)
+			| ((right << 6) & 0x00000800)
+			| ((right >> 1) & 0x00000400)
+			| ((right >> 14) & 0x00000200)
+			| (right & 0x00000100)
+			| ((right >> 5) & 0x00000020)
+			| ((right >> 10) & 0x00000010)
+			| ((right >> 3) & 0x00000008)
+			| ((right >> 18) & 0x00000004)
+			| ((right >> 26) & 0x00000002)
+			| ((right >> 24) & 0x00000001);
+
+		*subkey++ = ((left << 15) & 0x20000000)
+			| ((left << 17) & 0x10000000)
+			| ((left << 10) & 0x08000000)
+			| ((left << 22) & 0x04000000)
+			| ((left >> 2) & 0x02000000)
+			| ((left << 1) & 0x01000000)
+			| ((left << 16) & 0x00200000)
+			| ((left << 11) & 0x00100000)
+			| ((left << 3) & 0x00080000)
+			| ((left >> 6) & 0x00040000)
+			| ((left << 15) & 0x00020000)
+			| ((left >> 4) & 0x00010000)
+			| ((right >> 2) & 0x00002000)
+			| ((right << 8) & 0x00001000)
+			| ((right >> 14) & 0x00000808)
+			| ((right >> 9) & 0x00000400)
+			| ((right) & 0x00000200)
+			| ((right << 7) & 0x00000100)
+			| ((right >> 7) & 0x00000020)
+			| ((right >> 3) & 0x00000011)
+			| ((right << 2) & 0x00000004)
+			| ((right >> 21) & 0x00000002);
+	}
+}
+
+
+/*
+ *  Fill a DES context with subkeys calculated from a 64bit key.
+ *  Does not check parity bits, but simply ignore them.
+ *  Does not check for weak keys.
+ **/
+static void
+des_set_key (PurpleCipherContext *context, const guchar * key)
+{
+	struct _des_ctx *ctx = purple_cipher_context_get_data(context);
+	int i;
+
+	des_key_schedule (key, ctx->encrypt_subkeys);
+
+	for(i=0; i<32; i+=2)
+	{
+		ctx->decrypt_subkeys[i] = ctx->encrypt_subkeys[30-i];
+		ctx->decrypt_subkeys[i+1] = ctx->encrypt_subkeys[31-i];
+	}
+}
+
+
+/*
+ *  Electronic Codebook Mode DES encryption/decryption of data according
+ *  to 'mode'.
+ **/
+static int
+des_ecb_crypt (struct _des_ctx *ctx, const guint8 * from, guint8 * to, int mode)
+{
+	guint32 left, right, work;
+	guint32 *keys;
+
+	keys = mode ? ctx->decrypt_subkeys : ctx->encrypt_subkeys;
+
+	READ_64BIT_DATA (from, left, right)
+	INITIAL_PERMUTATION (left, work, right)
+
+	DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+	DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+	DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+	DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+	DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+	DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+	DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+	DES_ROUND (right, left, work, keys) DES_ROUND (left, right, work, keys)
+
+	FINAL_PERMUTATION (right, work, left)
+	WRITE_64BIT_DATA (to, right, left)
+
+	return 0;
+}
+
+static gint
+des_encrypt(PurpleCipherContext *context, const guchar data[],
+            size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while(offset+8<=len) {
+		des_ecb_crypt(purple_cipher_context_get_data(context),
+		              data+offset,
+		              output+offset,
+		              0);
+		offset+=8;
+	}
+	*outlen = len;
+	if(offset<len) {
+		*outlen += len - offset;
+		tmp = offset;
+		while(tmp<len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(purple_cipher_context_get_data(context),
+		              buf,
+		              output+offset,
+		              0);
+	}
+	return 0;
+}
+
+static gint
+des_decrypt(PurpleCipherContext *context, const guchar data[],
+            size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while(offset+8<=len) {
+		des_ecb_crypt(purple_cipher_context_get_data(context),
+		              data+offset,
+		              output+offset,
+		              1);
+		offset+=8;
+	}
+	*outlen = len;
+	if(offset<len) {
+		*outlen += len - offset;
+		tmp = offset;
+		while(tmp<len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(purple_cipher_context_get_data(context),
+		              buf,
+		              output+offset,
+		              1);
+	}
+	return 0;
+}
+
+static void
+des_init(PurpleCipherContext *context, gpointer extra) {
+	struct _des_ctx *mctx;
+	mctx = g_new0(struct _des_ctx, 1);
+	purple_cipher_context_set_data(context, mctx);
+}
+
+static void
+des_uninit(PurpleCipherContext *context) {
+	struct _des_ctx *des_context;
+
+	des_context = purple_cipher_context_get_data(context);
+	memset(des_context, 0, sizeof(*des_context));
+
+	g_free(des_context);
+	des_context = NULL;
+}
+
+static PurpleCipherOps DESOps = {
+	NULL,              /* Set option */
+	NULL,              /* Get option */
+	des_init,          /* init */
+ 	NULL,              /* reset */
+	des_uninit,        /* uninit */
+	NULL,              /* set iv */
+	NULL,              /* append */
+	NULL,              /* digest */
+	des_encrypt,       /* encrypt */
+	des_decrypt,       /* decrypt */
+	NULL,              /* set salt */
+	NULL,              /* get salt size */
+	des_set_key,       /* set key */
+	NULL,              /* get key size */
+	NULL,              /* set batch mode */
+	NULL,              /* get batch mode */
+	NULL,              /* get block size */
+	NULL               /* set key with len */
+};
+
+/******************************************************************************
+ * Triple-DES
+ *****************************************************************************/
+
+typedef struct _des3_ctx
+{
+	PurpleCipherBatchMode mode;
+	guchar iv[8];
+	/* First key for encryption */
+	struct _des_ctx key1;
+	/* Second key for decryption */
+	struct _des_ctx key2;
+	/* Third key for encryption */
+	struct _des_ctx key3;
+} des3_ctx[1];
+
+/*
+ *  Fill a DES3 context with subkeys calculated from 3 64bit key.
+ *  Does not check parity bits, but simply ignore them.
+ *  Does not check for weak keys.
+ **/
+static void
+des3_set_key(PurpleCipherContext *context, const guchar * key)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+	int i;
+
+	des_key_schedule (key +  0, ctx->key1.encrypt_subkeys);
+	des_key_schedule (key +  8, ctx->key2.encrypt_subkeys);
+	des_key_schedule (key + 16, ctx->key3.encrypt_subkeys);
+
+	for (i = 0; i < 32; i += 2)
+	{
+		ctx->key1.decrypt_subkeys[i]    = ctx->key1.encrypt_subkeys[30-i];
+		ctx->key1.decrypt_subkeys[i+1]  = ctx->key1.encrypt_subkeys[31-i];
+		ctx->key2.decrypt_subkeys[i]    = ctx->key2.encrypt_subkeys[30-i];
+		ctx->key2.decrypt_subkeys[i+1]  = ctx->key2.encrypt_subkeys[31-i];
+		ctx->key3.decrypt_subkeys[i]    = ctx->key3.encrypt_subkeys[30-i];
+		ctx->key3.decrypt_subkeys[i+1]  = ctx->key3.encrypt_subkeys[31-i];
+	}
+}
+
+static gint
+des3_ecb_encrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while (offset + 8 <= len) {
+		des_ecb_crypt(&ctx->key1,
+		              data+offset,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+		offset += 8;
+	}
+	*outlen = len;
+	if (offset < len) {
+		*outlen += len - offset;
+		tmp = offset;
+		memset(buf, 0, 8);
+		while (tmp < len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+	}
+	return 0;
+}
+
+static gint
+des3_cbc_encrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8];
+	memcpy(buf, ctx->iv, 8);
+	while (offset + 8 <= len) {
+		for (i = 0; i < 8; i++)
+			buf[i] ^= data[offset + i];
+
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+		memcpy(buf, output+offset, 8);
+		offset += 8;
+	}
+	*outlen = len;
+	if (offset < len) {
+		*outlen += len - offset;
+		tmp = offset;
+		i = 0;
+		while (tmp < len) {
+			buf[i++] ^= data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+	}
+	return 0;
+}
+
+static gint
+des3_encrypt(PurpleCipherContext *context, const guchar data[],
+             size_t len, guchar output[], size_t *outlen)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+		return des3_ecb_encrypt(ctx, data, len, output, outlen);
+	} else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
+		return des3_cbc_encrypt(ctx, data, len, output, outlen);
+	} else {
+		g_return_val_if_reached(0);
+	}
+
+	return 0;
+}
+
+static gint
+des3_ecb_decrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while (offset + 8 <= len) {
+		/* NOTE: Apply key in reverse */
+		des_ecb_crypt(&ctx->key3,
+		              data+offset,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+		offset+=8;
+	}
+	*outlen = len;
+	if (offset < len) {
+		*outlen += len - offset;
+		tmp = offset;
+		memset(buf, 0, 8);
+		while (tmp < len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+	}
+	return 0;
+}
+
+static gint
+des3_cbc_decrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	guint8 link[8];
+	memcpy(link, ctx->iv, 8);
+	while (offset + 8 <= len) {
+		des_ecb_crypt(&ctx->key3,
+		              data+offset,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+		for (i = 0; i < 8; i++)
+			output[offset + i] ^= link[i];
+		memcpy(link, data + offset, 8);
+		offset+=8;
+	}
+	*outlen = len;
+	if(offset<len) {
+		*outlen += len - offset;
+		tmp = offset;
+		memset(buf, 0, 8);
+		i = 0;
+		while(tmp<len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+		for (i = 0; i < 8; i++)
+			output[offset + i] ^= link[i];
+	}
+	return 0;
+}
+
+static gint
+des3_decrypt(PurpleCipherContext *context, const guchar data[],
+             size_t len, guchar output[], size_t *outlen)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+		return des3_ecb_decrypt(ctx, data, len, output, outlen);
+	} else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
+		return des3_cbc_decrypt(ctx, data, len, output, outlen);
+	} else {
+		g_return_val_if_reached(0);
+	}
+
+	return 0;
+}
+
+static void
+des3_set_batch(PurpleCipherContext *context, PurpleCipherBatchMode mode)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	ctx->mode = mode;
+}
+
+static PurpleCipherBatchMode
+des3_get_batch(PurpleCipherContext *context)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	return ctx->mode;
+}
+
+static void
+des3_set_iv(PurpleCipherContext *context, guchar *iv, size_t len)
+{
+	struct _des3_ctx *ctx;
+
+	g_return_if_fail(len == 8);
+
+	ctx = purple_cipher_context_get_data(context);
+
+	memcpy(ctx->iv, iv, len);
+}
+
+static void
+des3_init(PurpleCipherContext *context, gpointer extra)
+{
+	struct _des3_ctx *mctx;
+	mctx = g_new0(struct _des3_ctx, 1);
+	purple_cipher_context_set_data(context, mctx);
+}
+
+static void
+des3_uninit(PurpleCipherContext *context)
+{
+	struct _des3_ctx *des3_context;
+
+	des3_context = purple_cipher_context_get_data(context);
+	memset(des3_context, 0, sizeof(*des3_context));
+
+	g_free(des3_context);
+	des3_context = NULL;
+}
+
+static PurpleCipherOps DES3Ops = {
+	NULL,              /* Set option */
+	NULL,              /* Get option */
+	des3_init,         /* init */
+	NULL,              /* reset */
+	des3_uninit,       /* uninit */
+	des3_set_iv,       /* set iv */
+	NULL,              /* append */
+	NULL,              /* digest */
+	des3_encrypt,      /* encrypt */
+	des3_decrypt,      /* decrypt */
+	NULL,              /* set salt */
+	NULL,              /* get salt size */
+	des3_set_key,      /* set key */
+	NULL,              /* get key size */
+	des3_set_batch,    /* set batch mode */
+	des3_get_batch,    /* get batch mode */
+	NULL,              /* get block size */
+	NULL               /* set key with len */
+};
+
+/******************************************************************************
+ * Registration
+ *****************************************************************************/
+PurpleCipherOps *
+purple_des_cipher_get_ops(void) {
+	return &DESOps;
+}
+
+PurpleCipherOps *
+purple_des3_cipher_get_ops(void) {
+	return &DES3Ops;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/ciphers/gchecksum.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,144 @@
+#include <cipher.h>
+
+#if GLIB_CHECK_VERSION(2,16,0)
+
+static void
+purple_g_checksum_init(PurpleCipherContext *context, GChecksumType type)
+{
+    GChecksum *checksum;
+
+    checksum = g_checksum_new(type);
+    purple_cipher_context_set_data(context, checksum);
+}
+
+static void
+purple_g_checksum_reset(PurpleCipherContext *context, GChecksumType type)
+{
+    GChecksum *checksum;
+
+    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
+purple_g_checksum_uninit(PurpleCipherContext *context)
+{
+    GChecksum *checksum;
+
+    checksum = purple_cipher_context_get_data(context);
+    g_return_if_fail(checksum != NULL);
+
+    g_checksum_free(checksum);
+}
+
+static void
+purple_g_checksum_append(PurpleCipherContext *context, const guchar *data,
+                         gsize len)
+{
+    GChecksum *checksum;
+
+    checksum = purple_cipher_context_get_data(context);
+    g_return_if_fail(checksum != NULL);
+
+    while (len >= G_MAXSSIZE) {
+        g_checksum_update(checksum, data, G_MAXSSIZE);
+        len -= G_MAXSSIZE;
+        data += G_MAXSSIZE;
+    }
+
+    if (len)
+        g_checksum_update(checksum, data, len);
+}
+
+static gboolean
+purple_g_checksum_digest(PurpleCipherContext *context, GChecksumType type,
+                         gsize len, guchar *digest, gsize *out_len)
+{
+    GChecksum *checksum;
+    const gssize required_length = g_checksum_type_get_length(type);
+
+    checksum = purple_cipher_context_get_data(context);
+
+    g_return_val_if_fail(len >= required_length, FALSE);
+    g_return_val_if_fail(checksum != NULL, FALSE);
+
+    g_checksum_get_digest(checksum, digest, &len);
+
+    purple_cipher_context_reset(context, NULL);
+
+    if (out_len)
+        *out_len = len;
+
+    return TRUE;
+}
+
+/******************************************************************************
+ * Macros
+ *****************************************************************************/
+#define PURPLE_G_CHECKSUM_IMPLEMENTATION(lower, camel, type, block_size) \
+	static size_t \
+	lower##_get_block_size(PurpleCipherContext *context) { \
+		return (block_size); \
+	} \
+	\
+	static void \
+	lower##_init(PurpleCipherContext *context, gpointer extra) { \
+		purple_g_checksum_init(context, (type)); \
+	} \
+	\
+	static void \
+	lower##_reset(PurpleCipherContext *context, gpointer extra) { \
+		purple_g_checksum_reset(context, (type)); \
+	} \
+	\
+	static gboolean \
+	lower##_digest(PurpleCipherContext *context, gsize in_len, \
+	                 guchar digest[], size_t *out_len) \
+	{ \
+		return purple_g_checksum_digest(context, (type), in_len, digest, \
+		                                out_len); \
+	} \
+	\
+	static PurpleCipherOps camel##Ops = { \
+		NULL,                     /* Set option */       \
+		NULL,                     /* Get option */       \
+		lower##_init,             /* init */             \
+		lower##_reset,            /* reset */            \
+		purple_g_checksum_uninit, /* uninit */           \
+		NULL,                     /* set iv */           \
+		purple_g_checksum_append, /* append */           \
+		lower##_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 */   \
+		lower##_get_block_size,   /* get block size */   \
+		NULL                      /* set key with len */ \
+	}; \
+	\
+	PurpleCipherOps * \
+	purple_##lower##_cipher_get_ops(void) { \
+		return &camel##Ops; \
+	}
+
+/******************************************************************************
+ * Macro Expansion
+ *****************************************************************************/
+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) */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/ciphers/hmac.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,218 @@
+/*
+ * 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>
+
+struct HMAC_Context {
+	PurpleCipherContext *hash;
+	char *name;
+	int blocksize;
+	guchar *opad;
+};
+
+	static void
+hmac_init(PurpleCipherContext *context, gpointer extra)
+{
+	struct HMAC_Context *hctx;
+	hctx = g_new0(struct HMAC_Context, 1);
+	purple_cipher_context_set_data(context, hctx);
+	purple_cipher_context_reset(context, extra);
+}
+
+	static void
+hmac_reset(PurpleCipherContext *context, gpointer extra)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	g_free(hctx->name);
+	hctx->name = NULL;
+	if (hctx->hash)
+		purple_cipher_context_destroy(hctx->hash);
+	hctx->hash = NULL;
+	hctx->blocksize = 0;
+	g_free(hctx->opad);
+	hctx->opad = NULL;
+}
+
+	static void
+hmac_set_opt(PurpleCipherContext *context, const gchar *name, void *value)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	if (purple_strequal(name, "hash")) {
+		g_free(hctx->name);
+		if (hctx->hash)
+			purple_cipher_context_destroy(hctx->hash);
+		hctx->name = g_strdup((char*)value);
+		hctx->hash = purple_cipher_context_new_by_name((char *)value, NULL);
+		hctx->blocksize = purple_cipher_context_get_block_size(hctx->hash);
+	}
+}
+
+	static void *
+hmac_get_opt(PurpleCipherContext *context, const gchar *name)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	if (purple_strequal(name, "hash")) {
+		return hctx->name;
+	}
+
+	return NULL;
+}
+
+	static void
+hmac_append(PurpleCipherContext *context, const guchar *data, size_t len)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(hctx->hash != NULL);
+
+	purple_cipher_context_append(hctx->hash, data, len);
+}
+
+	static gboolean
+hmac_digest(PurpleCipherContext *context, size_t in_len, guchar *out, size_t *out_len)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+	PurpleCipherContext *hash = hctx->hash;
+	guchar *inner_hash;
+	size_t hash_len;
+	gboolean result;
+
+	g_return_val_if_fail(hash != NULL, FALSE);
+
+	inner_hash = g_malloc(100); /* TODO: Should be enough for now... */
+	result = purple_cipher_context_digest(hash, 100, inner_hash, &hash_len);
+
+	purple_cipher_context_reset(hash, NULL);
+
+	purple_cipher_context_append(hash, hctx->opad, hctx->blocksize);
+	purple_cipher_context_append(hash, inner_hash, hash_len);
+
+	g_free(inner_hash);
+
+	result = result && purple_cipher_context_digest(hash, in_len, out, out_len);
+
+	return result;
+}
+
+	static void
+hmac_uninit(PurpleCipherContext *context)
+{
+	struct HMAC_Context *hctx;
+
+	purple_cipher_context_reset(context, NULL);
+
+	hctx = purple_cipher_context_get_data(context);
+
+	g_free(hctx);
+}
+
+	static void
+hmac_set_key_with_len(PurpleCipherContext *context, const guchar * key, size_t key_len)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+	int blocksize, i;
+	guchar *ipad;
+	guchar *full_key;
+
+	g_return_if_fail(hctx->hash != NULL);
+
+	g_free(hctx->opad);
+
+	blocksize = hctx->blocksize;
+	ipad = g_malloc(blocksize);
+	hctx->opad = g_malloc(blocksize);
+
+	if (key_len > blocksize) {
+		purple_cipher_context_reset(hctx->hash, NULL);
+		purple_cipher_context_append(hctx->hash, key, key_len);
+		full_key = g_malloc(100); /* TODO: Should be enough for now... */
+		purple_cipher_context_digest(hctx->hash, 100, full_key, &key_len);
+	} else
+		full_key = g_memdup(key, key_len);
+
+	if (key_len < blocksize) {
+		full_key = g_realloc(full_key, blocksize);
+		memset(full_key + key_len, 0, blocksize - key_len);
+	}
+
+	for(i = 0; i < blocksize; i++) {
+		ipad[i] = 0x36 ^ full_key[i];
+		hctx->opad[i] = 0x5c ^ full_key[i];
+	}
+
+	g_free(full_key);
+
+	purple_cipher_context_reset(hctx->hash, NULL);
+	purple_cipher_context_append(hctx->hash, ipad, blocksize);
+	g_free(ipad);
+}
+
+	static void
+hmac_set_key(PurpleCipherContext *context, const guchar * key)
+{
+	hmac_set_key_with_len(context, key, strlen((char *)key));
+}
+
+	static size_t
+hmac_get_block_size(PurpleCipherContext *context)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+
+	return hctx->blocksize;
+}
+
+static PurpleCipherOps HMACOps = {
+	hmac_set_opt,           /* Set option */
+	hmac_get_opt,           /* Get option */
+	hmac_init,               /* init */
+	hmac_reset,              /* reset */
+	hmac_uninit,             /* uninit */
+	NULL,                   /* set iv */
+	hmac_append,             /* append */
+	hmac_digest,             /* digest */
+	NULL,                   /* encrypt */
+	NULL,                   /* decrypt */
+	NULL,                   /* set salt */
+	NULL,                   /* get salt size */
+	hmac_set_key,           /* set key */
+	NULL,                   /* get key size */
+	NULL,                   /* set batch mode */
+	NULL,                   /* get batch mode */
+	hmac_get_block_size,    /* get block size */
+	hmac_set_key_with_len   /* set key with len */
+};
+
+PurpleCipherOps *
+purple_hmac_cipher_get_ops(void) {
+	return &HMACOps;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/ciphers/md4.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,297 @@
+/*
+ * 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 md4 taken from linux kernel
+ * MD4 Message Digest Algorithm (RFC1320).
+ *
+ * Implementation derived from Andrew Tridgell and Steve French's
+ * CIFS MD4 implementation, and the cryptoapi implementation
+ * originally based on the public domain implementation written
+ * by Colin Plumb in 1993.
+ *
+ * Copyright (c) Andrew Tridgell 1997-1998.
+ * Modified by Steve French (sfrench@us.ibm.com) 2002
+ * Copyright (c) Cryptoapi developers.
+ * Copyright (c) 2002 David S. Miller (davem@redhat.com)
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ *
+ * 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>
+
+#define MD4_DIGEST_SIZE     16
+#define MD4_HMAC_BLOCK_SIZE 64
+#define MD4_BLOCK_WORDS     16
+#define MD4_HASH_WORDS      4
+
+struct MD4_Context {
+	guint32 hash[MD4_HASH_WORDS];
+	guint32 block[MD4_BLOCK_WORDS];
+	guint64 byte_count;
+};
+
+static inline guint32 lshift(guint32 x, unsigned int s)
+{
+	x &= 0xFFFFFFFF;
+	return ((x << s) & 0xFFFFFFFF) | (x >> (32 - s));
+}
+
+static inline guint32 F(guint32 x, guint32 y, guint32 z)
+{
+	return (x & y) | ((~x) & z);
+}
+
+static inline guint32 G(guint32 x, guint32 y, guint32 z)
+{
+	return (x & y) | (x & z) | (y & z);
+}
+
+static inline guint32 H(guint32 x, guint32 y, guint32 z)
+{
+	return x ^ y ^ z;
+}
+
+#define ROUND1(a,b,c,d,k,s) (a = lshift(a + F(b,c,d) + k, s))
+#define ROUND2(a,b,c,d,k,s) (a = lshift(a + G(b,c,d) + k + (guint32)0x5A827999,s))
+#define ROUND3(a,b,c,d,k,s) (a = lshift(a + H(b,c,d) + k + (guint32)0x6ED9EBA1,s))
+
+static inline void le32_to_cpu_array(guint32 *buf, unsigned int words)
+{
+	while (words--) {
+		*buf=GUINT_FROM_LE(*buf);
+		buf++;
+	}
+}
+
+static inline void cpu_to_le32_array(guint32 *buf, unsigned int words)
+{
+	while (words--) {
+		*buf=GUINT_TO_LE(*buf);
+		buf++;
+	}
+}
+
+static void md4_transform(guint32 *hash, guint32 const *in)
+{
+	guint32 a, b, c, d;
+
+	a = hash[0];
+	b = hash[1];
+	c = hash[2];
+	d = hash[3];
+
+	ROUND1(a, b, c, d, in[0], 3);
+	ROUND1(d, a, b, c, in[1], 7);
+	ROUND1(c, d, a, b, in[2], 11);
+	ROUND1(b, c, d, a, in[3], 19);
+	ROUND1(a, b, c, d, in[4], 3);
+	ROUND1(d, a, b, c, in[5], 7);
+	ROUND1(c, d, a, b, in[6], 11);
+	ROUND1(b, c, d, a, in[7], 19);
+	ROUND1(a, b, c, d, in[8], 3);
+	ROUND1(d, a, b, c, in[9], 7);
+	ROUND1(c, d, a, b, in[10], 11);
+	ROUND1(b, c, d, a, in[11], 19);
+	ROUND1(a, b, c, d, in[12], 3);
+	ROUND1(d, a, b, c, in[13], 7);
+	ROUND1(c, d, a, b, in[14], 11);
+	ROUND1(b, c, d, a, in[15], 19);
+
+	ROUND2(a, b, c, d,in[ 0], 3);
+	ROUND2(d, a, b, c, in[4], 5);
+	ROUND2(c, d, a, b, in[8], 9);
+	ROUND2(b, c, d, a, in[12], 13);
+	ROUND2(a, b, c, d, in[1], 3);
+	ROUND2(d, a, b, c, in[5], 5);
+	ROUND2(c, d, a, b, in[9], 9);
+	ROUND2(b, c, d, a, in[13], 13);
+	ROUND2(a, b, c, d, in[2], 3);
+	ROUND2(d, a, b, c, in[6], 5);
+	ROUND2(c, d, a, b, in[10], 9);
+	ROUND2(b, c, d, a, in[14], 13);
+	ROUND2(a, b, c, d, in[3], 3);
+	ROUND2(d, a, b, c, in[7], 5);
+	ROUND2(c, d, a, b, in[11], 9);
+	ROUND2(b, c, d, a, in[15], 13);
+
+	ROUND3(a, b, c, d,in[ 0], 3);
+	ROUND3(d, a, b, c, in[8], 9);
+	ROUND3(c, d, a, b, in[4], 11);
+	ROUND3(b, c, d, a, in[12], 15);
+	ROUND3(a, b, c, d, in[2], 3);
+	ROUND3(d, a, b, c, in[10], 9);
+	ROUND3(c, d, a, b, in[6], 11);
+	ROUND3(b, c, d, a, in[14], 15);
+	ROUND3(a, b, c, d, in[1], 3);
+	ROUND3(d, a, b, c, in[9], 9);
+	ROUND3(c, d, a, b, in[5], 11);
+	ROUND3(b, c, d, a, in[13], 15);
+	ROUND3(a, b, c, d, in[3], 3);
+	ROUND3(d, a, b, c, in[11], 9);
+	ROUND3(c, d, a, b, in[7], 11);
+	ROUND3(b, c, d, a, in[15], 15);
+
+	hash[0] += a;
+	hash[1] += b;
+	hash[2] += c;
+	hash[3] += d;
+}
+
+static inline void md4_transform_helper(struct MD4_Context *ctx)
+{
+	le32_to_cpu_array(ctx->block, sizeof(ctx->block) / sizeof(guint32));
+	md4_transform(ctx->hash, ctx->block);
+}
+
+static void
+md4_init(PurpleCipherContext *context, gpointer extra) {
+	struct MD4_Context *mctx;
+	mctx = g_new0(struct MD4_Context, 1);
+	purple_cipher_context_set_data(context, mctx);
+	purple_cipher_context_reset(context, extra);
+
+	mctx->hash[0] = 0x67452301;
+	mctx->hash[1] = 0xefcdab89;
+	mctx->hash[2] = 0x98badcfe;
+	mctx->hash[3] = 0x10325476;
+	mctx->byte_count = 0;
+}
+
+static void
+md4_reset(PurpleCipherContext *context, gpointer extra) {
+	struct MD4_Context *mctx;
+
+	mctx = purple_cipher_context_get_data(context);
+
+	mctx->hash[0] = 0x67452301;
+	mctx->hash[1] = 0xefcdab89;
+	mctx->hash[2] = 0x98badcfe;
+	mctx->hash[3] = 0x10325476;
+	mctx->byte_count = 0;
+}
+
+	static void
+md4_append(PurpleCipherContext *context, const guchar *data, size_t len)
+{
+	struct MD4_Context *mctx = purple_cipher_context_get_data(context);
+	const guint32 avail = sizeof(mctx->block) - (mctx->byte_count & 0x3f);
+
+	mctx->byte_count += len;
+
+	if (avail > len) {
+		memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
+				data, len);
+		return;
+	}
+
+	memcpy((char *)mctx->block + (sizeof(mctx->block) - avail),
+			data, avail);
+
+	md4_transform_helper(mctx);
+	data += avail;
+	len -= avail;
+
+	while (len >= sizeof(mctx->block)) {
+		memcpy(mctx->block, data, sizeof(mctx->block));
+		md4_transform_helper(mctx);
+		data += sizeof(mctx->block);
+		len -= sizeof(mctx->block);
+	}
+
+	memcpy(mctx->block, data, len);
+}
+
+	static gboolean
+md4_digest(PurpleCipherContext *context, size_t in_len, guchar *out,
+		size_t *out_len)
+{
+	struct MD4_Context *mctx = purple_cipher_context_get_data(context);
+	const unsigned int offset = mctx->byte_count & 0x3f;
+	char *p = (char *)mctx->block + offset;
+	int padding = 56 - (offset + 1);
+
+
+	if(in_len<16) return FALSE;
+	if(out_len) *out_len = 16;
+	*p++ = 0x80;
+	if (padding < 0) {
+		memset(p, 0x00, padding + sizeof (guint64));
+		md4_transform_helper(mctx);
+		p = (char *)mctx->block;
+		padding = 56;
+	}
+
+	memset(p, 0, padding);
+	mctx->block[14] = mctx->byte_count << 3;
+	mctx->block[15] = mctx->byte_count >> 29;
+	le32_to_cpu_array(mctx->block, (sizeof(mctx->block) -
+				sizeof(guint64)) / sizeof(guint32));
+	md4_transform(mctx->hash, mctx->block);
+	cpu_to_le32_array(mctx->hash, sizeof(mctx->hash) / sizeof(guint32));
+	memcpy(out, mctx->hash, sizeof(mctx->hash));
+	memset(mctx, 0, sizeof(*mctx));
+	return TRUE;
+}
+
+static void
+md4_uninit(PurpleCipherContext *context) {
+	struct MD4_Context *md4_context;
+
+	purple_cipher_context_reset(context, NULL);
+
+	md4_context = purple_cipher_context_get_data(context);
+	memset(md4_context, 0, sizeof(*md4_context));
+
+	g_free(md4_context);
+	md4_context = NULL;
+}
+
+	static size_t
+md4_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return MD4_HMAC_BLOCK_SIZE;
+}
+
+static PurpleCipherOps MD4Ops = {
+	NULL,                   /* Set option */
+	NULL,                   /* Get option */
+	md4_init,               /* init */
+	md4_reset,              /* reset */
+	md4_uninit,             /* uninit */
+	NULL,                   /* set iv */
+	md4_append,             /* append */
+	md4_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 */
+	md4_get_block_size,     /* get block size */
+	NULL                    /* set key with len */
+};
+
+PurpleCipherOps *
+purple_md4_cipher_get_ops(void) {
+	return &MD4Ops;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/ciphers/md5.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,326 @@
+/*
+ * 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) */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/ciphers/rc4.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,192 @@
+/*
+ * 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>
+
+struct RC4Context {
+	guchar state[256];
+	guchar x;
+	guchar y;
+	gint key_len;
+};
+
+static void
+rc4_init(PurpleCipherContext *context, void *extra) {
+	struct RC4Context *rc4_ctx;
+	rc4_ctx = g_new0(struct RC4Context, 1);
+	purple_cipher_context_set_data(context, rc4_ctx);
+	purple_cipher_context_reset(context, extra);
+}
+
+
+static void
+rc4_reset(PurpleCipherContext *context, void *extra) {
+	struct RC4Context *rc4_ctx;
+	guint i;
+
+	rc4_ctx = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(rc4_ctx);
+
+	for(i = 0; i < 256; i++)
+		rc4_ctx->state[i] = i;
+	rc4_ctx->x = 0;
+	rc4_ctx->y = 0;
+
+	/* default is 5 bytes (40bit key) */
+	rc4_ctx->key_len = 5;
+
+}
+
+static void
+rc4_uninit(PurpleCipherContext *context) {
+	struct RC4Context *rc4_ctx;
+
+	rc4_ctx = purple_cipher_context_get_data(context);
+	memset(rc4_ctx, 0, sizeof(*rc4_ctx));
+
+	g_free(rc4_ctx);
+	rc4_ctx = NULL;
+}
+
+
+
+static void
+rc4_set_key (PurpleCipherContext *context, const guchar * key) {
+	struct RC4Context *ctx;
+	guchar *state;
+	guchar temp_swap;
+	guchar x, y;
+	guint i;
+
+	ctx = purple_cipher_context_get_data(context);
+
+	x = 0;
+	y = 0;
+	state = &ctx->state[0];
+	for(i = 0; i < 256; i++)
+	{
+		y = (key[x] + state[i] + y) % 256;
+		temp_swap = state[i];
+		state[i] = state[y];
+		state[y] = temp_swap;
+		x = (x + 1) % ctx->key_len;
+	}
+}
+
+static void
+rc4_set_opt(PurpleCipherContext *context, const gchar *name, void *value) {
+	struct RC4Context *ctx;
+
+	ctx = purple_cipher_context_get_data(context);
+
+	if(purple_strequal(name, "key_len")) {
+		ctx->key_len = GPOINTER_TO_INT(value);
+	}
+}
+
+static size_t
+rc4_get_key_size (PurpleCipherContext *context)
+{
+	struct RC4Context *ctx;
+
+	g_return_val_if_fail(context, -1);
+
+	ctx = purple_cipher_context_get_data(context);
+
+	g_return_val_if_fail(ctx, -1);
+
+	return ctx->key_len;
+}
+
+static void *
+rc4_get_opt(PurpleCipherContext *context, const gchar *name) {
+	struct RC4Context *ctx;
+
+	ctx = purple_cipher_context_get_data(context);
+
+	if(purple_strequal(name, "key_len")) {
+		return GINT_TO_POINTER(ctx->key_len);
+	}
+
+	return NULL;
+}
+
+static gint
+rc4_encrypt(PurpleCipherContext *context, const guchar data[],
+            size_t len, guchar output[], size_t *outlen) {
+	struct RC4Context *ctx;
+	guchar temp_swap;
+	guchar x, y, z;
+	guchar *state;
+	guint i;
+
+	ctx = purple_cipher_context_get_data(context);
+
+	x = ctx->x;
+	y = ctx->y;
+	state = &ctx->state[0];
+
+	for(i = 0; i < len; i++)
+	{
+		x = (x + 1) % 256;
+		y = (state[x] + y) % 256;
+		temp_swap = state[x];
+		state[x] = state[y];
+		state[y] = temp_swap;
+		z = state[x] + (state[y]) % 256;
+		output[i] = data[i] ^ state[z];
+	}
+	ctx->x = x;
+	ctx->y = y;
+	if(outlen)
+		*outlen = len;
+
+	return 0;
+}
+
+static PurpleCipherOps RC4Ops = {
+	rc4_set_opt,   /* Set Option    */
+	rc4_get_opt,   /* Get Option    */
+	rc4_init,      /* init          */
+	rc4_reset,     /* reset         */
+	rc4_uninit,    /* uninit        */
+	NULL,          /* set iv        */
+	NULL,          /* append        */
+	NULL,          /* digest        */
+	rc4_encrypt,   /* encrypt       */
+	NULL,          /* decrypt       */
+	NULL,          /* set salt      */
+	NULL,          /* get salt size */
+	rc4_set_key,   /* set key       */
+	rc4_get_key_size, /* get key size  */
+	NULL,          /* set batch mode */
+	NULL,          /* get batch mode */
+	NULL,          /* get block size */
+	NULL           /* set key with len */
+};
+
+PurpleCipherOps *
+purple_rc4_cipher_get_ops(void) {
+	return &RC4Ops;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/ciphers/sha1.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,281 @@
+/*
+ * 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) */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/ciphers/sha256.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,283 @@
+/*
+ * 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/circbuffer.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/circbuffer.h	Wed Jun 13 19:30:27 2012 -0400
@@ -26,10 +26,6 @@
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 typedef struct _PurpleCircBuffer {
 
 	/** A pointer to the starting address of our chunk of memory. */
@@ -55,6 +51,8 @@
 
 } PurpleCircBuffer;
 
+G_BEGIN_DECLS
+
 /**
  * Creates a new circular buffer.  This will not allocate any memory for the
  * actual buffer until data is appended to it.
@@ -111,8 +109,6 @@
  */
 gboolean purple_circ_buffer_mark_read(PurpleCircBuffer *buf, gsize len);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _CIRCBUFFER_H */
--- a/libpurple/cmds.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/cmds.c	Wed Jun 13 19:30:27 2012 -0400
@@ -21,8 +21,6 @@
  *
  */
 
-#include <string.h>
-
 #include "internal.h"
 
 #include "account.h"
@@ -387,5 +385,10 @@
 void purple_cmds_uninit(void)
 {
 	purple_signals_unregister_by_instance(purple_cmds_get_handle());
+
+	while (cmds) {
+		purple_cmd_free(cmds->data);
+		cmds = g_list_delete_link(cmds, cmds);
+	}
 }
 
--- a/libpurple/cmds.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/cmds.h	Wed Jun 13 19:30:27 2012 -0400
@@ -32,7 +32,7 @@
 /*@{*/
 
 /** The possible results of running a command with purple_cmd_do_command(). */
-typedef enum _PurpleCmdStatus {
+typedef enum {
 	PURPLE_CMD_STATUS_OK,
 	PURPLE_CMD_STATUS_FAILED,
 	PURPLE_CMD_STATUS_NOT_FOUND,
@@ -48,7 +48,7 @@
  *  #PURPLE_CMD_RET_CONTINUE to cause the core to fall through to other
  *  commands with the same name.
  */
-typedef enum _PurpleCmdRet {
+typedef enum {
 	PURPLE_CMD_RET_OK,       /**< Everything's okay; Don't look for another command to call. */
 	PURPLE_CMD_RET_FAILED,   /**< The command failed, but stop looking.*/
 	PURPLE_CMD_RET_CONTINUE /**< Continue, looking for other commands with the same name to call. */
@@ -68,7 +68,7 @@
  */
 typedef guint PurpleCmdId;
 
-typedef enum _PurpleCmdPriority {
+typedef enum {
 	PURPLE_CMD_P_VERY_LOW  = -1000,
 	PURPLE_CMD_P_LOW       =     0,
 	PURPLE_CMD_P_DEFAULT   =  1000,
@@ -85,7 +85,7 @@
  *
  *  @see purple_cmd_register
  */
-typedef enum _PurpleCmdFlag {
+typedef enum {
 	/** Command is usable in IMs. */
 	PURPLE_CMD_FLAG_IM               = 0x01,
 	/** Command is usable in multi-user chats. */
@@ -99,9 +99,7 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Commands API                                                    */
@@ -225,26 +223,21 @@
 /**
  * Get the handle for the commands API
  * @return The handle
- * @since 2.5.0
  */
 gpointer purple_cmds_get_handle(void);
 
 /**
  * Initialize the commands subsystem.
- * @since 2.5.0
  */
 void purple_cmds_init(void);
 
 /**
  * Uninitialize the commands subsystem.
- * @since 2.5.0
  */
 void purple_cmds_uninit(void);
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_CMDS_H_ */
--- a/libpurple/connection.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/connection.c	Wed Jun 13 19:30:27 2012 -0400
@@ -52,10 +52,7 @@
 send_keepalive(gpointer data)
 {
 	PurpleConnection *gc = data;
-	PurplePluginProtocolInfo *prpl_info = NULL;
-
-	if (gc == NULL)
-		return TRUE;
+	PurplePluginProtocolInfo *prpl_info;
 
 	/* Only send keep-alives if we haven't heard from the
 	 * server in a while.
@@ -63,12 +60,8 @@
 	if ((time(NULL) - gc->last_received) < KEEPALIVE_INTERVAL)
 		return TRUE;
 
-	if (gc->prpl == NULL)
-		return TRUE;
-
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-
-	if (prpl_info && prpl_info->keepalive)
+	if (prpl_info->keepalive)
 		prpl_info->keepalive(gc);
 
 	return TRUE;
@@ -99,12 +92,6 @@
 }
 
 void
-purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
-{
-	_purple_connection_new(account, regist, password);
-}
-
-void
 _purple_connection_new(PurpleAccount *account, gboolean regist, const char *password)
 {
 	PurpleConnection *gc;
@@ -142,7 +129,7 @@
 			!(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
 			!(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
 		{
-			purple_debug_error("connection", "Can not connect to account %s without "
+			purple_debug_error("connection", "Cannot connect to account %s without "
 							 "a password.\n", purple_account_get_username(account));
 			return;
 		}
@@ -178,11 +165,6 @@
 		prpl_info->login(account);
 	}
 }
-void
-purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data)
-{
-	_purple_connection_new_unregister(account, password, cb, user_data);
-}
 
 void
 _purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data)
@@ -217,7 +199,7 @@
 		!(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
 		!(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
 	{
-		purple_debug_error("connection", "Can not connect to account %s without "
+		purple_debug_error("connection", "Cannot connect to account %s without "
 						   "a password.\n", purple_account_get_username(account));
 		return;
 	}
@@ -241,12 +223,6 @@
 }
 
 void
-purple_connection_destroy(PurpleConnection *gc)
-{
-	_purple_connection_destroy(gc);
-}
-
-void
 _purple_connection_destroy(PurpleConnection *gc)
 {
 	PurpleAccount *account;
@@ -379,6 +355,7 @@
 		purple_blist_add_account(account);
 
 		purple_signal_emit(purple_connections_get_handle(), "signed-on", gc);
+		purple_signal_emit_return_1(purple_connections_get_handle(), "autojoin", gc);
 
 		serv_set_permit_deny(gc);
 
@@ -410,6 +387,14 @@
 }
 
 void
+purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags)
+{
+	g_return_if_fail(gc != NULL);
+
+	gc->flags = flags;
+}
+
+void
 purple_connection_set_account(PurpleConnection *gc, PurpleAccount *account)
 {
 	g_return_if_fail(gc != NULL);
@@ -442,6 +427,14 @@
 	return gc->state;
 }
 
+PurpleConnectionFlags
+purple_connection_get_flags(const PurpleConnection *gc)
+{
+	g_return_val_if_fail(gc != NULL, 0);
+
+	return gc->flags;
+}
+
 PurpleAccount *
 purple_connection_get_account(const PurpleConnection *gc)
 {
@@ -463,7 +456,7 @@
 {
 	g_return_val_if_fail(gc != NULL, NULL);
 
-	return gc->password ? gc->password : gc->account->password;
+	return gc->password ? gc->password : purple_account_get_password(gc->account);
 }
 
 const char *
@@ -522,7 +515,8 @@
 	account = data;
 	gc = purple_account_get_connection(account);
 
-	gc->disconnect_timeout = 0;
+	if (gc != NULL)
+		gc->disconnect_timeout = 0;
 
 	password = g_strdup(purple_account_get_password(account));
 	purple_account_disconnect(account);
@@ -533,22 +527,7 @@
 }
 
 void
-purple_connection_error(PurpleConnection *gc, const char *text)
-{
-	/* prpls that have not been updated to use disconnection reasons will
-	 * be setting wants_to_die before calling this function, so choose
-	 * PURPLE_CONNECTION_ERROR_OTHER_ERROR (which is fatal) if it's true,
-	 * and PURPLE_CONNECTION_ERROR_NETWORK_ERROR (which isn't) if not.  See
-	 * the documentation in connection.h.
-	 */
-	PurpleConnectionError reason = gc->wants_to_die
-	                             ? PURPLE_CONNECTION_ERROR_OTHER_ERROR
-	                             : PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
-	purple_connection_error_reason (gc, reason, text);
-}
-
-void
-purple_connection_error_reason (PurpleConnection *gc,
+purple_connection_error (PurpleConnection *gc,
                                 PurpleConnectionError reason,
                                 const char *description)
 {
@@ -562,13 +541,13 @@
 	 */
 	if (reason > PURPLE_CONNECTION_ERROR_OTHER_ERROR) {
 		purple_debug_error("connection",
-			"purple_connection_error_reason: reason %u isn't a "
+			"purple_connection_error: reason %u isn't a "
 			"valid reason\n", reason);
 		reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
 	}
 
 	if (description == NULL) {
-		purple_debug_error("connection", "purple_connection_error_reason called with NULL description\n");
+		purple_debug_error("connection", "purple_connection_error called with NULL description\n");
 		description = _("Unknown error");
 	}
 
@@ -578,15 +557,13 @@
 
 	gc->wants_to_die = purple_connection_error_is_fatal (reason);
 
+	purple_debug_info("connection", "Connection error on %p (reason: %u description: %s)\n",
+	                  gc, reason, description);
+
 	ops = purple_connections_get_ui_ops();
 
-	if (ops != NULL)
-	{
-		if (ops->report_disconnect_reason != NULL)
-			ops->report_disconnect_reason (gc, reason, description);
-		if (ops->report_disconnect != NULL)
-			ops->report_disconnect (gc, description);
-	}
+	if (ops && ops->report_disconnect)
+		ops->report_disconnect(gc, reason, description);
 
 	purple_signal_emit(purple_connections_get_handle(), "connection-error",
 		gc, reason, description);
@@ -617,7 +594,7 @@
 			reason = PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR;
 	}
 
-	purple_connection_error_reason (gc, reason,
+	purple_connection_error (gc, reason,
 		purple_ssl_strerror(ssl_error));
 }
 
@@ -650,6 +627,13 @@
 	}
 }
 
+void purple_connection_update_last_received(PurpleConnection *gc)
+{
+	g_return_if_fail(gc != NULL);
+
+	gc->last_received = time(NULL);
+}
+
 void
 purple_connections_disconnect_all(void)
 {
@@ -719,6 +703,11 @@
 	                       purple_value_new(PURPLE_TYPE_ENUM),
 	                       purple_value_new(PURPLE_TYPE_STRING));
 
+	purple_signal_register(handle, "autojoin",
+	                       purple_marshal_BOOLEAN__POINTER, NULL, 1,
+	                       purple_value_new(PURPLE_TYPE_SUBTYPE,
+	                                        PURPLE_SUBTYPE_CONNECTION));
+
 }
 
 void
--- a/libpurple/connection.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/connection.h	Wed Jun 13 19:30:27 2012 -0400
@@ -44,8 +44,9 @@
 	PURPLE_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */
 	PURPLE_CONNECTION_NO_URLDESC = 0x0040,  /**< Connection does not support descriptions with links */
 	PURPLE_CONNECTION_NO_IMAGES = 0x0080,  /**< Connection does not support sending of images */
-	PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100 /**< Connection supports sending and receiving custom smileys */
-
+	PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100, /**< Connection supports sending and receiving custom smileys */
+	PURPLE_CONNECTION_SUPPORT_MOODS = 0x0200, /**< Connection supports setting moods */
+	PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES = 0x0400 /**< Connection supports setting a message on moods */
 } PurpleConnectionFlags;
 
 typedef enum
@@ -58,8 +59,6 @@
 
 /**
  * Possible errors that can cause a connection to be closed.
- *
- *  @since 2.3.0
  */
 typedef enum
 {
@@ -127,7 +126,7 @@
 	/** Some other error occurred which fits into none of the other
 	 *  categories.
 	 */
-	/* purple_connection_error_reason() in connection.c uses the fact that
+	/* purple_connection_error() in connection.c uses the fact that
 	 * this is the last member of the enum when sanity-checking; if other
 	 * reasons are added after it, the check must be updated.
 	 */
@@ -193,16 +192,6 @@
 	void (*notice)(PurpleConnection *gc, const char *text);
 
 	/**
-	 * Called when an error causes a connection to be disconnected.
-	 * Called before #disconnected.
-	 * @param text  a localized error message.
-	 * @see #purple_connection_error
-	 * @deprecated in favour of
-	 *             #PurpleConnectionUiOps.report_disconnect_reason.
-	 */
-	void (*report_disconnect)(PurpleConnection *gc, const char *text);
-
-	/**
 	 * Called when libpurple discovers that the computer's network
 	 * connection is active.  On Linux, this uses Network Manager if
 	 * available; on Windows, it uses Win32's network change notification
@@ -218,21 +207,17 @@
 
 	/**
 	 * Called when an error causes a connection to be disconnected.
-	 *  Called before #disconnected.  This op is intended to replace
-	 *  #report_disconnect.  If both are implemented, this will be called
-	 *  first; however, there's no real reason to implement both.
+	 * Called before #disconnected.
 	 *
-	 *  @param reason  why the connection ended, if known, or
-	 *                 #PURPLE_CONNECTION_ERROR_OTHER_ERROR, if not.
-	 *  @param text  a localized message describing the disconnection
-	 *               in more detail to the user.
-	 *  @see #purple_connection_error_reason
-	 *
-	 *  @since 2.3.0
+	 * @param reason  why the connection ended, if known, or
+	 *                #PURPLE_CONNECTION_ERROR_OTHER_ERROR, if not.
+	 * @param text  a localized message describing the disconnection
+	 *              in more detail to the user.
+	 * @see #purple_connection_error
 	 */
-	void (*report_disconnect_reason)(PurpleConnection *gc,
-	                                 PurpleConnectionError reason,
-	                                 const char *text);
+	void (*report_disconnect)(PurpleConnection *gc,
+	                          PurpleConnectionError reason,
+	                          const char *text);
 
 	void (*_purple_reserved1)(void);
 	void (*_purple_reserved2)(void);
@@ -250,7 +235,6 @@
 
 	PurpleAccount *account;        /**< The account being connected to.    */
 	char *password;              /**< The password used.                 */
-	int inpa;                    /**< The input watcher.                 */
 
 	GSList *buddy_chats;         /**< A list of active chats
 	                                  (#PurpleConversation structs of type
@@ -263,7 +247,7 @@
 	/** Wants to Die state.  This is set when the user chooses to log out, or
 	 * when the protocol is disconnected and should not be automatically
 	 * reconnected (incorrect password, etc.).  prpls should rely on
-	 * purple_connection_error_reason() to set this for them rather than
+	 * purple_connection_error() to set this for them rather than
 	 * setting it themselves.
 	 * @see purple_connection_error_is_fatal
 	 */
@@ -274,72 +258,13 @@
 					  prpl to avoid sending unneeded keepalives */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Connection API                                                  */
 /**************************************************************************/
 /*@{*/
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_CONNECTION_C_)
-/**
- * This function should only be called by purple_account_connect()
- * in account.c.  If you're trying to sign on an account, use that
- * function instead.
- *
- * Creates a connection to the specified account and either connects
- * or attempts to register a new account.  If you are logging in,
- * the connection uses the current active status for this account.
- * So if you want to sign on as "away," for example, you need to
- * have called purple_account_set_status(account, "away").
- * (And this will call purple_account_connect() automatically).
- *
- * @param account  The account the connection should be connecting to.
- * @param regist   Whether we are registering a new account or just
- *                 trying to do a normal signon.
- * @param password The password to use.
- *
- * @deprecated As this is internal, we should make it private in 3.0.0.
- */
-void purple_connection_new(PurpleAccount *account, gboolean regist,
-									const char *password);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_CONNECTION_C_)
-/**
- * This function should only be called by purple_account_unregister()
- * in account.c.
- *
- * Tries to unregister the account on the server. If the account is not
- * connected, also creates a new connection.
- *
- * @param account  The account to unregister
- * @param password The password to use.
- * @param cb Optional callback to be called when unregistration is complete
- * @param user_data user data to pass to the callback
- *
- * @deprecated As this is internal, we should make it private in 3.0.0.
- */
-void purple_connection_new_unregister(PurpleAccount *account, const char *password, PurpleAccountUnregistrationCb cb, void *user_data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_CONNECTION_C_)
-/**
- * Disconnects and destroys a PurpleConnection.
- *
- * This function should only be called by purple_account_disconnect()
- * in account.c.  If you're trying to sign off an account, use that
- * function instead.
- *
- * @param gc The purple connection to destroy.
- *
- * @deprecated As this is internal, we should make it private in 3.0.0.
- */
-void purple_connection_destroy(PurpleConnection *gc);
-#endif
-
 /**
  * Sets the connection state.  PRPLs should call this and pass in
  * the state #PURPLE_CONNECTED when the account is completely
@@ -353,6 +278,14 @@
 void purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state);
 
 /**
+ * Sets the connection flags.
+ *
+ * @param gc    The connection.
+ * @param flags The flags.
+ */
+void purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags);
+
+/**
  * Sets the connection's account.
  *
  * @param gc      The connection.
@@ -373,8 +306,6 @@
  *
  * @param connection The PurpleConnection.
  * @param proto_data The protocol data to set for the connection.
- *
- * @since 2.6.0
  */
 void purple_connection_set_protocol_data(PurpleConnection *connection, void *proto_data);
 
@@ -388,6 +319,15 @@
 PurpleConnectionState purple_connection_get_state(const PurpleConnection *gc);
 
 /**
+ * Returns the connection flags.
+ *
+ * @param gc The connection.
+ *
+ * @return The connection flags.
+ */
+PurpleConnectionFlags purple_connection_get_flags(const PurpleConnection *gc);
+
+/**
  * Returns TRUE if the account is connected, otherwise returns FALSE.
  *
  * @return TRUE if the account is connected, otherwise returns FALSE.
@@ -410,8 +350,6 @@
  * @param gc The connection.
  *
  * @return The protocol plugin.
- *
- * @since 2.4.0
  */
 PurplePlugin * purple_connection_get_prpl(const PurpleConnection *gc);
 
@@ -439,8 +377,6 @@
  * @param connection The PurpleConnection.
  *
  * @return The protocol data for the connection.
- *
- * @since 2.6.0
  */
 void *purple_connection_get_protocol_data(const PurpleConnection *connection);
 
@@ -464,21 +400,6 @@
 void purple_connection_notice(PurpleConnection *gc, const char *text);
 
 /**
- * Closes a connection with an error.
- *
- * @param gc     The connection.
- * @param reason The error text, which may not be @c NULL.
- * @deprecated in favour of #purple_connection_error_reason.  Calling
- *  @c purple_connection_error(gc, text) is equivalent to calling
- *  @c purple_connection_error_reason(gc, reason, text) where @c reason is
- *  #PURPLE_CONNECTION_ERROR_OTHER_ERROR if @c gc->wants_to_die is @c TRUE, and
- *  #PURPLE_CONNECTION_ERROR_NETWORK_ERROR if not.  (This is to keep
- *  auto-reconnection behaviour the same when using old prpls which don't use
- *  reasons yet.)
- */
-void purple_connection_error(PurpleConnection *gc, const char *reason);
-
-/**
  * Closes a connection with an error and a human-readable description of the
  * error.  It also sets @c gc->wants_to_die to the value of
  * #purple_connection_error_is_fatal(@a reason), mainly for
@@ -487,20 +408,16 @@
  * @param gc          the connection which is closing.
  * @param reason      why the connection is closing.
  * @param description a non-@c NULL localized description of the error.
- *
- * @since 2.3.0
  */
 void
-purple_connection_error_reason (PurpleConnection *gc,
-                                PurpleConnectionError reason,
-                                const char *description);
+purple_connection_error(PurpleConnection *gc,
+                        PurpleConnectionError reason,
+                        const char *description);
 
 /**
  * Closes a connection due to an SSL error; this is basically a shortcut to
  * turning the #PurpleSslErrorType into a #PurpleConnectionError and a
- * human-readable string and then calling purple_connection_error_reason().
- *
- * @since 2.3.0
+ * human-readable string and then calling purple_connection_error().
  */
 void
 purple_connection_ssl_error (PurpleConnection *gc,
@@ -523,12 +440,18 @@
  *
  * @return @c TRUE if the account should not be automatically reconnected, and
  *         @c FALSE otherwise.
- *
- * @since 2.3.0
  */
 gboolean
 purple_connection_error_is_fatal (PurpleConnectionError reason);
 
+/**
+ * Indicate that a packet was received on the connection.
+ * Set by the prpl to avoid sending unneeded keepalives.
+ *
+ * @param gc   The connection.
+ */
+void purple_connection_update_last_received(PurpleConnection *gc);
+
 /*@}*/
 
 /**************************************************************************/
@@ -618,8 +541,6 @@
 /*@}*/
 
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_CONNECTION_H_ */
--- a/libpurple/conversation.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/conversation.c	Wed Jun 13 19:30:27 2012 -0400
@@ -35,6 +35,131 @@
 
 #define SEND_TYPED_TIMEOUT_SECONDS 5
 
+/**
+ * Data specific to Chats.
+ */
+struct _PurpleConvChat
+{
+	PurpleConversation *conv;          /**< The parent conversation.      */
+
+	GList *in_room;                  /**< The users in the room.
+	                                  *   @deprecated Will be removed in 3.0.0
+									  */
+	GList *ignored;                  /**< Ignored users.                */
+	char  *who;                      /**< The person who set the topic. */
+	char  *topic;                    /**< The topic.                    */
+	int    id;                       /**< The chat ID.                  */
+	char *nick;                      /**< Your nick in this chat.       */
+
+	gboolean left;                   /**< We left the chat and kept the window open */
+	GHashTable *users;               /**< Hash table of the users in the room. */
+};
+
+/**
+ * Data specific to Instant Messages.
+ */
+struct _PurpleConvIm
+{
+	PurpleConversation *conv;            /**< The parent conversation.     */
+
+	PurpleTypingState typing_state;      /**< The current typing state.    */
+	guint  typing_timeout;             /**< The typing timer handle.     */
+	time_t type_again;                 /**< The type again time.         */
+	guint  send_typed_timeout;         /**< The type again timer handle. */
+
+	PurpleBuddyIcon *icon;               /**< The buddy icon.              */
+};
+
+/**
+ * Data for "Chat Buddies"
+ */
+struct _PurpleConvChatBuddy
+{
+	/** The chat participant's name in the chat. */
+	char *name;
+
+	/** The chat participant's alias, if known; @a NULL otherwise. */
+	char *alias;
+
+	/**
+	 * A string by which this buddy will be sorted, or @c NULL if the
+	 * buddy should be sorted by its @c name.  (This is currently always
+	 * @c NULL.
+	 */
+	char *alias_key;
+
+	/**
+	 * @a TRUE if this chat participant is on the buddy list;
+	 * @a FALSE otherwise.
+	 */
+	gboolean buddy;
+
+	/**
+	 * A bitwise OR of flags for this participant, such as whether they
+	 * are a channel operator.
+	 */
+	PurpleConvChatBuddyFlags flags;
+
+	/**
+	 * A hash table of attributes about the user, such as real name,
+	 * user\@host, etc.
+	 */
+	GHashTable *attributes;
+
+	/** The UI can put whatever it wants here. */
+	gpointer ui_data;
+};
+
+/**
+ * A core representation of a conversation between two or more people.
+ *
+ * The conversation can be an IM or a chat.
+ */
+struct _PurpleConversation
+{
+	PurpleConversationType type;  /**< The type of conversation.          */
+
+	PurpleAccount *account;       /**< The user using this conversation.  */
+
+
+	char *name;                 /**< The name of the conversation.      */
+	char *title;                /**< The window title.                  */
+
+	gboolean logging;           /**< The status of logging.             */
+
+	GList *logs;                /**< This conversation's logs           */
+
+	union
+	{
+		PurpleConvIm   *im;       /**< IM-specific data.                  */
+		PurpleConvChat *chat;     /**< Chat-specific data.                */
+		void *misc;               /**< Misc. data.                        */
+
+	} u;
+
+	PurpleConversationUiOps *ui_ops;           /**< UI-specific operations. */
+	void *ui_data;                           /**< UI-specific data.       */
+
+	GHashTable *data;                        /**< Plugin-specific data.   */
+
+	PurpleConnectionFlags features; /**< The supported features */
+	GList *message_history;         /**< Message history, as a GList of PurpleConvMessage's */
+};
+
+/**
+ * Description of a conversation message
+ */
+struct _PurpleConvMessage
+{
+	char *who;
+	char *what;
+	PurpleMessageFlags flags;
+	time_t when;
+	PurpleConversation *conv;
+	char *alias;
+};
+
+
 static GList *conversations = NULL;
 static GList *ims = NULL;
 static GList *chats = NULL;
@@ -70,6 +195,23 @@
 	g_free(hc);
 }
 
+static guint _purple_conversation_user_hash(gconstpointer data)
+{
+	const gchar *name = data;
+	gchar *collated;
+	guint hash;
+
+	collated = g_utf8_collate_key(name, -1);
+	hash     = g_str_hash(collated);
+	g_free(collated);
+	return hash;
+}
+
+static gboolean _purple_conversation_user_equal(gconstpointer a, gconstpointer b)
+{
+	return !g_utf8_collate(a, b);
+}
+
 void
 purple_conversations_set_ui_ops(PurpleConversationUiOps *ops)
 {
@@ -99,7 +241,7 @@
 
 	g_return_val_if_fail(conv != NULL, FALSE);
 
-	gc   = purple_conversation_get_gc(conv);
+	gc   = purple_conversation_get_connection(conv);
 	name = purple_conversation_get_name(conv);
 
 	if (gc != NULL && name != NULL) {
@@ -129,7 +271,7 @@
 		return;
 
 	account = purple_conversation_get_account(conv);
-	gc = purple_conversation_get_gc(conv);
+	gc = purple_conversation_get_connection(conv);
 
 	g_return_if_fail(account != NULL);
 	g_return_if_fail(gc != NULL);
@@ -245,7 +387,7 @@
 		if (gc)
 			me = purple_connection_get_display_name(gc);
 		if (!me)
-			me = conv->account->username;
+			me = purple_account_get_username(conv->account);
 		who = me;
 	}
 
@@ -364,7 +506,7 @@
 	conv->data         = g_hash_table_new_full(g_str_hash, g_str_equal,
 											   g_free, NULL);
 	/* copy features from the connection. */
-	conv->features = gc->flags;
+	conv->features = purple_connection_get_flags(gc);
 
 	if (type == PURPLE_CONV_TYPE_IM)
 	{
@@ -393,11 +535,13 @@
 
 		conv->u.chat = g_new0(PurpleConvChat, 1);
 		conv->u.chat->conv = conv;
+		conv->u.chat->users = g_hash_table_new_full(_purple_conversation_user_hash,
+				_purple_conversation_user_equal, g_free, NULL);
 		PURPLE_DBUS_REGISTER_POINTER(conv->u.chat, PurpleConvChat);
 
 		chats = g_list_prepend(chats, conv);
 
-		if ((disp = purple_connection_get_display_name(account->gc)))
+		if ((disp = purple_connection_get_display_name(purple_account_get_connection(account))))
 			purple_conv_chat_set_nick(conv->u.chat, disp);
 		else
 			purple_conv_chat_set_nick(conv->u.chat,
@@ -450,7 +594,7 @@
 	purple_request_close_with_handle(conv);
 
 	ops  = purple_conversation_get_ui_ops(conv);
-	gc   = purple_conversation_get_gc(conv);
+	gc   = purple_conversation_get_connection(conv);
 	name = purple_conversation_get_name(conv);
 
 	if (gc != NULL)
@@ -547,6 +691,8 @@
 		conv->u.im = NULL;
 	}
 	else if (conv->type == PURPLE_CONV_TYPE_CHAT) {
+		g_hash_table_destroy(conv->u.chat->users);
+		conv->u.chat->users = NULL;
 
 		g_list_foreach(conv->u.chat->in_room, (GFunc)purple_conv_chat_cb_destroy, NULL);
 		g_list_free(conv->u.chat->in_room);
@@ -575,6 +721,7 @@
 
 	if (ops != NULL && ops->destroy_conversation != NULL)
 		ops->destroy_conversation(conv);
+	conv->ui_data = NULL;
 
 	purple_conversation_close_logs(conv);
 
@@ -672,7 +819,7 @@
 }
 
 PurpleConnection *
-purple_conversation_get_gc(const PurpleConversation *conv)
+purple_conversation_get_connection(const PurpleConversation *conv)
 {
 	PurpleAccount *account;
 
@@ -683,7 +830,7 @@
 	if (account == NULL)
 		return NULL;
 
-	return account->gc;
+	return purple_account_get_connection(account);
 }
 
 void
@@ -968,7 +1115,7 @@
 							purple_account_get_username(account));
 
 				if (purple_account_get_alias(account) != NULL)
-					alias = account->alias;
+					alias = purple_account_get_alias(account);
 				else if (b != NULL && !purple_strequal(purple_buddy_get_name(b), purple_buddy_get_contact_alias(b)))
 					alias = purple_buddy_get_contact_alias(b);
 				else if (purple_connection_get_display_name(gc) != NULL)
@@ -999,12 +1146,6 @@
 		}
 	}
 
-	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
-		if ((flags & PURPLE_MESSAGE_RECV) == PURPLE_MESSAGE_RECV) {
-			purple_conv_im_set_typing_state(PURPLE_CONV_IM(conv), PURPLE_NOT_TYPING);
-		}
-	}
-
 	if (ops && ops->write_conv)
 		ops->write_conv(conv, who, alias, displayed, flags, mtime);
 
@@ -1124,7 +1265,6 @@
 purple_conv_im_start_typing_timeout(PurpleConvIm *im, int timeout)
 {
 	PurpleConversation *conv;
-	const char *name;
 
 	g_return_if_fail(im != NULL);
 
@@ -1132,7 +1272,6 @@
 		purple_conv_im_stop_typing_timeout(im);
 
 	conv = purple_conv_im_get_conversation(im);
-	name = purple_conversation_get_name(conv);
 
 	im->typing_timeout = purple_timeout_add_seconds(timeout, reset_typing_cb, conv);
 }
@@ -1226,6 +1365,10 @@
 
 	c = purple_conv_im_get_conversation(im);
 
+	if ((flags & PURPLE_MESSAGE_RECV) == PURPLE_MESSAGE_RECV) {
+		purple_conv_im_set_typing_state(im, PURPLE_NOT_TYPING);
+	}
+
 	/* Pass this on to either the ops structure or the default write func. */
 	if (c->ui_ops != NULL && c->ui_ops->write_im != NULL)
 		c->ui_ops->write_im(c, who, message, flags, mtime);
@@ -1361,16 +1504,6 @@
 }
 
 GList *
-purple_conv_chat_set_users(PurpleConvChat *chat, GList *users)
-{
-	g_return_val_if_fail(chat != NULL, NULL);
-
-	chat->in_room = users;
-
-	return users;
-}
-
-GList *
 purple_conv_chat_get_users(const PurpleConvChat *chat)
 {
 	g_return_val_if_fail(chat != NULL, NULL);
@@ -1520,16 +1653,14 @@
 	PurpleAccount *account;
 	PurpleConversation *conv;
 	PurpleConnection *gc;
-	PurplePluginProtocolInfo *prpl_info;
 
 	g_return_if_fail(chat != NULL);
 	g_return_if_fail(who != NULL);
 	g_return_if_fail(message != NULL);
 
 	conv      = purple_conv_chat_get_conversation(chat);
-	gc        = purple_conversation_get_gc(conv);
+	gc        = purple_conversation_get_connection(conv);
 	account   = purple_connection_get_account(gc);
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
 
 	/* Don't display this if the person who wrote it is ignored. */
 	if (purple_conv_chat_is_user_ignored(chat, who))
@@ -1644,7 +1775,7 @@
 	conv = purple_conv_chat_get_conversation(chat);
 	ops  = purple_conversation_get_ui_ops(conv);
 
-	gc = purple_conversation_get_gc(conv);
+	gc = purple_conversation_get_connection(conv);
 	g_return_if_fail(gc != NULL);
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
 	g_return_if_fail(prpl_info != NULL);
@@ -1671,7 +1802,7 @@
 				}
 			} else {
 				PurpleBuddy *buddy;
-				if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
+				if ((buddy = purple_find_buddy(purple_connection_get_account(gc), user)) != NULL)
 					alias = purple_buddy_get_contact_alias(buddy);
 			}
 		}
@@ -1682,9 +1813,9 @@
 
 		cbuddy = purple_conv_chat_cb_new(user, alias, flag);
 		cbuddy->buddy = purple_find_buddy(conv->account, user) != NULL;
-		/* This seems dumb. Why should we set users thousands of times? */
-		purple_conv_chat_set_users(chat,
-				g_list_prepend(chat->in_room, cbuddy));
+
+		chat->in_room = g_list_prepend(chat->in_room, cbuddy);
+		g_hash_table_replace(chat->users, g_strdup(cbuddy->name), cbuddy);
 
 		cbuddies = g_list_prepend(cbuddies, cbuddy);
 
@@ -1745,7 +1876,7 @@
 	conv = purple_conv_chat_get_conversation(chat);
 	ops  = purple_conversation_get_ui_ops(conv);
 
-	gc = purple_conversation_get_gc(conv);
+	gc = purple_conversation_get_connection(conv);
 	g_return_if_fail(gc != NULL);
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
 	g_return_if_fail(prpl_info != NULL);
@@ -1764,20 +1895,21 @@
 			{
 				const char *display_name = purple_connection_get_display_name(gc);
 				if (display_name != NULL)
-					alias = display_name;
+					new_alias = display_name;
 			}
 		}
 	} else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
 		PurpleBuddy *buddy;
-		if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
+		if ((buddy = purple_find_buddy(purple_connection_get_account(gc), new_user)) != NULL)
 			new_alias = purple_buddy_get_contact_alias(buddy);
 	}
 
 	flags = purple_conv_chat_user_get_flags(chat, old_user);
 	cb = purple_conv_chat_cb_new(new_user, new_alias, flags);
 	cb->buddy = purple_find_buddy(conv->account, new_user) != NULL;
-	purple_conv_chat_set_users(chat,
-		g_list_prepend(chat->in_room, cb));
+
+	chat->in_room = g_list_prepend(chat->in_room, cb);
+	g_hash_table_replace(chat->users, g_strdup(cb->name), cb);
 
 	if (ops != NULL && ops->chat_rename_user != NULL)
 		ops->chat_rename_user(conv, old_user, new_user, new_alias);
@@ -1785,8 +1917,8 @@
 	cb = purple_conv_chat_cb_find(chat, old_user);
 
 	if (cb) {
-		purple_conv_chat_set_users(chat,
-				g_list_remove(chat->in_room, cb));
+		chat->in_room = g_list_remove(chat->in_room, cb);
+		g_hash_table_remove(chat->users, cb->name);
 		purple_conv_chat_cb_destroy(cb);
 	}
 
@@ -1817,9 +1949,9 @@
 			if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
 				PurpleBuddy *buddy;
 
-				if ((buddy = purple_find_buddy(gc->account, old_user)) != NULL)
+				if ((buddy = purple_find_buddy(purple_connection_get_account(gc), old_user)) != NULL)
 					old_alias = purple_buddy_get_contact_alias(buddy);
-				if ((buddy = purple_find_buddy(gc->account, new_user)) != NULL)
+				if ((buddy = purple_find_buddy(purple_connection_get_account(gc), new_user)) != NULL)
 					new_alias = purple_buddy_get_contact_alias(buddy);
 			}
 
@@ -1863,7 +1995,7 @@
 
 	conv = purple_conv_chat_get_conversation(chat);
 
-	gc = purple_conversation_get_gc(conv);
+	gc = purple_conversation_get_connection(conv);
 	g_return_if_fail(gc != NULL);
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
 	g_return_if_fail(prpl_info != NULL);
@@ -1879,8 +2011,8 @@
 		cb = purple_conv_chat_cb_find(chat, user);
 
 		if (cb) {
-			purple_conv_chat_set_users(chat,
-					g_list_remove(chat->in_room, cb));
+			chat->in_room = g_list_remove(chat->in_room, cb);
+			g_hash_table_remove(chat->users, cb->name);
 			purple_conv_chat_cb_destroy(cb);
 		}
 
@@ -1894,7 +2026,7 @@
 			if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
 				PurpleBuddy *buddy;
 
-				if ((buddy = purple_find_buddy(gc->account, user)) != NULL)
+				if ((buddy = purple_find_buddy(purple_connection_get_account(gc), user)) != NULL)
 					alias = purple_buddy_get_contact_alias(buddy);
 			}
 
@@ -1960,8 +2092,10 @@
 		purple_conv_chat_cb_destroy(cb);
 	}
 
+	g_hash_table_remove_all(chat->users);
+
 	g_list_free(users);
-	purple_conv_chat_set_users(chat, NULL);
+	chat->in_room = NULL;
 }
 
 
@@ -2046,7 +2180,7 @@
 		conv = (PurpleConversation *)l->data;
 
 		if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)) == id &&
-			purple_conversation_get_gc(conv) == gc)
+			purple_conversation_get_connection(conv) == gc)
 			return conv;
 	}
 
@@ -2074,7 +2208,7 @@
 	user = purple_request_fields_get_string(fields, "screenname");
 	message = purple_request_fields_get_string(fields, "message");
 
-	serv_chat_invite(purple_conversation_get_gc(conv), chat->id, message, user);
+	serv_chat_invite(purple_conversation_get_connection(conv), chat->id, message, user);
 }
 
 void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user,
@@ -2141,6 +2275,8 @@
 	cb->name = g_strdup(name);
 	cb->flags = flags;
 	cb->alias = g_strdup(alias);
+	cb->attributes = g_hash_table_new_full(g_str_hash, g_str_equal,
+										   g_free, g_free);
 
 	PURPLE_DBUS_REGISTER_POINTER(cb, PurpleConvChatBuddy);
 	return cb;
@@ -2149,19 +2285,10 @@
 PurpleConvChatBuddy *
 purple_conv_chat_cb_find(PurpleConvChat *chat, const char *name)
 {
-	GList *l;
-	PurpleConvChatBuddy *cb = NULL;
-
 	g_return_val_if_fail(chat != NULL, NULL);
 	g_return_val_if_fail(name != NULL, NULL);
 
-	for (l = purple_conv_chat_get_users(chat); l; l = l->next) {
-		cb = l->data;
-		if (!g_utf8_collate(cb->name, name))
-			return cb;
-	}
-
-	return NULL;
+	return g_hash_table_lookup(chat->users, name);
 }
 
 void
@@ -2170,22 +2297,133 @@
 	if (cb == NULL)
 		return;
 
+	purple_signal_emit(purple_conversations_get_handle(),
+			"deleting-chat-buddy", cb);
+
 	g_free(cb->alias);
 	g_free(cb->alias_key);
 	g_free(cb->name);
+	g_hash_table_destroy(cb->attributes);
 
 	PURPLE_DBUS_UNREGISTER_POINTER(cb);
 	g_free(cb);
 }
 
+void purple_conv_chat_cb_set_ui_data(PurpleConvChatBuddy *cb, gpointer ui_data)
+{
+	g_return_if_fail(cb != NULL);
+
+	cb->ui_data = ui_data;
+}
+
+gpointer purple_conv_chat_cb_get_ui_data(const PurpleConvChatBuddy *cb)
+{
+	g_return_val_if_fail(cb != NULL, NULL);
+
+	return cb->ui_data;
+}
+
 const char *
-purple_conv_chat_cb_get_name(PurpleConvChatBuddy *cb)
+purple_conv_chat_cb_get_alias(const PurpleConvChatBuddy *cb)
+{
+	g_return_val_if_fail(cb != NULL, NULL);
+
+	return cb->alias;
+}
+
+const char *
+purple_conv_chat_cb_get_name(const PurpleConvChatBuddy *cb)
 {
 	g_return_val_if_fail(cb != NULL, NULL);
 
 	return cb->name;
 }
 
+PurpleConvChatBuddyFlags
+purple_conv_chat_cb_get_flags(const PurpleConvChatBuddy *cb)
+{
+	g_return_val_if_fail(cb != NULL, PURPLE_CBFLAGS_NONE);
+
+	return cb->flags;
+}
+
+gboolean purple_conv_chat_cb_is_buddy(const PurpleConvChatBuddy *cb)
+{
+	g_return_val_if_fail(cb != NULL, FALSE);
+
+	return cb->buddy;
+}
+
+const char *
+purple_conv_chat_cb_get_attribute(PurpleConvChatBuddy *cb, const char *key)
+{
+	g_return_val_if_fail(cb != NULL, NULL);
+	g_return_val_if_fail(key != NULL, NULL);
+	
+	return g_hash_table_lookup(cb->attributes, key);
+}
+
+static void
+append_attribute_key(gpointer key, gpointer value, gpointer user_data)
+{
+	GList **list = user_data;
+	*list = g_list_prepend(*list, key);
+}
+
+GList *
+purple_conv_chat_cb_get_attribute_keys(PurpleConvChatBuddy *cb)
+{
+	GList *keys = NULL;
+	
+	g_return_val_if_fail(cb != NULL, NULL);
+	
+	g_hash_table_foreach(cb->attributes, (GHFunc)append_attribute_key, &keys);
+	
+	return keys;
+}
+
+void
+purple_conv_chat_cb_set_attribute(PurpleConvChat *chat, PurpleConvChatBuddy *cb, const char *key, const char *value)
+{
+	PurpleConversation *conv;
+	PurpleConversationUiOps *ops;
+	
+	g_return_if_fail(cb != NULL);
+	g_return_if_fail(key != NULL);
+	g_return_if_fail(value != NULL);
+	
+	g_hash_table_replace(cb->attributes, g_strdup(key), g_strdup(value));
+	
+	conv = purple_conv_chat_get_conversation(chat);
+	ops = purple_conversation_get_ui_ops(conv);
+	
+	if (ops != NULL && ops->chat_update_user != NULL)
+		ops->chat_update_user(conv, cb->name);
+}
+
+void
+purple_conv_chat_cb_set_attributes(PurpleConvChat *chat, PurpleConvChatBuddy *cb, GList *keys, GList *values)
+{
+	PurpleConversation *conv;
+	PurpleConversationUiOps *ops;
+	
+	g_return_if_fail(cb != NULL);
+	g_return_if_fail(keys != NULL);
+	g_return_if_fail(values != NULL);
+	
+	while (keys != NULL && values != NULL) {
+		g_hash_table_replace(cb->attributes, g_strdup(keys->data), g_strdup(values->data));
+		keys = g_list_next(keys);
+		values = g_list_next(values);
+	}
+	
+	conv = purple_conv_chat_get_conversation(chat);
+	ops = purple_conversation_get_ui_ops(conv);
+	
+	if (ops != NULL && ops->chat_update_user != NULL)
+		ops->chat_update_user(conv, cb->name);
+}
+
 GList *
 purple_conversation_get_extended_menu(PurpleConversation *conv)
 {
@@ -2203,6 +2441,9 @@
 	GList *list = conv->message_history;
 	message_history_free(list);
 	conv->message_history = NULL;
+
+	purple_signal_emit(purple_conversations_get_handle(),
+			"cleared-message-history", conv);
 }
 
 GList *purple_conversation_get_message_history(PurpleConversation *conv)
@@ -2210,30 +2451,57 @@
 	return conv->message_history;
 }
 
-const char *purple_conversation_message_get_sender(PurpleConvMessage *msg)
+const char *purple_conversation_message_get_sender(const PurpleConvMessage *msg)
 {
 	g_return_val_if_fail(msg, NULL);
 	return msg->who;
 }
 
-const char *purple_conversation_message_get_message(PurpleConvMessage *msg)
+const char *purple_conversation_message_get_message(const PurpleConvMessage *msg)
 {
 	g_return_val_if_fail(msg, NULL);
 	return msg->what;
 }
 
-PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg)
+PurpleMessageFlags purple_conversation_message_get_flags(const PurpleConvMessage *msg)
 {
 	g_return_val_if_fail(msg, 0);
 	return msg->flags;
 }
 
-time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg)
+time_t purple_conversation_message_get_timestamp(const PurpleConvMessage *msg)
 {
 	g_return_val_if_fail(msg, 0);
 	return msg->when;
 }
 
+const char *purple_conversation_message_get_alias(const PurpleConvMessage *msg)
+{
+	g_return_val_if_fail(msg, NULL);
+	return msg->alias;
+}
+
+PurpleConversation *purple_conversation_message_get_conv(const PurpleConvMessage *msg)
+{
+	g_return_val_if_fail(msg, NULL);
+	return msg->conv;
+}
+
+void purple_conversation_set_ui_data(PurpleConversation *conv, gpointer ui_data)
+{
+	g_return_if_fail(conv != NULL);
+
+	conv->ui_data = ui_data;
+}
+
+gpointer purple_conversation_get_ui_data(const PurpleConversation *conv)
+{
+	g_return_val_if_fail(conv != NULL, NULL);
+
+	return conv->ui_data;
+}
+
+
 gboolean
 purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
 				const gchar *markup, gchar **error)
@@ -2303,6 +2571,26 @@
 										PURPLE_SUBTYPE_CONVERSATION),
 						 purple_value_new(PURPLE_TYPE_UINT));
 
+	purple_signal_register(handle, "sent-attention",
+						 purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
+						 NULL, 4,
+						 purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_ACCOUNT),
+						 purple_value_new(PURPLE_TYPE_STRING),
+						 purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_CONVERSATION),
+						 purple_value_new(PURPLE_TYPE_UINT));
+
+	purple_signal_register(handle, "got-attention",
+						 purple_marshal_VOID__POINTER_POINTER_POINTER_UINT,
+						 NULL, 4,
+						 purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_ACCOUNT),
+						 purple_value_new(PURPLE_TYPE_STRING),
+						 purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_CONVERSATION),
+						 purple_value_new(PURPLE_TYPE_UINT));
+
 	purple_signal_register(handle, "sending-im-msg",
 						 purple_marshal_VOID__POINTER_POINTER_POINTER,
 						 NULL, 3,
@@ -2482,6 +2770,11 @@
 						 purple_value_new(PURPLE_TYPE_STRING),
 						 purple_value_new(PURPLE_TYPE_STRING));
 
+	purple_signal_register(handle, "deleting-chat-buddy",
+						 purple_marshal_VOID__POINTER, NULL, 1,
+						 purple_value_new(PURPLE_TYPE_SUBTYPE,
+										PURPLE_SUBTYPE_CHATBUDDY));
+
 	purple_signal_register(handle, "chat-inviting-user",
 						 purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3,
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
@@ -2539,6 +2832,11 @@
 						 purple_value_new(PURPLE_TYPE_STRING),
 						 purple_value_new(PURPLE_TYPE_STRING));
 
+	purple_signal_register(handle, "cleared-message-history",
+	                       purple_marshal_VOID__POINTER, NULL, 1,
+	                       purple_value_new(PURPLE_TYPE_SUBTYPE,
+	                                        PURPLE_SUBTYPE_CONVERSATION));
+
 	purple_signal_register(handle, "conversation-extended-menu",
 			     purple_marshal_VOID__POINTER_POINTER, NULL, 2,
 			     purple_value_new(PURPLE_TYPE_SUBTYPE,
--- a/libpurple/conversation.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/conversation.h	Wed Jun 13 19:30:27 2012 -0400
@@ -125,7 +125,7 @@
 	PURPLE_MESSAGE_IMAGES      = 0x1000, /**< Message contains images  */
 	PURPLE_MESSAGE_NOTIFY      = 0x2000, /**< Message is a notification */
 	PURPLE_MESSAGE_NO_LINKIFY  = 0x4000, /**< Message should not be auto-
-										   linkified @since 2.1.0 */
+										   linkified */
 	PURPLE_MESSAGE_INVISIBLE   = 0x8000  /**< Message should not be displayed */
 } PurpleMessageFlags;
 
@@ -139,7 +139,8 @@
 	PURPLE_CBFLAGS_HALFOP        = 0x0002, /**< Half-op                      */
 	PURPLE_CBFLAGS_OP            = 0x0004, /**< Channel Op or Moderator      */
 	PURPLE_CBFLAGS_FOUNDER       = 0x0008, /**< Channel Founder              */
-	PURPLE_CBFLAGS_TYPING        = 0x0010  /**< Currently typing             */
+	PURPLE_CBFLAGS_TYPING        = 0x0010, /**< Currently typing             */
+	PURPLE_CBFLAGS_AWAY          = 0x0020  /**< Currently away.              */
 
 } PurpleConvChatBuddyFlags;
 
@@ -248,114 +249,7 @@
 	void (*_purple_reserved4)(void);
 };
 
-/**
- * Data specific to Instant Messages.
- */
-struct _PurpleConvIm
-{
-	PurpleConversation *conv;            /**< The parent conversation.     */
-
-	PurpleTypingState typing_state;      /**< The current typing state.    */
-	guint  typing_timeout;             /**< The typing timer handle.     */
-	time_t type_again;                 /**< The type again time.         */
-	guint  send_typed_timeout;         /**< The type again timer handle. */
-
-	PurpleBuddyIcon *icon;               /**< The buddy icon.              */
-};
-
-/**
- * Data specific to Chats.
- */
-struct _PurpleConvChat
-{
-	PurpleConversation *conv;          /**< The parent conversation.      */
-
-	GList *in_room;                  /**< The users in the room.        */
-	GList *ignored;                  /**< Ignored users.                */
-	char  *who;                      /**< The person who set the topic. */
-	char  *topic;                    /**< The topic.                    */
-	int    id;                       /**< The chat ID.                  */
-	char *nick;                      /**< Your nick in this chat.       */
-
-	gboolean left;                   /**< We left the chat and kept the window open */
-};
-
-/**
- * Data for "Chat Buddies"
- */
-struct _PurpleConvChatBuddy
-{
-	char *name;                      /**< The chat participant's name in the chat. */
-	char *alias;                     /**< The chat participant's alias, if known;
-	                                  *   @a NULL otherwise.
-	                                  */
-	char *alias_key;                 /**< A string by which this buddy will be sorted,
-	                                  *   or @c NULL if the buddy should be sorted by
-	                                  *   its @c name.  (This is currently always @c
-	                                  *   NULL.)
-	                                  */
-	gboolean buddy;                  /**< @a TRUE if this chat participant is on the
-	                                  *   buddy list; @a FALSE otherwise.
-	                                  */
-	PurpleConvChatBuddyFlags flags;  /**< A bitwise OR of flags for this participant,
-	                                  *   such as whether they are a channel operator.
-	                                  */
-};
-
-/**
- * Description of a conversation message
- *
- * @since 2.2.0
- */
-struct _PurpleConvMessage
-{
-	char *who;
-	char *what;
-	PurpleMessageFlags flags;
-	time_t when;
-	PurpleConversation *conv;  /**< @since 2.3.0 */
-	char *alias;               /**< @since 2.3.0 */
-};
-
-/**
- * A core representation of a conversation between two or more people.
- *
- * The conversation can be an IM or a chat.
- */
-struct _PurpleConversation
-{
-	PurpleConversationType type;  /**< The type of conversation.          */
-
-	PurpleAccount *account;       /**< The user using this conversation.  */
-
-
-	char *name;                 /**< The name of the conversation.      */
-	char *title;                /**< The window title.                  */
-
-	gboolean logging;           /**< The status of logging.             */
-
-	GList *logs;                /**< This conversation's logs           */
-
-	union
-	{
-		PurpleConvIm   *im;       /**< IM-specific data.                  */
-		PurpleConvChat *chat;     /**< Chat-specific data.                */
-		void *misc;             /**< Misc. data.                        */
-
-	} u;
-
-	PurpleConversationUiOps *ui_ops;           /**< UI-specific operations. */
-	void *ui_data;                           /**< UI-specific data.       */
-
-	GHashTable *data;                        /**< Plugin-specific data.   */
-
-	PurpleConnectionFlags features; /**< The supported features */
-	GList *message_history;         /**< Message history, as a GList of PurpleConvMessage's */
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Conversation API                                                */
@@ -368,7 +262,8 @@
  * @param type    The type of conversation.
  * @param account The account opening the conversation window on the purple
  *                user's end.
- * @param name    The name of the conversation.
+ * @param name    The name of the conversation.  For PURPLE_CONV_TYPE_IM,
+ *                this is the name of the buddy.
  *
  * @return The new conversation.
  */
@@ -458,13 +353,11 @@
 /**
  * Returns the specified conversation's purple_connection.
  *
- * This is the same as purple_conversation_get_user(conv)->gc.
- *
  * @param conv The conversation.
  *
  * @return The conversation's purple_connection.
  */
-PurpleConnection *purple_conversation_get_gc(const PurpleConversation *conv);
+PurpleConnection *purple_conversation_get_connection(const PurpleConversation *conv);
 
 /**
  * Sets the specified conversation's title.
@@ -512,6 +405,46 @@
 const char *purple_conversation_get_name(const PurpleConversation *conv);
 
 /**
+ * Get an attribute of a chat buddy
+ *
+ * @param cb	The chat buddy.
+ * @param key	The key of the attribute.
+ *
+ * @return The value of the attribute key.
+ */
+const char *purple_conv_chat_cb_get_attribute(PurpleConvChatBuddy *cb, const char *key);
+
+/**
+ * Get the keys of all atributes of a chat buddy
+ *
+ * @param cb	The chat buddy.
+ *
+ * @return A list of the attributes of a chat buddy.
+ */
+GList *purple_conv_chat_cb_get_attribute_keys(PurpleConvChatBuddy *cb);
+	
+/**
+ * Set an attribute of a chat buddy
+ *
+ * @param chat	The chat.
+ * @param cb	The chat buddy.
+ * @param key	The key of the attribute.
+ * @param value	The value of the attribute.
+ */
+void purple_conv_chat_cb_set_attribute(PurpleConvChat *chat, PurpleConvChatBuddy *cb, const char *key, const char *value);
+
+/**
+ * Set attributes of a chat buddy
+ *
+ * @param chat	The chat.
+ * @param cb	The chat buddy.
+ * @param keys	A GList of the keys.
+ * @param values A GList of the values.
+ */
+void
+purple_conv_chat_cb_set_attributes(PurpleConvChat *chat, PurpleConvChatBuddy *cb, GList *keys, GList *values);
+
+/**
  * Enables or disables logging for this conversation.
  *
  * @param conv The conversation.
@@ -645,7 +578,6 @@
 		const char *message, PurpleMessageFlags flags,
 		time_t mtime);
 
-
 /**
 	Set the features as supported for the given conversation.
 	@param conv      The conversation
@@ -694,8 +626,6 @@
  * @return  A GList of PurpleConvMessage's. The must not modify the list or the data within.
  *          The list contains the newest message at the beginning, and the oldest message at
  *          the end.
- *
- * @since 2.2.0
  */
 GList *purple_conversation_get_message_history(PurpleConversation *conv);
 
@@ -703,8 +633,6 @@
  * Clear the message history of a conversation.
  *
  * @param conv  The conversation
- *
- * @since 2.2.0
  */
 void purple_conversation_clear_message_history(PurpleConversation *conv);
 
@@ -714,10 +642,8 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The name of the sender of the message
- *
- * @since 2.2.0
  */
-const char *purple_conversation_message_get_sender(PurpleConvMessage *msg);
+const char *purple_conversation_message_get_sender(const PurpleConvMessage *msg);
 
 /**
  * Get the message from a PurpleConvMessage
@@ -725,10 +651,8 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The name of the sender of the message
- *
- * @since 2.2.0
  */
-const char *purple_conversation_message_get_message(PurpleConvMessage *msg);
+const char *purple_conversation_message_get_message(const PurpleConvMessage *msg);
 
 /**
  * Get the message-flags of a PurpleConvMessage
@@ -736,10 +660,8 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The message flags
- *
- * @since 2.2.0
  */
-PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg);
+PurpleMessageFlags purple_conversation_message_get_flags(const PurpleConvMessage *msg);
 
 /**
  * Get the timestamp of a PurpleConvMessage
@@ -747,10 +669,45 @@
  * @param msg   A PurpleConvMessage
  *
  * @return   The timestamp of the message
+ */
+time_t purple_conversation_message_get_timestamp(const PurpleConvMessage *msg);
+
+/**
+ * Get the alias from a PurpleConvMessage
  *
- * @since 2.2.0
+ * @param msg   A PurpleConvMessage
+ *
+ * @return   The alias of the sender of the message
+ */
+const char *purple_conversation_message_get_alias(const PurpleConvMessage *msg);
+
+/**
+ * Get the conversation associated with the PurpleConvMessage
+ *
+ * @param msg   A PurpleConvMessage
+ *
+ * @return   The conversation
  */
-time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg);
+PurpleConversation *purple_conversation_message_get_conv(const PurpleConvMessage *msg);
+
+/**
+ * Set the UI data associated with this conversation.
+ *
+ * @param conv			The conversation.
+ * @param ui_data		A pointer to associate with this conversation.
+ */
+void purple_conversation_set_ui_data(PurpleConversation *conv, gpointer ui_data);
+
+/**
+ * Get the UI data associated with this conversation.
+ *
+ * @param conv			The conversation.
+ *
+ * @return The UI data associated with this conversation.  This is a
+ *         convenience field provided to the UIs--it is not
+ *         used by the libpurple core.
+ */
+gpointer purple_conversation_get_ui_data(const PurpleConversation *conv);
 
 /*@}*/
 
@@ -1011,21 +968,8 @@
 PurpleConversation *purple_conv_chat_get_conversation(const PurpleConvChat *chat);
 
 /**
- * Sets the list of users in the chat room.
- *
- * @note Calling this function will not update the display of the users.
- *       Please use purple_conv_chat_add_user(), purple_conv_chat_add_users(),
- *       purple_conv_chat_remove_user(), and purple_conv_chat_remove_users() instead.
- *
- * @param chat  The chat.
- * @param users The list of users.
- *
- * @return The list passed.
- */
-GList *purple_conv_chat_set_users(PurpleConvChat *chat, GList *users);
-
-/**
- * Returns a list of users in the chat room.
+ * Returns a list of users in the chat room.  The members of the list
+ * are PurpleConvChatBuddy objects.
  *
  * @param chat The chat.
  *
@@ -1308,9 +1252,7 @@
  * @param user     The user to invite to the chat.
  * @param message  The message to send with the invitation.
  * @param confirm  Prompt before sending the invitation. The user is always
- *                 prompted if either #user or #message is @c NULL.
- *
- * @since 2.6.0
+ *                 prompted if either \a user or \a message is @c NULL.
  */
 void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user,
 		const char *message, gboolean confirm);
@@ -1347,13 +1289,59 @@
 PurpleConvChatBuddy *purple_conv_chat_cb_find(PurpleConvChat *chat, const char *name);
 
 /**
+ * Set the UI data associated with this chat buddy.
+ *
+ * @param cb			The chat buddy
+ * @param ui_data		A pointer to associate with this chat buddy.
+ */
+void purple_conv_chat_cb_set_ui_data(PurpleConvChatBuddy *cb, gpointer ui_data);
+
+/**
+ * Get the UI data associated with this chat buddy.
+ *
+ * @param cb			The chat buddy.
+ *
+ * @return The UI data associated with this chat buddy.  This is a
+ *         convenience field provided to the UIs--it is not
+ *         used by the libpurple core.
+ */
+gpointer purple_conv_chat_cb_get_ui_data(const PurpleConvChatBuddy *conv);
+
+/**
+ * Get the alias of a chat buddy
+ *
+ * @param cb    The chat buddy.
+ *
+ * @return The alias of the chat buddy.
+ */
+const char *purple_conv_chat_cb_get_alias(const PurpleConvChatBuddy *cb);
+
+/**
  * Get the name of a chat buddy
  *
  * @param cb    The chat buddy.
  *
  * @return The name of the chat buddy.
  */
-const char *purple_conv_chat_cb_get_name(PurpleConvChatBuddy *cb);
+const char *purple_conv_chat_cb_get_name(const PurpleConvChatBuddy *cb);
+
+/**
+ * Get the flags of a chat buddy.
+ *
+ * @param cb	The chat buddy.
+ *
+ * @return The flags of the chat buddy.
+ */
+PurpleConvChatBuddyFlags purple_conv_chat_cb_get_flags(const PurpleConvChatBuddy *cb);
+
+/**
+ * Indicates if this chat buddy is on the buddy list.
+ *
+ * @param cb	The chat buddy.
+ *
+ * @return TRUE if the chat buddy is on the buddy list.
+ */
+gboolean purple_conv_chat_cb_is_buddy(const PurpleConvChatBuddy *cb);
 
 /**
  * Destroys a chat buddy
@@ -1370,8 +1358,6 @@
  * @return  A list of PurpleMenuAction items, harvested by the
  *          chat-extended-menu signal. The list and the menuaction
  *          items should be freed by the caller.
- *
- * @since 2.1.0
  */
 GList * purple_conversation_get_extended_menu(PurpleConversation *conv);
 
@@ -1385,8 +1371,6 @@
  *                message, if not @c NULL. It must be freed by the caller with g_free().
  *
  * @return  @c TRUE if the command was executed successfully, @c FALSE otherwise.
- *
- * @since 2.1.0
  */
 gboolean purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **error);
 
@@ -1416,8 +1400,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_CONVERSATION_H_ */
--- a/libpurple/core.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/core.c	Wed Jun 13 19:30:27 2012 -0400
@@ -371,408 +371,6 @@
 	return is_single_instance;
 }
 
-static gboolean
-move_and_symlink_dir(const char *path, const char *basename, const char *old_base, const char *new_base, const char *relative)
-{
-	char *new_name = g_build_filename(new_base, basename, NULL);
-#ifndef _WIN32
-	char *old_name;
-#endif
-	if (g_rename(path, new_name))
-	{
-		purple_debug_error("core", "Error renaming %s to %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-		                   path, new_name, g_strerror(errno));
-		g_free(new_name);
-		return FALSE;
-	}
-	g_free(new_name);
-
-#ifndef _WIN32
-	/* NOTE: This new_name is relative. */
-	new_name = g_build_filename(relative, basename, NULL);
-	old_name = g_build_filename(old_base, basename, NULL);
-	if (symlink(new_name, old_name))
-	{
-		purple_debug_warning("core", "Error symlinking %s to %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-		                     old_name, new_name, g_strerror(errno));
-	}
-	g_free(old_name);
-	g_free(new_name);
-#endif
-
-	return TRUE;
-}
-
-gboolean
-purple_core_migrate(void)
-{
-	const char *user_dir = purple_user_dir();
-	char *old_user_dir = g_strconcat(purple_home_dir(),
-	                                 G_DIR_SEPARATOR_S ".gaim", NULL);
-	char *status_file;
-	FILE *fp;
-	GDir *dir;
-	GError *err;
-	const char *entry;
-#ifndef _WIN32
-	char *logs_dir;
-#endif
-	char *old_icons_dir;
-
-	if (!g_file_test(old_user_dir, G_FILE_TEST_EXISTS))
-	{
-		/* ~/.gaim doesn't exist, so there's nothing to migrate. */
-		g_free(old_user_dir);
-		return TRUE;
-	}
-
-	status_file = g_strconcat(user_dir, G_DIR_SEPARATOR_S "migrating", NULL);
-
-	if (g_file_test(user_dir, G_FILE_TEST_EXISTS))
-	{
-		/* If we're here, we have both ~/.gaim and .purple. */
-
-		if (!g_file_test(status_file, G_FILE_TEST_EXISTS))
-		{
-			/* There's no "migrating" status file,
-			 * so ~/.purple is all up to date. */
-			g_free(status_file);
-			g_free(old_user_dir);
-			return TRUE;
-		}
-	}
-
-	/* If we're here, it's time to migrate from ~/.gaim to ~/.purple. */
-
-        /* Ensure the user directory exists */
-	if (!g_file_test(user_dir, G_FILE_TEST_IS_DIR))
-	{
-		if (g_mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
-		{
-			purple_debug_error("core", "Error creating directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-			                   user_dir, g_strerror(errno));
-			g_free(status_file);
-			g_free(old_user_dir);
-			return FALSE;
-		}
-	}
-
-	/* This writes ~/.purple/migrating, which allows us to detect
-	 * incomplete migrations and properly retry. */
-	if (!(fp = g_fopen(status_file, "w")))
-	{
-		purple_debug_error("core", "Error opening file %s for writing: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-		                   status_file, g_strerror(errno));
-		g_free(status_file);
-		g_free(old_user_dir);
-		return FALSE;
-	}
-	fclose(fp);
-
-	/* Open ~/.gaim so we can loop over its contents. */
-	err = NULL;
-	if (!(dir = g_dir_open(old_user_dir, 0, &err)))
-	{
-		purple_debug_error("core", "Error opening directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-		                   status_file,
-		                   (err ? err->message : "Unknown error"));
-		if (err)
-			g_error_free(err);
-		g_free(status_file);
-		g_free(old_user_dir);
-		return FALSE;
-	}
-
-	/* Loop over the contents of ~/.gaim */
-	while ((entry = g_dir_read_name(dir)))
-	{
-		char *name = g_build_filename(old_user_dir, entry, NULL);
-
-#ifndef _WIN32
-		/* Deal with symlinks... */
-		if (g_file_test(name, G_FILE_TEST_IS_SYMLINK))
-		{
-			/* We're only going to duplicate a logs symlink. */
-			if (purple_strequal(entry, "logs"))
-			{
-				char *link;
-#if GLIB_CHECK_VERSION(2,4,0)
-				GError *err = NULL;
-
-				if ((link = g_file_read_link(name, &err)) == NULL)
-				{
-					char *name_utf8 = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
-					purple_debug_error("core", "Error reading symlink %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-					                   name_utf8 ? name_utf8 : name, err->message);
-					g_free(name_utf8);
-					g_error_free(err);
-					g_free(name);
-					g_dir_close(dir);
-					g_free(status_file);
-					g_free(old_user_dir);
-					return FALSE;
-				}
-#else
-				char buf[MAXPATHLEN];
-				size_t linklen;
-
-				if ((linklen = readlink(name, buf, sizeof(buf) - 1) == -1))
-				{
-					char *name_utf8 = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
-					purple_debug_error("core", "Error reading symlink %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-					                   name_utf8, g_strerror(errno));
-					g_free(name_utf8);
-					g_free(name);
-					g_dir_close(dir);
-					g_free(status_file);
-					g_free(old_user_dir);
-					return FALSE;
-				}
-				buf[linklen] = '\0';
-
-				/* This way we don't have to GLIB_VERSION_CHECK every g_free(link) below. */
-				link = g_strdup(buf);
-#endif
-
-				logs_dir = g_build_filename(user_dir, "logs", NULL);
-
-				if (purple_strequal(link, "../.purple/logs") ||
-				    purple_strequal(link, logs_dir))
-				{
-					/* If the symlink points to the new directory, we're
-					 * likely just trying again after a failed migration,
-					 * so there's no need to fail here. */
-					g_free(link);
-					g_free(logs_dir);
-					continue;
-				}
-
-				/* In case we are trying again after a failed migration, we need
-				 * to unlink any existing symlink.  If it's a directory, this
-				 * will fail, and so will the symlink below, which is good
-				 * because the user should sort things out. */
-				g_unlink(logs_dir);
-
-				/* Relative links will most likely still be
-				 * valid from ~/.purple, though it's not
-				 * guaranteed.  Oh well. */
-				if (symlink(link, logs_dir))
-				{
-					purple_debug_error("core", "Error symlinking %s to %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-					                   logs_dir, link, g_strerror(errno));
-					g_free(link);
-					g_free(name);
-					g_free(logs_dir);
-					g_dir_close(dir);
-					g_free(status_file);
-					g_free(old_user_dir);
-					return FALSE;
-				}
-
-				g_free(link);
-				g_free(logs_dir);
-				continue;
-			}
-
-			/* Ignore all other symlinks. */
-			continue;
-		}
-#endif
-
-		/* Deal with directories... */
-		if (g_file_test(name, G_FILE_TEST_IS_DIR))
-		{
-			if (purple_strequal(entry, "icons"))
-			{
-				/* This is a special case for the Album plugin, which
-				 * stores data in the icons folder.  We're not copying
-				 * the icons directory over because previous bugs
-				 * meant that it filled up with junk for many users.
-				 * This is a great time to purge it. */
-
-				GDir *icons_dir;
-				char *new_icons_dir;
-				const char *icons_entry;
-
-				err = NULL;
-				if (!(icons_dir = g_dir_open(name, 0, &err)))
-				{
-					purple_debug_error("core", "Error opening directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-					                   name,
-					                   (err ? err->message : "Unknown error"));
-					if (err)
-						g_error_free(err);
-					g_free(name);
-					g_dir_close(dir);
-					g_free(status_file);
-					g_free(old_user_dir);
-					return FALSE;
-				}
-
-				new_icons_dir = g_build_filename(user_dir, "icons", NULL);
-			        /* Ensure the new icon directory exists */
-				if (!g_file_test(new_icons_dir, G_FILE_TEST_IS_DIR))
-				{
-					if (g_mkdir(new_icons_dir, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
-					{
-						purple_debug_error("core", "Error creating directory %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-						                   new_icons_dir, g_strerror(errno));
-						g_free(new_icons_dir);
-						g_dir_close(icons_dir);
-						g_free(name);
-						g_dir_close(dir);
-						g_free(status_file);
-						g_free(old_user_dir);
-						return FALSE;
-					}
-				}
-
-				while ((icons_entry = g_dir_read_name(icons_dir)))
-				{
-					char *icons_name = g_build_filename(name, icons_entry, NULL);
-
-					if (g_file_test(icons_name, G_FILE_TEST_IS_DIR))
-					{
-						if (!move_and_symlink_dir(icons_name, icons_entry,
-						                          name, new_icons_dir, "../../.purple/icons"))
-						{
-							g_free(icons_name);
-							g_free(new_icons_dir);
-							g_dir_close(icons_dir);
-							g_free(name);
-							g_dir_close(dir);
-							g_free(status_file);
-							g_free(old_user_dir);
-							return FALSE;
-						}
-					}
-					g_free(icons_name);
-				}
-
-				g_dir_close(icons_dir);
-			}
-			else if (purple_strequal(entry, "plugins"))
-			{
-				/* Do nothing, because we broke plugin compatibility.
-				 * This means that the plugins directory gets left behind. */
-			}
-			else
-			{
-				/* All other directories are moved and symlinked. */
-				if (!move_and_symlink_dir(name, entry, old_user_dir, user_dir, "../.purple"))
-				{
-					g_free(name);
-					g_dir_close(dir);
-					g_free(status_file);
-					g_free(old_user_dir);
-					return FALSE;
-				}
-			}
-		}
-		else if (g_file_test(name, G_FILE_TEST_IS_REGULAR))
-		{
-			/* Regular files are copied. */
-
-			char *new_name;
-			FILE *new_file;
-
-			if (!(fp = g_fopen(name, "rb")))
-			{
-				purple_debug_error("core", "Error opening file %s for reading: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-				                   name, g_strerror(errno));
-				g_free(name);
-				g_dir_close(dir);
-				g_free(status_file);
-				g_free(old_user_dir);
-				return FALSE;
-			}
-
-			new_name = g_build_filename(user_dir, entry, NULL);
-			if (!(new_file = g_fopen(new_name, "wb")))
-			{
-				purple_debug_error("core", "Error opening file %s for writing: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-				                   new_name, g_strerror(errno));
-				fclose(fp);
-				g_free(new_name);
-				g_free(name);
-				g_dir_close(dir);
-				g_free(status_file);
-				g_free(old_user_dir);
-				return FALSE;
-			}
-
-			while (!feof(fp))
-			{
-				unsigned char buf[256];
-				size_t size;
-
-				size = fread(buf, 1, sizeof(buf), fp);
-				if (size != sizeof(buf) && !feof(fp))
-				{
-					purple_debug_error("core", "Error reading %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-					                   name, g_strerror(errno));
-					fclose(new_file);
-					fclose(fp);
-					g_free(new_name);
-					g_free(name);
-					g_dir_close(dir);
-					g_free(status_file);
-					g_free(old_user_dir);
-					return FALSE;
-				}
-
-				if (!fwrite(buf, size, 1, new_file) && ferror(new_file) != 0)
-				{
-					purple_debug_error("core", "Error writing %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-					                   new_name, g_strerror(errno));
-					fclose(new_file);
-					fclose(fp);
-					g_free(new_name);
-					g_free(name);
-					g_dir_close(dir);
-					g_free(status_file);
-					g_free(old_user_dir);
-					return FALSE;
-				}
-			}
-
-			if (fclose(new_file))
-			{
-				purple_debug_error("core", "Error writing: %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-				                   new_name, g_strerror(errno));
-			}
-			if (fclose(fp))
-			{
-				purple_debug_warning("core", "Error closing %s: %s\n",
-				                     name, g_strerror(errno));
-			}
-			g_free(new_name);
-		}
-		else
-			purple_debug_warning("core", "Not a regular file or directory: %s\n", name);
-
-		g_free(name);
-	}
-
-	/* The migration was successful, so delete the status file. */
-	if (g_unlink(status_file))
-	{
-		purple_debug_error("core", "Error unlinking file %s: %s. Please report this at " PURPLE_DEVEL_WEBSITE "\n",
-		                   status_file, g_strerror(errno));
-		g_free(status_file);
-		return FALSE;
-	}
-
-	old_icons_dir = g_build_filename(old_user_dir, "icons", NULL);
-	_purple_buddy_icon_set_old_icons_dir(old_icons_dir);
-	g_free(old_icons_dir);
-
-	g_free(old_user_dir);
-
-	g_free(status_file);
-	return TRUE;
-}
-
 GHashTable* purple_core_get_ui_info() {
 	PurpleCoreUiOps *ops = purple_core_get_ui_ops();
 
--- a/libpurple/core.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/core.h	Wed Jun 13 19:30:27 2012 -0400
@@ -24,6 +24,16 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+
+/*! @mainpage Pidgin/Finch/libpurple API Documentation
+ *
+ * <a href="group__core.html">libpurple</a> is intended to be the core of an IM
+ * program.  <a href="group__pidgin.html">Pidgin</a> is a GTK+ frontend
+ * to libpurple, and <a href="group__finch.html">Finch</a> is an ncurses
+ * frontend built using <a href="group__gnt.html">libgnt</a>
+ * (GLib Ncurses Toolkit).
+ */
+
 #ifndef _PURPLE_CORE_H_
 #define _PURPLE_CORE_H_
 
@@ -66,9 +76,7 @@
 	void (*_purple_reserved3)(void);
 } PurpleCoreUiOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Initializes the core of purple.
@@ -146,17 +154,6 @@
 PurpleCoreUiOps *purple_core_get_ui_ops(void);
 
 /**
- * Migrates from <tt>.gaim</tt> to <tt>.purple</tt>.
- *
- * UIs <strong>must not</strong> call this if they have been told to use a
- * custom user directory.
- *
- * @return A boolean indicating success or migration failure. On failure,
- *         the application must display an error to the user and then exit.
- */
-gboolean purple_core_migrate(void);
-
-/**
  * Ensures that only one instance is running.  If libpurple is built with D-Bus
  * support, this checks if another process owns the libpurple bus name and if
  * so whether that process is using the same configuration directory as this
@@ -164,8 +161,6 @@
  *
  * @return @c TRUE if this is the first instance of libpurple running;
  *         @c FALSE if there is another instance running.
- *
- * @since 2.1.0
  */
 gboolean purple_core_ensure_single_instance(void);
 
@@ -191,20 +186,16 @@
  *   <dd>the type of UI. Possible values include 'pc', 'console', 'phone',
  *       'handheld', 'web', and 'bot'. These values are compared
  *       programmatically and should not be localized.</dd>
- *   
+ *
  * </dl>
  *
  * @return A GHashTable with strings for keys and values.  This
  * hash table must not be freed and should not be modified.
  *
- * @since 2.1.0
- *
  */
 GHashTable* purple_core_get_ui_info(void);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_CORE_H_ */
 
--- a/libpurple/dbus-analyze-functions.py	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dbus-analyze-functions.py	Wed Jun 13 19:30:27 2012 -0400
@@ -3,7 +3,7 @@
 import sys
 
 # types translated into "int"
-simpletypes = ["int", "gint", "guint", "gboolean", "gpointer", "size_t", "gssize", "time_t"]
+simpletypes = ["int", "gint", "guint", "gboolean", "size_t", "gssize", "time_t"]
 
 # List "excluded" contains functions that shouldn't be exported via
 # DBus.  If you remove a function from this list, please make sure
@@ -29,7 +29,11 @@
     # Similar to the above:
     "purple_account_set_register_callback",
     "purple_account_unregister",
-    "purple_connection_new_unregister",
+
+    # Similar to the above, again
+    "purple_menu_action_new",
+    "purple_menu_action_set_callback",
+    "purple_menu_action_get_callback",
 
     # These functions are excluded because they involve setting arbitrary
     # data via pointers for protocols and UIs.  This just won't work.
@@ -170,7 +174,7 @@
                 return self.inputpurplestructure(type, name)
 
             # special case for *_get_data functions, be careful here...
-            elif (type[0] == "size_t") and (name == "len"):
+            elif (type[0] == "size_t" or type[0] == "gsize") and name == "len":
                 return self.inputgetdata(type, name)
             
             # unknown pointers are always replaced with NULL
@@ -181,15 +185,20 @@
 
    
     def processoutput(self, type, name):
+        const = False
+        unsigned = False
         # the "void" type is simple ...
         if type == ["void"]:
             return self.outputvoid(type, name)
 
-        const = False
         if type[0] == "const":
             type = type[1:]
             const = True
 
+        if type[0] == "unsigned":
+            type = type[1:]
+            unsigned = True
+
         # a string
         if type == ["char", pointer] or type == ["gchar", pointer]:
             return self.outputstring(type, name, const)
@@ -197,7 +206,7 @@
         # simple types (ints, booleans, enums, ...)
         if (len(type) == 1) and \
                ((type[0] in simpletypes) or (type[0].startswith("Purple"))):
-            return self.outputsimple(type, name)
+            return self.outputsimple(type, name, unsigned)
 
         # pointers ...
         if (len(type) == 2) and (type[1] == pointer):
@@ -303,10 +312,13 @@
 #        self.returncode.append("NULLIFY(%s);" % name)
         self.returncode.append("return %s;" % name);
 
-    def outputsimple(self, type, name):
+    def outputsimple(self, type, name, us):
         self.functiontype = type[0]
         self.decls.append("%s %s = 0;" % (type[0], name))
-        self.outputparams.append(("G_TYPE_INT", name))
+        if us:
+            self.outputparams.append(("G_TYPE_UINT", name))
+        else:
+            self.outputparams.append(("G_TYPE_INT", name))
         self.returncode.append("return %s;" % name);
 
     # we could add "const" to the return type but this would probably
@@ -455,11 +467,16 @@
         if not const:
             self.ccodeout.append("\tg_free(%s);" % name)
 
-    def outputsimple(self, type, name):
-        self.cdecls.append("\tdbus_int32_t %s;" % name)
+    def outputsimple(self, type, name, us):
+        if us:
+            self.cdecls.append("\tdbus_uint32_t %s;" % name)
+            self.cparamsout.append(("UINT32", name))
+            self.addouttype("u", name)
+        else:
+            self.cdecls.append("\tdbus_int32_t %s;" % name)
+            self.cparamsout.append(("INT32", name))
+            self.addouttype("i", name)
         self.ccode.append("\t%s = %s;" % (name, self.call))
-        self.cparamsout.append(("INT32", name))
-        self.addouttype("i", name)
 
     def outputpurplestructure(self, type, name):
         self.cdecls.append("\tdbus_int32_t %s;" % name)
@@ -478,7 +495,7 @@
         if self.function.name in stringlists:
             self.cdecls.append("\tchar **%s;" % name)
             self.ccode.append("\tlist = %s;" % self.call)
-            self.ccode.append("\t%s = (char **)purple_%s_to_array(list, FALSE, &%s_LEN);" % \
+            self.ccode.append("\t%s = (char **)purple_%s_to_array(list, &%s_LEN);" % \
                          (name, type[0], name))
             self.cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &%s, %s_LEN" \
                           % (name, name))
@@ -490,7 +507,7 @@
         else:
             self.cdecls.append("\tdbus_int32_t *%s;" % name)
             self.ccode.append("\tlist = %s;" % self.call)
-            self.ccode.append("\t%s = purple_dbusify_%s(list, FALSE, &%s_LEN);" % \
+            self.ccode.append("\t%s = purple_dbusify_%s(list, &%s_LEN);" % \
                          (name, type[0], name))
             if (not (self.function.name in constlists)):
                 self.ccode.append("\tg_%s_free(list);" % type[0].lower()[1:])
--- a/libpurple/dbus-analyze-signals.py	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dbus-analyze-signals.py	Wed Jun 13 19:30:27 2012 -0400
@@ -32,7 +32,7 @@
         continue
 
     signal = nameregex.sub(lambda x:x.group()[1].upper(), '-'+signal)
-    print "\"<signal name='%s'>\\n\""%signal
+    print "\"    <signal name='%s'>\\n\""%signal
 
     args = marshal.split('_')
     # ['purple', 'marshal', <return type>, '', args...]
@@ -52,9 +52,9 @@
                 type = 't'
             elif arg == "BOOLEAN":
                 type = 'b'
-            print "\"<arg type='%s'/>\\n\""%type
+            print "\"      <arg type='%s'/>\\n\""%type
 
-    print "\"</signal>\\n\""
+    print "\"    </signal>\\n\""
 
 print ";"
 
--- a/libpurple/dbus-bindings.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dbus-bindings.h	Wed Jun 13 19:30:27 2012 -0400
@@ -32,9 +32,7 @@
 #include <dbus/dbus-glib-lowlevel.h>
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 gint purple_dbus_pointer_to_id(gconstpointer node);
 gpointer purple_dbus_id_to_pointer(gint id, PurpleDBusType *type);
@@ -84,14 +82,10 @@
 					int              first_arg_type,
 					va_list          var_args);
 
-dbus_int32_t* purple_dbusify_GList(GList *list, gboolean free_memory,
-				 dbus_int32_t *len);
-dbus_int32_t* purple_dbusify_GSList(GSList *list, gboolean free_memory,
-				  dbus_int32_t *len);
-gpointer* purple_GList_to_array(GList *list, gboolean free_memory,
-			      dbus_int32_t *len);
-gpointer* purple_GSList_to_array(GSList *list, gboolean free_memory,
-			      dbus_int32_t *len);
+dbus_int32_t* purple_dbusify_GList(GList *list, dbus_int32_t *len);
+dbus_int32_t* purple_dbusify_GSList(GSList *list, dbus_int32_t *len);
+gpointer* purple_GList_to_array(GList *list, dbus_int32_t *len);
+gpointer* purple_GSList_to_array(GSList *list, dbus_int32_t *len);
 GHashTable *purple_dbus_iter_hash_table(DBusMessageIter *iter, DBusError *error);
 
 const char* empty_to_null(const char *str);
@@ -107,8 +101,6 @@
 
 DBusConnection *purple_dbus_get_connection(void);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif
--- a/libpurple/dbus-server.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dbus-server.c	Wed Jun 13 19:30:27 2012 -0400
@@ -25,14 +25,16 @@
 #define DBUS_API_SUBJECT_TO_CHANGE
 #endif
 
+/* Allow the code below to see deprecated functions, so we can continue to
+ * export them via DBus. */
+#undef PURPLE_DISABLE_DEPRECATED
+
+#include "internal.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-/* Allow the code below to see deprecated functions, so we can continue to
- * export them via DBus. */
-#undef PURPLE_DISABLE_DEPRECATED
-
 #include "account.h"
 #include "blist.h"
 #include "conversation.h"
@@ -42,7 +44,6 @@
 #include "dbus-bindings.h"
 #include "debug.h"
 #include "core.h"
-#include "internal.h"
 #include "savedstatuses.h"
 #include "smiley.h"
 #include "util.h"
@@ -126,8 +127,10 @@
 	gint id = GPOINTER_TO_INT(g_hash_table_lookup(map_node_id, node));
 	if ((id == 0) && (node != NULL))
 	{
-		purple_debug_warning("dbus",
-				"Need to register an object with the dbus subsystem. (If you are not a developer, please ignore this message.)\n");
+		if (purple_debug_is_verbose())
+			purple_debug_warning("dbus",
+				"Need to register an object with the dbus subsystem."
+				" (If you are not a developer, please ignore this message.)\n");
 		return 0;
 	}
 	return id;
@@ -297,7 +300,7 @@
 }
 
 dbus_int32_t *
-purple_dbusify_GList(GList *list, gboolean free_memory, dbus_int32_t *len)
+purple_dbusify_GList(GList *list, dbus_int32_t *len)
 {
 	dbus_int32_t *array;
 	int i;
@@ -308,14 +311,11 @@
 	for (i = 0, elem = list; elem != NULL; elem = elem->next, i++)
 		array[i] = purple_dbus_pointer_to_id(elem->data);
 
-	if (free_memory)
-		g_list_free(list);
-
 	return array;
 }
 
 dbus_int32_t *
-purple_dbusify_GSList(GSList *list, gboolean free_memory, dbus_int32_t *len)
+purple_dbusify_GSList(GSList *list, dbus_int32_t *len)
 {
 	dbus_int32_t *array;
 	int i;
@@ -326,14 +326,11 @@
 	for (i = 0, elem = list; elem != NULL; elem = elem->next, i++)
 		array[i] = purple_dbus_pointer_to_id(elem->data);
 
-	if (free_memory)
-		g_slist_free(list);
-
 	return array;
 }
 
 gpointer *
-purple_GList_to_array(GList *list, gboolean free_memory, dbus_int32_t *len)
+purple_GList_to_array(GList *list, dbus_int32_t *len)
 {
 	gpointer *array;
 	int i;
@@ -344,14 +341,11 @@
 	for (i = 0, elem = list; elem != NULL; elem = elem->next, i++)
 		array[i] = elem->data;
 
-	if (free_memory)
-		g_list_free(list);
-
 	return array;
 }
 
 gpointer *
-purple_GSList_to_array(GSList *list, gboolean free_memory, dbus_int32_t *len)
+purple_GSList_to_array(GSList *list, dbus_int32_t *len)
 {
 	gpointer *array;
 	int i;
@@ -362,9 +356,6 @@
 	for (i = 0, elem = list; elem != NULL; elem = elem->next, i++)
 		array[i] = elem->data;
 
-	if (free_memory)
-		g_slist_free(list);
-
 	return array;
 }
 
@@ -498,7 +489,9 @@
 
 	g_string_append(str, "<!DOCTYPE node PUBLIC '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN' 'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>\n");
 	g_string_append_printf(str, "<node name='%s'>\n", DBUS_PATH_PURPLE);
-	g_string_append_printf(str, "<interface name='%s'>\n", DBUS_INTERFACE_PURPLE);
+	g_string_append(str, "  <interface name='org.freedesktop.DBus.Introspectable'>\n    <method name='Introspect'>\n      <arg name='data' direction='out' type='s'/>\n    </method>\n  </interface>\n\n");
+
+	g_string_append_printf(str, "  <interface name='%s'>\n", DBUS_INTERFACE_PURPLE);
 
 	bindings_list = NULL;
 	purple_signal_emit(purple_dbus_get_handle(), "dbus-introspect", &bindings_list);
@@ -514,7 +507,7 @@
 		{
 			const char *text;
 
-			g_string_append_printf(str, "<method name='%s'>\n", bindings[i].name);
+			g_string_append_printf(str, "    <method name='%s'>\n", bindings[i].name);
 
 			text = bindings[i].parameters;
 			while (*text)
@@ -526,10 +519,10 @@
 				name = dbus_gettext(&text);
 
 				g_string_append_printf(str,
-						"<arg name='%s' type='%s' direction='%s'/>\n",
+						"      <arg name='%s' type='%s' direction='%s'/>\n",
 						name, type, direction);
 			}
-			g_string_append(str, "</method>\n");
+			g_string_append(str, "    </method>\n");
 		}
 	}
 
@@ -546,7 +539,7 @@
 	}
 	g_string_append(str, signals);
 
-	g_string_append(str, "</interface>\n</node>\n");
+	g_string_append(str, "  </interface>\n</node>\n");
 
 	reply = dbus_message_new_method_return(message);
 	dbus_message_append_args(reply, DBUS_TYPE_STRING, &(str->str),
@@ -565,10 +558,8 @@
 			"dbus-method-called", connection, message))
 		return DBUS_HANDLER_RESULT_HANDLED;
 
-	if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL &&
-			dbus_message_has_path(message, DBUS_PATH_PURPLE) &&
-			dbus_message_has_interface(message, DBUS_INTERFACE_INTROSPECTABLE) &&
-			dbus_message_has_member(message, "Introspect"))
+	if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") &&
+			dbus_message_has_path(message, DBUS_PATH_PURPLE))
 	{
 		DBusMessage *reply;
 		reply = purple_dbus_introspect(message);
@@ -598,7 +589,6 @@
 {
 	static DBusObjectPathVTable vtable = {NULL, &purple_dbus_dispatch, NULL, NULL, NULL, NULL};
 	DBusError error;
-	int result;
 
 	dbus_error_init(&error);
 	purple_dbus_connection = dbus_bus_get(DBUS_BUS_STARTER, &error);
@@ -622,16 +612,15 @@
 		return;
 	}
 
-	dbus_request_name_reply =
-	result = dbus_bus_request_name(purple_dbus_connection,
+	dbus_request_name_reply = dbus_bus_request_name(purple_dbus_connection,
 			DBUS_SERVICE_PURPLE, 0, &error);
 
 	if (dbus_error_is_set(&error))
 	{
 		dbus_connection_unref(purple_dbus_connection);
-		dbus_error_free(&error);
 		purple_dbus_connection = NULL;
 		init_error = g_strdup_printf(N_("Failed to get serv name: %s"), error.name);
+		dbus_error_free(&error);
 		return;
 	}
 
@@ -710,7 +699,7 @@
 			g_return_val_if_fail(ptr, TRUE);
 		}
 
-		switch (purple_values[i]->type)
+		switch (purple_value_get_type(purple_values[i]))
 		{
 		case PURPLE_TYPE_INT:
 		case PURPLE_TYPE_ENUM:
@@ -795,7 +784,11 @@
 	dbus_message_iter_init_append(signal, &iter);
 
 	if (purple_dbus_message_append_purple_values(&iter, num_values, values, vargs))
-		purple_debug_warning("dbus", "The signal \"%s\" caused some dbus error. (If you are not a developer, please ignore this message.)\n", name);
+		if (purple_debug_is_verbose())
+			purple_debug_warning("dbus",
+				"The signal \"%s\" caused some dbus error."
+				" (If you are not a developer, please ignore this message.)\n",
+				name);
 
 	dbus_connection_send(purple_dbus_connection, signal, NULL);
 
--- a/libpurple/dbus-server.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dbus-server.h	Wed Jun 13 19:30:27 2012 -0400
@@ -173,8 +173,6 @@
 
 /**
  * Determines whether this instance owns the DBus service name
- *
- * @since 2.1.0
  */
 gboolean purple_dbus_is_owner(void);
 
--- a/libpurple/dbus-useful.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dbus-useful.c	Wed Jun 13 19:30:27 2012 -0400
@@ -25,7 +25,7 @@
 		if (who && strcmp(purple_normalize(NULL, purple_account_get_username(account)), who))
 			continue;
 
-		if (protocol_id && strcmp(account->protocol_id, protocol_id))
+		if (protocol_id && strcmp(purple_account_get_protocol_id(account), protocol_id))
 			continue;
 
 		if (account_test && !account_test(account))
--- a/libpurple/dbus-useful.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dbus-useful.h	Wed Jun 13 19:30:27 2012 -0400
@@ -1,5 +1,7 @@
 #include "conversation.h"
 
+G_BEGIN_DECLS
+
 PurpleAccount *purple_accounts_find_ext(const char *name, const char *protocol_id,
 				    gboolean (*account_test)(const PurpleAccount *account));
 
@@ -7,7 +9,5 @@
 
 PurpleAccount *purple_accounts_find_connected(const char *name, const char *protocol);
 
-
+G_END_DECLS
 
-
-
--- a/libpurple/debug.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/debug.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,8 +23,8 @@
  * 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 "debug.h"
-#include "internal.h"
 #include "prefs.h"
 #include "util.h"
 
@@ -224,12 +224,5 @@
 		purple_debug_set_verbose(TRUE);
 
 	purple_prefs_add_none("/purple/debug");
-
-	/*
-	 * This pref is obsolete and no longer referenced anywhere. It only
-	 * survives here because it would be an API break if we removed it.
-	 * Remove this when we get to 3.0.0 :)
-	 */
-	purple_prefs_add_bool("/purple/debug/timestamps", TRUE);
 }
 
--- a/libpurple/debug.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/debug.h	Wed Jun 13 19:30:27 2012 -0400
@@ -59,9 +59,7 @@
 	void (*_purple_reserved4)(void);
 } PurpleDebugUiOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Debug API                                                       */
@@ -161,8 +159,6 @@
  * plugins.
  *
  * @param verbose TRUE to enable verbose debugging or FALSE to disable it.
- *
- * @since 2.6.0
  */
 void purple_debug_set_verbose(gboolean verbose);
 
@@ -170,28 +166,26 @@
  * Check if verbose logging is enabled.
  *
  * @return TRUE if verbose debugging is enabled, FALSE if it is not.
- *
- * @since 2.6.0
  */
 gboolean purple_debug_is_verbose(void);
 
 /**
- * Enable or disable verbose debugging.  This ordinarily should only be called
+ * Enable or disable unsafe debugging.  This ordinarily should only be called
  * by #purple_debug_init, but there are cases where this can be useful for
  * plugins.
  *
- * @param unsafe  TRUE to enable verbose debugging or FALSE to disable it.
- *
- * @since 2.6.0
+ * @param unsafe TRUE to enable debug logging of messages that could
+ *        potentially contain passwords and other sensitive information.
+ *        FALSE to disable it.
  */
 void purple_debug_set_unsafe(gboolean unsafe);
 
 /**
- * Check if unsafe debugging is enabled.
+ * Check if unsafe debugging is enabled.  Defaults to FALSE.
  *
- * @return TRUE if verbose debugging is enabled, FALSE if it is not.
- *
- * @since 2.6.0
+ * @return TRUE if the debug logging of all messages is enabled, FALSE
+ *         if messages that could potentially contain passwords and other
+ *         sensitive information are not logged.
  */
 gboolean purple_debug_is_unsafe(void);
 
@@ -232,8 +226,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_DEBUG_H_ */
--- a/libpurple/desktopitem.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/desktopitem.c	Wed Jun 13 19:30:27 2012 -0400
@@ -53,12 +53,12 @@
  * Boston, MA 02111-1301, USA.
  */
 
+#include "internal.h"
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
 #include "desktopitem.h"
-#include "internal.h"
 
 struct _PurpleDesktopItem {
 	int refcount;
@@ -330,7 +330,7 @@
 	if (c == EOF && pos == 0)
 		return NULL;
 
-	buf[pos++] = '\0';
+	buf[pos] = '\0';
 
 	return buf;
 }
@@ -831,11 +831,10 @@
 static char *
 try_english_key (PurpleDesktopItem *item, const char *key)
 {
-	char *str;
+	char *str = NULL;
 	char *locales[] = { "en_US", "en_GB", "en_AU", "en", NULL };
 	int i;
 
-	str = NULL;
 	for (i = 0; locales[i] != NULL && str == NULL; i++) {
 		str = g_strdup (lookup_locale (item, key, locales[i]));
 	}
--- a/libpurple/dnsquery.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dnsquery.c	Wed Jun 13 19:30:27 2012 -0400
@@ -24,6 +24,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  *
  */
+#define _PURPLE_DNSQUERY_C_
 
 #include "internal.h"
 #include "debug.h"
@@ -54,6 +55,7 @@
 	PurpleDnsQueryConnectFunction callback;
 	gpointer data;
 	guint timeout;
+	PurpleAccount *account;
 
 #if defined(PURPLE_DNSQUERY_USE_FORK)
 	PurpleDnsQueryResolverProcess *resolver;
@@ -152,6 +154,26 @@
 static gboolean
 resolve_ip(PurpleDnsQueryData *query_data)
 {
+#if defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST)
+	struct addrinfo hints, *res;
+	char servname[20];
+
+	g_snprintf(servname, sizeof(servname), "%d", query_data->port);
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_flags |= AI_NUMERICHOST;
+
+	if (0 == getaddrinfo(query_data->hostname, servname, &hints, &res))
+	{
+		GSList *hosts = NULL;
+		hosts = g_slist_append(hosts, GINT_TO_POINTER(res->ai_addrlen));
+		hosts = g_slist_append(hosts, g_memdup(res->ai_addr, res->ai_addrlen));
+		purple_dnsquery_resolved(query_data, hosts);
+
+		freeaddrinfo(res);
+		return TRUE;
+	}
+#else /* defined(HAVE_GETADDRINFO) && defined(AI_NUMERICHOST) */
 	struct sockaddr_in sin;
 	if (inet_aton(query_data->hostname, &sin.sin_addr))
 	{
@@ -168,10 +190,12 @@
 
 		return TRUE;
 	}
+#endif
 
 	return FALSE;
 }
 
+#ifdef USE_IDN
 static gboolean
 dns_str_is_ascii(const char *name)
 {
@@ -183,6 +207,7 @@
 
 	return TRUE;
 }
+#endif
 
 #if defined(PURPLE_DNSQUERY_USE_FORK)
 
@@ -272,6 +297,10 @@
 		}
 		rc = read(child_in, &dns_params, sizeof(dns_params_t));
 		if (rc < 0) {
+			if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
+				/* Try again */
+				continue;
+			}
 			fprintf(stderr, "dns[%d]: Error: Could not read dns_params: "
 					"%s\n", getpid(), strerror(errno));
 			break;
@@ -293,12 +322,11 @@
 			rc = purple_network_convert_idn_to_ascii(dns_params.hostname, &hostname);
 			if (rc != 0) {
 				write_to_parent(child_out, &rc, sizeof(rc));
-				close(child_out);
 				if (show_debug)
 					fprintf(stderr, "dns[%d] Error: IDN conversion returned "
 							"%d\n", getpid(), rc);
 				dns_params.hostname[0] = '\0';
-				continue;
+				break;
 			}
 		} else /* intentional to execute the g_strdup */
 #endif
@@ -323,12 +351,13 @@
 		rc = getaddrinfo(hostname, servname, &hints, &res);
 		write_to_parent(child_out, &rc, sizeof(rc));
 		if (rc != 0) {
-			close(child_out);
 			if (show_debug)
 				printf("dns[%d] Error: getaddrinfo returned %d\n",
 					getpid(), rc);
 			dns_params.hostname[0] = '\0';
-			continue;
+			g_free(hostname);
+			hostname = NULL;
+			break;
 		}
 		tmp = res;
 		while (res) {
@@ -339,20 +368,17 @@
 		}
 		freeaddrinfo(tmp);
 #else
-		if (!inet_aton(hostname, &sin.sin_addr)) {
-			struct hostent *hp;
-			if (!(hp = gethostbyname(hostname))) {
-				write_to_parent(child_out, &h_errno, sizeof(int));
-				close(child_out);
-				if (show_debug)
-					printf("DNS Error: %d\n", h_errno);
-				_exit(0);
-			}
-			memset(&sin, 0, sizeof(struct sockaddr_in));
-			memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
-			sin.sin_family = hp->h_addrtype;
-		} else
-			sin.sin_family = AF_INET;
+		struct hostent *hp;
+		if (!(hp = gethostbyname(hostname))) {
+			write_to_parent(child_out, &h_errno, sizeof(int));
+			close(child_out);
+			if (show_debug)
+				printf("DNS Error: %d\n", h_errno);
+			_exit(0);
+		}
+		memset(&sin, 0, sizeof(struct sockaddr_in));
+		memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
+		sin.sin_family = hp->h_addrtype;
 
 		sin.sin_port = htons(dns_params.port);
 		rc = 0;
@@ -667,62 +693,12 @@
 	handle_next_queued_request();
 }
 
-static gboolean
-resolve_host(gpointer data)
+static void
+resolve_host(PurpleDnsQueryData *query_data)
 {
-	PurpleDnsQueryData *query_data;
-
-	query_data = data;
-	query_data->timeout = 0;
-
-	if (resolve_ip(query_data))
-	{
-		/* resolve_ip calls purple_dnsquery_resolved */
-		return FALSE;
-	}
-
-	if (purple_dnsquery_ui_resolve(query_data))
-	{
-		/* The UI is handling the resolve; we're done */
-		return FALSE;
-	}
-
 	queued_requests = g_slist_append(queued_requests, query_data);
 
 	handle_next_queued_request();
-
-	return FALSE;
-}
-
-PurpleDnsQueryData *
-purple_dnsquery_a(const char *hostname, int port,
-				PurpleDnsQueryConnectFunction callback, gpointer data)
-{
-	PurpleDnsQueryData *query_data;
-
-	g_return_val_if_fail(hostname != NULL, NULL);
-	g_return_val_if_fail(port	  != 0, NULL);
-	g_return_val_if_fail(callback != NULL, NULL);
-
-	query_data = g_new(PurpleDnsQueryData, 1);
-	query_data->hostname = g_strdup(hostname);
-	g_strstrip(query_data->hostname);
-	query_data->port = port;
-	query_data->callback = callback;
-	query_data->data = data;
-	query_data->resolver = NULL;
-
-	if (*query_data->hostname == '\0')
-	{
-		purple_dnsquery_destroy(query_data);
-		g_return_val_if_reached(NULL);
-	}
-
-	purple_debug_info("dns", "DNS query for '%s' queued\n", query_data->hostname);
-
-	query_data->timeout = purple_timeout_add(0, resolve_host, query_data);
-
-	return query_data;
 }
 
 #elif defined _WIN32 /* end PURPLE_DNSQUERY_USE_FORK  */
@@ -774,11 +750,8 @@
 	if (!dns_str_is_ascii(query_data->hostname)) {
 		rc = purple_network_convert_idn_to_ascii(query_data->hostname, &hostname);
 		if (rc != 0) {
-			/* FIXME: Dirty 2.6.0 string freeze hack */
-			char tmp[8];
-			g_snprintf(tmp, sizeof(tmp), "%d", rc);
-			query_data->error_message = g_strdup_printf(_("Error resolving %s:\n%s"),
-					query_data->hostname, tmp);
+			query_data->error_message = g_strdup_printf(_("Error converting %s "
+					"to punycode: %d"), query_data->hostname, rc);
 			/* back to main thread */
 			purple_timeout_add(0, dns_main_thread_cb, query_data);
 			return 0;
@@ -838,50 +811,116 @@
 	return 0;
 }
 
+static void
+resolve_host(PurpleDnsQueryData *query_data)
+{
+	GError *err = NULL;
+
+	/*
+	 * Spin off a separate thread to perform the DNS lookup so
+	 * that we don't block the UI.
+	 */
+	query_data->resolver = g_thread_create(dns_thread,
+			query_data, FALSE, &err);
+	if (query_data->resolver == NULL)
+	{
+		char message[1024];
+		g_snprintf(message, sizeof(message), _("Thread creation failure: %s"),
+				(err && err->message) ? err->message : _("Unknown reason"));
+		g_error_free(err);
+		purple_dnsquery_failed(query_data, message);
+	}
+}
+
+#else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
+
+/*
+ * We weren't able to do anything fancier above, so use the
+ * fail-safe name resolution code, which is blocking.
+ */
+
+static void
+resolve_host(PurpleDnsQueryData *query_data)
+{
+	struct sockaddr_in sin;
+	GSList *hosts = NULL;
+	struct hostent *hp;
+	gchar *hostname;
+#ifdef USE_IDN
+	if (!dns_str_is_ascii(query_data->hostname)) {
+		int ret = purple_network_convert_idn_to_ascii(query_data->hostname,
+				&hostname);
+		if (ret != 0) {
+			char message[1024];
+			g_snprintf(message, sizeof(message), _("Error resolving %s: %d"),
+					query_data->hostname, ret);
+			purple_dnsquery_failed(query_data, message);
+			return;
+		}
+	} else /* fallthrough is intentional to the g_strdup */
+#endif
+	hostname = g_strdup(query_data->hostname);
+
+	if(!(hp = gethostbyname(hostname))) {
+		char message[1024];
+		g_snprintf(message, sizeof(message), _("Error resolving %s: %d"),
+				query_data->hostname, h_errno);
+		purple_dnsquery_failed(query_data, message);
+		g_free(hostname);
+		return;
+	}
+	memset(&sin, 0, sizeof(struct sockaddr_in));
+	memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
+	sin.sin_family = hp->h_addrtype;
+	g_free(hostname);
+	sin.sin_port = htons(query_data->port);
+
+	hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin)));
+	hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin)));
+
+	purple_dnsquery_resolved(query_data, hosts);
+}
+
+#endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
+
 static gboolean
-resolve_host(gpointer data)
+initiate_resolving(gpointer data)
 {
 	PurpleDnsQueryData *query_data;
-	GError *err = NULL;
+	PurpleProxyType proxy_type;
 
 	query_data = data;
 	query_data->timeout = 0;
 
-	if (purple_dnsquery_ui_resolve(query_data))
-	{
-		/* The UI is handling the resolve; we're done */
+	if (resolve_ip(query_data))
+		/* resolve_ip calls purple_dnsquery_resolved */
+		return FALSE;
+
+	proxy_type = purple_proxy_info_get_type(
+		purple_proxy_get_setup(query_data->account));
+	if (proxy_type == PURPLE_PROXY_TOR) {
+		purple_dnsquery_failed(query_data,
+			_("Aborting DNS lookup in Tor Proxy mode."));
 		return FALSE;
 	}
 
-	if (!resolve_ip(query_data))
-	{
-		/*
-		 * Spin off a separate thread to perform the DNS lookup so
-		 * that we don't block the UI.
-		 */
-		query_data->resolver = g_thread_create(dns_thread,
-				query_data, FALSE, &err);
-		if (query_data->resolver == NULL)
-		{
-			char message[1024];
-			g_snprintf(message, sizeof(message), _("Thread creation failure: %s"),
-					(err && err->message) ? err->message : _("Unknown reason"));
-			g_error_free(err);
-			purple_dnsquery_failed(query_data, message);
-		}
-	}
+	if (purple_dnsquery_ui_resolve(query_data))
+		/* The UI is handling the resolve; we're done */
+		return FALSE;
+
+	resolve_host(query_data);
 
 	return FALSE;
 }
 
 PurpleDnsQueryData *
-purple_dnsquery_a(const char *hostname, int port,
+purple_dnsquery_a(PurpleAccount *account, const char *hostname, int port,
 				PurpleDnsQueryConnectFunction callback, gpointer data)
 {
 	PurpleDnsQueryData *query_data;
 
 	g_return_val_if_fail(hostname != NULL, NULL);
-	g_return_val_if_fail(port	  != 0, NULL);
+	g_return_val_if_fail(port != 0, NULL);
 	g_return_val_if_fail(callback != NULL, NULL);
 
 	purple_debug_info("dnsquery", "Performing DNS lookup for %s\n", hostname);
@@ -892,114 +931,19 @@
 	query_data->port = port;
 	query_data->callback = callback;
 	query_data->data = data;
+	query_data->account = account;
 
-	if (strlen(query_data->hostname) == 0)
+	if (*query_data->hostname == '\0')
 	{
 		purple_dnsquery_destroy(query_data);
 		g_return_val_if_reached(NULL);
 	}
 
-	/* Don't call the callback before returning */
-	query_data->timeout = purple_timeout_add(0, resolve_host, query_data);
+	query_data->timeout = purple_timeout_add(0, initiate_resolving, query_data);
 
 	return query_data;
 }
 
-#else /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
-
-/*
- * We weren't able to do anything fancier above, so use the
- * fail-safe name resolution code, which is blocking.
- */
-
-static gboolean
-resolve_host(gpointer data)
-{
-	PurpleDnsQueryData *query_data;
-	struct sockaddr_in sin;
-	GSList *hosts = NULL;
-	char *hostname;
-
-	query_data = data;
-	query_data->timeout = 0;
-
-	if (purple_dnsquery_ui_resolve(query_data))
-	{
-		/* The UI is handling the resolve; we're done */
-		return FALSE;
-	}
-
-	if (!inet_aton(query_data->hostname, &sin.sin_addr)) {
-		struct hostent *hp;
-#ifdef USE_IDN
-		if (!dns_str_is_ascii(query_data->hostname)) {
-			int ret = purple_network_convert_idn_to_ascii(query_data->hostname,
-					&hostname);
-			if (ret != 0) {
-				char message[1024];
-				g_snprintf(message, sizeof(message), _("Error resolving %s: %d"),
-						query_data->hostname, ret);
-				purple_dnsquery_failed(query_data, message);
-				return FALSE;
-			}
-		} else /* fallthrough is intentional to the g_strdup */
-#endif
-		hostname = g_strdup(query_data->hostname);
-
-		if(!(hp = gethostbyname(hostname))) {
-			char message[1024];
-			g_snprintf(message, sizeof(message), _("Error resolving %s: %d"),
-					query_data->hostname, h_errno);
-			purple_dnsquery_failed(query_data, message);
-			return FALSE;
-		}
-		memset(&sin, 0, sizeof(struct sockaddr_in));
-		memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
-		sin.sin_family = hp->h_addrtype;
-	} else
-		sin.sin_family = AF_INET;
-	sin.sin_port = htons(query_data->port);
-
-	g_free(hostname);
-	hosts = g_slist_append(hosts, GINT_TO_POINTER(sizeof(sin)));
-	hosts = g_slist_append(hosts, g_memdup(&sin, sizeof(sin)));
-
-	purple_dnsquery_resolved(query_data, hosts);
-
-	return FALSE;
-}
-
-PurpleDnsQueryData *
-purple_dnsquery_a(const char *hostname, int port,
-				PurpleDnsQueryConnectFunction callback, gpointer data)
-{
-	PurpleDnsQueryData *query_data;
-
-	g_return_val_if_fail(hostname != NULL, NULL);
-	g_return_val_if_fail(port	  != 0, NULL);
-	g_return_val_if_fail(callback != NULL, NULL);
-
-	query_data = g_new(PurpleDnsQueryData, 1);
-	query_data->hostname = g_strdup(hostname);
-	g_strstrip(query_data->hostname);
-	query_data->port = port;
-	query_data->callback = callback;
-	query_data->data = data;
-
-	if (strlen(query_data->hostname) == 0)
-	{
-		purple_dnsquery_destroy(query_data);
-		g_return_val_if_reached(NULL);
-	}
-
-	/* Don't call the callback before returning */
-	query_data->timeout = purple_timeout_add(0, resolve_host, query_data);
-
-	return query_data;
-}
-
-#endif /* not PURPLE_DNSQUERY_USE_FORK or _WIN32 */
-
 void
 purple_dnsquery_destroy(PurpleDnsQueryData *query_data)
 {
--- a/libpurple/dnsquery.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dnsquery.h	Wed Jun 13 19:30:27 2012 -0400
@@ -30,6 +30,11 @@
 #include "eventloop.h"
 #include "account.h"
 
+/**
+ * An opaque structure representing a DNS query.  The hostname and port
+ * associated with the query can be retrieved using
+ * purple_dnsquery_get_host() and purple_dnsquery_get_port().
+ */
 typedef struct _PurpleDnsQueryData PurpleDnsQueryData;
 
 /**
@@ -53,7 +58,8 @@
  */
 typedef struct
 {
-	/** If implemented, the UI is responsible for DNS queries */
+	/** If implemented, return TRUE if the UI takes responsibility for DNS
+	  * queries. When returning FALSE, the standard implementation is used. */
 	gboolean (*resolve_host)(PurpleDnsQueryData *query_data,
 	                         PurpleDnsQueryResolvedCallback resolved_cb,
 	                         PurpleDnsQueryFailedCallback failed_cb);
@@ -70,9 +76,7 @@
 	void (*_purple_reserved4)(void);
 } PurpleDnsQueryUiOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name DNS query API                                                   */
@@ -82,6 +86,7 @@
 /**
  * Perform an asynchronous DNS query.
  *
+ * @param account  The account that the query is being done for (or NULL)
  * @param hostname The hostname to resolve.
  * @param port     A port number which is stored in the struct sockaddr.
  * @param callback The callback function to call after resolving.
@@ -90,8 +95,9 @@
  * @return NULL if there was an error, otherwise return a reference to
  *         a data structure that can be used to cancel the pending
  *         DNS query, if needed.
+ *
  */
-PurpleDnsQueryData *purple_dnsquery_a(const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data);
+PurpleDnsQueryData *purple_dnsquery_a(PurpleAccount *account, const char *hostname, int port, PurpleDnsQueryConnectFunction callback, gpointer data);
 
 /**
  * Cancel a DNS query and destroy the associated data structure.
@@ -146,8 +152,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_DNSQUERY_H_ */
--- a/libpurple/dnssrv.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dnssrv.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,6 +20,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
+#define _PURPLE_DNSSRV_C_
 
 #include "internal.h"
 #include "util.h"
@@ -30,21 +31,22 @@
 #ifdef HAVE_ARPA_NAMESER_COMPAT_H
 #include <arpa/nameser_compat.h>
 #endif
-#ifndef T_SRV
-#define T_SRV	33
-#endif
-#ifndef T_TXT
-#define T_TXT	16
-#endif
 #else /* WIN32 */
 #include <windns.h>
 /* Missing from the mingw headers */
 #ifndef DNS_TYPE_SRV
-# define DNS_TYPE_SRV 33
+# define DNS_TYPE_SRV PurpleDnsTypeSrv
 #endif
 #ifndef DNS_TYPE_TXT
-# define DNS_TYPE_TXT 16
+# define DNS_TYPE_TXT PurpleDnsTypeTxt
+#endif
 #endif
+
+#ifndef T_SRV
+#define T_SRV	PurpleDnsTypeSrv
+#endif
+#ifndef T_TXT
+#define T_TXT	PurpleDnsTypeTxt
 #endif
 
 #include "debug.h"
@@ -52,6 +54,8 @@
 #include "eventloop.h"
 #include "network.h"
 
+static PurpleSrvTxtQueryUiOps *srv_txt_query_ui_ops = NULL;
+
 #ifndef _WIN32
 typedef union {
 	HEADER hdr;
@@ -66,11 +70,7 @@
 	DNS_FREE_TYPE FreeType) = NULL;
 #endif
 
-struct _PurpleTxtResponse {
-	char *content;
-};
-
-struct _PurpleSrvQueryData {
+struct _PurpleSrvTxtQueryData {
 	union {
 		PurpleSrvCallback srv;
 		PurpleTxtCallback txt;
@@ -79,9 +79,9 @@
 	gpointer extradata;
 	guint handle;
 	int type;
+	char *query;
 #ifdef _WIN32
 	GThread *resolver;
-	char *query;
 	char *error_message;
 	GList *results;
 #else
@@ -100,6 +100,8 @@
 	int sum;
 } PurpleSrvResponseContainer;
 
+static gboolean purple_srv_txt_query_ui_resolve(PurpleSrvTxtQueryData *query_data);
+
 /**
  * Sort by priority, then by weight.  Strictly numerically--no
  * randomness.  Technically we only need to sort by pref and then
@@ -248,6 +250,53 @@
 	return list;
 }
 
+static PurpleSrvTxtQueryData *
+query_data_new(int type, gchar *query, gpointer extradata)
+{
+	PurpleSrvTxtQueryData *query_data = g_new0(PurpleSrvTxtQueryData, 1);
+	query_data->type = type;
+	query_data->extradata = extradata;
+	query_data->query = query;
+#ifndef _WIN32
+	query_data->fd_in = -1;
+	query_data->fd_out = -1;
+#endif
+	return query_data;
+}
+
+void
+purple_srv_txt_query_destroy(PurpleSrvTxtQueryData *query_data)
+{
+	PurpleSrvTxtQueryUiOps *ops = purple_srv_txt_query_get_ui_ops();
+
+	if (ops && ops->destroy)
+		ops->destroy(query_data);
+
+	if (query_data->handle > 0)
+		purple_input_remove(query_data->handle);
+#ifdef _WIN32
+	if (query_data->resolver != NULL)
+	{
+		/*
+		 * It's not really possible to kill a thread.  So instead we
+		 * just set the callback to NULL and let the DNS lookup
+		 * finish.
+		 */
+		query_data->cb.srv = NULL;
+		return;
+	}
+	g_free(query_data->error_message);
+#else
+	if (query_data->fd_out != -1)
+		close(query_data->fd_out);
+	if (query_data->fd_in != -1)
+		close(query_data->fd_in);
+#endif
+	g_free(query_data->query);
+	g_free(query_data);
+}
+
+#ifdef USE_IDN
 static gboolean
 dns_str_is_ascii(const char *name)
 {
@@ -259,8 +308,60 @@
 
 	return TRUE;
 }
+#endif
 
 #ifndef _WIN32
+static void
+write_to_parent(int in, int out, gconstpointer data, gsize size)
+{
+	const guchar *buf = data;
+	gssize w;
+
+	do {
+		w = write(out, buf, size);
+		if (w > 0) {
+			buf += w;
+			size -= w;
+		} else if (w < 0 && errno == EINTR) {
+			/* Let's try some more; */
+			w = 1;
+		}
+	} while (size > 0 && w > 0);
+
+	if (size != 0) {
+		/* An error occurred */
+		close(out);
+		close(in);
+		_exit(0);
+	}
+}
+
+/* Read size bytes to data. Dies if an error occurs. */
+static void
+read_from_parent(int in, int out, gpointer data, gsize size)
+{
+	guchar *buf = data;
+	gssize r;
+
+	do {
+		r = read(in, data, size);
+		if (r > 0) {
+			buf += r;
+			size -= r;
+		} else if (r < 0 && errno == EINTR) {
+			/* Let's try some more; */
+			r = 1;
+		}
+	} while (size > 0 && r > 0);
+
+	if (size != 0) {
+		/* An error occurred */
+		close(out);
+		close(in);
+		_exit(0);
+	}
+}
+
 
 G_GNUC_NORETURN static void
 resolve(int in, int out)
@@ -279,16 +380,12 @@
 	purple_restore_default_signal_handlers();
 #endif
 
-	if (read(in, &query, sizeof(query)) <= 0) {
-		close(out);
-		close(in);
-		_exit(0);
-	}
+	read_from_parent(in, out, &query, sizeof(query));
 
 	size = res_query( query.query, C_IN, query.type, (u_char*)&answer, sizeof( answer));
 	if (size == -1) {
-		write(out, &(query.type), sizeof(query.type));
-		write(out, &size, sizeof(int));
+		write_to_parent(in, out, &(query.type), sizeof(query.type));
+		write_to_parent(in, out, &size, sizeof(size));
 		close(out);
 		close(in);
 		_exit(0);
@@ -317,7 +414,7 @@
 		cp += 6;
 
 		GETSHORT(dlen,cp);
-		if (query.type == T_SRV) {
+		if (type == T_SRV) {
 			GETSHORT(pref,cp);
 
 			GETSHORT(weight,cp);
@@ -331,13 +428,17 @@
 			cp += size;
 
 			srvres = g_new0(PurpleSrvResponse, 1);
-			strcpy(srvres->hostname, name);
+			if (strlen(name) > sizeof(srvres->hostname) - 1) {
+				purple_debug_error("dnssrv", "hostname is longer than available buffer ('%s', %zd bytes)!",
+				                   name, strlen(name));
+			}
+			g_strlcpy(srvres->hostname, name, sizeof(srvres->hostname));
 			srvres->pref = pref;
 			srvres->port = port;
 			srvres->weight = weight;
 
 			ret = g_list_prepend(ret, srvres);
-		} else if (query.type == T_TXT) {
+		} else if (type == T_TXT) {
 			txtres = g_new0(PurpleTxtResponse, 1);
 			txtres->content = g_strndup((gchar*)(++cp), dlen-1);
 			ret = g_list_append(ret, txtres);
@@ -353,16 +454,18 @@
 	if (query.type == T_SRV)
 		ret = purple_srv_sort(ret);
 
-	/* TODO: Check return value */
-	write(out, &(query.type), sizeof(query.type));
-	write(out, &size, sizeof(size));
+	write_to_parent(in, out, &(query.type), sizeof(query.type));
+	write_to_parent(in, out, &size, sizeof(size));
 	while (ret != NULL)
 	{
-		/* TODO: Check return value */
 		if (query.type == T_SRV)
-			write(out, ret->data, sizeof(PurpleSrvResponse));
-		if (query.type == T_TXT)
-			write(out, ret->data, sizeof(PurpleTxtResponse));
+			write_to_parent(in, out, ret->data, sizeof(PurpleSrvResponse));
+		if (query.type == T_TXT) {
+			PurpleTxtResponse *response = ret->data;
+			gsize l = strlen(response->content) + 1 /* null byte */;
+			write_to_parent(in, out, &l, sizeof(l));
+			write_to_parent(in, out, response->content, l);
+		}
 
 		g_free(ret->data);
 		ret = g_list_remove(ret, ret->data);
@@ -379,7 +482,7 @@
 {
 	int size;
 	int type;
-	PurpleSrvQueryData *query_data = (PurpleSrvQueryData*)data;
+	PurpleSrvTxtQueryData *query_data = (PurpleSrvTxtQueryData*)data;
 	int i;
 	int status;
 
@@ -429,21 +532,38 @@
 					PurpleTxtCallback cb = query_data->cb.txt;
 					ssize_t red;
 					purple_debug_info("dnssrv","found %d TXT entries\n", size);
-					res = g_new0(PurpleTxtResponse, 1);
 					for (i = 0; i < size; i++) {
-						red = read(source, res, sizeof(PurpleTxtResponse));
-						if (red != sizeof(PurpleTxtResponse)) {
+						gsize len;
+
+						red = read(source, &len, sizeof(len));
+						if (red != sizeof(len)) {
 							purple_debug_error("dnssrv","unable to read txt "
-									"response: %s\n", g_strerror(errno));
+									"response length: %s\n", g_strerror(errno));
 							size = 0;
-							g_free(res);
 							g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
 							g_list_free(responses);
 							responses = NULL;
 							break;
 						}
+
+						res = g_new0(PurpleTxtResponse, 1);
+						res->content = g_new0(gchar, len);
+
+						red = read(source, res->content, len);
+						if (red != len) {
+							purple_debug_error("dnssrv","unable to read txt "
+									"response: %s\n", g_strerror(errno));
+							size = 0;
+							purple_txt_response_destroy(res);
+							g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL);
+							g_list_free(responses);
+							responses = NULL;
+							break;
+						}
+						responses = g_list_prepend(responses, res);
 					}
 
+					responses = g_list_reverse(responses);
 					cb(responses, query_data->extradata);
 				} else {
 					purple_debug_error("dnssrv", "type unknown of DNS result entry; errno is %i\n", errno);
@@ -453,7 +573,7 @@
 	}
 
 	waitpid(query_data->pid, &status, 0);
-	purple_srv_cancel(query_data);
+	purple_srv_txt_query_destroy(query_data);
 }
 
 #else /* _WIN32 */
@@ -464,10 +584,17 @@
 res_main_thread_cb(gpointer data)
 {
 	PurpleSrvResponse *srvres = NULL;
-	PurpleSrvQueryData *query_data = data;
-	if(query_data->error_message != NULL)
-		purple_debug_error("dnssrv", query_data->error_message);
-	else {
+	PurpleSrvTxtQueryData *query_data = data;
+	if(query_data->error_message != NULL) {
+		purple_debug_error("dnssrv", "%s", query_data->error_message);
+		if (query_data->type == DNS_TYPE_SRV) {
+			if (query_data->cb.srv)
+				query_data->cb.srv(srvres, 0, query_data->extradata);
+		} else if (query_data->type == DNS_TYPE_TXT) {
+			if (query_data->cb.txt)
+				query_data->cb.txt(NULL, query_data->extradata);
+		}
+	} else {
 		if (query_data->type == DNS_TYPE_SRV) {
 			PurpleSrvResponse *srvres_tmp = NULL;
 			GList *lst = query_data->results;
@@ -506,7 +633,7 @@
 	query_data->resolver = NULL;
 	query_data->handle = 0;
 
-	purple_srv_cancel(query_data);
+	purple_srv_txt_query_destroy(query_data);
 
 	return FALSE;
 }
@@ -517,7 +644,7 @@
 	PDNS_RECORD dr = NULL;
 	int type;
 	DNS_STATUS ds;
-	PurpleSrvQueryData *query_data = data;
+	PurpleSrvTxtQueryData *query_data = data;
 	type = query_data->type;
 	ds = MyDnsQuery_UTF8(query_data->query, type, DNS_QUERY_STANDARD, NULL, &dr, NULL);
 	if (ds != ERROR_SUCCESS) {
@@ -597,12 +724,15 @@
 
 #endif
 
-PurpleSrvQueryData *
-purple_srv_resolve(const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata)
+PurpleSrvTxtQueryData *
+purple_srv_resolve(PurpleAccount *account, const char *protocol,
+	const char *transport, const char *domain, PurpleSrvCallback cb,
+	gpointer extradata)
 {
 	char *query;
 	char *hostname;
-	PurpleSrvQueryData *query_data;
+	PurpleSrvTxtQueryData *query_data;
+	PurpleProxyType proxy_type;
 #ifndef _WIN32
 	PurpleSrvInternalQuery internal_query;
 	int in[2], out[2];
@@ -618,6 +748,14 @@
 		g_return_val_if_reached(NULL);
 	}
 
+	proxy_type = purple_proxy_info_get_type(
+		purple_proxy_get_setup(account));
+	if (proxy_type == PURPLE_PROXY_TOR) {
+		purple_debug_info("dnssrv", "Aborting SRV lookup in Tor Proxy mode.");
+		cb(NULL, 0, extradata);
+		return NULL;
+	}
+
 #ifdef USE_IDN
 	if (!dns_str_is_ascii(domain)) {
 		int ret = purple_network_convert_idn_to_ascii(domain, &hostname);
@@ -635,19 +773,35 @@
 			query);
 	g_free(hostname);
 
+	query_data = query_data_new(PurpleDnsTypeSrv, query, extradata);
+	query_data->cb.srv = cb;
+
+	if (purple_srv_txt_query_ui_resolve(query_data))
+	{
+		return query_data;
+	}
+
 #ifndef _WIN32
 	if(pipe(in) || pipe(out)) {
 		purple_debug_error("dnssrv", "Could not create pipe\n");
 		g_free(query);
+		g_free(query_data);
 		cb(NULL, 0, extradata);
 		return NULL;
 	}
 
+	/*
+	 * TODO: We should put a cap on the number of forked processes that we
+	 *       allow at any given time.  If we get too many requests they
+	 *       should be put into a queue and handled later.  (This is what
+	 *       we do for A record lookups.)
+	 */
 	pid = fork();
 	if (pid == -1) {
 		purple_debug_error("dnssrv", "Could not create process!\n");
+		g_free(query);
+		g_free(query_data);
 		cb(NULL, 0, extradata);
-		g_free(query);
 		return NULL;
 	}
 
@@ -655,6 +809,7 @@
 	if (pid == 0)
 	{
 		g_free(query);
+		g_free(query_data);
 
 		close(out[0]);
 		close(in[1]);
@@ -667,21 +822,16 @@
 
 	internal_query.type = T_SRV;
 	strncpy(internal_query.query, query, 255);
+	internal_query.query[255] = '\0';
 
 	if (write(in[1], &internal_query, sizeof(internal_query)) < 0)
 		purple_debug_error("dnssrv", "Could not write to SRV resolver\n");
 
-	query_data = g_new0(PurpleSrvQueryData, 1);
-	query_data->type = T_SRV;
-	query_data->cb.srv = cb;
-	query_data->extradata = extradata;
 	query_data->pid = pid;
 	query_data->fd_out = out[0];
 	query_data->fd_in = in[1];
 	query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data);
 
-	g_free(query);
-
 	return query_data;
 #else
 	if (!initialized) {
@@ -691,12 +841,6 @@
 		initialized = TRUE;
 	}
 
-	query_data = g_new0(PurpleSrvQueryData, 1);
-	query_data->type = DNS_TYPE_SRV;
-	query_data->cb.srv = cb;
-	query_data->query = query;
-	query_data->extradata = extradata;
-
 	if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree)
 		query_data->error_message = g_strdup("System missing DNS API (Requires W2K+)\n");
 	else {
@@ -717,11 +861,14 @@
 #endif
 }
 
-PurpleSrvQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata)
+PurpleSrvTxtQueryData *purple_txt_resolve(PurpleAccount *account,
+	const char *owner, const char *domain, PurpleTxtCallback cb,
+	gpointer extradata)
 {
 	char *query;
 	char *hostname;
-	PurpleSrvQueryData *query_data;
+	PurpleSrvTxtQueryData *query_data;
+	PurpleProxyType proxy_type;
 #ifndef _WIN32
 	PurpleSrvInternalQuery internal_query;
 	int in[2], out[2];
@@ -731,6 +878,14 @@
 	static gboolean initialized = FALSE;
 #endif
 
+	proxy_type = purple_proxy_info_get_type(
+		purple_proxy_get_setup(account));
+	if (proxy_type == PURPLE_PROXY_TOR) {
+		purple_debug_info("dnssrv", "Aborting TXT lookup in Tor Proxy mode.");
+		cb(NULL, extradata);
+		return NULL;
+	}
+
 #ifdef USE_IDN
 	if (!dns_str_is_ascii(domain)) {
 		int ret = purple_network_convert_idn_to_ascii(domain, &hostname);
@@ -748,19 +903,36 @@
 			query);
 	g_free(hostname);
 
+	query_data = query_data_new(PurpleDnsTypeTxt, query, extradata);
+	query_data->cb.txt = cb;
+
+	if (purple_srv_txt_query_ui_resolve(query_data)) {
+		/* query intentionally not freed
+		 */
+		return query_data;
+	}
+
 #ifndef _WIN32
 	if(pipe(in) || pipe(out)) {
 		purple_debug_error("dnssrv", "Could not create pipe\n");
 		g_free(query);
+		g_free(query_data);
 		cb(NULL, extradata);
 		return NULL;
 	}
 
+	/*
+	 * TODO: We should put a cap on the number of forked processes that we
+	 *       allow at any given time.  If we get too many requests they
+	 *       should be put into a queue and handled later.  (This is what
+	 *       we do for A record lookups.)
+	 */
 	pid = fork();
 	if (pid == -1) {
 		purple_debug_error("dnssrv", "Could not create process!\n");
+		g_free(query);
+		g_free(query_data);
 		cb(NULL, extradata);
-		g_free(query);
 		return NULL;
 	}
 
@@ -768,6 +940,7 @@
 	if (pid == 0)
 	{
 		g_free(query);
+		g_free(query_data);
 
 		close(out[0]);
 		close(in[1]);
@@ -780,21 +953,16 @@
 
 	internal_query.type = T_TXT;
 	strncpy(internal_query.query, query, 255);
+	internal_query.query[255] = '\0';
 
 	if (write(in[1], &internal_query, sizeof(internal_query)) < 0)
 		purple_debug_error("dnssrv", "Could not write to TXT resolver\n");
 
-	query_data = g_new0(PurpleSrvQueryData, 1);
-	query_data->type = T_TXT;
-	query_data->cb.txt = cb;
-	query_data->extradata = extradata;
 	query_data->pid = pid;
 	query_data->fd_out = out[0];
 	query_data->fd_in = in[1];
 	query_data->handle = purple_input_add(out[0], PURPLE_INPUT_READ, resolved, query_data);
 
-	g_free(query);
-
 	return query_data;
 #else
 	if (!initialized) {
@@ -804,12 +972,6 @@
 		initialized = TRUE;
 	}
 
-	query_data = g_new0(PurpleSrvQueryData, 1);
-	query_data->type = DNS_TYPE_TXT;
-	query_data->cb.txt = cb;
-	query_data->query = query;
-	query_data->extradata = extradata;
-
 	if (!MyDnsQuery_UTF8 || !MyDnsRecordListFree)
 		query_data->error_message = g_strdup("System missing DNS API (Requires W2K+)\n");
 	else {
@@ -830,37 +992,6 @@
 #endif
 }
 
-void
-purple_srv_cancel(PurpleSrvQueryData *query_data)
-{
-	if (query_data->handle > 0)
-		purple_input_remove(query_data->handle);
-#ifdef _WIN32
-	if (query_data->resolver != NULL)
-	{
-		/*
-		 * It's not really possible to kill a thread.  So instead we
-		 * just set the callback to NULL and let the DNS lookup
-		 * finish.
-		 */
-		query_data->cb.srv = NULL;
-		return;
-	}
-	g_free(query_data->query);
-	g_free(query_data->error_message);
-#else
-	close(query_data->fd_out);
-	close(query_data->fd_in);
-#endif
-	g_free(query_data);
-}
-
-void
-purple_txt_cancel(PurpleSrvQueryData *query_data)
-{
-	purple_srv_cancel(query_data);
-}
-
 const gchar *
 purple_txt_response_get_content(PurpleTxtResponse *resp)
 {
@@ -876,3 +1007,124 @@
 	g_free(resp->content);
 	g_free(resp);
 }
+
+/*
+ * Only used as the callback for the ui ops.
+ */
+static void
+purple_srv_query_resolved(PurpleSrvTxtQueryData *query_data, GList *records)
+{
+	GList *l;
+	PurpleSrvResponse *records_array;
+	int i = 0, length;
+
+	g_return_if_fail(records != NULL);
+
+	if (query_data->cb.srv == NULL) {
+		purple_srv_txt_query_destroy(query_data);
+
+		while (records) {
+			g_free(records->data);
+			records = g_list_delete_link(records, records);
+		}
+		return;
+	}
+
+	records = purple_srv_sort(records);
+	length = g_list_length(records);
+
+	purple_debug_info("dnssrv", "SRV records resolved for %s, count: %d\n",
+	                            query_data->query, length);
+
+	records_array = g_new(PurpleSrvResponse, length);
+	for (l = records; l; l = l->next, i++) {
+		records_array[i] = *(PurpleSrvResponse *)l->data;
+	}
+
+	query_data->cb.srv(records_array, length, query_data->extradata);
+
+	purple_srv_txt_query_destroy(query_data);
+
+	while (records) {
+		g_free(records->data);
+		records = g_list_delete_link(records, records);
+	}
+}
+
+/*
+ * Only used as the callback for the ui ops.
+ */
+static void
+purple_txt_query_resolved(PurpleSrvTxtQueryData *query_data, GList *entries)
+{
+	g_return_if_fail(entries != NULL);
+
+	purple_debug_info("dnssrv", "TXT entries resolved for %s, count: %d\n", query_data->query, g_list_length(entries));
+
+	/* the callback should g_free the entries.
+	 */
+	if (query_data->cb.txt != NULL)
+		query_data->cb.txt(entries, query_data->extradata);
+	else {
+		while (entries) {
+			g_free(entries->data);
+			entries = g_list_delete_link(entries, entries);
+		}
+	}
+
+	purple_srv_txt_query_destroy(query_data);
+}
+
+static void
+purple_srv_query_failed(PurpleSrvTxtQueryData *query_data, const gchar *error_message)
+{
+	purple_debug_error("dnssrv", "%s\n", error_message);
+
+	if (query_data->cb.srv != NULL)
+		query_data->cb.srv(NULL, 0, query_data->extradata);
+
+	purple_srv_txt_query_destroy(query_data);
+}
+
+static gboolean
+purple_srv_txt_query_ui_resolve(PurpleSrvTxtQueryData *query_data)
+{
+	PurpleSrvTxtQueryUiOps *ops = purple_srv_txt_query_get_ui_ops();
+
+	if (ops && ops->resolve)
+		return ops->resolve(query_data, (query_data->type == T_SRV ? purple_srv_query_resolved : purple_txt_query_resolved), purple_srv_query_failed);
+
+	return FALSE;
+}
+
+void
+purple_srv_txt_query_set_ui_ops(PurpleSrvTxtQueryUiOps *ops)
+{
+	srv_txt_query_ui_ops = ops;
+}
+
+PurpleSrvTxtQueryUiOps *
+purple_srv_txt_query_get_ui_ops(void)
+{
+	/* It is perfectly acceptable for srv_txt_query_ui_ops to be NULL; this just
+	 * means that the default platform-specific implementation will be used.
+	 */
+	return srv_txt_query_ui_ops;
+}
+
+char *
+purple_srv_txt_query_get_query(PurpleSrvTxtQueryData *query_data)
+{
+	g_return_val_if_fail(query_data != NULL, NULL);
+
+	return query_data->query;
+}
+
+
+int
+purple_srv_txt_query_get_type(PurpleSrvTxtQueryData *query_data)
+{
+	g_return_val_if_fail(query_data != NULL, 0);
+
+	return query_data->type;
+}
--- a/libpurple/dnssrv.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/dnssrv.h	Wed Jun 13 19:30:27 2012 -0400
@@ -24,16 +24,17 @@
 #ifndef _PURPLE_DNSSRV_H
 #define _PURPLE_DNSSRV_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct _PurpleSrvQueryData PurpleSrvQueryData;
+typedef struct _PurpleSrvTxtQueryData PurpleSrvTxtQueryData;
 typedef struct _PurpleSrvResponse PurpleSrvResponse;
 typedef struct _PurpleTxtResponse PurpleTxtResponse;
 
 #include <glib.h>
 
+enum PurpleDnsType {
+	PurpleDnsTypeTxt = 16,
+	PurpleDnsTypeSrv = 33
+};
+
 struct _PurpleSrvResponse {
 	char hostname[256];
 	int port;
@@ -41,6 +42,40 @@
 	int pref;
 };
 
+struct _PurpleTxtResponse {
+	char *content;
+};
+
+typedef void  (*PurpleSrvTxtQueryResolvedCallback) (PurpleSrvTxtQueryData *query_data, GList *records);
+typedef void  (*PurpleSrvTxtQueryFailedCallback) (PurpleSrvTxtQueryData *query_data, const gchar *error_message);
+
+/**
+ * SRV Request UI operations;  UIs should implement this if they want to do SRV
+ * lookups themselves, rather than relying on the core.
+ *
+ * @see @ref ui-ops
+ */
+typedef struct
+{
+	/** If implemented, return TRUE if the UI takes responsibility for SRV
+	  * queries. When returning FALSE, the standard implementation is used. 
+	  * These callbacks MUST be called asynchronously. */
+	gboolean (*resolve)(PurpleSrvTxtQueryData *query_data,
+	                    PurpleSrvTxtQueryResolvedCallback resolved_cb,
+	                    PurpleSrvTxtQueryFailedCallback failed_cb);
+
+	/** Called just before @a query_data is freed; this should cancel any
+	 *  further use of @a query_data the UI would make. Unneeded if
+	 *  #resolve is not implemented.
+	 */
+	void (*destroy)(PurpleSrvTxtQueryData *query_data);
+
+	void (*_purple_reserved1)(void);
+	void (*_purple_reserved2)(void);
+	void (*_purple_reserved3)(void);
+	void (*_purple_reserved4)(void);
+} PurpleSrvTxtQueryUiOps;
+
 /**
  * @param resp An array of PurpleSrvResponse of size results.  The array
  *        is sorted based on the order described in the DNS SRV RFC.
@@ -57,63 +92,97 @@
  */
 typedef void (*PurpleTxtCallback)(GList *responses, gpointer data);
 
+G_BEGIN_DECLS
+
 /**
  * Queries an SRV record.
  *
- * @param protocol Name of the protocol (e.g. "sip")
+ * @param account   The account that the query is being done for (or NULL)
+ * @param protocol  Name of the protocol (e.g. "sip")
  * @param transport Name of the transport ("tcp" or "udp")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
+ * @param domain    Domain name to query (e.g. "blubb.com")
+ * @param cb        A callback which will be called with the results
  * @param extradata Extra data to be passed to the callback
+ *
+ * @return NULL if there was an error, otherwise return a reference to
+ *         a data structure that can be used to cancel the pending
+ *         DNS query, if needed.
  */
-PurpleSrvQueryData *purple_srv_resolve(const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata);
-
-/**
- * Cancel an SRV DNS query.
- *
- * @param query_data The request to cancel.
- */
-void purple_srv_cancel(PurpleSrvQueryData *query_data);
+PurpleSrvTxtQueryData *purple_srv_resolve(PurpleAccount *account, const char *protocol, const char *transport, const char *domain, PurpleSrvCallback cb, gpointer extradata);
 
 /**
  * Queries an TXT record.
  *
- * @param owner Name of the protocol (e.g. "_xmppconnect")
- * @param domain Domain name to query (e.g. "blubb.com")
- * @param cb A callback which will be called with the results
+ * @param account   The account that the query is being done for (or NULL)
+ * @param owner     Name of the protocol (e.g. "_xmppconnect")
+ * @param domain    Domain name to query (e.g. "blubb.com")
+ * @param cb        A callback which will be called with the results
  * @param extradata Extra data to be passed to the callback
  *
- * @since 2.6.0
+ * @return NULL if there was an error, otherwise return a reference to
+ *         a data structure that can be used to cancel the pending
+ *         DNS query, if needed.
  */
-PurpleSrvQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata);
-
-/**
- * Cancel an TXT DNS query.
- *
- * @param query_data The request to cancel.
- * @since 2.6.0
- */
-void purple_txt_cancel(PurpleSrvQueryData *query_data);
+PurpleSrvTxtQueryData *purple_txt_resolve(PurpleAccount *account, const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata);
 
 /**
  * Get the value of the current TXT record.
  *
- * @param resp  The TXT response record
- * @returns The value of the current TXT record.
- * @since 2.6.0
+ * @param response  The TXT response record
+ *
+ * @return The value of the current TXT record.
  */
-const gchar *purple_txt_response_get_content(PurpleTxtResponse *resp);
+const gchar *purple_txt_response_get_content(PurpleTxtResponse *response);
 
 /**
  * Destroy a TXT DNS response object.
  *
  * @param response The PurpleTxtResponse to destroy.
- * @since 2.6.0
+ */
+void purple_txt_response_destroy(PurpleTxtResponse *response);
+
+/**
+ * Cancel a SRV/TXT query and destroy the associated data structure.
+ *
+ * @param query_data The SRV/TXT query to cancel.  This data structure
+ *        is freed by this function.
  */
-void purple_txt_response_destroy(PurpleTxtResponse *resp);
+void purple_srv_txt_query_destroy(PurpleSrvTxtQueryData *query_data);
+
+/**
+ * Sets the UI operations structure to be used when doing a SRV/TXT
+ * resolve.  The UI operations need only be set if the UI wants to
+ * handle the resolve itself; otherwise, leave it as NULL.
+ *
+ * @param ops The UI operations structure.
+ */
+void purple_srv_txt_query_set_ui_ops(PurpleSrvTxtQueryUiOps *ops);
 
-#ifdef __cplusplus
-}
-#endif
+/**
+ * Returns the UI operations structure to be used when doing a SRV/TXT
+ * resolve.
+ *
+ * @return The UI operations structure.
+ */
+PurpleSrvTxtQueryUiOps *purple_srv_txt_query_get_ui_ops(void);
+
+/**
+ * Get the query from a PurpleSrvTxtQueryData
+ *
+ * @param query_data The SRV/TXT query
+ * @return The query.
+ */
+char *purple_srv_txt_query_get_query(PurpleSrvTxtQueryData *query_data);
+
+/**
+ * Get the type from a PurpleSrvTxtQueryData (TXT or SRV)
+ *
+ * @param query_data The query
+ * @return The query.
+ */
+int purple_srv_txt_query_get_type(PurpleSrvTxtQueryData *query_data);
+
+G_END_DECLS
 
 #endif /* _PURPLE_DNSSRV_H */
+
--- a/libpurple/eventloop.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/eventloop.c	Wed Jun 13 19:30:27 2012 -0400
@@ -23,8 +23,8 @@
  * 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 "eventloop.h"
-#include "internal.h"
 
 static PurpleEventLoopUiOps *eventloop_ui_ops = NULL;
 
--- a/libpurple/eventloop.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/eventloop.h	Wed Jun 13 19:30:27 2012 -0400
@@ -28,10 +28,6 @@
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * An input condition.
  */
@@ -145,7 +141,6 @@
 	 * #timeout_add.
 	 *
 	 * @see purple_timeout_add_seconds()
-	 * @since 2.1.0
 	 **/
 	guint (*timeout_add_seconds)(guint interval, GSourceFunc function,
 	                             gpointer data);
@@ -155,6 +150,8 @@
 	void (*_purple_reserved4)(void);
 };
 
+G_BEGIN_DECLS
+
 /**************************************************************************/
 /** @name Event Loop API                                                  */
 /**************************************************************************/
@@ -192,8 +189,6 @@
  * @param data		data to pass to @a function.
  * @return A handle to the timer which can be passed to
  *         purple_timeout_remove() to remove the timer.
- *
- * @since 2.1.0
  */
 guint purple_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data);
 
@@ -269,8 +264,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_EVENTLOOP_H_ */
--- a/libpurple/example/Makefile.am	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/example/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -12,7 +12,6 @@
 
 AM_CPPFLAGS = \
 	-DSTANDALONE \
-	-DBR_PTHREADS=0 \
 	-DDATADIR=\"$(datadir)\" \
 	-DLIBDIR=\"$(libdir)/purple-$(PURPLE_MAJOR_VERSION)/\" \
 	-DLOCALEDIR=\"$(datadir)/locale\" \
--- a/libpurple/example/nullclient.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/example/nullclient.c	Wed Jun 13 19:30:27 2012 -0400
@@ -27,7 +27,11 @@
 
 #include <signal.h>
 #include <string.h>
+#ifndef _WIN32
 #include <unistd.h>
+#else
+#include "win32/win32dep.h"
+#endif
 
 #include "defines.h"
 
@@ -80,7 +84,11 @@
 	if (condition & PURPLE_INPUT_WRITE)
 		cond |= PURPLE_GLIB_WRITE_COND;
 
+#if defined _WIN32 && !defined WINPIDGIN_USE_GLIB_IO_CHANNEL
+	channel = wpurple_g_io_channel_win32_new_socket(fd);
+#else
 	channel = g_io_channel_unix_new(fd);
+#endif
 	closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
 					      purple_glib_io_invoke, closure, purple_glib_io_destroy);
 
@@ -88,7 +96,7 @@
 	return closure->result;
 }
 
-static PurpleEventLoopUiOps glib_eventloops = 
+static PurpleEventLoopUiOps glib_eventloops =
 {
 	g_timeout_add,
 	g_source_remove,
@@ -126,7 +134,7 @@
 			name, message);
 }
 
-static PurpleConversationUiOps null_conv_uiops = 
+static PurpleConversationUiOps null_conv_uiops =
 {
 	NULL,                      /* create_conversation  */
 	NULL,                      /* destroy_conversation */
@@ -159,7 +167,7 @@
 	purple_conversations_set_ui_ops(&null_conv_uiops);
 }
 
-static PurpleCoreUiOps null_core_uiops = 
+static PurpleCoreUiOps null_core_uiops =
 {
 	NULL,
 	NULL,
@@ -229,7 +237,7 @@
 signed_on(PurpleConnection *gc, gpointer null)
 {
 	PurpleAccount *account = purple_connection_get_account(gc);
-	printf("Account connected: %s %s\n", account->username, account->protocol_id);
+	printf("Account connected: %s %s\n", purple_account_get_username(account), purple_account_get_protocol_id(account));
 }
 
 static void
@@ -253,12 +261,14 @@
 	PurpleSavedStatus *status;
 	char *res;
 
+#ifndef _WIN32
 	/* libpurple's built-in DNS resolution forks processes to perform
 	 * blocking lookups without blocking the main process.  It does not
 	 * handle SIGCHLD itself, so if the UI does not you quickly get an army
 	 * of zombie subprocesses marching around.
 	 */
 	signal(SIGCHLD, SIG_IGN);
+#endif
 
 	init_libpurple();
 
--- a/libpurple/ft.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/ft.c	Wed Jun 13 19:30:27 2012 -0400
@@ -40,8 +40,73 @@
 static PurpleXferUiOps *xfer_ui_ops = NULL;
 static GList *xfers;
 
+/*
+ * A hack to store more data since we can't extend the size of PurpleXfer
+ * easily.
+ */
+static GHashTable *xfers_data = NULL;
+
+typedef struct _PurpleXferPrivData {
+	/*
+	 * Used to moderate the file transfer when either the read/write ui_ops are
+	 * set or fd is not set. In those cases, the UI/prpl call the respective
+	 * function, which is somewhat akin to a fd watch being triggered.
+	 */
+	enum {
+		PURPLE_XFER_READY_NONE = 0x0,
+		PURPLE_XFER_READY_UI   = 0x1,
+		PURPLE_XFER_READY_PRPL = 0x2,
+	} ready;
+
+	/* TODO: Should really use a PurpleCircBuffer for this. */
+	GByteArray *buffer;
+
+	gpointer thumbnail_data;		/**< thumbnail image */
+	gsize thumbnail_size;
+	gchar *thumbnail_mimetype;
+} PurpleXferPrivData;
+
 static int purple_xfer_choose_file(PurpleXfer *xfer);
 
+static void
+purple_xfer_priv_data_destroy(gpointer data)
+{
+	PurpleXferPrivData *priv = data;
+
+	if (priv->buffer)
+		g_byte_array_free(priv->buffer, TRUE);
+
+	g_free(priv->thumbnail_data);
+
+	g_free(priv->thumbnail_mimetype);
+
+	g_free(priv);
+}
+
+static const gchar *
+purple_xfer_status_type_to_string(PurpleXferStatusType type)
+{
+	static const struct {
+		PurpleXferStatusType type;
+		const char *name;
+	} type_names[] = {
+		{ PURPLE_XFER_STATUS_UNKNOWN, "unknown" },
+		{ PURPLE_XFER_STATUS_NOT_STARTED, "not started" },
+		{ PURPLE_XFER_STATUS_ACCEPTED, "accepted" },
+		{ PURPLE_XFER_STATUS_STARTED, "started" },
+		{ PURPLE_XFER_STATUS_DONE, "done" },
+		{ PURPLE_XFER_STATUS_CANCEL_LOCAL, "cancelled locally" },
+		{ PURPLE_XFER_STATUS_CANCEL_REMOTE, "cancelled remotely" }
+	};
+	int i;
+
+	for (i = 0; i < G_N_ELEMENTS(type_names); ++i)
+		if (type_names[i].type == type)
+			return type_names[i].name;
+
+	return "invalid state";
+}
+
 GList *
 purple_xfers_get_all()
 {
@@ -53,6 +118,7 @@
 {
 	PurpleXfer *xfer;
 	PurpleXferUiOps *ui_ops;
+	PurpleXferPrivData *priv;
 
 	g_return_val_if_fail(type    != PURPLE_XFER_UNKNOWN, NULL);
 	g_return_val_if_fail(account != NULL,              NULL);
@@ -65,17 +131,33 @@
 	xfer->type    = type;
 	xfer->account = account;
 	xfer->who     = g_strdup(who);
-	xfer->ui_ops  = purple_xfers_get_ui_ops();
+	xfer->ui_ops  = ui_ops = purple_xfers_get_ui_ops();
 	xfer->message = NULL;
 	xfer->current_buffer_size = FT_INITIAL_BUFFER_SIZE;
 	xfer->fd = -1;
 
+	priv = g_new0(PurpleXferPrivData, 1);
+	priv->ready = PURPLE_XFER_READY_NONE;
+
+	if (ui_ops && ui_ops->data_not_sent) {
+		/* If the ui will handle unsent data no need for buffer */
+		priv->buffer = NULL;
+	} else {
+		priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE);
+	}
+
+	g_hash_table_insert(xfers_data, xfer, priv);
+
 	ui_ops = purple_xfer_get_ui_ops(xfer);
 
 	if (ui_ops != NULL && ui_ops->new_xfer != NULL)
 		ui_ops->new_xfer(xfer);
 
 	xfers = g_list_prepend(xfers, xfer);
+
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "new %p [%d]\n", xfer, xfer->ref);
+
 	return xfer;
 }
 
@@ -86,6 +168,9 @@
 
 	g_return_if_fail(xfer != NULL);
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "destroyed %p [%d]\n", xfer, xfer->ref);
+
 	/* Close the file browser, if it's open */
 	purple_request_close_with_handle(xfer);
 
@@ -102,9 +187,11 @@
 	g_free(xfer->remote_ip);
 	g_free(xfer->local_filename);
 
+	g_hash_table_remove(xfers_data, xfer);
+
 	PURPLE_DBUS_UNREGISTER_POINTER(xfer);
+	xfers = g_list_remove(xfers, xfer);
 	g_free(xfer);
-	xfers = g_list_remove(xfers, xfer);
 }
 
 void
@@ -113,6 +200,9 @@
 	g_return_if_fail(xfer != NULL);
 
 	xfer->ref++;
+
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "ref'd %p [%d]\n", xfer, xfer->ref);
 }
 
 void
@@ -123,15 +213,26 @@
 
 	xfer->ref--;
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "unref'd %p [%d]\n", xfer, xfer->ref);
+
 	if (xfer->ref == 0)
 		purple_xfer_destroy(xfer);
 }
 
-static void
+void
 purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatusType status)
 {
 	g_return_if_fail(xfer != NULL);
 
+	if (purple_debug_is_verbose())
+		purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n",
+				xfer, purple_xfer_status_type_to_string(xfer->status),
+				purple_xfer_status_type_to_string(status));
+
+	if (xfer->status == status)
+		return;
+
 	xfer->status = status;
 
 	if(xfer->type == PURPLE_XFER_SEND) {
@@ -173,15 +274,21 @@
 	}
 }
 
-void purple_xfer_conversation_write(PurpleXfer *xfer, char *message, gboolean is_error)
+static void
+purple_xfer_conversation_write_internal(PurpleXfer *xfer,
+	const char *message, gboolean is_error, gboolean print_thumbnail)
 {
 	PurpleConversation *conv = NULL;
 	PurpleMessageFlags flags = PURPLE_MESSAGE_SYSTEM;
 	char *escaped;
+	gconstpointer thumbnail_data;
+	gsize size;
 
 	g_return_if_fail(xfer != NULL);
 	g_return_if_fail(message != NULL);
 
+	thumbnail_data = purple_xfer_get_thumbnail(xfer, &size);
+
 	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, xfer->who,
 											   purple_xfer_get_account(xfer));
 
@@ -191,12 +298,41 @@
 	escaped = g_markup_escape_text(message, -1);
 
 	if (is_error)
-		flags = PURPLE_MESSAGE_ERROR;
+		flags |= PURPLE_MESSAGE_ERROR;
+
+	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);
 
-	purple_conversation_write(conv, NULL, escaped, flags, time(NULL));
+		message_with_img =
+			g_strdup_printf("<img id='%d'> %s", id, escaped);
+		purple_conversation_write(conv, NULL, message_with_img, flags,
+			time(NULL));
+		purple_imgstore_unref_by_id(id);
+		g_free(message_with_img);
+	} else {
+		purple_conversation_write(conv, NULL, escaped, flags, time(NULL));
+	}
 	g_free(escaped);
 }
 
+void
+purple_xfer_conversation_write(PurpleXfer *xfer, gchar *message,
+	gboolean is_error)
+{
+	purple_xfer_conversation_write_internal(xfer, message, is_error, FALSE);
+}
+
+/* maybe this one should be exported publically? */
+static void
+purple_xfer_conversation_write_with_thumbnail(PurpleXfer *xfer,
+	const gchar *message)
+{
+	purple_xfer_conversation_write_internal(xfer, message, FALSE, TRUE);
+}
+
+
 static void purple_xfer_show_file_error(PurpleXfer *xfer, const char *filename)
 {
 	int err = errno;
@@ -230,14 +366,16 @@
 purple_xfer_choose_file_ok_cb(void *user_data, const char *filename)
 {
 	PurpleXfer *xfer;
+	PurpleXferType type;
 	struct stat st;
 	gchar *dir;
 
 	xfer = (PurpleXfer *)user_data;
+	type = purple_xfer_get_type(xfer);
 
 	if (g_stat(filename, &st) != 0) {
 		/* File not found. */
-		if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
+		if (type == PURPLE_XFER_RECEIVE) {
 #ifndef _WIN32
 			int mode = W_OK;
 #else
@@ -259,29 +397,26 @@
 		}
 		else {
 			purple_xfer_show_file_error(xfer, filename);
-			purple_xfer_request_denied(xfer);
+			purple_xfer_cancel_local(xfer);
 		}
 	}
-	else if ((purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) &&
-			 (st.st_size == 0)) {
+	else if ((type == PURPLE_XFER_SEND) && (st.st_size == 0)) {
 
 		purple_notify_error(NULL, NULL,
 						  _("Cannot send a file of 0 bytes."), NULL);
 
-		purple_xfer_request_denied(xfer);
+		purple_xfer_cancel_local(xfer);
 	}
-	else if ((purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) &&
-			 S_ISDIR(st.st_mode)) {
+	else if ((type == PURPLE_XFER_SEND) && S_ISDIR(st.st_mode)) {
 		/*
 		 * XXX - Sending a directory should be valid for some protocols.
 		 */
 		purple_notify_error(NULL, NULL,
 						  _("Cannot send a directory."), NULL);
 
-		purple_xfer_request_denied(xfer);
+		purple_xfer_cancel_local(xfer);
 	}
-	else if ((purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) &&
-			 S_ISDIR(st.st_mode)) {
+	else if ((type == PURPLE_XFER_RECEIVE) && S_ISDIR(st.st_mode)) {
 		char *msg, *utf8;
 		utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
 		msg = g_strdup_printf(
@@ -291,6 +426,23 @@
 		g_free(msg);
 		purple_xfer_request_denied(xfer);
 	}
+	else if (type == PURPLE_XFER_SEND) {
+#ifndef _WIN32
+		int mode = R_OK;
+#else
+		int mode = F_OK;
+#endif
+
+		if (g_access(filename, mode) == 0) {
+			purple_xfer_request_accepted(xfer, filename);
+		} else {
+			purple_xfer_ref(xfer);
+			purple_notify_message(
+				NULL, PURPLE_NOTIFY_MSG_ERROR, NULL,
+				_("File is not readable."), NULL,
+				(PurpleNotifyCloseCallback)purple_xfer_choose_file, xfer);
+		}
+	}
 	else {
 		purple_xfer_request_accepted(xfer, filename);
 	}
@@ -304,7 +456,11 @@
 	PurpleXfer *xfer = (PurpleXfer *)user_data;
 
 	purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
-	purple_xfer_request_denied(xfer);
+	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
+		purple_xfer_cancel_local(xfer);
+	else
+		purple_xfer_request_denied(xfer);
+	purple_xfer_unref(xfer);
 }
 
 static int
@@ -334,7 +490,9 @@
 purple_xfer_ask_recv(PurpleXfer *xfer)
 {
 	char *buf, *size_buf;
-	size_t size;
+	goffset size;
+	gconstpointer thumb;
+	gsize thumb_size;
 
 	/* If we have already accepted the request, ask the destination file
 	   name directly */
@@ -360,12 +518,18 @@
 			serv_got_im(purple_account_get_connection(xfer->account),
 								 xfer->who, xfer->message, 0, time(NULL));
 
-		purple_request_accept_cancel(xfer, NULL, buf, NULL,
-								  PURPLE_DEFAULT_ACTION_NONE,
-								  xfer->account, xfer->who, NULL,
-								  xfer,
-								  G_CALLBACK(purple_xfer_choose_file),
-								  G_CALLBACK(cancel_recv_cb));
+		if ((thumb = purple_xfer_get_thumbnail(xfer, &thumb_size))) {
+			purple_request_accept_cancel_with_icon(xfer, NULL, buf, NULL,
+				PURPLE_DEFAULT_ACTION_NONE, xfer->account, xfer->who, NULL,
+				thumb, thumb_size, xfer,
+				G_CALLBACK(purple_xfer_choose_file),
+				G_CALLBACK(cancel_recv_cb));
+		} else {
+			purple_request_accept_cancel(xfer, NULL, buf, NULL,
+				PURPLE_DEFAULT_ACTION_NONE, xfer->account, xfer->who, NULL,
+				xfer, G_CALLBACK(purple_xfer_choose_file),
+				G_CALLBACK(cancel_recv_cb));
+		}
 
 		g_free(buf);
 	} else
@@ -434,10 +598,12 @@
 		{
 			gchar* message = NULL;
 			PurpleBuddy *buddy = purple_find_buddy(xfer->account, xfer->who);
+
 			message = g_strdup_printf(_("%s is offering to send file %s"),
 				buddy ? purple_buddy_get_alias(buddy) : xfer->who, purple_xfer_get_filename(xfer));
-			purple_xfer_conversation_write(xfer, message, FALSE);
+			purple_xfer_conversation_write_with_thumbnail(xfer, message);
 			g_free(message);
+
 			/* Ask for a filename to save to if it's not already given by a plugin */
 			if (xfer->local_filename == NULL)
 				purple_xfer_ask_recv(xfer);
@@ -468,6 +634,8 @@
 	type = purple_xfer_get_type(xfer);
 	account = purple_xfer_get_account(xfer);
 
+	purple_debug_misc("xfer", "request accepted for %p\n", xfer);
+
 	if (!filename && type == PURPLE_XFER_RECEIVE) {
 		xfer->status = PURPLE_XFER_STATUS_ACCEPTED;
 		xfer->ops.init(xfer);
@@ -479,13 +647,16 @@
 	if (type == PURPLE_XFER_SEND) {
 		/* Sending a file */
 		/* Check the filename. */
+		PurpleXferUiOps *ui_ops;
+		ui_ops = purple_xfer_get_ui_ops(xfer);
+
 #ifdef _WIN32
 		if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\"))
 #else
 		if (g_strrstr(filename, "../"))
 #endif
 		{
-			char *utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
+			utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
 
 			msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8);
 			purple_xfer_error(type, account, xfer->who, msg);
@@ -496,15 +667,19 @@
 			return;
 		}
 
-		if (g_stat(filename, &st) == -1) {
-			purple_xfer_show_file_error(xfer, filename);
-			purple_xfer_unref(xfer);
-			return;
+		if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
+			if (g_stat(filename, &st) == -1) {
+				purple_xfer_show_file_error(xfer, filename);
+				purple_xfer_unref(xfer);
+				return;
+			}
+
+			purple_xfer_set_local_filename(xfer, filename);
+			purple_xfer_set_size(xfer, st.st_size);
+		} else {
+			purple_xfer_set_local_filename(xfer, filename);
 		}
 
-		purple_xfer_set_local_filename(xfer, filename);
-		purple_xfer_set_size(xfer, st.st_size);
-
 		base = g_path_get_basename(filename);
 		utf8 = g_filename_to_utf8(base, -1, NULL, NULL, NULL);
 		g_free(base);
@@ -513,7 +688,6 @@
 		msg = g_strdup_printf(_("Offering to send %s to %s"),
 				utf8, buddy ? purple_buddy_get_alias(buddy) : xfer->who);
 		g_free(utf8);
-
 		purple_xfer_conversation_write(xfer, msg, FALSE);
 		g_free(msg);
 	}
@@ -538,12 +712,28 @@
 {
 	g_return_if_fail(xfer != NULL);
 
+	purple_debug_misc("xfer", "xfer %p denied\n", xfer);
+
 	if (xfer->ops.request_denied != NULL)
 		xfer->ops.request_denied(xfer);
 
 	purple_xfer_unref(xfer);
 }
 
+int purple_xfer_get_fd(PurpleXfer *xfer)
+{
+	g_return_val_if_fail(xfer != NULL, 0);
+
+	return xfer->fd;
+}
+
+int purple_xfer_get_watcher(PurpleXfer *xfer)
+{
+	g_return_val_if_fail(xfer != NULL, 0);
+
+	return xfer->watcher;
+}
+
 PurpleXferType
 purple_xfer_get_type(const PurpleXfer *xfer)
 {
@@ -576,7 +766,7 @@
 }
 
 gboolean
-purple_xfer_is_canceled(const PurpleXfer *xfer)
+purple_xfer_is_cancelled(const PurpleXfer *xfer)
 {
 	g_return_val_if_fail(xfer != NULL, TRUE);
 
@@ -611,7 +801,7 @@
 	return xfer->local_filename;
 }
 
-size_t
+goffset
 purple_xfer_get_bytes_sent(const PurpleXfer *xfer)
 {
 	g_return_val_if_fail(xfer != NULL, 0);
@@ -619,7 +809,7 @@
 	return xfer->bytes_sent;
 }
 
-size_t
+goffset
 purple_xfer_get_bytes_remaining(const PurpleXfer *xfer)
 {
 	g_return_val_if_fail(xfer != NULL, 0);
@@ -627,7 +817,7 @@
 	return xfer->bytes_remaining;
 }
 
-size_t
+goffset
 purple_xfer_get_size(const PurpleXfer *xfer)
 {
 	g_return_val_if_fail(xfer != NULL, 0);
@@ -687,6 +877,20 @@
 	return xfer->end_time;
 }
 
+void purple_xfer_set_fd(PurpleXfer *xfer, int fd)
+{
+	g_return_if_fail(xfer != NULL);
+
+	xfer->fd = fd;
+}
+
+void purple_xfer_set_watcher(PurpleXfer *xfer, int watcher)
+{
+	g_return_if_fail(xfer != NULL);
+
+	xfer->watcher = watcher;
+}
+
 void
 purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed)
 {
@@ -761,7 +965,7 @@
 }
 
 void
-purple_xfer_set_size(PurpleXfer *xfer, size_t size)
+purple_xfer_set_size(PurpleXfer *xfer, goffset size)
 {
 	g_return_if_fail(xfer != NULL);
 
@@ -770,7 +974,15 @@
 }
 
 void
-purple_xfer_set_bytes_sent(PurpleXfer *xfer, size_t bytes_sent)
+purple_xfer_set_local_port(PurpleXfer *xfer, unsigned int local_port)
+{
+	g_return_if_fail(xfer != NULL);
+
+	xfer->local_port = local_port;
+}
+
+void
+purple_xfer_set_bytes_sent(PurpleXfer *xfer, goffset bytes_sent)
 {
 	g_return_if_fail(xfer != NULL);
 
@@ -881,9 +1093,6 @@
 
 	if (xfer->ops.read != NULL)	{
 		r = (xfer->ops.read)(buffer, xfer);
-		if ((purple_xfer_get_size(xfer) > 0) &&
-			((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)))
-			purple_xfer_set_completed(xfer, TRUE);
 	}
 	else {
 		*buffer = g_malloc0(s);
@@ -893,9 +1102,6 @@
 			r = 0;
 		else if (r < 0)
 			r = -1;
-		else if ((purple_xfer_get_size(xfer) > 0) &&
-			((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)))
-			purple_xfer_set_completed(xfer, TRUE);
 		else if (r == 0)
 			r = -1;
 	}
@@ -928,39 +1134,53 @@
 		r = write(xfer->fd, buffer, s);
 		if (r < 0 && errno == EAGAIN)
 			r = 0;
-		if ((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer))
-			purple_xfer_set_completed(xfer, TRUE);
 	}
+	if (r >= 0 && (purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer) &&
+		!purple_xfer_is_completed(xfer))
+		purple_xfer_set_completed(xfer, TRUE);
+	
 
 	return r;
 }
 
 static void
-transfer_cb(gpointer data, gint source, PurpleInputCondition condition)
+do_transfer(PurpleXfer *xfer)
 {
 	PurpleXferUiOps *ui_ops;
-	PurpleXfer *xfer = (PurpleXfer *)data;
 	guchar *buffer = NULL;
 	gssize r = 0;
 
-	if (condition & PURPLE_INPUT_READ) {
+	ui_ops = purple_xfer_get_ui_ops(xfer);
+
+	if (xfer->type == PURPLE_XFER_RECEIVE) {
 		r = purple_xfer_read(xfer, &buffer);
 		if (r > 0) {
-			const size_t wc = fwrite(buffer, 1, r, xfer->dest_fp);
+			size_t wc;
+			if (ui_ops && ui_ops->ui_write)
+				wc = ui_ops->ui_write(xfer, buffer, r);
+			else
+				wc = fwrite(buffer, 1, r, xfer->dest_fp);
+
 			if (wc != r) {
 				purple_debug_error("filetransfer", "Unable to write whole buffer.\n");
 				purple_xfer_cancel_local(xfer);
+				g_free(buffer);
 				return;
 			}
+
+			if ((purple_xfer_get_size(xfer) > 0) &&
+				((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer)))
+				purple_xfer_set_completed(xfer, TRUE);
 		} else if(r < 0) {
 			purple_xfer_cancel_remote(xfer);
+			g_free(buffer);
 			return;
 		}
-	}
-
-	if (condition & PURPLE_INPUT_WRITE) {
-		size_t result;
+	} else if (xfer->type == PURPLE_XFER_SEND) {
+		size_t result = 0;
 		size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size);
+		PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
+		gboolean read = TRUE;
 
 		/* this is so the prpl can keep the connection open
 		   if it needs to for some odd reason. */
@@ -972,33 +1192,88 @@
 			return;
 		}
 
-		buffer = g_malloc0(s);
-
-		result = fread(buffer, 1, s, xfer->dest_fp);
-		if (result != s) {
-			purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
-			purple_xfer_cancel_remote(xfer);
-			g_free(buffer);
-			return;
+		if (priv->buffer) {
+			if (priv->buffer->len < s) {
+				s -= priv->buffer->len;
+				read = TRUE;
+			} else {
+				read = FALSE;
+			}
 		}
 
-		/* Write as much as we're allowed to. */
-		r = purple_xfer_write(xfer, buffer, s);
+		if (read) {
+			if (ui_ops && ui_ops->ui_read) {
+				gssize tmp = ui_ops->ui_read(xfer, &buffer, s);
+				if (tmp == 0) {
+					/*
+					 * The UI claimed it was ready, but didn't have any data for
+					 * us...  It will call purple_xfer_ui_ready when ready, which
+					 * sets back up this watcher.
+					 */
+					if (xfer->watcher != 0) {
+						purple_input_remove(xfer->watcher);
+						xfer->watcher = 0;
+					}
+
+					/* Need to indicate the prpl is still ready... */
+					priv->ready |= PURPLE_XFER_READY_PRPL;
+
+					g_return_if_reached();
+				} else if (tmp < 0) {
+					purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
+					purple_xfer_cancel_local(xfer);
+					return;
+				}
+
+				result = tmp;
+			} else {
+				buffer = g_malloc(s);
+				result = fread(buffer, 1, s, xfer->dest_fp);
+				if (result != s) {
+					purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
+					purple_xfer_cancel_local(xfer);
+					g_free(buffer);
+					return;
+				}
+			}
+		}
+
+		if (priv->buffer) {
+			g_byte_array_append(priv->buffer, buffer, result);
+			g_free(buffer);
+			buffer = priv->buffer->data;
+			result = priv->buffer->len;
+		}
+
+		r = purple_xfer_write(xfer, buffer, result);
 
 		if (r == -1) {
 			purple_xfer_cancel_remote(xfer);
-			g_free(buffer);
+			if (!priv->buffer)
+				/* We don't free buffer if priv->buffer is set, because in
+				   that case buffer doesn't belong to us. */
+				g_free(buffer);
 			return;
-		} else if (r < s) {
-			/* We have to seek back in the file now. */
-			fseek(xfer->dest_fp, r - s, SEEK_CUR);
-		} else {
+		} else if (r == result) {
 			/*
 			 * We managed to write the entire buffer.  This means our
 			 * network is fast and our buffer is too small, so make it
 			 * bigger.
 			 */
 			purple_xfer_increase_buffer_size(xfer);
+		} else {
+			if (ui_ops && ui_ops->data_not_sent)
+				ui_ops->data_not_sent(xfer, buffer + r, result - r);
+		}
+
+		if (priv->buffer) {
+			/*
+			 * Remove what we wrote
+			 * If we wrote the whole buffer the byte array will be empty
+			 * Otherwise we'll keep what wasn't sent for next time.
+			 */
+			buffer = NULL;
+			g_byte_array_remove_range(priv->buffer, 0, r);
 		}
 	}
 
@@ -1013,8 +1288,6 @@
 
 		g_free(buffer);
 
-		ui_ops = purple_xfer_get_ui_ops(xfer);
-
 		if (ui_ops != NULL && ui_ops->update_progress != NULL)
 			ui_ops->update_progress(xfer,
 				purple_xfer_get_progress(xfer));
@@ -1025,22 +1298,54 @@
 }
 
 static void
+transfer_cb(gpointer data, gint source, PurpleInputCondition condition)
+{
+	PurpleXfer *xfer = data;
+
+	if (xfer->dest_fp == NULL) {
+		/* The UI is moderating its side manually */
+		PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
+		if (0 == (priv->ready & PURPLE_XFER_READY_UI)) {
+			priv->ready |= PURPLE_XFER_READY_PRPL;
+
+			purple_input_remove(xfer->watcher);
+			xfer->watcher = 0;
+
+			purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
+			return;
+		}
+
+		priv->ready = PURPLE_XFER_READY_NONE;
+	}
+
+	do_transfer(xfer);
+}
+
+static void
 begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond)
 {
 	PurpleXferType type = purple_xfer_get_type(xfer);
-
-	xfer->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer),
-						  type == PURPLE_XFER_RECEIVE ? "wb" : "rb");
+	PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer);
 
-	if (xfer->dest_fp == NULL) {
-		purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
-		purple_xfer_cancel_local(xfer);
-		return;
+	if (xfer->start_time != 0) {
+		purple_debug_error("xfer", "Transfer is being started multiple times\n");
+		g_return_if_reached();
 	}
 
-	fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET);
+	if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
+		xfer->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer),
+		                        type == PURPLE_XFER_RECEIVE ? "wb" : "rb");
 
-	if (xfer->fd)
+		if (xfer->dest_fp == NULL) {
+			purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
+			purple_xfer_cancel_local(xfer);
+			return;
+		}
+
+		fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET);
+	}
+
+	if (xfer->fd != -1)
 		xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer);
 
 	xfer->start_time = time(NULL);
@@ -1065,6 +1370,62 @@
 }
 
 void
+purple_xfer_ui_ready(PurpleXfer *xfer)
+{
+	PurpleInputCondition cond;
+	PurpleXferType type;
+	PurpleXferPrivData *priv;
+
+	g_return_if_fail(xfer != NULL);
+
+	priv = g_hash_table_lookup(xfers_data, xfer);
+	priv->ready |= PURPLE_XFER_READY_UI;
+
+	if (0 == (priv->ready & PURPLE_XFER_READY_PRPL)) {
+		purple_debug_misc("xfer", "UI is ready on ft %p, waiting for prpl\n", xfer);
+		return;
+	}
+
+	purple_debug_misc("xfer", "UI (and prpl) ready on ft %p, so proceeding\n", xfer);
+
+	type = purple_xfer_get_type(xfer);
+	if (type == PURPLE_XFER_SEND)
+		cond = PURPLE_INPUT_WRITE;
+	else /* if (type == PURPLE_XFER_RECEIVE) */
+		cond = PURPLE_INPUT_READ;
+
+	if (xfer->watcher == 0 && xfer->fd != -1)
+		xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer);
+
+	priv->ready = PURPLE_XFER_READY_NONE;
+
+	do_transfer(xfer);
+}
+
+void
+purple_xfer_prpl_ready(PurpleXfer *xfer)
+{
+	PurpleXferPrivData *priv;
+
+	g_return_if_fail(xfer != NULL);
+
+	priv = g_hash_table_lookup(xfers_data, xfer);
+	priv->ready |= PURPLE_XFER_READY_PRPL;
+
+	/* I don't think fwrite/fread are ever *not* ready */
+	if (xfer->dest_fp == NULL && 0 == (priv->ready & PURPLE_XFER_READY_UI)) {
+		purple_debug_misc("xfer", "prpl is ready on ft %p, waiting for UI\n", xfer);
+		return;
+	}
+
+	purple_debug_misc("xfer", "Prpl (and UI) ready on ft %p, so proceeding\n", xfer);
+
+	priv->ready = PURPLE_XFER_READY_NONE;
+
+	do_transfer(xfer);
+}
+
+void
 purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip,
 				unsigned int port)
 {
@@ -1124,7 +1485,7 @@
 		xfer->watcher = 0;
 	}
 
-	if (xfer->fd != 0)
+	if (xfer->fd != -1)
 		close(xfer->fd);
 
 	if (xfer->dest_fp != NULL) {
@@ -1156,12 +1517,26 @@
 
 	g_return_if_fail(xfer != NULL);
 
+	/* TODO: We definitely want to close any open request dialogs associated
+	   with this transfer.  However, in some cases the request dialog might
+	   own a reference on the xfer.  This happens at least with the "%s wants
+	   to send you %s" dialog from purple_xfer_ask_recv().  In these cases
+	   the ref count will not be decremented when the request dialog is
+	   closed, so the ref count will never reach 0 and the xfer will never
+	   be freed.  This is a memleak and should be fixed.  It's not clear what
+	   the correct fix is.  Probably requests should have a destroy function
+	   that is called when the request is destroyed.  But also, ref counting
+	   xfer objects makes this code REALLY complicated.  An alternate fix is
+	   to not ref count and instead just make sure the object still exists
+	   when we try to use it. */
+	purple_request_close_with_handle(xfer);
+
 	purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
 	xfer->end_time = time(NULL);
 
 	if (purple_xfer_get_filename(xfer) != NULL)
 	{
-		msg = g_strdup_printf(_("You canceled the transfer of %s"),
+		msg = g_strdup_printf(_("You cancelled the transfer of %s"),
 							  purple_xfer_get_filename(xfer));
 	}
 	else
@@ -1187,7 +1562,7 @@
 		xfer->watcher = 0;
 	}
 
-	if (xfer->fd != 0)
+	if (xfer->fd != -1)
 		close(xfer->fd);
 
 	if (xfer->dest_fp != NULL) {
@@ -1224,12 +1599,12 @@
 
 	if (purple_xfer_get_filename(xfer) != NULL)
 	{
-		msg = g_strdup_printf(_("%s canceled the transfer of %s"),
+		msg = g_strdup_printf(_("%s cancelled the transfer of %s"),
 				buddy ? purple_buddy_get_alias(buddy) : xfer->who, purple_xfer_get_filename(xfer));
 	}
 	else
 	{
-		msg = g_strdup_printf(_("%s canceled the file transfer"),
+		msg = g_strdup_printf(_("%s cancelled the file transfer"),
 				buddy ? purple_buddy_get_alias(buddy) : xfer->who);
 	}
 	purple_xfer_conversation_write(xfer, msg, TRUE);
@@ -1252,7 +1627,7 @@
 		xfer->watcher = 0;
 	}
 
-	if (xfer->fd != 0)
+	if (xfer->fd != -1)
 		close(xfer->fd);
 
 	if (xfer->dest_fp != NULL) {
@@ -1307,6 +1682,83 @@
 		ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
 }
 
+gconstpointer
+purple_xfer_get_thumbnail(const PurpleXfer *xfer, gsize *len)
+{
+	PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
+
+	if (len)
+		*len = priv->thumbnail_size;
+
+	return priv->thumbnail_data;
+}
+
+const gchar *
+purple_xfer_get_thumbnail_mimetype(const PurpleXfer *xfer)
+{
+	PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
+
+	return priv->thumbnail_mimetype;
+}
+
+void
+purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
+	gsize size, const gchar *mimetype)
+{
+	PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
+
+	g_free(priv->thumbnail_data);
+	g_free(priv->thumbnail_mimetype);
+
+	if (thumbnail && size > 0) {
+		priv->thumbnail_data = g_memdup(thumbnail, size);
+		priv->thumbnail_size = size;
+		priv->thumbnail_mimetype = g_strdup(mimetype);
+	} else {
+		priv->thumbnail_data = NULL;
+		priv->thumbnail_size = 0;
+		priv->thumbnail_mimetype = NULL;
+	}
+}
+
+void
+purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats)
+{
+	if (xfer->ui_ops->add_thumbnail) {
+		xfer->ui_ops->add_thumbnail(xfer, formats);
+	}
+}
+
+void
+purple_xfer_set_protocol_data(PurpleXfer *xfer, gpointer proto_data)
+{
+	g_return_if_fail(xfer != NULL);
+
+	xfer->proto_data = proto_data;
+}
+
+gpointer
+purple_xfer_get_protocol_data(const PurpleXfer *xfer)
+{
+	g_return_val_if_fail(xfer != NULL, NULL);
+
+	return xfer->proto_data;
+}
+
+void purple_xfer_set_ui_data(PurpleXfer *xfer, gpointer ui_data)
+{
+	g_return_if_fail(xfer != NULL);
+
+	xfer->ui_data = ui_data;
+}
+
+gpointer purple_xfer_get_ui_data(const PurpleXfer *xfer)
+{
+	g_return_val_if_fail(xfer != NULL, NULL);
+
+	return xfer->ui_data;
+}
+
 
 /**************************************************************************
  * File Transfer Subsystem API
@@ -1322,6 +1774,9 @@
 purple_xfers_init(void) {
 	void *handle = purple_xfers_get_handle();
 
+	xfers_data = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+	                                   NULL, purple_xfer_priv_data_destroy);
+
 	/* register signals */
 	purple_signal_register(handle, "file-recv-accept",
 	                     purple_marshal_VOID__POINTER, NULL, 1,
@@ -1368,6 +1823,9 @@
 
 	purple_signals_disconnect_by_handle(handle);
 	purple_signals_unregister_by_instance(handle);
+
+	g_hash_table_destroy(xfers_data);
+	xfers_data = NULL;
 }
 
 void
--- a/libpurple/ft.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/ft.h	Wed Jun 13 19:30:27 2012 -0400
@@ -58,8 +58,8 @@
 	PURPLE_XFER_STATUS_ACCEPTED,      /**< Receive accepted, but destination file not selected yet */
 	PURPLE_XFER_STATUS_STARTED,       /**< purple_xfer_start has been called. */
 	PURPLE_XFER_STATUS_DONE,          /**< The xfer completed successfully. */
-	PURPLE_XFER_STATUS_CANCEL_LOCAL,  /**< The xfer was canceled by us. */
-	PURPLE_XFER_STATUS_CANCEL_REMOTE  /**< The xfer was canceled by the other end, or we couldn't connect. */
+	PURPLE_XFER_STATUS_CANCEL_LOCAL,  /**< The xfer was cancelled by us. */
+	PURPLE_XFER_STATUS_CANCEL_REMOTE  /**< The xfer was cancelled by the other end, or we couldn't connect. */
 } PurpleXferStatusType;
 
 /**
@@ -77,10 +77,51 @@
 	void (*cancel_local)(PurpleXfer *xfer);
 	void (*cancel_remote)(PurpleXfer *xfer);
 
-	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
+	/**
+	 * UI op to write data received from the prpl. The UI must deal with the
+	 * entire buffer and return size, or it is treated as an error.
+	 *
+	 * @param xfer    The file transfer structure
+	 * @param buffer  The buffer to write
+	 * @param size    The size of the buffer
+	 *
+	 * @return size if the write was successful, or a value between 0 and
+	 *         size on error.
+	 */
+	gssize (*ui_write)(PurpleXfer *xfer, const guchar *buffer, gssize size);
+
+	/**
+	 * UI op to read data to send to the prpl for a file transfer.
+	 *
+	 * @param xfer    The file transfer structure
+	 * @param buffer  A pointer to a buffer. The UI must allocate this buffer.
+	 *                libpurple will free the data.
+	 * @param size    The maximum amount of data to put in the buffer.
+	 *
+	 * @returns The amount of data in the buffer, 0 if nothing is available,
+	 *          and a negative value if an error occurred and the transfer
+	 *          should be cancelled (libpurple will cancel).
+	 */
+	gssize (*ui_read)(PurpleXfer *xfer, guchar **buffer, gssize size);
+
+	/**
+	 * Op to notify the UI that not all the data read in was written. The UI
+	 * should re-enqueue this data and return it the next time read is called.
+	 *
+	 * This MUST be implemented if read and write are implemented.
+	 *
+	 * @param xfer    The file transfer structure
+	 * @param buffer  A pointer to the beginning of the unwritten data.
+	 * @param size    The amount of unwritten data.
+	 */
+	void (*data_not_sent)(PurpleXfer *xfer, const guchar *buffer, gsize size);
+
+	/**
+	 * Op to create a thumbnail image for a file transfer
+	 *
+	 * @param xfer   The file transfer structure
+	 */
+	void (*add_thumbnail)(PurpleXfer *xfer, const gchar *formats);
 } PurpleXferUiOps;
 
 /**
@@ -99,7 +140,7 @@
 	char *message;                /**< A message sent with the request     */
 	char *filename;               /**< The name sent over the network.     */
 	char *local_filename;         /**< The name on the local hard drive.   */
-	size_t size;                  /**< The size of the file.               */
+	goffset size;                 /**< The size of the file.               */
 
 	FILE *dest_fp;                /**< The destination file pointer.       */
 
@@ -110,8 +151,8 @@
 	int fd;                       /**< The socket file descriptor.         */
 	int watcher;                  /**< Watcher.                            */
 
-	size_t bytes_sent;            /**< The number of bytes sent.           */
-	size_t bytes_remaining;       /**< The number of bytes remaining.      */
+	goffset bytes_sent;           /**< The number of bytes sent.           */
+	goffset bytes_remaining;      /**< The number of bytes remaining.      */
 	time_t start_time;            /**< When the transfer of data began.    */
 	time_t end_time;              /**< When the transfer of data ended.    */
 
@@ -120,7 +161,10 @@
 
 	PurpleXferStatusType status;    /**< File Transfer's status.             */
 
-	/* I/O operations. */
+	/** I/O operations, which should be set by the prpl using
+	 *  purple_xfer_set_init_fnc() and friends.  Setting #init is
+	 *  mandatory; all others are optional.
+	 */
 	struct
 	{
 		void (*init)(PurpleXfer *xfer);
@@ -132,18 +176,15 @@
 		gssize (*read)(guchar **buffer, PurpleXfer *xfer);
 		gssize (*write)(const guchar *buffer, size_t size, PurpleXfer *xfer);
 		void (*ack)(PurpleXfer *xfer, const guchar *buffer, size_t size);
-
 	} ops;
 
 	PurpleXferUiOps *ui_ops;            /**< UI-specific operations. */
 	void *ui_data;                    /**< UI-specific data.       */
 
-	void *data;                       /**< prpl-specific data.     */
+	void *proto_data;                 /**< prpl-specific data.     */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name File Transfer API                                               */
@@ -219,6 +260,24 @@
 void purple_xfer_request_denied(PurpleXfer *xfer);
 
 /**
+ * Returns the socket file descriptor.
+ *
+ * @param xfer The file transfer.
+ *
+ * @return The socket file descriptor.
+ */
+int purple_xfer_get_fd(PurpleXfer *xfer);
+
+/**
+ * Returns the Watcher for the transfer.
+ *
+ * @param xfer The file transfer.
+ *
+ * @return The watcher.
+ */
+int purple_xfer_get_watcher(PurpleXfer *xfer);
+
+/**
  * Returns the type of file transfer.
  *
  * @param xfer The file transfer.
@@ -242,8 +301,6 @@
  * @param xfer The file transfer.
  *
  * @return The name of the remote user.
- *
- * @since 2.1.0
  */
 const char *purple_xfer_get_remote_user(const PurpleXfer *xfer);
 
@@ -257,13 +314,13 @@
 PurpleXferStatusType purple_xfer_get_status(const PurpleXfer *xfer);
 
 /**
- * Returns true if the file transfer was canceled.
+ * Returns true if the file transfer was cancelled.
  *
  * @param xfer The file transfer.
  *
- * @return Whether or not the transfer was canceled.
+ * @return Whether or not the transfer was cancelled.
  */
-gboolean purple_xfer_is_canceled(const PurpleXfer *xfer);
+gboolean purple_xfer_is_cancelled(const PurpleXfer *xfer);
 
 /**
  * Returns the completed state for a file transfer.
@@ -299,7 +356,7 @@
  *
  * @return The number of bytes sent.
  */
-size_t purple_xfer_get_bytes_sent(const PurpleXfer *xfer);
+goffset purple_xfer_get_bytes_sent(const PurpleXfer *xfer);
 
 /**
  * Returns the number of bytes remaining to send or receive.
@@ -308,7 +365,7 @@
  *
  * @return The number of bytes remaining.
  */
-size_t purple_xfer_get_bytes_remaining(const PurpleXfer *xfer);
+goffset purple_xfer_get_bytes_remaining(const PurpleXfer *xfer);
 
 /**
  * Returns the size of the file being sent or received.
@@ -317,7 +374,7 @@
  *
  * @return The total size of the file.
  */
-size_t purple_xfer_get_size(const PurpleXfer *xfer);
+goffset purple_xfer_get_size(const PurpleXfer *xfer);
 
 /**
  * Returns the current percentage of progress of the transfer.
@@ -363,7 +420,6 @@
  * @param xfer  The file transfer.
  *
  * @return The time when the transfer started.
- * @since 2.4.0
  */
 time_t purple_xfer_get_start_time(const PurpleXfer *xfer);
 
@@ -373,11 +429,26 @@
  * @param xfer  The file transfer.
  *
  * @return The time when the transfer ended.
- * @since 2.4.0
  */
 time_t purple_xfer_get_end_time(const PurpleXfer *xfer);
 
 /**
+ * Sets the socket file descriptor.
+ *
+ * @param xfer      The file transfer.
+ * @param fd        The file descriptor.
+ */
+void purple_xfer_set_fd(PurpleXfer *xfer, int fd);
+
+/**
+ * Sets the watcher for the file transfer.
+ *
+ * @param xfer      The file transfer.
+ * @param watcher   The watcher.
+ */
+void purple_xfer_set_watcher(PurpleXfer *xfer, int watcher);
+
+/**
  * Sets the completed state for the file transfer.
  *
  * @param xfer      The file transfer.
@@ -386,6 +457,14 @@
 void purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed);
 
 /**
+ * Sets the current status for the file transfer.
+ *
+ * @param xfer      The file transfer.
+ * @param status    The current status.
+ */
+void purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatusType status);
+
+/**
  * Sets the filename for the file transfer.
  *
  * @param xfer     The file transfer.
@@ -415,7 +494,15 @@
  * @param xfer The file transfer.
  * @param size The size of the file.
  */
-void purple_xfer_set_size(PurpleXfer *xfer, size_t size);
+void purple_xfer_set_size(PurpleXfer *xfer, goffset size);
+
+/**
+ * Sets the local port of the file transfer.
+ *
+ * @param xfer          The file transfer.
+ * @param local_port    The local port.
+ */
+void purple_xfer_set_local_port(PurpleXfer *xfer, unsigned int local_port);
 
 /**
  * Sets the current working position in the active file transfer.  This
@@ -430,7 +517,7 @@
  *                   send.  If we're receiving a file, this is the
  *                   next byte that we expect to receive.
  */
-void purple_xfer_set_bytes_sent(PurpleXfer *xfer, size_t bytes_sent);
+void purple_xfer_set_bytes_sent(PurpleXfer *xfer, goffset bytes_sent);
 
 /**
  * Returns the UI operations structure for a file transfer.
@@ -548,6 +635,9 @@
  * file receive transfer. On send, @a fd must be specified, and
  * @a ip and @a port are ignored.
  *
+ * Passing @a fd as '-1' is a special-case and indicates to the
+ * protocol plugin to facilitate the file transfer itself.
+ *
  * @param xfer The file transfer.
  * @param fd   The file descriptor for the socket.
  * @param ip   The IP address to connect to.
@@ -617,6 +707,100 @@
  */
 void purple_xfer_conversation_write(PurpleXfer *xfer, char *message, gboolean is_error);
 
+/**
+ * Allows the UI to signal it's ready to send/receive data (depending on
+ * the direction of the file transfer. Used when the UI is providing
+ * read/write/data_not_sent UI ops.
+ *
+ * @param xfer The file transfer which is ready.
+ */
+void purple_xfer_ui_ready(PurpleXfer *xfer);
+
+/**
+ * Allows the prpl to signal it's ready to send/receive data (depending on
+ * the direction of the file transfer. Used when the prpl provides read/write
+ * ops and cannot/does not provide a raw fd to the core.
+ *
+ * @param xfer The file transfer which is ready.
+ */
+void purple_xfer_prpl_ready(PurpleXfer *xfer);
+
+/**
+ * Gets the thumbnail data for a transfer
+ *
+ * @param xfer The file transfer to get the thumbnail for
+ * @param len  If not @c NULL, the length of the thumbnail data returned
+ *             will be set in the location pointed to by this.
+ * @return The thumbnail data, or NULL if there is no thumbnail
+ */
+gconstpointer purple_xfer_get_thumbnail(const PurpleXfer *xfer, gsize *len);
+
+/**
+ * Gets the mimetype of the thumbnail preview for a transfer
+ *
+ * @param xfer The file transfer to get the mimetype for
+ * @return The mimetype of the thumbnail, or @c NULL if not thumbnail is set
+ */
+const gchar *purple_xfer_get_thumbnail_mimetype(const PurpleXfer *xfer);
+
+
+/**
+ * Sets the thumbnail data for a transfer
+ *
+ * @param xfer The file transfer to set the data for
+ * @param thumbnail A pointer to the thumbnail data, this will be copied
+ * @param size The size in bytes of the passed in thumbnail data
+ * @param mimetype The mimetype of the generated thumbnail
+ */
+void purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
+	gsize size, const gchar *mimetype);
+
+/**
+ * Prepare a thumbnail for a transfer (if the UI supports it)
+ * will be no-op in case the UI doesn't implement thumbnail creation
+ *
+ * @param xfer The file transfer to create a thumbnail for
+ * @param formats A comma-separated list of mimetypes for image formats
+ *	 	  the protocols can use for thumbnails.
+ */
+void purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats);
+
+/**
+ * Sets the protocol data for a file transfer.
+ *
+ * @param xfer			The file transfer.
+ * @param proto_data	The protocol data to set for the file transfer.
+ */
+void purple_xfer_set_protocol_data(PurpleXfer *xfer, gpointer proto_data);
+ 
+/**
+ * Gets the protocol data for a file transfer.
+ *
+ * @param xfer			The file transfer.
+ *
+ * @return The protocol data for the file transfer.
+ */
+gpointer purple_xfer_get_protocol_data(const PurpleXfer *xfer);
+
+/**
+ * Set the UI data associated with this file transfer.
+ *
+ * @param xfer			The file transfer.
+ * @param ui_data		A pointer to associate with this file transfer.
+ */
+void purple_xfer_set_ui_data(PurpleXfer *xfer, gpointer ui_data);
+
+/**
+ * Get the UI data associated with this file transfer.
+ *
+ * @param xfer			The file transfer.
+ *
+ * @return The UI data associated with this file transfer.  This is a
+ *         convenience field provided to the UIs--it is not
+ *         used by the libpurple core.
+ */
+gpointer purple_xfer_get_ui_data(const PurpleXfer *xfer);
+
 /*@}*/
 
 /**************************************************************************/
@@ -657,8 +841,7 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_FT_H_ */
+
--- a/libpurple/gaim-compat.h	Wed Jun 13 19:28:57 2012 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2317 +0,0 @@
-/**
- * @file gaim-compat.h Gaim Compat macros
- * @ingroup core
- */
-
-/* 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 _GAIM_COMPAT_H_
-#define _GAIM_COMPAT_H_
-
-#include <glib.h>
-
-/* from account.h */
-#define GaimAccountUiOps PurpleAccountUiOps
-#define GaimAccount PurpleAccount
-
-#define GaimFilterAccountFunc PurpleFilterAccountFunc
-#define GaimAccountRequestAuthorizationCb PurpleAccountRequestAuthorizationCb
-
-#define gaim_account_new           purple_account_new
-#define gaim_account_destroy       purple_account_destroy
-#define gaim_account_connect       purple_account_connect
-#define gaim_account_register      purple_account_register
-#define gaim_account_disconnect    purple_account_disconnect
-#define gaim_account_notify_added  purple_account_notify_added
-#define gaim_account_request_add   purple_account_request_add
-#define gaim_account_request_close   purple_account_request_close
-
-#define gaim_account_request_authorization     purple_account_request_authorization
-#define gaim_account_request_change_password   purple_account_request_change_password
-#define gaim_account_request_change_user_info  purple_account_request_change_user_info
-
-#define gaim_account_set_username            purple_account_set_username
-#define gaim_account_set_password            purple_account_set_password
-#define gaim_account_set_alias               purple_account_set_alias
-#define gaim_account_set_user_info           purple_account_set_user_info
-#define gaim_account_set_buddy_icon_path     purple_account_set_buddy_icon_path
-#define gaim_account_set_protocol_id         purple_account_set_protocol_id
-#define gaim_account_set_connection          purple_account_set_connection
-#define gaim_account_set_remember_password   purple_account_set_remember_password
-#define gaim_account_set_check_mail          purple_account_set_check_mail
-#define gaim_account_set_enabled             purple_account_set_enabled
-#define gaim_account_set_proxy_info          purple_account_set_proxy_info
-#define gaim_account_set_status_types        purple_account_set_status_types
-#define gaim_account_set_status              purple_account_set_status
-#define gaim_account_set_status_list         purple_account_set_status_list
-
-#define gaim_account_clear_settings   purple_account_clear_settings
-
-#define gaim_account_set_int    purple_account_set_int
-#define gaim_account_set_string purple_account_set_string
-#define gaim_account_set_bool   purple_account_set_bool
-
-#define gaim_account_set_ui_int     purple_account_set_ui_int
-#define gaim_account_set_ui_string  purple_account_set_ui_string
-#define gaim_account_set_ui_bool    purple_account_set_ui_bool
-
-#define gaim_account_is_connected     purple_account_is_connected
-#define gaim_account_is_connecting    purple_account_is_connecting
-#define gaim_account_is_disconnected  purple_account_is_disconnected
-
-#define gaim_account_get_username           purple_account_get_username
-#define gaim_account_get_password           purple_account_get_password
-#define gaim_account_get_alias              purple_account_get_alias
-#define gaim_account_get_user_info          purple_account_get_user_info
-#define gaim_account_get_buddy_icon_path    purple_account_get_buddy_icon_path
-#define gaim_account_get_protocol_id        purple_account_get_protocol_id
-#define gaim_account_get_protocol_name      purple_account_get_protocol_name
-#define gaim_account_get_connection         purple_account_get_connection
-#define gaim_account_get_remember_password  purple_account_get_remember_password
-#define gaim_account_get_check_mail         purple_account_get_check_mail
-#define gaim_account_get_enabled            purple_account_get_enabled
-#define gaim_account_get_proxy_info         purple_account_get_proxy_info
-#define gaim_account_get_active_status      purple_account_get_active_status
-#define gaim_account_get_status             purple_account_get_status
-#define gaim_account_get_status_type        purple_account_get_status_type
-#define gaim_account_get_status_type_with_primitive \
-	purple_account_get_status_type_with_primitive
-
-#define gaim_account_get_presence       purple_account_get_presence
-#define gaim_account_is_status_active   purple_account_is_status_active
-#define gaim_account_get_status_types   purple_account_get_status_types
-
-#define gaim_account_get_int            purple_account_get_int
-#define gaim_account_get_string         purple_account_get_string
-#define gaim_account_get_bool           purple_account_get_bool
-
-#define gaim_account_get_ui_int     purple_account_get_ui_int
-#define gaim_account_get_ui_string  purple_account_get_ui_string
-#define gaim_account_get_ui_bool    purple_account_get_ui_bool
-
-
-#define gaim_account_get_log      purple_account_get_log
-#define gaim_account_destroy_log  purple_account_destroy_log
-
-#define gaim_account_add_buddy       purple_account_add_buddy
-#define gaim_account_add_buddies     purple_account_add_buddies
-#define gaim_account_remove_buddy    purple_account_remove_buddy
-#define gaim_account_remove_buddies  purple_account_remove_buddies
-
-#define gaim_account_remove_group  purple_account_remove_group
-
-#define gaim_account_change_password  purple_account_change_password
-
-#define gaim_account_supports_offline_message  purple_account_supports_offline_message
-
-#define gaim_accounts_add      purple_accounts_add
-#define gaim_accounts_remove   purple_accounts_remove
-#define gaim_accounts_delete   purple_accounts_delete
-#define gaim_accounts_reorder  purple_accounts_reorder
-
-#define gaim_accounts_get_all         purple_accounts_get_all
-#define gaim_accounts_get_all_active  purple_accounts_get_all_active
-
-#define gaim_accounts_find   purple_accounts_find
-
-#define gaim_accounts_restore_current_statuses  purple_accounts_restore_current_statuses
-
-#define gaim_accounts_set_ui_ops  purple_accounts_set_ui_ops
-#define gaim_accounts_get_ui_ops  purple_accounts_get_ui_ops
-
-#define gaim_accounts_get_handle  purple_accounts_get_handle
-
-#define gaim_accounts_init    purple_accounts_init
-#define gaim_accounts_uninit  purple_accounts_uninit
-
-/* from accountopt.h */
-
-#define GaimAccountOption     PurpleAccountOption
-#define GaimAccountUserSplit  PurpleAccountUserSplit
-
-#define gaim_account_option_new         purple_account_option_new
-#define gaim_account_option_bool_new    purple_account_option_bool_new
-#define gaim_account_option_int_new     purple_account_option_int_new
-#define gaim_account_option_string_new  purple_account_option_string_new
-#define gaim_account_option_list_new    purple_account_option_list_new
-
-#define gaim_account_option_destroy  purple_account_option_destroy
-
-#define gaim_account_option_set_default_bool    purple_account_option_set_default_bool
-#define gaim_account_option_set_default_int     purple_account_option_set_default_int
-#define gaim_account_option_set_default_string  purple_account_option_set_default_string
-
-#define gaim_account_option_set_masked  purple_account_option_set_masked
-
-#define gaim_account_option_set_list  purple_account_option_set_list
-
-#define gaim_account_option_add_list_item  purple_account_option_add_list_item
-
-#define gaim_account_option_get_type     purple_account_option_get_type
-#define gaim_account_option_get_text     purple_account_option_get_text
-#define gaim_account_option_get_setting  purple_account_option_get_setting
-
-#define gaim_account_option_get_default_bool        purple_account_option_get_default_bool
-#define gaim_account_option_get_default_int         purple_account_option_get_default_int
-#define gaim_account_option_get_default_string      purple_account_option_get_default_string
-#define gaim_account_option_get_default_list_value  purple_account_option_get_default_list_value
-
-#define gaim_account_option_get_masked  purple_account_option_get_masked
-#define gaim_account_option_get_list    purple_account_option_get_list
-
-#define gaim_account_user_split_new      purple_account_user_split_new
-#define gaim_account_user_split_destroy  purple_account_user_split_destroy
-
-#define gaim_account_user_split_get_text           purple_account_user_split_get_text
-#define gaim_account_user_split_get_default_value  purple_account_user_split_get_default_value
-#define gaim_account_user_split_get_separator      purple_account_user_split_get_separator
-
-/* from blist.h */
-
-#define GaimBuddyList    PurpleBuddyList
-#define GaimBlistUiOps   PurpleBlistUiOps
-#define GaimBlistNode    PurpleBlistNode
-
-#define GaimChat     PurpleChat
-#define GaimGroup    PurpleGroup
-#define GaimContact  PurpleContact
-#define GaimBuddy    PurpleBuddy
-
-#define GAIM_BLIST_GROUP_NODE     PURPLE_BLIST_GROUP_NODE
-#define GAIM_BLIST_CONTACT_NODE   PURPLE_BLIST_CONTACT_NODE
-#define GAIM_BLIST_BUDDY_NODE     PURPLE_BLIST_BUDDY_NODE
-#define GAIM_BLIST_CHAT_NODE      PURPLE_BLIST_CHAT_NODE
-#define GAIM_BLIST_OTHER_NODE     PURPLE_BLIST_OTHER_NODE
-#define GaimBlistNodeType         PurpleBlistNodeType
-
-#define GAIM_BLIST_NODE_IS_CHAT       PURPLE_BLIST_NODE_IS_CHAT
-#define GAIM_BLIST_NODE_IS_BUDDY      PURPLE_BLIST_NODE_IS_BUDDY
-#define GAIM_BLIST_NODE_IS_CONTACT    PURPLE_BLIST_NODE_IS_CONTACT
-#define GAIM_BLIST_NODE_IS_GROUP      PURPLE_BLIST_NODE_IS_GROUP
-
-#define GAIM_BUDDY_IS_ONLINE PURPLE_BUDDY_IS_ONLINE
-
-#define GAIM_BLIST_NODE_FLAG_NO_SAVE  PURPLE_BLIST_NODE_FLAG_NO_SAVE
-#define GaimBlistNodeFlags            PurpleBlistNodeFlags
-
-#define GAIM_BLIST_NODE_HAS_FLAG     PURPLE_BLIST_NODE_HAS_FLAG
-#define GAIM_BLIST_NODE_SHOULD_SAVE  PURPLE_BLIST_NODE_SHOULD_SAVE
-
-#define GAIM_BLIST_NODE_NAME   PURPLE_BLIST_NODE_NAME
-
-
-#define gaim_blist_new  purple_blist_new
-#define gaim_set_blist  purple_set_blist
-#define gaim_get_blist  purple_get_blist
-
-#define gaim_blist_get_root   purple_blist_get_root
-#define gaim_blist_node_next  purple_blist_node_next
-
-#define gaim_blist_show  purple_blist_show
-
-#define gaim_blist_destroy  purple_blist_destroy
-
-#define gaim_blist_set_visible  purple_blist_set_visible
-
-#define gaim_blist_update_buddy_status  purple_blist_update_buddy_status
-#define gaim_blist_update_buddy_icon    purple_blist_update_buddy_icon
-
-
-#define gaim_blist_alias_contact       purple_blist_alias_contact
-#define gaim_blist_alias_buddy         purple_blist_alias_buddy
-#define gaim_blist_server_alias_buddy  purple_blist_server_alias_buddy
-#define gaim_blist_alias_chat          purple_blist_alias_chat
-
-#define gaim_blist_rename_buddy  purple_blist_rename_buddy
-#define gaim_blist_rename_group  purple_blist_rename_group
-
-#define gaim_chat_new        purple_chat_new
-#define gaim_blist_add_chat  purple_blist_add_chat
-
-#define gaim_buddy_new           purple_buddy_new
-#define gaim_buddy_set_icon      purple_buddy_set_icon
-#define gaim_buddy_get_account   purple_buddy_get_account
-#define gaim_buddy_get_name      purple_buddy_get_name
-#define gaim_buddy_get_icon      purple_buddy_get_icon
-#define gaim_buddy_get_contact   purple_buddy_get_contact
-#define gaim_buddy_get_presence  purple_buddy_get_presence
-
-#define gaim_blist_add_buddy  purple_blist_add_buddy
-
-#define gaim_group_new  purple_group_new
-
-#define gaim_blist_add_group  purple_blist_add_group
-
-#define gaim_contact_new  purple_contact_new
-
-#define gaim_blist_add_contact    purple_blist_add_contact
-#define gaim_blist_merge_contact  purple_blist_merge_contact
-
-#define gaim_contact_get_priority_buddy  purple_contact_get_priority_buddy
-#define gaim_contact_set_alias           purple_contact_set_alias
-#define gaim_contact_get_alias           purple_contact_get_alias
-#define gaim_contact_on_account          purple_contact_on_account
-
-#define gaim_contact_invalidate_priority_buddy  purple_contact_invalidate_priority_buddy
-
-#define gaim_blist_remove_buddy    purple_blist_remove_buddy
-#define gaim_blist_remove_contact  purple_blist_remove_contact
-#define gaim_blist_remove_chat     purple_blist_remove_chat
-#define gaim_blist_remove_group    purple_blist_remove_group
-
-#define gaim_buddy_get_alias_only     purple_buddy_get_alias_only
-#define gaim_buddy_get_server_alias   purple_buddy_get_server_alias
-#define gaim_buddy_get_contact_alias  purple_buddy_get_contact_alias
-#define gaim_buddy_get_local_alias    purple_buddy_get_local_alias
-#define gaim_buddy_get_alias          purple_buddy_get_alias
-
-#define gaim_chat_get_name  purple_chat_get_name
-
-#define gaim_find_buddy           purple_find_buddy
-#define gaim_find_buddy_in_group  purple_find_buddy_in_group
-#define gaim_find_buddies         purple_find_buddies
-
-#define gaim_find_group  purple_find_group
-
-#define gaim_blist_find_chat  purple_blist_find_chat
-
-#define gaim_chat_get_group   purple_chat_get_group
-#define gaim_buddy_get_group  purple_buddy_get_group
-
-#define gaim_group_get_accounts  purple_group_get_accounts
-#define gaim_group_on_account    purple_group_on_account
-
-#define gaim_blist_add_account     purple_blist_add_account
-#define gaim_blist_remove_account  purple_blist_remove_account
-
-#define gaim_blist_get_group_size          purple_blist_get_group_size
-#define gaim_blist_get_group_online_count  purple_blist_get_group_online_count
-
-#define gaim_blist_load           purple_blist_load
-#define gaim_blist_schedule_save  purple_blist_schedule_save
-
-#define gaim_blist_request_add_buddy  purple_blist_request_add_buddy
-#define gaim_blist_request_add_chat   purple_blist_request_add_chat
-#define gaim_blist_request_add_group  purple_blist_request_add_group
-
-#define gaim_blist_node_set_bool    purple_blist_node_set_bool
-#define gaim_blist_node_get_bool    purple_blist_node_get_bool
-#define gaim_blist_node_set_int     purple_blist_node_set_int
-#define gaim_blist_node_get_int     purple_blist_node_get_int
-#define gaim_blist_node_set_string  purple_blist_node_set_string
-#define gaim_blist_node_get_string  purple_blist_node_get_string
-
-#define gaim_blist_node_remove_setting  purple_blist_node_remove_setting
-
-#define gaim_blist_node_set_flags  purple_blist_node_set_flags
-#define gaim_blist_node_get_flags  purple_blist_node_get_flags
-
-#define gaim_blist_node_get_extended_menu  purple_blist_node_get_extended_menu
-
-#define gaim_blist_set_ui_ops  purple_blist_set_ui_ops
-#define gaim_blist_get_ui_ops  purple_blist_get_ui_ops
-
-#define gaim_blist_get_handle  purple_blist_get_handle
-
-#define gaim_blist_init    purple_blist_init
-#define gaim_blist_uninit  purple_blist_uninit
-
-
-#define GaimBuddyIcon  PurpleBuddyIcon
-
-#define gaim_buddy_icon_new(account, username, icon_data, icon_len)\
-        purple_buddy_icon_new(account, username, g_memdup(icon_data, icon_len), icon_len)
-#define gaim_buddy_icon_ref      purple_buddy_icon_ref
-#define gaim_buddy_icon_unref    purple_buddy_icon_unref
-#define gaim_buddy_icon_update   purple_buddy_icon_update
-
-#define gaim_buddy_icon_set_data(icon, data, len) \
-        purple_buddy_icon_set_data(icon, g_memdup(data, len), len, NULL);
-
-#define gaim_buddy_icon_get_account   purple_buddy_icon_get_account
-#define gaim_buddy_icon_get_username  purple_buddy_icon_get_username
-#define gaim_buddy_icon_get_data      purple_buddy_icon_get_data
-#define gaim_buddy_icon_get_type      purple_buddy_icon_get_extension
-
-#define gaim_buddy_icons_set_for_user(icon, data, len) \
-        purple_buddy_icons_set_for_user(icon, g_memdup(data, len), len, NULL)
-#define gaim_buddy_icons_set_caching    purple_buddy_icons_set_caching
-#define gaim_buddy_icons_is_caching     purple_buddy_icons_is_caching
-#define gaim_buddy_icons_set_cache_dir  purple_buddy_icons_set_cache_dir
-#define gaim_buddy_icons_get_cache_dir  purple_buddy_icons_get_cache_dir
-#define gaim_buddy_icons_get_handle     purple_buddy_icons_get_handle
-
-#define gaim_buddy_icons_init    purple_buddy_icons_init
-#define gaim_buddy_icons_uninit  purple_buddy_icons_uninit
-
-#define gaim_buddy_icon_get_scale_size  purple_buddy_icon_get_scale_size
-
-/* from cipher.h */
-
-#define GAIM_CIPHER          PURPLE_CIPHER
-#define GAIM_CIPHER_OPS      PURPLE_CIPHER_OPS
-#define GAIM_CIPHER_CONTEXT  PURPLE_CIPHER_CONTEXT
-
-#define GaimCipher         PurpleCipher
-#define GaimCipherOps      PurpleCipherOps
-#define GaimCipherContext  PurpleCipherContext
-
-#define GAIM_CIPHER_CAPS_SET_OPT  PURPLE_CIPHER_CAPS_SET_OPT
-#define GAIM_CIPHER_CAPS_GET_OPT  PURPLE_CIPHER_CAPS_GET_OPT
-#define GAIM_CIPHER_CAPS_INIT     PURPLE_CIPHER_CAPS_INIT
-#define GAIM_CIPHER_CAPS_RESET    PURPLE_CIPHER_CAPS_RESET
-#define GAIM_CIPHER_CAPS_UNINIT   PURPLE_CIPHER_CAPS_UNINIT
-#define GAIM_CIPHER_CAPS_SET_IV   PURPLE_CIPHER_CAPS_SET_IV
-#define GAIM_CIPHER_CAPS_APPEND   PURPLE_CIPHER_CAPS_APPEND
-#define GAIM_CIPHER_CAPS_DIGEST   PURPLE_CIPHER_CAPS_DIGEST
-#define GAIM_CIPHER_CAPS_ENCRYPT  PURPLE_CIPHER_CAPS_ENCRYPT
-#define GAIM_CIPHER_CAPS_DECRYPT  PURPLE_CIPHER_CAPS_DECRYPT
-#define GAIM_CIPHER_CAPS_SET_SALT  PURPLE_CIPHER_CAPS_SET_SALT
-#define GAIM_CIPHER_CAPS_GET_SALT_SIZE  PURPLE_CIPHER_CAPS_GET_SALT_SIZE
-#define GAIM_CIPHER_CAPS_SET_KEY        PURPLE_CIPHER_CAPS_SET_KEY
-#define GAIM_CIPHER_CAPS_GET_KEY_SIZE   PURPLE_CIPHER_CAPS_GET_KEY_SIZE
-#define GAIM_CIPHER_CAPS_UNKNOWN        PURPLE_CIPHER_CAPS_UNKNOWN
-
-#define gaim_cipher_get_name          purple_cipher_get_name
-#define gaim_cipher_get_capabilities  purple_cipher_get_capabilities
-#define gaim_cipher_digest_region     purple_cipher_digest_region
-
-#define gaim_ciphers_find_cipher        purple_ciphers_find_cipher
-#define gaim_ciphers_register_cipher    purple_ciphers_register_cipher
-#define gaim_ciphers_unregister_cipher  purple_ciphers_unregister_cipher
-#define gaim_ciphers_get_ciphers        purple_ciphers_get_ciphers
-
-#define gaim_ciphers_get_handle  purple_ciphers_get_handle
-#define gaim_ciphers_init        purple_ciphers_init
-#define gaim_ciphers_uninit      purple_ciphers_uninit
-
-#define gaim_cipher_context_set_option  purple_cipher_context_set_option
-#define gaim_cipher_context_get_option  purple_cipher_context_get_option
-
-#define gaim_cipher_context_new            purple_cipher_context_new
-#define gaim_cipher_context_new_by_name    purple_cipher_context_new_by_name
-#define gaim_cipher_context_reset          purple_cipher_context_reset
-#define gaim_cipher_context_destroy        purple_cipher_context_destroy
-#define gaim_cipher_context_set_iv         purple_cipher_context_set_iv
-#define gaim_cipher_context_append         purple_cipher_context_append
-#define gaim_cipher_context_digest         purple_cipher_context_digest
-#define gaim_cipher_context_digest_to_str  purple_cipher_context_digest_to_str
-#define gaim_cipher_context_encrypt        purple_cipher_context_encrypt
-#define gaim_cipher_context_decrypt        purple_cipher_context_decrypt
-#define gaim_cipher_context_set_salt       purple_cipher_context_set_salt
-#define gaim_cipher_context_get_salt_size  purple_cipher_context_get_salt_size
-#define gaim_cipher_context_set_key        purple_cipher_context_set_key
-#define gaim_cipher_context_get_key_size   purple_cipher_context_get_key_size
-#define gaim_cipher_context_set_data       purple_cipher_context_set_data
-#define gaim_cipher_context_get_data       purple_cipher_context_get_data
-
-#define gaim_cipher_http_digest_calculate_session_key \
-	purple_cipher_http_digest_calculate_session_key
-
-#define gaim_cipher_http_digest_calculate_response \
-	purple_cipher_http_digest_calculate_response
-
-/* from circbuffer.h */
-
-#define GaimCircBuffer  PurpleCircBuffer
-
-#define gaim_circ_buffer_new           purple_circ_buffer_new
-#define gaim_circ_buffer_destroy       purple_circ_buffer_destroy
-#define gaim_circ_buffer_append        purple_circ_buffer_append
-#define gaim_circ_buffer_get_max_read  purple_circ_buffer_get_max_read
-#define gaim_circ_buffer_mark_read     purple_circ_buffer_mark_read
-
-/* from cmds.h */
-
-#define GaimCmdPriority  PurpleCmdPriority
-#define GaimCmdFlag      PurpleCmdFlag
-#define GaimCmdStatus    PurpleCmdStatus
-#define GaimCmdRet       PurpleCmdRet
-
-#define GAIM_CMD_STATUS_OK            PURPLE_CMD_STATUS_OK
-#define GAIM_CMD_STATUS_FAILED        PURPLE_CMD_STATUS_FAILED
-#define GAIM_CMD_STATUS_NOT_FOUND     PURPLE_CMD_STATUS_NOT_FOUND
-#define GAIM_CMD_STATUS_WRONG_ARGS    PURPLE_CMD_STATUS_WRONG_ARGS
-#define GAIM_CMD_STATUS_WRONG_PRPL    PURPLE_CMD_STATUS_WRONG_PRPL
-#define GAIM_CMD_STATUS_WRONG_TYPE    PURPLE_CMD_STATUS_WRONG_TYPE
-
-#define GAIM_CMD_FUNC  PURPLE_CMD_FUNC
-
-#define GAIM_CMD_RET_OK			PURPLE_CMD_RET_OK
-#define GAIM_CMD_RET_FAILED		PURPLE_CMD_RET_FAILED
-#define GAIM_CMD_RET_CONTINUE	PURPLE_CMD_RET_CONTINUE
-
-#define GAIM_CMD_P_VERY_LOW		PURPLE_CMD_P_VERY_LOW
-#define GAIM_CMD_P_LOW			PURPLE_CMD_P_LOW
-#define GAIM_CMD_P_DEFAULT		PURPLE_CMD_P_DEFAULT
-#define GAIM_CMD_P_PRPL			PURPLE_CMD_P_PRPL
-#define GAIM_CMD_P_PLUGIN		PURPLE_CMD_P_PLUGIN
-#define GAIM_CMD_P_ALIAS		PURPLE_CMD_P_ALIAS
-#define GAIM_CMD_P_HIGH			PURPLE_CMD_P_HIGH
-#define GAIM_CMD_P_VERY_HIGH	PURPLE_CMD_P_VERY_HIGH
-
-#define GAIM_CMD_FLAG_IM		PURPLE_CMD_FLAG_IM
-#define GAIM_CMD_FLAG_CHAT		PURPLE_CMD_FLAG_CHAT
-#define GAIM_CMD_FLAG_PRPL_ONLY	PURPLE_CMD_FLAG_PRPL_ONLY
-#define GAIM_CMD_FLAG_ALLOW_WRONG_ARGS	PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS
-
-
-#define GaimCmdFunc  PurpleCmdFunc
-
-#define GaimCmdId  PurpleCmdId
-
-#define gaim_cmd_register    purple_cmd_register
-#define gaim_cmd_unregister  purple_cmd_unregister
-#define gaim_cmd_do_command  purple_cmd_do_command
-#define gaim_cmd_list        purple_cmd_list
-#define gaim_cmd_help        purple_cmd_help
-
-/* from connection.h */
-
-#define GaimConnection  PurpleConnection
-
-#define GAIM_CONNECTION_HTML              PURPLE_CONNECTION_HTML
-#define GAIM_CONNECTION_NO_BGCOLOR        PURPLE_CONNECTION_NO_BGCOLOR
-#define GAIM_CONNECTION_AUTO_RESP         PURPLE_CONNECTION_AUTO_RESP
-#define GAIM_CONNECTION_FORMATTING_WBFO   PURPLE_CONNECTION_FORMATTING_WBFO
-#define GAIM_CONNECTION_NO_NEWLINES       PURPLE_CONNECTION_NO_NEWLINES
-#define GAIM_CONNECTION_NO_FONTSIZE       PURPLE_CONNECTION_NO_FONTSIZE
-#define GAIM_CONNECTION_NO_URLDESC        PURPLE_CONNECTION_NO_URLDESC
-#define GAIM_CONNECTION_NO_IMAGES         PURPLE_CONNECTION_NO_IMAGES
-
-#define GaimConnectionFlags  PurpleConnectionFlags
-
-#define GAIM_DISCONNECTED  PURPLE_DISCONNECTED
-#define GAIM_CONNECTED     PURPLE_CONNECTED
-#define GAIM_CONNECTING    PURPLE_CONNECTING
-
-#define GaimConnectionState  PurpleConnectionState
-
-#define GaimConnectionUiOps  PurpleConnectionUiOps
-
-#define gaim_connection_new      purple_connection_new
-#define gaim_connection_destroy  purple_connection_destroy
-
-#define gaim_connection_set_state         purple_connection_set_state
-#define gaim_connection_set_account       purple_connection_set_account
-#define gaim_connection_set_display_name  purple_connection_set_display_name
-#define gaim_connection_get_state         purple_connection_get_state
-
-#define GAIM_CONNECTION_IS_CONNECTED  PURPLE_CONNECTION_IS_CONNECTED
-
-#define gaim_connection_get_account       purple_connection_get_account
-#define gaim_connection_get_password      purple_connection_get_password
-#define gaim_connection_get_display_name  purple_connection_get_display_name
-
-#define gaim_connection_update_progress  purple_connection_update_progress
-
-#define gaim_connection_notice  purple_connection_notice
-#define gaim_connection_error   purple_connection_error
-
-#define gaim_connections_disconnect_all  purple_connections_disconnect_all
-
-#define gaim_connections_get_all         purple_connections_get_all
-#define gaim_connections_get_connecting  purple_connections_get_connecting
-
-#define GAIM_CONNECTION_IS_VALID  PURPLE_CONNECTION_IS_VALID
-
-#define gaim_connections_set_ui_ops  purple_connections_set_ui_ops
-#define gaim_connections_get_ui_ops  purple_connections_get_ui_ops
-
-#define gaim_connections_init    purple_connections_init
-#define gaim_connections_uninit  purple_connections_uninit
-#define gaim_connections_get_handle  purple_connections_get_handle
-
-
-/* from conversation.h */
-
-#define GaimConversationUiOps  PurpleConversationUiOps
-#define GaimConversation       PurpleConversation
-#define GaimConvIm             PurpleConvIm
-#define GaimConvChat           PurpleConvChat
-#define GaimConvChatBuddy      PurpleConvChatBuddy
-
-#define GAIM_CONV_TYPE_UNKNOWN  PURPLE_CONV_TYPE_UNKNOWN
-#define GAIM_CONV_TYPE_IM       PURPLE_CONV_TYPE_IM
-#define GAIM_CONV_TYPE_CHAT     PURPLE_CONV_TYPE_CHAT
-#define GAIM_CONV_TYPE_MISC     PURPLE_CONV_TYPE_MISC
-#define GAIM_CONV_TYPE_ANY      PURPLE_CONV_TYPE_ANY
-
-#define GaimConversationType  PurpleConversationType
-
-#define GAIM_CONV_UPDATE_ADD       PURPLE_CONV_UPDATE_ADD
-#define GAIM_CONV_UPDATE_REMOVE    PURPLE_CONV_UPDATE_REMOVE
-#define GAIM_CONV_UPDATE_ACCOUNT   PURPLE_CONV_UPDATE_ACCOUNT
-#define GAIM_CONV_UPDATE_TYPING    PURPLE_CONV_UPDATE_TYPING
-#define GAIM_CONV_UPDATE_UNSEEN    PURPLE_CONV_UPDATE_UNSEEN
-#define GAIM_CONV_UPDATE_LOGGING   PURPLE_CONV_UPDATE_LOGGING
-#define GAIM_CONV_UPDATE_TOPIC     PURPLE_CONV_UPDATE_TOPIC
-#define GAIM_CONV_ACCOUNT_ONLINE   PURPLE_CONV_ACCOUNT_ONLINE
-#define GAIM_CONV_ACCOUNT_OFFLINE  PURPLE_CONV_ACCOUNT_OFFLINE
-#define GAIM_CONV_UPDATE_AWAY      PURPLE_CONV_UPDATE_AWAY
-#define GAIM_CONV_UPDATE_ICON      PURPLE_CONV_UPDATE_ICON
-#define GAIM_CONV_UPDATE_TITLE     PURPLE_CONV_UPDATE_TITLE
-#define GAIM_CONV_UPDATE_CHATLEFT  PURPLE_CONV_UPDATE_CHATLEFT
-#define GAIM_CONV_UPDATE_FEATURES  PURPLE_CONV_UPDATE_FEATURES
-
-#define GaimConvUpdateType  PurpleConvUpdateType
-
-#define GAIM_NOT_TYPING  PURPLE_NOT_TYPING
-#define GAIM_TYPING      PURPLE_TYPING
-#define GAIM_TYPED       PURPLE_TYPED
-
-#define GaimTypingState  PurpleTypingState
-
-#define GAIM_MESSAGE_SEND         PURPLE_MESSAGE_SEND
-#define GAIM_MESSAGE_RECV         PURPLE_MESSAGE_RECV
-#define GAIM_MESSAGE_SYSTEM       PURPLE_MESSAGE_SYSTEM
-#define GAIM_MESSAGE_AUTO_RESP    PURPLE_MESSAGE_AUTO_RESP
-#define GAIM_MESSAGE_ACTIVE_ONLY  PURPLE_MESSAGE_ACTIVE_ONLY
-#define GAIM_MESSAGE_NICK         PURPLE_MESSAGE_NICK
-#define GAIM_MESSAGE_NO_LOG       PURPLE_MESSAGE_NO_LOG
-#define GAIM_MESSAGE_WHISPER      PURPLE_MESSAGE_WHISPER
-#define GAIM_MESSAGE_ERROR        PURPLE_MESSAGE_ERROR
-#define GAIM_MESSAGE_DELAYED      PURPLE_MESSAGE_DELAYED
-#define GAIM_MESSAGE_RAW          PURPLE_MESSAGE_RAW
-#define GAIM_MESSAGE_IMAGES       PURPLE_MESSAGE_IMAGES
-
-#define GaimMessageFlags  PurpleMessageFlags
-
-#define GAIM_CBFLAGS_NONE     PURPLE_CBFLAGS_NONE
-#define GAIM_CBFLAGS_VOICE    PURPLE_CBFLAGS_VOICE
-#define GAIM_CBFLAGS_HALFOP   PURPLE_CBFLAGS_HALFOP
-#define GAIM_CBFLAGS_OP       PURPLE_CBFLAGS_OP
-#define GAIM_CBFLAGS_FOUNDER  PURPLE_CBFLAGS_FOUNDER
-#define GAIM_CBFLAGS_TYPING   PURPLE_CBFLAGS_TYPING
-
-#define GaimConvChatBuddyFlags  PurpleConvChatBuddyFlags
-
-#define gaim_conversations_set_ui_ops  purple_conversations_set_ui_ops
-
-#define gaim_conversation_new          purple_conversation_new
-#define gaim_conversation_destroy      purple_conversation_destroy
-#define gaim_conversation_present      purple_conversation_present
-#define gaim_conversation_get_type     purple_conversation_get_type
-#define gaim_conversation_set_ui_ops   purple_conversation_set_ui_ops
-#define gaim_conversation_get_ui_ops   purple_conversation_get_ui_ops
-#define gaim_conversation_set_account  purple_conversation_set_account
-#define gaim_conversation_get_account  purple_conversation_get_account
-#define gaim_conversation_get_gc       purple_conversation_get_gc
-#define gaim_conversation_set_title    purple_conversation_set_title
-#define gaim_conversation_get_title    purple_conversation_get_title
-#define gaim_conversation_autoset_title  purple_conversation_autoset_title
-#define gaim_conversation_set_name       purple_conversation_set_name
-#define gaim_conversation_get_name       purple_conversation_get_name
-#define gaim_conversation_set_logging    purple_conversation_set_logging
-#define gaim_conversation_is_logging     purple_conversation_is_logging
-#define gaim_conversation_close_logs     purple_conversation_close_logs
-#define gaim_conversation_get_im_data    purple_conversation_get_im_data
-
-#define GAIM_CONV_IM    PURPLE_CONV_IM
-
-#define gaim_conversation_get_chat_data  purple_conversation_get_chat_data
-
-#define GAIM_CONV_CHAT  PURPLE_CONV_CHAT
-
-#define gaim_conversation_set_data       purple_conversation_set_data
-#define gaim_conversation_get_data       purple_conversation_get_data
-
-#define gaim_get_conversations  purple_get_conversations
-#define gaim_get_ims            purple_get_ims
-#define gaim_get_chats          purple_get_chats
-
-#define gaim_find_conversation_with_account \
-	purple_find_conversation_with_account
-
-#define gaim_conversation_write         purple_conversation_write
-#define gaim_conversation_set_features  purple_conversation_set_features
-#define gaim_conversation_get_features  purple_conversation_get_features
-#define gaim_conversation_has_focus     purple_conversation_has_focus
-#define gaim_conversation_update        purple_conversation_update
-#define gaim_conversation_foreach       purple_conversation_foreach
-
-#define gaim_conv_im_get_conversation  purple_conv_im_get_conversation
-#define gaim_conv_im_set_icon          purple_conv_im_set_icon
-#define gaim_conv_im_get_icon          purple_conv_im_get_icon
-#define gaim_conv_im_set_typing_state  purple_conv_im_set_typing_state
-#define gaim_conv_im_get_typing_state  purple_conv_im_get_typing_state
-
-#define gaim_conv_im_start_typing_timeout  purple_conv_im_start_typing_timeout
-#define gaim_conv_im_stop_typing_timeout   purple_conv_im_stop_typing_timeout
-#define gaim_conv_im_get_typing_timeout    purple_conv_im_get_typing_timeout
-#define gaim_conv_im_set_type_again        purple_conv_im_set_type_again
-#define gaim_conv_im_get_type_again        purple_conv_im_get_type_again
-
-#define gaim_conv_im_start_send_typed_timeout \
-	purple_conv_im_start_send_typed_timeout
-
-#define gaim_conv_im_stop_send_typed_timeout \
-	purple_conv_im_stop_send_typed_timeout
-
-#define gaim_conv_im_get_send_typed_timeout \
-	purple_conv_im_get_send_typed_timeout
-
-#define gaim_conv_present_error     purple_conv_present_error
-#define gaim_conv_send_confirm      purple_conv_send_confirm
-
-#define gaim_conv_im_update_typing    purple_conv_im_update_typing
-#define gaim_conv_im_write            purple_conv_im_write
-#define gaim_conv_im_send             purple_conv_im_send
-#define gaim_conv_im_send_with_flags  purple_conv_im_send_with_flags
-
-#define gaim_conv_custom_smiley_add    purple_conv_custom_smiley_add
-#define gaim_conv_custom_smiley_write  purple_conv_custom_smiley_write
-#define gaim_conv_custom_smiley_close  purple_conv_custom_smiley_close
-
-#define gaim_conv_chat_get_conversation  purple_conv_chat_get_conversation
-#define gaim_conv_chat_set_users         purple_conv_chat_set_users
-#define gaim_conv_chat_get_users         purple_conv_chat_get_users
-#define gaim_conv_chat_ignore            purple_conv_chat_ignore
-#define gaim_conv_chat_unignore          purple_conv_chat_unignore
-#define gaim_conv_chat_set_ignored       purple_conv_chat_set_ignored
-#define gaim_conv_chat_get_ignored       purple_conv_chat_get_ignored
-#define gaim_conv_chat_get_ignored_user  purple_conv_chat_get_ignored_user
-#define gaim_conv_chat_is_user_ignored   purple_conv_chat_is_user_ignored
-#define gaim_conv_chat_set_topic         purple_conv_chat_set_topic
-#define gaim_conv_chat_get_topic         purple_conv_chat_get_topic
-#define gaim_conv_chat_set_id            purple_conv_chat_set_id
-#define gaim_conv_chat_get_id            purple_conv_chat_get_id
-#define gaim_conv_chat_write             purple_conv_chat_write
-#define gaim_conv_chat_send              purple_conv_chat_send
-#define gaim_conv_chat_send_with_flags   purple_conv_chat_send_with_flags
-#define gaim_conv_chat_add_user          purple_conv_chat_add_user
-#define gaim_conv_chat_add_users         purple_conv_chat_add_users
-#define gaim_conv_chat_rename_user       purple_conv_chat_rename_user
-#define gaim_conv_chat_remove_user       purple_conv_chat_remove_user
-#define gaim_conv_chat_remove_users      purple_conv_chat_remove_users
-#define gaim_conv_chat_find_user         purple_conv_chat_find_user
-#define gaim_conv_chat_user_set_flags    purple_conv_chat_user_set_flags
-#define gaim_conv_chat_user_get_flags    purple_conv_chat_user_get_flags
-#define gaim_conv_chat_clear_users       purple_conv_chat_clear_users
-#define gaim_conv_chat_set_nick          purple_conv_chat_set_nick
-#define gaim_conv_chat_get_nick          purple_conv_chat_get_nick
-#define gaim_conv_chat_left              purple_conv_chat_left
-#define gaim_conv_chat_has_left          purple_conv_chat_has_left
-
-#define gaim_find_chat                   purple_find_chat
-
-#define gaim_conv_chat_cb_new            purple_conv_chat_cb_new
-#define gaim_conv_chat_cb_find           purple_conv_chat_cb_find
-#define gaim_conv_chat_cb_get_name       purple_conv_chat_cb_get_name
-#define gaim_conv_chat_cb_destroy        purple_conv_chat_cb_destroy
-
-#define gaim_conversations_get_handle    purple_conversations_get_handle
-#define gaim_conversations_init          purple_conversations_init
-#define gaim_conversations_uninit        purple_conversations_uninit
-
-/* from core.h */
-
-#define GaimCore  PurpleCore
-
-#define GaimCoreUiOps  PurpleCoreUiOps
-
-#define gaim_core_init  purple_core_init
-#define gaim_core_quit  purple_core_quit
-
-#define gaim_core_quit_cb      purple_core_quit_cb
-#define gaim_core_get_version  purple_core_get_version
-#define gaim_core_get_ui       purple_core_get_ui
-#define gaim_get_core          purple_get_core
-#define gaim_core_set_ui_ops   purple_core_set_ui_ops
-#define gaim_core_get_ui_ops   purple_core_get_ui_ops
-
-/* from debug.h */
-
-#define GAIM_DEBUG_ALL      PURPLE_DEBUG_ALL
-#define GAIM_DEBUG_MISC     PURPLE_DEBUG_MISC
-#define GAIM_DEBUG_INFO     PURPLE_DEBUG_INFO
-#define GAIM_DEBUG_WARNING  PURPLE_DEBUG_WARNING
-#define GAIM_DEBUG_ERROR    PURPLE_DEBUG_ERROR
-#define GAIM_DEBUG_FATAL    PURPLE_DEBUG_FATAL
-
-#define GaimDebugLevel  PurpleDebugLevel
-
-#define GaimDebugUiOps  PurpleDebugUiOps
-
-
-#define gaim_debug          purple_debug
-#define gaim_debug_misc     purple_debug_misc
-#define gaim_debug_info     purple_debug_info
-#define gaim_debug_warning  purple_debug_warning
-#define gaim_debug_error    purple_debug_error
-#define gaim_debug_fatal    purple_debug_fatal
-
-#define gaim_debug_set_enabled  purple_debug_set_enabled
-#define gaim_debug_is_enabled   purple_debug_is_enabled
-
-#define gaim_debug_set_ui_ops  purple_debug_set_ui_ops
-#define gaim_debug_get_ui_ops  purple_debug_get_ui_ops
-
-#define gaim_debug_init  purple_debug_init
-
-/* from desktopitem.h */
-
-#define GAIM_DESKTOP_ITEM_TYPE_NULL          PURPLE_DESKTOP_ITEM_TYPE_NULL
-#define GAIM_DESKTOP_ITEM_TYPE_OTHER         PURPLE_DESKTOP_ITEM_TYPE_OTHER
-#define GAIM_DESKTOP_ITEM_TYPE_APPLICATION   PURPLE_DESKTOP_ITEM_TYPE_APPLICATION
-#define GAIM_DESKTOP_ITEM_TYPE_LINK          PURPLE_DESKTOP_ITEM_TYPE_LINK
-#define GAIM_DESKTOP_ITEM_TYPE_FSDEVICE      PURPLE_DESKTOP_ITEM_TYPE_FSDEVICE
-#define GAIM_DESKTOP_ITEM_TYPE_MIME_TYPE     PURPLE_DESKTOP_ITEM_TYPE_MIME_TYPE
-#define GAIM_DESKTOP_ITEM_TYPE_DIRECTORY     PURPLE_DESKTOP_ITEM_TYPE_DIRECTORY
-#define GAIM_DESKTOP_ITEM_TYPE_SERVICE       PURPLE_DESKTOP_ITEM_TYPE_SERVICE
-#define GAIM_DESKTOP_ITEM_TYPE_SERVICE_TYPE  PURPLE_DESKTOP_ITEM_TYPE_SERVICE_TYPE
-
-#define GaimDesktopItemType  PurpleDesktopItemType
-
-#define GaimDesktopItem  PurpleDesktopItem
-
-#define GAIM_TYPE_DESKTOP_ITEM         PURPLE_TYPE_DESKTOP_ITEM
-#define gaim_desktop_item_get_type     purple_desktop_item_get_type
-
-/* standard */
-/* ugh, i'm just copying these as strings, rather than pidginifying them */
-#define GAIM_DESKTOP_ITEM_ENCODING	"Encoding" /* string */
-#define GAIM_DESKTOP_ITEM_VERSION	"Version"  /* numeric */
-#define GAIM_DESKTOP_ITEM_NAME		"Name" /* localestring */
-#define GAIM_DESKTOP_ITEM_GENERIC_NAME	"GenericName" /* localestring */
-#define GAIM_DESKTOP_ITEM_TYPE		"Type" /* string */
-#define GAIM_DESKTOP_ITEM_FILE_PATTERN "FilePattern" /* regexp(s) */
-#define GAIM_DESKTOP_ITEM_TRY_EXEC	"TryExec" /* string */
-#define GAIM_DESKTOP_ITEM_NO_DISPLAY	"NoDisplay" /* boolean */
-#define GAIM_DESKTOP_ITEM_COMMENT	"Comment" /* localestring */
-#define GAIM_DESKTOP_ITEM_EXEC		"Exec" /* string */
-#define GAIM_DESKTOP_ITEM_ACTIONS	"Actions" /* strings */
-#define GAIM_DESKTOP_ITEM_ICON		"Icon" /* string */
-#define GAIM_DESKTOP_ITEM_MINI_ICON	"MiniIcon" /* string */
-#define GAIM_DESKTOP_ITEM_HIDDEN	"Hidden" /* boolean */
-#define GAIM_DESKTOP_ITEM_PATH		"Path" /* string */
-#define GAIM_DESKTOP_ITEM_TERMINAL	"Terminal" /* boolean */
-#define GAIM_DESKTOP_ITEM_TERMINAL_OPTIONS "TerminalOptions" /* string */
-#define GAIM_DESKTOP_ITEM_SWALLOW_TITLE "SwallowTitle" /* string */
-#define GAIM_DESKTOP_ITEM_SWALLOW_EXEC	"SwallowExec" /* string */
-#define GAIM_DESKTOP_ITEM_MIME_TYPE	"MimeType" /* regexp(s) */
-#define GAIM_DESKTOP_ITEM_PATTERNS	"Patterns" /* regexp(s) */
-#define GAIM_DESKTOP_ITEM_DEFAULT_APP	"DefaultApp" /* string */
-#define GAIM_DESKTOP_ITEM_DEV		"Dev" /* string */
-#define GAIM_DESKTOP_ITEM_FS_TYPE	"FSType" /* string */
-#define GAIM_DESKTOP_ITEM_MOUNT_POINT	"MountPoint" /* string */
-#define GAIM_DESKTOP_ITEM_READ_ONLY	"ReadOnly" /* boolean */
-#define GAIM_DESKTOP_ITEM_UNMOUNT_ICON "UnmountIcon" /* string */
-#define GAIM_DESKTOP_ITEM_SORT_ORDER	"SortOrder" /* strings */
-#define GAIM_DESKTOP_ITEM_URL		"URL" /* string */
-#define GAIM_DESKTOP_ITEM_DOC_PATH	"X-GNOME-DocPath" /* string */
-
-#define gaim_desktop_item_new_from_file   purple_desktop_item_new_from_file
-#define gaim_desktop_item_get_entry_type  purple_desktop_item_get_entry_type
-#define gaim_desktop_item_get_string      purple_desktop_item_get_string
-#define gaim_desktop_item_copy            purple_desktop_item_copy
-#define gaim_desktop_item_unref           purple_desktop_item_unref
-
-/* from dnsquery.h */
-
-#define GaimDnsQueryData  PurpleDnsQueryData
-#define GaimDnsQueryConnectFunction  PurpleDnsQueryConnectFunction
-
-#define gaim_dnsquery_a        		purple_dnsquery_a
-#define gaim_dnsquery_destroy  		purple_dnsquery_destroy
-#define gaim_dnsquery_init     		purple_dnsquery_init
-#define gaim_dnsquery_uninit   		purple_dnsquery_uninit
-#define gaim_dnsquery_set_ui_ops	purple_dnsquery_set_ui_ops
-#define gaim_dnsquery_get_host 		purple_dnsquery_get_host
-#define gaim_dnsquery_get_port 		purple_dnsquery_get_port
-
-/* from dnssrv.h */
-
-#define GaimSrvResponse   PurpleSrvResponse
-#define GaimSrvQueryData  PurpleSrvQueryData
-#define GaimSrvCallback   PurpleSrvCallback
-
-#define gaim_srv_resolve  purple_srv_resolve
-#define gaim_srv_cancel   purple_srv_cancel
-
-/* from eventloop.h */
-
-#define GAIM_INPUT_READ   PURPLE_INPUT_READ
-#define GAIM_INPUT_WRITE  PURPLE_INPUT_WRITE
-
-#define GaimInputCondition  PurpleInputCondition
-#define GaimInputFunction   PurpleInputFunction
-#define GaimEventLoopUiOps  PurpleEventLoopUiOps
-
-#define gaim_timeout_add     purple_timeout_add
-#define gaim_timeout_remove  purple_timeout_remove
-#define gaim_input_add       purple_input_add
-#define gaim_input_remove    purple_input_remove
-
-#define gaim_eventloop_set_ui_ops  purple_eventloop_set_ui_ops
-#define gaim_eventloop_get_ui_ops  purple_eventloop_get_ui_ops
-
-/* from ft.h */
-
-#define GaimXfer  PurpleXfer
-
-#define GAIM_XFER_UNKNOWN  PURPLE_XFER_UNKNOWN
-#define GAIM_XFER_SEND     PURPLE_XFER_SEND
-#define GAIM_XFER_RECEIVE  PURPLE_XFER_RECEIVE
-
-#define GaimXferType  PurpleXferType
-
-#define GAIM_XFER_STATUS_UNKNOWN        PURPLE_XFER_STATUS_UNKNOWN
-#define GAIM_XFER_STATUS_NOT_STARTED    PURPLE_XFER_STATUS_NOT_STARTED
-#define GAIM_XFER_STATUS_ACCEPTED       PURPLE_XFER_STATUS_ACCEPTED
-#define GAIM_XFER_STATUS_STARTED        PURPLE_XFER_STATUS_STARTED
-#define GAIM_XFER_STATUS_DONE           PURPLE_XFER_STATUS_DONE
-#define GAIM_XFER_STATUS_CANCEL_LOCAL   PURPLE_XFER_STATUS_CANCEL_LOCAL
-#define GAIM_XFER_STATUS_CANCEL_REMOTE  PURPLE_XFER_STATUS_CANCEL_REMOTE
-
-#define GaimXferStatusType  PurpleXferStatusType
-
-#define GaimXferUiOps  PurpleXferUiOps
-
-#define gaim_xfer_new                  purple_xfer_new
-#define gaim_xfer_ref                  purple_xfer_ref
-#define gaim_xfer_unref                purple_xfer_unref
-#define gaim_xfer_request              purple_xfer_request
-#define gaim_xfer_request_accepted     purple_xfer_request_accepted
-#define gaim_xfer_request_denied       purple_xfer_request_denied
-#define gaim_xfer_get_type             purple_xfer_get_type
-#define gaim_xfer_get_account          purple_xfer_get_account
-#define gaim_xfer_get_status           purple_xfer_get_status
-#define gaim_xfer_is_canceled          purple_xfer_is_canceled
-#define gaim_xfer_is_completed         purple_xfer_is_completed
-#define gaim_xfer_get_filename         purple_xfer_get_filename
-#define gaim_xfer_get_local_filename   purple_xfer_get_local_filename
-#define gaim_xfer_get_bytes_sent       purple_xfer_get_bytes_sent
-#define gaim_xfer_get_bytes_remaining  purple_xfer_get_bytes_remaining
-#define gaim_xfer_get_size             purple_xfer_get_size
-#define gaim_xfer_get_progress         purple_xfer_get_progress
-#define gaim_xfer_get_local_port       purple_xfer_get_local_port
-#define gaim_xfer_get_remote_ip        purple_xfer_get_remote_ip
-#define gaim_xfer_get_remote_port      purple_xfer_get_remote_port
-#define gaim_xfer_set_completed        purple_xfer_set_completed
-#define gaim_xfer_set_message          purple_xfer_set_message
-#define gaim_xfer_set_filename         purple_xfer_set_filename
-#define gaim_xfer_set_local_filename   purple_xfer_set_local_filename
-#define gaim_xfer_set_size             purple_xfer_set_size
-#define gaim_xfer_set_bytes_sent       purple_xfer_set_bytes_sent
-#define gaim_xfer_get_ui_ops           purple_xfer_get_ui_ops
-#define gaim_xfer_set_read_fnc         purple_xfer_set_read_fnc
-#define gaim_xfer_set_write_fnc        purple_xfer_set_write_fnc
-#define gaim_xfer_set_ack_fnc          purple_xfer_set_ack_fnc
-#define gaim_xfer_set_request_denied_fnc  purple_xfer_set_request_denied_fnc
-#define gaim_xfer_set_init_fnc         purple_xfer_set_init_fnc
-#define gaim_xfer_set_start_fnc        purple_xfer_set_start_fnc
-#define gaim_xfer_set_end_fnc          purple_xfer_set_end_fnc
-#define gaim_xfer_set_cancel_send_fnc  purple_xfer_set_cancel_send_fnc
-#define gaim_xfer_set_cancel_recv_fnc  purple_xfer_set_cancel_recv_fnc
-
-#define gaim_xfer_read                purple_xfer_read
-#define gaim_xfer_write               purple_xfer_write
-#define gaim_xfer_start               purple_xfer_start
-#define gaim_xfer_end                 purple_xfer_end
-#define gaim_xfer_add                 purple_xfer_add
-#define gaim_xfer_cancel_local        purple_xfer_cancel_local
-#define gaim_xfer_cancel_remote       purple_xfer_cancel_remote
-#define gaim_xfer_error               purple_xfer_error
-#define gaim_xfer_update_progress     purple_xfer_update_progress
-#define gaim_xfer_conversation_write  purple_xfer_conversation_write
-
-#define gaim_xfers_get_handle  purple_xfers_get_handle
-#define gaim_xfers_init        purple_xfers_init
-#define gaim_xfers_uninit      purple_xfers_uninit
-#define gaim_xfers_set_ui_ops  purple_xfers_set_ui_ops
-#define gaim_xfers_get_ui_ops  purple_xfers_get_ui_ops
-
-/* from gaim-client.h */
-
-#define gaim_init  purple_init
-
-/* from idle.h */
-
-#define GaimIdleUiOps  PurpleIdleUiOps
-
-#define gaim_idle_touch       purple_idle_touch
-#define gaim_idle_set         purple_idle_set
-#define gaim_idle_set_ui_ops  purple_idle_set_ui_ops
-#define gaim_idle_get_ui_ops  purple_idle_get_ui_ops
-#define gaim_idle_init        purple_idle_init
-#define gaim_idle_uninit      purple_idle_uninit
-
-/* from imgstore.h */
-
-#define GaimStoredImage  PurpleStoredImage
-
-#define gaim_imgstore_add(data, size, filename) \
-        purple_imgstore_add_with_id(g_memdup(data, size), size, filename)
-#define gaim_imgstore_get           purple_imgstore_find_by_id
-#define gaim_imgstore_get_data      purple_imgstore_get_data
-#define gaim_imgstore_get_size      purple_imgstore_get_size
-#define gaim_imgstore_get_filename  purple_imgstore_get_filename
-#define gaim_imgstore_ref           purple_imgstore_ref_by_id
-#define gaim_imgstore_unref         purple_imgstore_unref_by_id
-
-
-/* from log.h */
-
-#define GaimLog                  PurpleLog
-#define GaimLogLogger            PurpleLogLogger
-#define GaimLogCommonLoggerData  PurpleLogCommonLoggerData
-#define GaimLogSet               PurpleLogSet
-
-#define GAIM_LOG_IM      PURPLE_LOG_IM
-#define GAIM_LOG_CHAT    PURPLE_LOG_CHAT
-#define GAIM_LOG_SYSTEM  PURPLE_LOG_SYSTEM
-
-#define GaimLogType  PurpleLogType
-
-#define GAIM_LOG_READ_NO_NEWLINE  PURPLE_LOG_READ_NO_NEWLINE
-
-#define GaimLogReadFlags  PurpleLogReadFlags
-
-#define GaimLogSetCallback  PurpleLogSetCallback
-
-#define gaim_log_new    purple_log_new
-#define gaim_log_free   purple_log_free
-#define gaim_log_write  purple_log_write
-#define gaim_log_read   purple_log_read
-
-#define gaim_log_get_logs         purple_log_get_logs
-#define gaim_log_get_log_sets     purple_log_get_log_sets
-#define gaim_log_get_system_logs  purple_log_get_system_logs
-#define gaim_log_get_size         purple_log_get_size
-#define gaim_log_get_total_size   purple_log_get_total_size
-#define gaim_log_get_log_dir      purple_log_get_log_dir
-#define gaim_log_compare          purple_log_compare
-#define gaim_log_set_compare      purple_log_set_compare
-#define gaim_log_set_free         purple_log_set_free
-
-#define gaim_log_common_writer       purple_log_common_writer
-#define gaim_log_common_lister       purple_log_common_lister
-#define gaim_log_common_total_sizer  purple_log_common_total_sizer
-#define gaim_log_common_sizer        purple_log_common_sizer
-
-#define gaim_log_logger_new     purple_log_logger_new
-#define gaim_log_logger_free    purple_log_logger_free
-#define gaim_log_logger_add     purple_log_logger_add
-#define gaim_log_logger_remove  purple_log_logger_remove
-#define gaim_log_logger_set     purple_log_logger_set
-#define gaim_log_logger_get     purple_log_logger_get
-
-#define gaim_log_logger_get_options  purple_log_logger_get_options
-
-#define gaim_log_init        purple_log_init
-#define gaim_log_get_handle  purple_log_get_handle
-#define gaim_log_uninit      purple_log_uninit
-
-/* from mime.h */
-
-#define GaimMimeDocument  PurpleMimeDocument
-#define GaimMimePart      PurpleMimePart
-
-#define gaim_mime_document_new         purple_mime_document_new
-#define gaim_mime_document_free        purple_mime_document_free
-#define gaim_mime_document_parse       purple_mime_document_parse
-#define gaim_mime_document_parsen      purple_mime_document_parsen
-#define gaim_mime_document_write       purple_mime_document_write
-#define gaim_mime_document_get_fields  purple_mime_document_get_fields
-#define gaim_mime_document_get_field   purple_mime_document_get_field
-#define gaim_mime_document_set_field   purple_mime_document_set_field
-#define gaim_mime_document_get_parts   purple_mime_document_get_parts
-
-#define gaim_mime_part_new                purple_mime_part_new
-#define gaim_mime_part_get_fields         purple_mime_part_get_fields
-#define gaim_mime_part_get_field          purple_mime_part_get_field
-#define gaim_mime_part_get_field_decoded  purple_mime_part_get_field_decoded
-#define gaim_mime_part_set_field          purple_mime_part_set_field
-#define gaim_mime_part_get_data           purple_mime_part_get_data
-#define gaim_mime_part_get_data_decoded   purple_mime_part_get_data_decoded
-#define gaim_mime_part_get_length         purple_mime_part_get_length
-#define gaim_mime_part_set_data           purple_mime_part_set_data
-
-
-/* from network.h */
-
-#define GaimNetworkListenData  PurpleNetworkListenData
-
-#define GaimNetworkListenCallback  PurpleNetworkListenCallback
-
-#define gaim_network_ip_atoi              purple_network_ip_atoi
-#define gaim_network_set_public_ip        purple_network_set_public_ip
-#define gaim_network_get_public_ip        purple_network_get_public_ip
-#define gaim_network_get_local_system_ip  purple_network_get_local_system_ip
-#define gaim_network_get_my_ip            purple_network_get_my_ip
-
-#define gaim_network_listen            purple_network_listen
-#define gaim_network_listen_range      purple_network_listen_range
-#define gaim_network_listen_cancel     purple_network_listen_cancel
-#define gaim_network_get_port_from_fd  purple_network_get_port_from_fd
-
-#define gaim_network_is_available  purple_network_is_available
-
-#define gaim_network_init    purple_network_init
-#define gaim_network_uninit  purple_network_uninit
-
-/* from notify.h */
-
-
-#define GaimNotifyUserInfoEntry  PurpleNotifyUserInfoEntry
-#define GaimNotifyUserInfo       PurpleNotifyUserInfo
-
-#define GaimNotifyCloseCallback  PurpleNotifyCloseCallback
-
-#define GAIM_NOTIFY_MESSAGE        PURPLE_NOTIFY_MESSAGE
-#define GAIM_NOTIFY_EMAIL          PURPLE_NOTIFY_EMAIL
-#define GAIM_NOTIFY_EMAILS         PURPLE_NOTIFY_EMAILS
-#define GAIM_NOTIFY_FORMATTED      PURPLE_NOTIFY_FORMATTED
-#define GAIM_NOTIFY_SEARCHRESULTS  PURPLE_NOTIFY_SEARCHRESULTS
-#define GAIM_NOTIFY_USERINFO       PURPLE_NOTIFY_USERINFO
-#define GAIM_NOTIFY_URI            PURPLE_NOTIFY_URI
-
-#define GaimNotifyType  PurpleNotifyType
-
-#define GAIM_NOTIFY_MSG_ERROR    PURPLE_NOTIFY_MSG_ERROR
-#define GAIM_NOTIFY_MSG_WARNING  PURPLE_NOTIFY_MSG_WARNING
-#define GAIM_NOTIFY_MSG_INFO     PURPLE_NOTIFY_MSG_INFO
-
-#define GaimNotifyMsgType  PurpleNotifyMsgType
-
-#define GAIM_NOTIFY_BUTTON_LABELED   PURPLE_NOTIFY_BUTTON_LABELED
-#define GAIM_NOTIFY_BUTTON_CONTINUE  PURPLE_NOTIFY_BUTTON_CONTINUE
-#define GAIM_NOTIFY_BUTTON_ADD       PURPLE_NOTIFY_BUTTON_ADD
-#define GAIM_NOTIFY_BUTTON_INFO      PURPLE_NOTIFY_BUTTON_INFO
-#define GAIM_NOTIFY_BUTTON_IM        PURPLE_NOTIFY_BUTTON_IM
-#define GAIM_NOTIFY_BUTTON_JOIN      PURPLE_NOTIFY_BUTTON_JOIN
-#define GAIM_NOTIFY_BUTTON_INVITE    PURPLE_NOTIFY_BUTTON_INVITE
-
-#define GaimNotifySearchButtonType  PurpleNotifySearchButtonType
-
-#define GaimNotifySearchResults  PurpleNotifySearchResults
-
-#define GAIM_NOTIFY_USER_INFO_ENTRY_PAIR            PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR
-#define GAIM_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK   PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK
-#define GAIM_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER  PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER
-
-#define GaimNotifyUserInfoEntryType  PurpleNotifyUserInfoEntryType
-
-#define GaimNotifySearchColumn           PurpleNotifySearchColumn
-#define GaimNotifySearchResultsCallback  PurpleNotifySearchResultsCallback
-#define GaimNotifySearchButton           PurpleNotifySearchButton
-
-#define GaimNotifyUiOps  PurpleNotifyUiOps
-
-#define gaim_notify_searchresults                     purple_notify_searchresults
-#define gaim_notify_searchresults_free                purple_notify_searchresults_free
-#define gaim_notify_searchresults_new_rows            purple_notify_searchresults_new_rows
-#define gaim_notify_searchresults_button_add          purple_notify_searchresults_button_add
-#define gaim_notify_searchresults_button_add_labeled  purple_notify_searchresults_button_add_labeled
-#define gaim_notify_searchresults_new                 purple_notify_searchresults_new
-#define gaim_notify_searchresults_column_new          purple_notify_searchresults_column_new
-#define gaim_notify_searchresults_column_add          purple_notify_searchresults_column_add
-#define gaim_notify_searchresults_row_add             purple_notify_searchresults_row_add
-#define gaim_notify_searchresults_get_rows_count      purple_notify_searchresults_get_rows_count
-#define gaim_notify_searchresults_get_columns_count   purple_notify_searchresults_get_columns_count
-#define gaim_notify_searchresults_row_get             purple_notify_searchresults_row_get
-#define gaim_notify_searchresults_column_get_title    purple_notify_searchresults_column_get_title
-
-#define gaim_notify_message    purple_notify_message
-#define gaim_notify_email      purple_notify_email
-#define gaim_notify_emails     purple_notify_emails
-#define gaim_notify_formatted  purple_notify_formatted
-#define gaim_notify_userinfo   purple_notify_userinfo
-
-#define gaim_notify_user_info_new                    purple_notify_user_info_new
-#define gaim_notify_user_info_destroy                purple_notify_user_info_destroy
-#define gaim_notify_user_info_get_entries            purple_notify_user_info_get_entries
-#define gaim_notify_user_info_get_text_with_newline  purple_notify_user_info_get_text_with_newline
-#define gaim_notify_user_info_add_pair               purple_notify_user_info_add_pair
-#define gaim_notify_user_info_prepend_pair           purple_notify_user_info_prepend_pair
-#define gaim_notify_user_info_remove_entry           purple_notify_user_info_remove_entry
-#define gaim_notify_user_info_entry_new              purple_notify_user_info_entry_new
-#define gaim_notify_user_info_add_section_break      purple_notify_user_info_add_section_break
-#define gaim_notify_user_info_add_section_header     purple_notify_user_info_add_section_header
-#define gaim_notify_user_info_remove_last_item       purple_notify_user_info_remove_last_item
-#define gaim_notify_user_info_entry_get_label        purple_notify_user_info_entry_get_label
-#define gaim_notify_user_info_entry_set_label        purple_notify_user_info_entry_set_label
-#define gaim_notify_user_info_entry_get_value        purple_notify_user_info_entry_get_value
-#define gaim_notify_user_info_entry_set_value        purple_notify_user_info_entry_set_value
-#define gaim_notify_user_info_entry_get_type         purple_notify_user_info_entry_get_type
-#define gaim_notify_user_info_entry_set_type         purple_notify_user_info_entry_set_type
-
-#define gaim_notify_uri                purple_notify_uri
-#define gaim_notify_close              purple_notify_close
-#define gaim_notify_close_with_handle  purple_notify_close_with_handle
-
-#define gaim_notify_info     purple_notify_info
-#define gaim_notify_warning  purple_notify_warning
-#define gaim_notify_error    purple_notify_error
-
-#define gaim_notify_set_ui_ops  purple_notify_set_ui_ops
-#define gaim_notify_get_ui_ops  purple_notify_get_ui_ops
-
-#define gaim_notify_get_handle  purple_notify_get_handle
-
-#define gaim_notify_init    purple_notify_init
-#define gaim_notify_uninit  purple_notify_uninit
-
-/* from ntlm.h */
-
-#define gaim_ntlm_gen_type1    purple_ntlm_gen_type1
-#define gaim_ntlm_parse_type2  purple_ntlm_parse_type2
-#define gaim_ntlm_gen_type3    purple_ntlm_gen_type3
-
-/* from plugin.h */
-
-#ifdef GAIM_PLUGINS
-#ifndef PURPLE_PLUGINS
-#define PURPLE_PLUGINS
-#endif
-#endif
-
-#define GaimPlugin            PurplePlugin
-#define GaimPluginInfo        PurplePluginInfo
-#define GaimPluginUiInfo      PurplePluginUiInfo
-#define GaimPluginLoaderInfo  PurplePluginLoaderInfo
-#define GaimPluginAction      PurplePluginAction
-#define GaimPluginPriority    PurplePluginPriority
-
-#define GAIM_PLUGIN_UNKNOWN   PURPLE_PLUGIN_UNKNOWN
-#define GAIM_PLUGIN_STANDARD  PURPLE_PLUGIN_STANDARD
-#define GAIM_PLUGIN_LOADER    PURPLE_PLUGIN_LOADER
-#define GAIM_PLUGIN_PROTOCOL  PURPLE_PLUGIN_PROTOCOL
-
-#define GaimPluginType        PurplePluginType
-
-#define GAIM_PRIORITY_DEFAULT  PURPLE_PRIORITY_DEFAULT
-#define GAIM_PRIORITY_HIGHEST  PURPLE_PRIORITY_HIGHEST
-#define GAIM_PRIORITY_LOWEST   PURPLE_PRIORITY_LOWEST
-
-#define GAIM_PLUGIN_FLAG_INVISIBLE  PURPLE_PLUGIN_FLAG_INVISIBLE
-
-#define GAIM_PLUGIN_MAGIC  PURPLE_PLUGIN_MAGIC
-
-#define GAIM_PLUGIN_LOADER_INFO     PURPLE_PLUGIN_LOADER_INFO
-#define GAIM_PLUGIN_HAS_PREF_FRAME  PURPLE_PLUGIN_HAS_PREF_FRAME
-#define GAIM_PLUGIN_UI_INFO         PURPLE_PLUGIN_UI_INFO
-
-#define GAIM_PLUGIN_HAS_ACTIONS  PURPLE_PLUGIN_HAS_ACTIONS
-#define GAIM_PLUGIN_ACTIONS      PURPLE_PLUGIN_ACTIONS
-
-#define GAIM_INIT_PLUGIN  PURPLE_INIT_PLUGIN
-
-#define gaim_plugin_new              purple_plugin_new
-#define gaim_plugin_probe            purple_plugin_probe
-#define gaim_plugin_register         purple_plugin_register
-#define gaim_plugin_load             purple_plugin_load
-#define gaim_plugin_unload           purple_plugin_unload
-#define gaim_plugin_reload           purple_plugin_reload
-#define gaim_plugin_destroy          purple_plugin_destroy
-#define gaim_plugin_is_loaded        purple_plugin_is_loaded
-#define gaim_plugin_is_unloadable    purple_plugin_is_unloadable
-#define gaim_plugin_get_id           purple_plugin_get_id
-#define gaim_plugin_get_name         purple_plugin_get_name
-#define gaim_plugin_get_version      purple_plugin_get_version
-#define gaim_plugin_get_summary      purple_plugin_get_summary
-#define gaim_plugin_get_description  purple_plugin_get_description
-#define gaim_plugin_get_author       purple_plugin_get_author
-#define gaim_plugin_get_homepage     purple_plugin_get_homepage
-
-#define gaim_plugin_ipc_register        purple_plugin_ipc_register
-#define gaim_plugin_ipc_unregister      purple_plugin_ipc_unregister
-#define gaim_plugin_ipc_unregister_all  purple_plugin_ipc_unregister_all
-#define gaim_plugin_ipc_get_params      purple_plugin_ipc_get_params
-#define gaim_plugin_ipc_call            purple_plugin_ipc_call
-
-#define gaim_plugins_add_search_path  purple_plugins_add_search_path
-#define gaim_plugins_unload_all       purple_plugins_unload_all
-#define gaim_plugins_destroy_all      purple_plugins_destroy_all
-#define gaim_plugins_save_loaded      purple_plugins_save_loaded
-#define gaim_plugins_load_saved       purple_plugins_load_saved
-#define gaim_plugins_probe            purple_plugins_probe
-#define gaim_plugins_enabled          purple_plugins_enabled
-
-#define gaim_plugins_register_probe_notify_cb     purple_plugins_register_probe_notify_cb
-#define gaim_plugins_unregister_probe_notify_cb   purple_plugins_unregister_probe_notify_cb
-#define gaim_plugins_register_load_notify_cb      purple_plugins_register_load_notify_cb
-#define gaim_plugins_unregister_load_notify_cb    purple_plugins_unregister_load_notify_cb
-#define gaim_plugins_register_unload_notify_cb    purple_plugins_register_unload_notify_cb
-#define gaim_plugins_unregister_unload_notify_cb  purple_plugins_unregister_unload_notify_cb
-
-#define gaim_plugins_find_with_name      purple_plugins_find_with_name
-#define gaim_plugins_find_with_filename  purple_plugins_find_with_filename
-#define gaim_plugins_find_with_basename  purple_plugins_find_with_basename
-#define gaim_plugins_find_with_id        purple_plugins_find_with_id
-
-#define gaim_plugins_get_loaded     purple_plugins_get_loaded
-#define gaim_plugins_get_protocols  purple_plugins_get_protocols
-#define gaim_plugins_get_all        purple_plugins_get_all
-
-#define gaim_plugins_get_handle  purple_plugins_get_handle
-#define gaim_plugins_init        purple_plugins_init
-#define gaim_plugins_uninit      purple_plugins_uninit
-
-#define gaim_plugin_action_new   purple_plugin_action_new
-#define gaim_plugin_action_free  purple_plugin_action_free
-
-/* pluginpref.h */
-
-#define GaimPluginPrefFrame  PurplePluginPrefFrame
-#define GaimPluginPref       PurplePluginPref
-
-#define GAIM_STRING_FORMAT_TYPE_NONE       PURPLE_STRING_FORMAT_TYPE_NONE
-#define GAIM_STRING_FORMAT_TYPE_MULTILINE  PURPLE_STRING_FORMAT_TYPE_MULTILINE
-#define GAIM_STRING_FORMAT_TYPE_HTML       PURPLE_STRING_FORMAT_TYPE_HTML
-
-#define GaimStringFormatType  PurpleStringFormatType
-
-#define GAIM_PLUGIN_PREF_NONE           PURPLE_PLUGIN_PREF_NONE
-#define GAIM_PLUGIN_PREF_CHOICE         PURPLE_PLUGIN_PREF_CHOICE
-#define GAIM_PLUGIN_PREF_INFO           PURPLE_PLUGIN_PREF_INFO
-#define GAIM_PLUGIN_PREF_STRING_FORMAT  PURPLE_PLUGIN_PREF_STRING_FORMAT
-
-#define GaimPluginPrefType  PurplePluginPrefType
-
-#define gaim_plugin_pref_frame_new        purple_plugin_pref_frame_new
-#define gaim_plugin_pref_frame_destroy    purple_plugin_pref_frame_destroy
-#define gaim_plugin_pref_frame_add        purple_plugin_pref_frame_add
-#define gaim_plugin_pref_frame_get_prefs  purple_plugin_pref_frame_get_prefs
-
-#define gaim_plugin_pref_new                      purple_plugin_pref_new
-#define gaim_plugin_pref_new_with_name            purple_plugin_pref_new_with_name
-#define gaim_plugin_pref_new_with_label           purple_plugin_pref_new_with_label
-#define gaim_plugin_pref_new_with_name_and_label  purple_plugin_pref_new_with_name_and_label
-#define gaim_plugin_pref_destroy                  purple_plugin_pref_destroy
-#define gaim_plugin_pref_set_name                 purple_plugin_pref_set_name
-#define gaim_plugin_pref_get_name                 purple_plugin_pref_get_name
-#define gaim_plugin_pref_set_label                purple_plugin_pref_set_label
-#define gaim_plugin_pref_get_label                purple_plugin_pref_get_label
-#define gaim_plugin_pref_set_bounds               purple_plugin_pref_set_bounds
-#define gaim_plugin_pref_get_bounds               purple_plugin_pref_get_bounds
-#define gaim_plugin_pref_set_type                 purple_plugin_pref_set_type
-#define gaim_plugin_pref_get_type                 purple_plugin_pref_get_type
-#define gaim_plugin_pref_add_choice               purple_plugin_pref_add_choice
-#define gaim_plugin_pref_get_choices              purple_plugin_pref_get_choices
-#define gaim_plugin_pref_set_max_length           purple_plugin_pref_set_max_length
-#define gaim_plugin_pref_get_max_length           purple_plugin_pref_get_max_length
-#define gaim_plugin_pref_set_masked               purple_plugin_pref_set_masked
-#define gaim_plugin_pref_get_masked               purple_plugin_pref_get_masked
-#define gaim_plugin_pref_set_format_type          purple_plugin_pref_set_format_type
-#define gaim_plugin_pref_get_format_type          purple_plugin_pref_get_format_type
-
-/* from pounce.h */
-
-#define GaimPounce  PurplePounce
-
-#define GAIM_POUNCE_NONE              PURPLE_POUNCE_NONE
-#define GAIM_POUNCE_SIGNON            PURPLE_POUNCE_SIGNON
-#define GAIM_POUNCE_SIGNOFF           PURPLE_POUNCE_SIGNOFF
-#define GAIM_POUNCE_AWAY              PURPLE_POUNCE_AWAY
-#define GAIM_POUNCE_AWAY_RETURN       PURPLE_POUNCE_AWAY_RETURN
-#define GAIM_POUNCE_IDLE              PURPLE_POUNCE_IDLE
-#define GAIM_POUNCE_IDLE_RETURN       PURPLE_POUNCE_IDLE_RETURN
-#define GAIM_POUNCE_TYPING            PURPLE_POUNCE_TYPING
-#define GAIM_POUNCE_TYPED             PURPLE_POUNCE_TYPED
-#define GAIM_POUNCE_TYPING_STOPPED    PURPLE_POUNCE_TYPING_STOPPED
-#define GAIM_POUNCE_MESSAGE_RECEIVED  PURPLE_POUNCE_MESSAGE_RECEIVED
-#define GaimPounceEvent  PurplePounceEvent
-
-#define GAIM_POUNCE_OPTION_NONE  PURPLE_POUNCE_OPTION_NONE
-#define GAIM_POUNCE_OPTION_AWAY  PURPLE_POUNCE_OPTION_AWAY
-#define GaimPounceOption  PurplePounceOption
-
-#define GaimPounceCb  PurplePounceCb
-
-#define gaim_pounce_new                     purple_pounce_new
-#define gaim_pounce_destroy                 purple_pounce_destroy
-#define gaim_pounce_destroy_all_by_account  purple_pounce_destroy_all_by_account
-#define gaim_pounce_set_events              purple_pounce_set_events
-#define gaim_pounce_set_options             purple_pounce_set_options
-#define gaim_pounce_set_pouncer             purple_pounce_set_pouncer
-#define gaim_pounce_set_pouncee             purple_pounce_set_pouncee
-#define gaim_pounce_set_save                purple_pounce_set_save
-#define gaim_pounce_action_register         purple_pounce_action_register
-#define gaim_pounce_action_set_enabled      purple_pounce_action_set_enabled
-#define gaim_pounce_action_set_attribute    purple_pounce_action_set_attribute
-#define gaim_pounce_set_data                purple_pounce_set_data
-#define gaim_pounce_get_events              purple_pounce_get_events
-#define gaim_pounce_get_options             purple_pounce_get_options
-#define gaim_pounce_get_pouncer             purple_pounce_get_pouncer
-#define gaim_pounce_get_pouncee             purple_pounce_get_pouncee
-#define gaim_pounce_get_save                purple_pounce_get_save
-#define gaim_pounce_action_is_enabled       purple_pounce_action_is_enabled
-#define gaim_pounce_action_get_attribute    purple_pounce_action_get_attribute
-#define gaim_pounce_get_data                purple_pounce_get_data
-#define gaim_pounce_execute                 purple_pounce_execute
-
-#define gaim_find_pounce                 purple_find_pounce
-#define gaim_pounces_load                purple_pounces_load
-#define gaim_pounces_register_handler    purple_pounces_register_handler
-#define gaim_pounces_unregister_handler  purple_pounces_unregister_handler
-#define gaim_pounces_get_all             purple_pounces_get_all
-#define gaim_pounces_get_handle          purple_pounces_get_handle
-#define gaim_pounces_init                purple_pounces_init
-#define gaim_pounces_uninit              purple_pounces_uninit
-
-/* from prefs.h */
-
-
-#define GAIM_PREF_NONE         PURPLE_PREF_NONE
-#define GAIM_PREF_BOOLEAN      PURPLE_PREF_BOOLEAN
-#define GAIM_PREF_INT          PURPLE_PREF_INT
-#define GAIM_PREF_STRING       PURPLE_PREF_STRING
-#define GAIM_PREF_STRING_LIST  PURPLE_PREF_STRING_LIST
-#define GAIM_PREF_PATH         PURPLE_PREF_PATH
-#define GAIM_PREF_PATH_LIST    PURPLE_PREF_PATH_LIST
-#define GaimPrefType  PurplePrefType
-
-#define GaimPrefCallback  PurplePrefCallback
-
-#define gaim_prefs_get_handle             purple_prefs_get_handle
-#define gaim_prefs_init                   purple_prefs_init
-#define gaim_prefs_uninit                 purple_prefs_uninit
-#define gaim_prefs_add_none               purple_prefs_add_none
-#define gaim_prefs_add_bool               purple_prefs_add_bool
-#define gaim_prefs_add_int                purple_prefs_add_int
-#define gaim_prefs_add_string             purple_prefs_add_string
-#define gaim_prefs_add_string_list        purple_prefs_add_string_list
-#define gaim_prefs_add_path               purple_prefs_add_path
-#define gaim_prefs_add_path_list          purple_prefs_add_path_list
-#define gaim_prefs_remove                 purple_prefs_remove
-#define gaim_prefs_rename                 purple_prefs_rename
-#define gaim_prefs_rename_boolean_toggle  purple_prefs_rename_boolean_toggle
-#define gaim_prefs_destroy                purple_prefs_destroy
-#define gaim_prefs_set_generic            purple_prefs_set_generic
-#define gaim_prefs_set_bool               purple_prefs_set_bool
-#define gaim_prefs_set_int                purple_prefs_set_int
-#define gaim_prefs_set_string             purple_prefs_set_string
-#define gaim_prefs_set_string_list        purple_prefs_set_string_list
-#define gaim_prefs_set_path               purple_prefs_set_path
-#define gaim_prefs_set_path_list          purple_prefs_set_path_list
-#define gaim_prefs_exists                 purple_prefs_exists
-#define gaim_prefs_get_type               purple_prefs_get_type
-#define gaim_prefs_get_bool               purple_prefs_get_bool
-#define gaim_prefs_get_int                purple_prefs_get_int
-#define gaim_prefs_get_string             purple_prefs_get_string
-#define gaim_prefs_get_string_list        purple_prefs_get_string_list
-#define gaim_prefs_get_path               purple_prefs_get_path
-#define gaim_prefs_get_path_list          purple_prefs_get_path_list
-#define gaim_prefs_connect_callback       purple_prefs_connect_callback
-#define gaim_prefs_disconnect_callback    purple_prefs_disconnect_callback
-#define gaim_prefs_disconnect_by_handle   purple_prefs_disconnect_by_handle
-#define gaim_prefs_trigger_callback       purple_prefs_trigger_callback
-#define gaim_prefs_load                   purple_prefs_load
-#define gaim_prefs_update_old             purple_prefs_update_old
-
-/* from privacy.h */
-
-#define GAIM_PRIVACY_ALLOW_ALL        PURPLE_PRIVACY_ALLOW_ALL
-#define GAIM_PRIVACY_DENY_ALL         PURPLE_PRIVACY_DENY_ALL
-#define GAIM_PRIVACY_ALLOW_USERS      PURPLE_PRIVACY_ALLOW_USERS
-#define GAIM_PRIVACY_DENY_USERS       PURPLE_PRIVACY_DENY_USERS
-#define GAIM_PRIVACY_ALLOW_BUDDYLIST  PURPLE_PRIVACY_ALLOW_BUDDYLIST
-#define GaimPrivacyType  PurplePrivacyType
-
-#define GaimPrivacyUiOps  PurplePrivacyUiOps
-
-#define gaim_privacy_permit_add     purple_privacy_permit_add
-#define gaim_privacy_permit_remove  purple_privacy_permit_remove
-#define gaim_privacy_deny_add       purple_privacy_deny_add
-#define gaim_privacy_deny_remove    purple_privacy_deny_remove
-#define gaim_privacy_allow          purple_privacy_allow
-#define gaim_privacy_deny           purple_privacy_deny
-#define gaim_privacy_check          purple_privacy_check
-#define gaim_privacy_set_ui_ops     purple_privacy_set_ui_ops
-#define gaim_privacy_get_ui_ops     purple_privacy_get_ui_ops
-#define gaim_privacy_init           purple_privacy_init
-
-/* from proxy.h */
-
-#define GAIM_PROXY_USE_GLOBAL  PURPLE_PROXY_USE_GLOBAL
-#define GAIM_PROXY_NONE        PURPLE_PROXY_NONE
-#define GAIM_PROXY_HTTP        PURPLE_PROXY_HTTP
-#define GAIM_PROXY_SOCKS4      PURPLE_PROXY_SOCKS4
-#define GAIM_PROXY_SOCKS5      PURPLE_PROXY_SOCKS5
-#define GAIM_PROXY_USE_ENVVAR  PURPLE_PROXY_USE_ENVVAR
-#define GaimProxyType  PurpleProxyType
-
-#define GaimProxyInfo  PurpleProxyInfo
-
-#define GaimProxyConnectData      PurpleProxyConnectData
-#define GaimProxyConnectFunction  PurpleProxyConnectFunction
-
-#define gaim_proxy_info_new           purple_proxy_info_new
-#define gaim_proxy_info_destroy       purple_proxy_info_destroy
-#define gaim_proxy_info_set_type      purple_proxy_info_set_type
-#define gaim_proxy_info_set_host      purple_proxy_info_set_host
-#define gaim_proxy_info_set_port      purple_proxy_info_set_port
-#define gaim_proxy_info_set_username  purple_proxy_info_set_username
-#define gaim_proxy_info_set_password  purple_proxy_info_set_password
-#define gaim_proxy_info_get_type      purple_proxy_info_get_type
-#define gaim_proxy_info_get_host      purple_proxy_info_get_host
-#define gaim_proxy_info_get_port      purple_proxy_info_get_port
-#define gaim_proxy_info_get_username  purple_proxy_info_get_username
-#define gaim_proxy_info_get_password  purple_proxy_info_get_password
-
-#define gaim_global_proxy_get_info    purple_global_proxy_get_info
-#define gaim_proxy_get_handle         purple_proxy_get_handle
-#define gaim_proxy_init               purple_proxy_init
-#define gaim_proxy_uninit             purple_proxy_uninit
-#define gaim_proxy_get_setup          purple_proxy_get_setup
-
-#define gaim_proxy_connect                     purple_proxy_connect
-#define gaim_proxy_connect_socks5              purple_proxy_connect_socks5
-#define gaim_proxy_connect_cancel              purple_proxy_connect_cancel
-#define gaim_proxy_connect_cancel_with_handle  purple_proxy_connect_cancel_with_handle
-
-/* from prpl.h */
-
-#define GaimPluginProtocolInfo  PurplePluginProtocolInfo
-
-#define GAIM_ICON_SCALE_DISPLAY  PURPLE_ICON_SCALE_DISPLAY
-#define GAIM_ICON_SCALE_SEND     PURPLE_ICON_SCALE_SEND
-#define GaimIconScaleRules  PurpleIconScaleRules
-
-#define GaimBuddyIconSpec  PurpleBuddyIconSpec
-
-#define GaimProtocolOptions  PurpleProtocolOptions
-
-#define GAIM_IS_PROTOCOL_PLUGIN  PURPLE_IS_PROTOCOL_PLUGIN
-
-#define GAIM_PLUGIN_PROTOCOL_INFO  PURPLE_PLUGIN_PROTOCOL_INFO
-
-#define gaim_prpl_got_account_idle        purple_prpl_got_account_idle
-#define gaim_prpl_got_account_login_time  purple_prpl_got_account_login_time
-#define gaim_prpl_got_account_status      purple_prpl_got_account_status
-#define gaim_prpl_got_user_idle           purple_prpl_got_user_idle
-#define gaim_prpl_got_user_login_time     purple_prpl_got_user_login_time
-#define gaim_prpl_got_user_status         purple_prpl_got_user_status
-#define gaim_prpl_change_account_status   purple_prpl_change_account_status
-#define gaim_prpl_get_statuses            purple_prpl_get_statuses
-
-#define gaim_find_prpl  purple_find_prpl
-
-/* from request.h */
-
-#define GAIM_DEFAULT_ACTION_NONE  PURPLE_DEFAULT_ACTION_NONE
-
-#define GAIM_REQUEST_INPUT   PURPLE_REQUEST_INPUT
-#define GAIM_REQUEST_CHOICE  PURPLE_REQUEST_CHOICE
-#define GAIM_REQUEST_ACTION  PURPLE_REQUEST_ACTION
-#define GAIM_REQUEST_FIELDS  PURPLE_REQUEST_FIELDS
-#define GAIM_REQUEST_FILE    PURPLE_REQUEST_FILE
-#define GAIM_REQUEST_FOLDER  PURPLE_REQUEST_FOLDER
-#define GaimRequestType  PurpleRequestType
-
-#define GAIM_REQUEST_FIELD_NONE     PURPLE_REQUEST_FIELD_NONE
-#define GAIM_REQUEST_FIELD_STRING   PURPLE_REQUEST_FIELD_STRING
-#define GAIM_REQUEST_FIELD_INTEGER  PURPLE_REQUEST_FIELD_INTEGER
-#define GAIM_REQUEST_FIELD_BOOLEAN  PURPLE_REQUEST_FIELD_BOOLEAN
-#define GAIM_REQUEST_FIELD_CHOICE   PURPLE_REQUEST_FIELD_CHOICE
-#define GAIM_REQUEST_FIELD_LIST     PURPLE_REQUEST_FIELD_LIST
-#define GAIM_REQUEST_FIELD_LABEL    PURPLE_REQUEST_FIELD_LABEL
-#define GAIM_REQUEST_FIELD_IMAGE    PURPLE_REQUEST_FIELD_IMAGE
-#define GAIM_REQUEST_FIELD_ACCOUNT  PURPLE_REQUEST_FIELD_ACCOUNT
-#define GaimRequestFieldType  PurpleRequestFieldType
-
-#define GaimRequestFields  PurpleRequestFields
-
-#define GaimRequestFieldGroup  PurpleRequestFieldGroup
-
-#define GaimRequestField  PurpleRequestField
-
-#define GaimRequestUiOps  PurpleRequestUiOps
-
-#define GaimRequestInputCb   PurpleRequestInputCb
-#define GaimRequestActionCb  PurpleRequestActionCb
-#define GaimRequestChoiceCb  PurpleRequestChoiceCb
-#define GaimRequestFieldsCb  PurpleRequestFieldsCb
-#define GaimRequestFileCb    PurpleRequestFileCb
-
-#define gaim_request_fields_new                  purple_request_fields_new
-#define gaim_request_fields_destroy              purple_request_fields_destroy
-#define gaim_request_fields_add_group            purple_request_fields_add_group
-#define gaim_request_fields_get_groups           purple_request_fields_get_groups
-#define gaim_request_fields_exists               purple_request_fields_exists
-#define gaim_request_fields_get_required         purple_request_fields_get_required
-#define gaim_request_fields_is_field_required    purple_request_fields_is_field_required
-#define gaim_request_fields_all_required_filled  purple_request_fields_all_required_filled
-#define gaim_request_fields_get_field            purple_request_fields_get_field
-#define gaim_request_fields_get_string           purple_request_fields_get_string
-#define gaim_request_fields_get_integer          purple_request_fields_get_integer
-#define gaim_request_fields_get_bool             purple_request_fields_get_bool
-#define gaim_request_fields_get_choice           purple_request_fields_get_choice
-#define gaim_request_fields_get_account          purple_request_fields_get_account
-
-#define gaim_request_field_group_new         purple_request_field_group_new
-#define gaim_request_field_group_destroy     purple_request_field_group_destroy
-#define gaim_request_field_group_add_field   purple_request_field_group_add_field
-#define gaim_request_field_group_get_title   purple_request_field_group_get_title
-#define gaim_request_field_group_get_fields  purple_request_field_group_get_fields
-
-#define gaim_request_field_new            purple_request_field_new
-#define gaim_request_field_destroy        purple_request_field_destroy
-#define gaim_request_field_set_label      purple_request_field_set_label
-#define gaim_request_field_set_visible    purple_request_field_set_visible
-#define gaim_request_field_set_type_hint  purple_request_field_set_type_hint
-#define gaim_request_field_set_required   purple_request_field_set_required
-#define gaim_request_field_get_type       purple_request_field_get_type
-#define gaim_request_field_get_id         purple_request_field_get_id
-#define gaim_request_field_get_label      purple_request_field_get_label
-#define gaim_request_field_is_visible     purple_request_field_is_visible
-#define gaim_request_field_get_type_hint  purple_request_field_get_type_hint
-#define gaim_request_field_is_required    purple_request_field_is_required
-
-#define gaim_request_field_string_new           purple_request_field_string_new
-#define gaim_request_field_string_set_default_value \
-	purple_request_field_string_set_default_value
-#define gaim_request_field_string_set_value     purple_request_field_string_set_value
-#define gaim_request_field_string_set_masked    purple_request_field_string_set_masked
-#define gaim_request_field_string_set_editable  purple_request_field_string_set_editable
-#define gaim_request_field_string_get_default_value \
-	purple_request_field_string_get_default_value
-#define gaim_request_field_string_get_value     purple_request_field_string_get_value
-#define gaim_request_field_string_is_multiline  purple_request_field_string_is_multiline
-#define gaim_request_field_string_is_masked     purple_request_field_string_is_masked
-#define gaim_request_field_string_is_editable   purple_request_field_string_is_editable
-
-#define gaim_request_field_int_new        purple_request_field_int_new
-#define gaim_request_field_int_set_default_value \
-	purple_request_field_int_set_default_value
-#define gaim_request_field_int_set_value  purple_request_field_int_set_value
-#define gaim_request_field_int_get_default_value \
-	purple_request_field_int_get_default_value
-#define gaim_request_field_int_get_value  purple_request_field_int_get_value
-
-#define gaim_request_field_bool_new        purple_request_field_bool_new
-#define gaim_request_field_bool_set_default_value \
-	purple_request_field_book_set_default_value
-#define gaim_request_field_bool_set_value  purple_request_field_bool_set_value
-#define gaim_request_field_bool_get_default_value \
-	purple_request_field_bool_get_default_value
-#define gaim_request_field_bool_get_value  purple_request_field_bool_get_value
-
-#define gaim_request_field_choice_new         purple_request_field_choice_new
-#define gaim_request_field_choice_add         purple_request_field_choice_add
-#define gaim_request_field_choice_set_default_value \
-	purple_request_field_choice_set_default_value
-#define gaim_request_field_choice_set_value   purple_request_field_choice_set_value
-#define gaim_request_field_choice_get_default_value \
-	purple_request_field_choice_get_default_value
-#define gaim_request_field_choice_get_value   purple_request_field_choice_get_value
-#define gaim_request_field_choice_get_labels  purple_request_field_choice_get_labels
-
-#define gaim_request_field_list_new               purple_request_field_list_new
-#define gaim_request_field_list_set_multi_select  purple_request_field_list_set_multi_select
-#define gaim_request_field_list_get_multi_select  purple_request_field_list_get_multi_select
-#define gaim_request_field_list_get_data          purple_request_field_list_get_data
-#define gaim_request_field_list_add               purple_request_field_list_add
-#define gaim_request_field_list_add_selected      purple_request_field_list_add_selected
-#define gaim_request_field_list_clear_selected    purple_request_field_list_clear_selected
-#define gaim_request_field_list_set_selected      purple_request_field_list_set_selected
-#define gaim_request_field_list_is_selected       purple_request_field_list_is_selected
-#define gaim_request_field_list_get_selected      purple_request_field_list_get_selected
-#define gaim_request_field_list_get_items         purple_request_field_list_get_items
-
-#define gaim_request_field_label_new  purple_request_field_label_new
-
-#define gaim_request_field_image_new          purple_request_field_image_new
-#define gaim_request_field_image_set_scale    purple_request_field_image_set_scale
-#define gaim_request_field_image_get_buffer   purple_request_field_image_get_buffer
-#define gaim_request_field_image_get_size     purple_request_field_image_get_size
-#define gaim_request_field_image_get_scale_x  purple_request_field_image_get_scale_x
-#define gaim_request_field_image_get_scale_y  purple_request_field_image_get_scale_y
-
-#define gaim_request_field_account_new                purple_request_field_account_new
-#define gaim_request_field_account_set_default_value  purple_request_field_account_set_default_value
-#define gaim_request_field_account_set_value          purple_request_field_account_set_value
-#define gaim_request_field_account_set_show_all       purple_request_field_account_set_show_all
-#define gaim_request_field_account_set_filter         purple_request_field_account_set_filter
-#define gaim_request_field_account_get_default_value  purple_request_field_account_get_default_value
-#define gaim_request_field_account_get_value          purple_request_field_account_get_value
-#define gaim_request_field_account_get_show_all       purple_request_field_account_get_show_all
-#define gaim_request_field_account_get_filter         purple_request_field_account_get_filter
-
-#define gaim_request_input              purple_request_input
-#define gaim_request_choice             purple_request_choice
-#define gaim_request_choice_varg        purple_request_choice_varg
-#define gaim_request_action             purple_request_action
-#define gaim_request_action_varg        purple_request_action_varg
-#define gaim_request_fields(handle, title, primary, secondary, fields, ok_text, ok_cb, cancel_text, cancel_cb, user_data)             purple_request_fields(handle, title, primary, secondary, fields, ok_text, ok_cb, cancel_text, cancel_cb, NULL, NULL, NULL, user_data)
-#define gaim_request_close              purple_request_close
-#define gaim_request_close_with_handle  purple_request_close_with_handle
-
-#define gaim_request_yes_no         purple_request_yes_no
-#define gaim_request_ok_cancel      purple_request_ok_cancel
-#define gaim_request_accept_cancel  purple_request_accept_cancel
-
-#define gaim_request_file    purple_request_file
-#define gaim_request_folder  purple_request_folder
-
-#define gaim_request_set_ui_ops  purple_request_set_ui_ops
-#define gaim_request_get_ui_ops  purple_request_get_ui_ops
-
-/* from roomlist.h */
-
-#define GaimRoomlist       PurpleRoomlist
-#define GaimRoomlistRoom   PurpleRoomlistRoom
-#define GaimRoomlistField  PurpleRoomlistField
-#define GaimRoomlistUiOps  PurpleRoomlistUiOps
-
-#define GAIM_ROOMLIST_ROOMTYPE_CATEGORY  PURPLE_ROOMLIST_ROOMTYPE_CATEGORY
-#define GAIM_ROOMLIST_ROOMTYPE_ROOM      PURPLE_ROOMLIST_ROOMTYPE_ROOM
-#define GaimRoomlistRoomType  PurpleRoomlistRoomType
-
-#define GAIM_ROOMLIST_FIELD_BOOL    PURPLE_ROOMLIST_BOOL
-#define GAIM_ROOMLIST_FIELD_INT     PURPLE_ROOMLIST_INT
-#define GAIM_ROOMLIST_FIELD_STRING  PURPLE_ROOMLIST_STRING
-#define GaimRoomlistFieldType  PurpleRoomlistFieldType
-
-#define gaim_roomlist_show_with_account  purple_roomlist_show_with_account
-#define gaim_roomlist_new                purple_roomlist_new
-#define gaim_roomlist_ref                purple_roomlist_ref
-#define gaim_roomlist_unref              purple_roomlist_unref
-#define gaim_roomlist_set_fields         purple_roomlist_set_fields
-#define gaim_roomlist_set_in_progress    purple_roomlist_set_in_progress
-#define gaim_roomlist_get_in_progress    purple_roomlist_get_in_progress
-#define gaim_roomlist_room_add           purple_roomlist_room_add
-
-#define gaim_roomlist_get_list         purple_roomlist_get_list
-#define gaim_roomlist_cancel_get_list  purple_roomlist_cancel_get_list
-#define gaim_roomlist_expand_category  purple_roomlist_expand_category
-
-#define gaim_roomlist_room_new        purple_roomlist_room_new
-#define gaim_roomlist_room_add_field  purple_roomlist_room_add_field
-#define gaim_roomlist_room_join       purple_roomlist_room_join
-#define gaim_roomlist_field_new       purple_roomlist_field_new
-
-#define gaim_roomlist_set_ui_ops  purple_roomlist_set_ui_ops
-#define gaim_roomlist_get_ui_ops  purple_roomlist_get_ui_ops
-
-/* from savedstatuses.h */
-
-#define GaimSavedStatus     PurpleSavedStatus
-#define GaimSavedStatusSub  PurpleSavedStatusSub
-
-#define gaim_savedstatus_new              purple_savedstatus_new
-#define gaim_savedstatus_set_title        purple_savedstatus_set_title
-#define gaim_savedstatus_set_type         purple_savedstatus_set_type
-#define gaim_savedstatus_set_message      purple_savedstatus_set_message
-#define gaim_savedstatus_set_substatus    purple_savedstatus_set_substatus
-#define gaim_savedstatus_unset_substatus  purple_savedstatus_unset_substatus
-#define gaim_savedstatus_delete           purple_savedstatus_delete
-
-#define gaim_savedstatuses_get_all              purple_savedstatuses_get_all
-#define gaim_savedstatuses_get_popular          purple_savedstatuses_get_popular
-#define gaim_savedstatus_get_current            purple_savedstatus_get_current
-#define gaim_savedstatus_get_default            purple_savedstatus_get_default
-#define gaim_savedstatus_get_idleaway           purple_savedstatus_get_idleaway
-#define gaim_savedstatus_is_idleaway            purple_savedstatus_is_idleaway
-#define gaim_savedstatus_set_idleaway           purple_savedstatus_set_idleaway
-#define gaim_savedstatus_get_startup            purple_savedstatus_get_startup
-#define gaim_savedstatus_find                   purple_savedstatus_find
-#define gaim_savedstatus_find_by_creation_time  purple_savedstatus_find_by_creation_time
-#define gaim_savedstatus_find_transient_by_type_and_message \
-	purple_savedstatus_find_transient_by_type_and_message
-
-#define gaim_savedstatus_is_transient           purple_savedstatus_is_transient
-#define gaim_savedstatus_get_title              purple_savedstatus_get_title
-#define gaim_savedstatus_get_type               purple_savedstatus_get_type
-#define gaim_savedstatus_get_message            purple_savedstatus_get_message
-#define gaim_savedstatus_get_creation_time      purple_savedstatus_get_creation_time
-#define gaim_savedstatus_has_substatuses        purple_savedstatus_has_substatuses
-#define gaim_savedstatus_get_substatus          purple_savedstatus_get_substatus
-#define gaim_savedstatus_substatus_get_type     purple_savedstatus_substatus_get_type
-#define gaim_savedstatus_substatus_get_message  purple_savedstatus_substatus_get_message
-#define gaim_savedstatus_activate               purple_savedstatus_activate
-#define gaim_savedstatus_activate_for_account   purple_savedstatus_activate_for_account
-
-#define gaim_savedstatuses_get_handle  purple_savedstatuses_get_handle
-#define gaim_savedstatuses_init        purple_savedstatuses_init
-#define gaim_savedstatuses_uninit      purple_savedstatuses_uninit
-
-/* from signals.h */
-
-#define GAIM_CALLBACK  PURPLE_CALLBACK
-
-#define GaimCallback           PurpleCallback
-#define GaimSignalMarshalFunc  PurpleSignalMarshalFunc
-
-#define GAIM_SIGNAL_PRIORITY_DEFAULT  PURPLE_SIGNAL_PRIORITY_DEFAULT
-#define GAIM_SIGNAL_PRIORITY_HIGHEST  PURPLE_SIGNAL_PRIORITY_HIGHEST
-#define GAIM_SIGNAL_PRIORITY_LOWEST   PURPLE_SIGNAL_PRIORITY_LOWEST
-
-#define gaim_signal_register    purple_signal_register
-#define gaim_signal_unregister  purple_signal_unregister
-
-#define gaim_signals_unregister_by_instance  purple_signals_unregister_by_instance
-
-#define gaim_signal_get_values              purple_signal_get_values
-#define gaim_signal_connect_priority        purple_signal_connect_priority
-#define gaim_signal_connect                 purple_signal_connect
-#define gaim_signal_connect_priority_vargs  purple_signal_connect_priority_vargs
-#define gaim_signal_connect_vargs           purple_signal_connect_vargs
-#define gaim_signal_disconnect              purple_signal_disconnect
-
-#define gaim_signals_disconnect_by_handle  purple_signals_disconnect_by_handle
-
-#define gaim_signal_emit                 purple_signal_emit
-#define gaim_signal_emit_vargs           purple_signal_emit_vargs
-#define gaim_signal_emit_return_1        purple_signal_emit_vargs
-#define gaim_signal_emit_vargs_return_1  purple_signal_emit_vargs_return_1
-
-#define gaim_signals_init    purple_signals_init
-#define gaim_signals_uninit  purple_signals_uninit
-
-#define gaim_marshal_VOID \
-	purple_marshal_VOID
-#define gaim_marshal_VOID__INT \
-	purple_marshal_VOID__INT
-#define gaim_marshal_VOID__INT_INT \
-	purple_marshal_VOID_INT_INT
-#define gaim_marshal_VOID__POINTER \
-	purple_marshal_VOID__POINTER
-#define gaim_marshal_VOID__POINTER_UINT \
-	purple_marshal_VOID__POINTER_UINT
-#define gaim_marshal_VOID__POINTER_INT_INT \
-	purple_marshal_VOID__POINTER_INT_INT
-#define gaim_marshal_VOID__POINTER_POINTER \
-	purple_marshal_VOID__POINTER_POINTER
-#define gaim_marshal_VOID__POINTER_POINTER_UINT \
-	purple_marshal_VOID__POINTER_POINTER_UINT
-#define gaim_marshal_VOID__POINTER_POINTER_UINT_UINT \
-	purple_marshal_VOID__POINTER_POINTER_UINT_UINT
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER \
-	purple_marshal_VOID__POINTER_POINTER_POINTER
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_POINTER \
-	purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER \
-	purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_UINT \
-	purple_marshal_VOID__POINTER_POINTER_POINTER_UINT
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT \
-	purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_UINT
-#define gaim_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT \
-	purple_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT
-
-#define gaim_marshal_INT__INT \
-	purple_marshal_INT__INT
-#define gaim_marshal_INT__INT_INT \
-	purple_marshal_INT__INT_INT
-#define gaim_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER \
-	purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER
-
-#define gaim_marshal_BOOLEAN__POINTER \
-	purple_marshal_BOOLEAN__POINTER
-#define gaim_marshal_BOOLEAN__POINTER_POINTER \
-	purple_marshal_BOOLEAN__POINTER_POINTER
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER \
-	purple_marshal_BOOLEAN__POINTER_POINTER_POINTER
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_UINT \
-	purple_marshal_BOOLEAN__POINTER_POINTER_UINT
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_UINT \
-	purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_UINT
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER \
-	purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER
-#define gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER \
-	purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER
-
-#define gaim_marshal_BOOLEAN__INT_POINTER \
-	purple_marshal_BOOLEAN__INT_POINTER
-
-#define gaim_marshal_POINTER__POINTER_INT \
-	purple_marshal_POINTER__POINTER_INT
-#define gaim_marshal_POINTER__POINTER_INT64 \
-	purple_marshal_POINTER__POINTER_INT64
-#define gaim_marshal_POINTER__POINTER_INT_BOOLEAN \
-	purple_marshal_POINTER__POINTER_INT_BOOLEAN
-#define gaim_marshal_POINTER__POINTER_INT64_BOOLEAN \
-	purple_marshal_POINTER__POINTER_INT64_BOOLEAN
-#define gaim_marshal_POINTER__POINTER_POINTER \
-	purple_marshal_POINTER__POINTER_POINTER
-
-/* from sound.h */
-
-#define GAIM_SOUND_BUDDY_ARRIVE    PURPLE_SOUND_BUDDY_ARRIVE
-#define GAIM_SOUND_BUDDY_LEAVE     PURPLE_SOUND_BUDDY_LEAVE
-#define GAIM_SOUND_RECEIVE         PURPLE_SOUND_RECEIVE
-#define GAIM_SOUND_FIRST_RECEIVE   PURPLE_SOUND_FIRST_RECEIVE
-#define GAIM_SOUND_SEND            PURPLE_SOUND_SEND
-#define GAIM_SOUND_CHAT_JOIN       PURPLE_SOUND_CHAT_JOIN
-#define GAIM_SOUND_CHAT_LEAVE      PURPLE_SOUND_CHAT_LEAVE
-#define GAIM_SOUND_CHAT_YOU_SAY    PURPLE_SOUND_CHAT_YOU_SAY
-#define GAIM_SOUND_CHAT_SAY        PURPLE_SOUND_CHAT_SAY
-#define GAIM_SOUND_POUNCE_DEFAULT  PURPLE_SOUND_POUNCE_DEFAULT
-#define GAIM_SOUND_CHAT_NICK       PURPLE_SOUND_CHAT_NICK
-#define GAIM_NUM_SOUNDS            PURPLE_NUM_SOUNDS
-#define GaimSoundEventID  PurpleSoundEventID
-
-#define GaimSoundUiOps  PurpleSoundUiOps
-
-#define gaim_sound_play_file   purple_sound_play_file
-#define gaim_sound_play_event  purple_sound_play_event
-#define gaim_sound_set_ui_ops  purple_sound_set_ui_ops
-#define gaim_sound_get_ui_ops  purple_sound_get_ui_ops
-#define gaim_sound_init        purple_sound_init
-#define gaim_sound_uninit      purple_sound_uninit
-
-#define gaim_sounds_get_handle  purple_sounds_get_handle
-
-/* from sslconn.h */
-
-#define GAIM_SSL_DEFAULT_PORT  PURPLE_SSL_DEFAULT_PORT
-
-#define GAIM_SSL_HANDSHAKE_FAILED  PURPLE_SSL_HANDSHAKE_FAILED
-#define GAIM_SSL_CONNECT_FAILED    PURPLE_SSL_CONNECT_FAILED
-#define GaimSslErrorType  PurpleSslErrorType
-
-#define GaimSslConnection  PurpleSslConnection
-
-#define GaimSslInputFunction  PurpleSslInputFunction
-#define GaimSslErrorFunction  PurpleSslErrorFunction
-
-#define GaimSslOps  PurpleSslOps
-
-#define gaim_ssl_is_supported  purple_ssl_is_supported
-#define gaim_ssl_connect       purple_ssl_connect
-#define gaim_ssl_connect_fd    purple_ssl_connect_fd
-#define gaim_ssl_input_add     purple_ssl_input_add
-#define gaim_ssl_close         purple_ssl_close
-#define gaim_ssl_read          purple_ssl_read
-#define gaim_ssl_write         purple_ssl_write
-
-#define gaim_ssl_set_ops  purple_ssl_set_ops
-#define gaim_ssl_get_ops  purple_ssl_get_ops
-#define gaim_ssl_init     purple_ssl_init
-#define gaim_ssl_uninit   purple_ssl_uninit
-
-/* from status.h */
-
-#define GaimStatusType  PurpleStatusType
-#define GaimStatusAttr  PurpleStatusAttr
-#define GaimPresence    PurplePresence
-#define GaimStatus      PurpleStatus
-
-#define GAIM_PRESENCE_CONTEXT_UNSET    PURPLE_PRESENCE_CONTEXT_UNSET
-#define GAIM_PRESENCE_CONTEXT_ACCOUNT  PURPLE_PRESENCE_CONTEXT_ACCOUNT
-#define GAIM_PRESENCE_CONTEXT_CONV     PURPLE_PRESENCE_CONTEXT_CONV
-#define GAIM_PRESENCE_CONTEXT_BUDDY    PURPLE_PRESENCE_CONTEXT_BUDDY
-#define GaimPresenceContext  PurplePresenceContext
-
-#define GAIM_STATUS_UNSET           PURPLE_STATUS_UNSET
-#define GAIM_STATUS_OFFLINE         PURPLE_STATUS_OFFLINE
-#define GAIM_STATUS_AVAILABLE       PURPLE_STATUS_AVAILABLE
-#define GAIM_STATUS_UNAVAILABLE     PURPLE_STATUS_UNAVAILABLE
-#define GAIM_STATUS_INVISIBLE       PURPLE_STATUS_INVISIBLE
-#define GAIM_STATUS_AWAY            PURPLE_STATUS_AWAY
-#define GAIM_STATUS_EXTENDED_AWAY   PURPLE_STATUS_EXTENDED_AWAY
-#define GAIM_STATUS_MOBILE          PURPLE_STATUS_MOBILE
-#define GAIM_STATUS_NUM_PRIMITIVES  PURPLE_STATUS_NUM_PRIMITIVES
-#define GaimStatusPrimitive  PurpleStatusPrimitive
-
-#define gaim_primitive_get_id_from_type    purple_primitive_get_id_from_type
-#define gaim_primitive_get_name_from_type  purple_primitive_get_name_from_type
-#define gaim_primitive_get_type_from_id    purple_primitive_get_type_from_id
-
-#define gaim_status_type_new_full          purple_status_type_new_full
-#define gaim_status_type_new               purple_status_type_new
-#define gaim_status_type_new_with_attrs    purple_status_type_new_with_attrs
-#define gaim_status_type_destroy           purple_status_type_destroy
-#define gaim_status_type_set_primary_attr  purple_status_type_set_primary_attr
-#define gaim_status_type_add_attr          purple_status_type_add_attr
-#define gaim_status_type_add_attrs         purple_status_type_add_attrs
-#define gaim_status_type_add_attrs_vargs   purple_status_type_add_attrs_vargs
-#define gaim_status_type_get_primitive     purple_status_type_get_primitive
-#define gaim_status_type_get_id            purple_status_type_get_id
-#define gaim_status_type_get_name          purple_status_type_get_name
-#define gaim_status_type_is_saveable       purple_status_type_is_saveable
-#define gaim_status_type_is_user_settable  purple_status_type_is_user_settable
-#define gaim_status_type_is_independent    purple_status_type_is_independent
-#define gaim_status_type_is_exclusive      purple_status_type_is_exclusive
-#define gaim_status_type_is_available      purple_status_type_is_available
-#define gaim_status_type_get_primary_attr  purple_status_type_get_primary_attr
-#define gaim_status_type_get_attr          purple_status_type_get_attr
-#define gaim_status_type_get_attrs         purple_status_type_get_attrs
-#define gaim_status_type_find_with_id      purple_status_type_find_with_id
-
-#define gaim_status_attr_new        purple_status_attr_new
-#define gaim_status_attr_destroy    purple_status_attr_destroy
-#define gaim_status_attr_get_id     purple_status_attr_get_id
-#define gaim_status_attr_get_name   purple_status_attr_get_name
-#define gaim_status_attr_get_value  purple_status_attr_get_value
-
-#define gaim_status_new                         purple_status_new
-#define gaim_status_destroy                     purple_status_destroy
-#define gaim_status_set_active                  purple_status_set_active
-#define gaim_status_set_active_with_attrs       purple_status_set_active_with_attrs
-#define gaim_status_set_active_with_attrs_list  purple_status_set_active_with_attrs_list
-#define gaim_status_set_attr_boolean            purple_status_set_attr_boolean
-#define gaim_status_set_attr_int                purple_status_set_attr_int
-#define gaim_status_set_attr_string             purple_status_set_attr_string
-#define gaim_status_get_type                    purple_status_get_type
-#define gaim_status_get_presence                purple_status_get_presence
-#define gaim_status_get_id                      purple_status_get_id
-#define gaim_status_get_name                    purple_status_get_name
-#define gaim_status_is_independent              purple_status_is_independent
-#define gaim_status_is_exclusive                purple_status_is_exclusive
-#define gaim_status_is_available                purple_status_is_available
-#define gaim_status_is_active                   purple_status_is_active
-#define gaim_status_is_online                   purple_status_is_online
-#define gaim_status_get_attr_value              purple_status_get_attr_value
-#define gaim_status_get_attr_boolean            purple_status_get_attr_boolean
-#define gaim_status_get_attr_int                purple_status_get_attr_int
-#define gaim_status_get_attr_string             purple_status_get_attr_string
-#define gaim_status_compare                     purple_status_compare
-
-#define gaim_presence_new                purple_presence_new
-#define gaim_presence_new_for_account    purple_presence_new_for_account
-#define gaim_presence_new_for_conv       purple_presence_new_for_conv
-#define gaim_presence_new_for_buddy      purple_presence_new_for_buddy
-#define gaim_presence_destroy            purple_presence_destroy
-#define gaim_presence_add_status         purple_presence_add_status
-#define gaim_presence_add_list           purple_presence_add_list
-#define gaim_presence_set_status_active  purple_presence_set_status_active
-#define gaim_presence_switch_status      purple_presence_switch_status
-#define gaim_presence_set_idle           purple_presence_set_idle
-#define gaim_presence_set_login_time     purple_presence_set_login_time
-#define gaim_presence_get_context        purple_presence_get_context
-#define gaim_presence_get_account        purple_presence_get_account
-#define gaim_presence_get_conversation   purple_presence_get_conversation
-#define gaim_presence_get_chat_user      purple_presence_get_chat_user
-#define gaim_presence_get_statuses       purple_presence_get_statuses
-#define gaim_presence_get_status         purple_presence_get_status
-#define gaim_presence_get_active_status  purple_presence_get_active_status
-#define gaim_presence_is_available       purple_presence_is_available
-#define gaim_presence_is_online          purple_presence_is_online
-#define gaim_presence_is_status_active   purple_presence_is_status_active
-#define gaim_presence_is_status_primitive_active \
-	purple_presence_is_status_primitive_active
-#define gaim_presence_is_idle            purple_presence_is_idle
-#define gaim_presence_get_idle_time      purple_presence_get_idle_time
-#define gaim_presence_get_login_time     purple_presence_get_login_time
-#define gaim_presence_compare            purple_presence_compare
-
-#define gaim_status_get_handle  purple_status_get_handle
-#define gaim_status_init        purple_status_init
-#define gaim_status_uninit      purple_status_uninit
-
-/* from stringref.h */
-
-#define GaimStringref  PurpleStringref
-
-#define gaim_stringref_new        purple_stringref_new
-#define gaim_stringref_new_noref  purple_stringref_new_noref
-#define gaim_stringref_printf     purple_stringref_printf
-#define gaim_stringref_ref        purple_stringref_ref
-#define gaim_stringref_unref      purple_stringref_unref
-#define gaim_stringref_value      purple_stringref_value
-#define gaim_stringref_cmp        purple_stringref_cmp
-#define gaim_stringref_len        purple_stringref_len
-
-/* from stun.h */
-
-#define GaimStunNatDiscovery  PurpleStunNatDiscovery
-
-#define GAIM_STUN_STATUS_UNDISCOVERED  PURPLE_STUN_STATUS_UNDISCOVERED
-#define GAIM_STUN_STATUS_UNKNOWN       PURPLE_STUN_STATUS_UNKNOWN
-#define GAIM_STUN_STATUS_DISCOVERING   PURPLE_STUN_STATUS_DISCOVERING
-#define GAIM_STUN_STATUS_DISCOVERED    PURPLE_STUN_STATUS_DISCOVERED
-#define GaimStunStatus  PurpleStunStatus
-
-#define GAIM_STUN_NAT_TYPE_PUBLIC_IP             PURPLE_STUN_NAT_TYPE_PUBLIC_IP
-#define GAIM_STUN_NAT_TYPE_UNKNOWN_NAT           PURPLE_STUN_NAT_TYPE_UNKNOWN_NAT
-#define GAIM_STUN_NAT_TYPE_FULL_CONE             PURPLE_STUN_NAT_TYPE_FULL_CONE
-#define GAIM_STUN_NAT_TYPE_RESTRICTED_CONE       PURPLE_STUN_NAT_TYPE_RESTRICTED_CONE
-#define GAIM_STUN_NAT_TYPE_PORT_RESTRICTED_CONE  PURPLE_STUN_NAT_TYPE_PORT_RESTRICTED_CONE
-#define GAIM_STUN_NAT_TYPE_SYMMETRIC             PURPLE_STUN_NAT_TYPE_SYMMETRIC
-#define GaimStunNatType  PurpleStunNatType
-
-/* why didn't this have a Gaim prefix before? */
-#define StunCallback  PurpleStunCallback
-
-#define gaim_stun_discover  purple_stun_discover
-#define gaim_stun_init      purple_stun_init
-
-/* from upnp.h */
-
-/* suggested rename: PurpleUPnpMappingHandle */
-#define UPnPMappingAddRemove  PurpleUPnPMappingAddRemove
-
-#define GaimUPnPCallback  PurpleUPnPCallback
-
-#define gaim_upnp_discover             purple_upnp_discover
-#define gaim_upnp_get_public_ip        purple_upnp_get_public_ip
-#define gaim_upnp_cancel_port_mapping  purple_upnp_cancel_port_mapping
-#define gaim_upnp_set_port_mapping     purple_upnp_set_port_mapping
-
-#define gaim_upnp_remove_port_mapping  purple_upnp_remove_port_mapping
-
-/* from util.h */
-
-#define GaimUtilFetchUrlData  PurpleUtilFetchUrlData
-#define GaimMenuAction        PurpleMenuAction
-
-#define GaimInfoFieldFormatCallback  PurpleIntoFieldFormatCallback
-
-#define GaimKeyValuePair  PurpleKeyValuePair
-
-#define gaim_menu_action_new   purple_menu_action_new
-#define gaim_menu_action_free  purple_menu_action_free
-
-#define gaim_base16_encode   purple_base16_encode
-#define gaim_base16_decode   purple_base16_decode
-#define gaim_base64_encode   purple_base64_encode
-#define gaim_base64_decode   purple_base64_decode
-#define gaim_quotedp_decode  purple_quotedp_decode
-
-#define gaim_mime_decode_field  purple_mime_deco_field
-
-#define gaim_utf8_strftime      purple_utf8_strftime
-#define gaim_date_format_short  purple_date_format_short
-#define gaim_date_format_long   purple_date_format_long
-#define gaim_date_format_full   purple_date_format_full
-#define gaim_time_format        purple_time_format
-#define gaim_time_build         purple_time_build
-
-#define GAIM_NO_TZ_OFF  PURPLE_NO_TZ_OFF
-
-#define gaim_str_to_time  purple_str_to_time
-
-#define gaim_markup_find_tag            purple_markup_find_tag
-#define gaim_markup_extract_info_field  purple_markup_extract_info_field
-#define gaim_markup_html_to_xhtml       purple_markup_html_to_xhtml
-#define gaim_markup_strip_html          purple_markup_strip_html
-#define gaim_markup_linkify             purple_markup_linkify
-#define gaim_markup_slice               purple_markup_slice
-#define gaim_markup_get_tag_name        purple_markup_get_tag_name
-#define gaim_unescape_html              purple_unescape_html
-
-#define gaim_home_dir  purple_home_dir
-#define gaim_user_dir  purple_user_dir
-
-#define gaim_util_set_user_dir  purple_util_set_user_dir
-
-#define gaim_build_dir  purple_build_dir
-
-#define gaim_util_write_data_to_file  purple_util_write_data_to_file
-
-#define gaim_util_read_xml_from_file  purple_util_read_xml_from_file
-
-#define gaim_mkstemp  purple_mkstemp
-
-#define gaim_program_is_valid  purple_program_is_valid
-
-#define gaim_running_gnome  purple_running_gnome
-#define gaim_running_kde    purple_running_kde
-#define gaim_running_osx    purple_running_osx
-
-#define gaim_fd_get_ip  purple_fd_get_ip
-
-#define gaim_normalize         purple_normalize
-#define gaim_normalize_nocase  purple_normalize_nocase
-
-#define gaim_strdup_withhtml  purple_strdup_withhtml
-
-#define gaim_str_has_prefix  purple_str_has_prefix
-#define gaim_str_has_suffix  purple_str_has_suffix
-#define gaim_str_add_cr      purple_str_add_cr
-#define gaim_str_strip_char  purple_str_strip_char
-
-#define gaim_util_chrreplace  purple_util_chrreplace
-
-#define gaim_strreplace  purple_strreplace
-
-#define gaim_utf8_ncr_encode  purple_utf8_ncr_encode
-#define gaim_utf8_ncr_decode  purple_utf8_ncr_decode
-
-#define gaim_strcasereplace  purple_strcasereplace
-#define gaim_strcasestr      purple_strcasestr
-
-#define gaim_str_size_to_units      purple_str_size_to_units
-#define gaim_str_seconds_to_string  purple_str_seconds_to_string
-#define gaim_str_binary_to_ascii    purple_str_binary_to_ascii
-
-
-#define gaim_got_protocol_handler_uri  purple_got_protocol_handler_uri
-
-#define gaim_url_parse  purple_url_parse
-
-#define GaimUtilFetchUrlCallback  PurpleUtilFetchUrlCallback
-#define gaim_util_fetch_url          purple_util_fetch_url
-#define gaim_util_fetch_url_request  purple_util_fetch_url_request
-#define gaim_util_fetch_url_cancel   purple_util_fetch_url_cancel
-
-#define gaim_url_decode  purple_url_decode
-#define gaim_url_encode  purple_url_encode
-
-#define gaim_email_is_valid  purple_email_is_valid
-
-#define gaim_uri_list_extract_uris       purple_uri_list_extract_uris
-#define gaim_uri_list_extract_filenames  purple_uri_list_extract_filenames
-
-#define gaim_utf8_try_convert  purple_utf8_try_convert
-#define gaim_utf8_salvage      purple_utf8_salvage
-#define gaim_utf8_strcasecmp   purple_utf8_strcasecmp
-#define gaim_utf8_has_word     purple_utf8_has_word
-
-#define gaim_print_utf8_to_console  purple_print_utf8_to_console
-
-#define gaim_message_meify  purple_message_meify
-
-#define gaim_text_strip_mnemonic  purple_text_strip_mnemonic
-
-#define gaim_unescape_filename  purple_unescape_filename
-#define gaim_escape_filename    purple_escape_filename
-
-/* from value.h */
-
-#define GAIM_TYPE_UNKNOWN  PURPLE_TYPE_UNKNOWN
-#define GAIM_TYPE_SUBTYPE  PURPLE_TYPE_SUBTYPE
-#define GAIM_TYPE_CHAR     PURPLE_TYPE_CHAR
-#define GAIM_TYPE_UCHAR    PURPLE_TYPE_UCHAR
-#define GAIM_TYPE_BOOLEAN  PURPLE_TYPE_BOOLEAN
-#define GAIM_TYPE_SHORT    PURPLE_TYPE_SHORT
-#define GAIM_TYPE_USHORT   PURPLE_TYPE_USHORT
-#define GAIM_TYPE_INT      PURPLE_TYPE_INT
-#define GAIM_TYPE_UINT     PURPLE_TYPE_UINT
-#define GAIM_TYPE_LONG     PURPLE_TYPE_LONG
-#define GAIM_TYPE_ULONG    PURPLE_TYPE_ULONG
-#define GAIM_TYPE_INT64    PURPLE_TYPE_INT64
-#define GAIM_TYPE_UINT64   PURPLE_TYPE_UINT64
-#define GAIM_TYPE_STRING   PURPLE_TYPE_STRING
-#define GAIM_TYPE_OBJECT   PURPLE_TYPE_OBJECT
-#define GAIM_TYPE_POINTER  PURPLE_TYPE_POINTER
-#define GAIM_TYPE_ENUM     PURPLE_TYPE_ENUM
-#define GAIM_TYPE_BOXED    PURPLE_TYPE_BOXED
-#define GaimType  PurpleType
-
-
-#define GAIM_SUBTYPE_UNKNOWN       PURPLE_SUBTYPE_UNKNOWN
-#define GAIM_SUBTYPE_ACCOUNT       PURPLE_SUBTYPE_ACCOUNT
-#define GAIM_SUBTYPE_BLIST         PURPLE_SUBTYPE_BLIST
-#define GAIM_SUBTYPE_BLIST_BUDDY   PURPLE_SUBTYPE_BLIST_BUDDY
-#define GAIM_SUBTYPE_BLIST_GROUP   PURPLE_SUBTYPE_BLIST_GROUP
-#define GAIM_SUBTYPE_BLIST_CHAT    PURPLE_SUBTYPE_BLIST_CHAT
-#define GAIM_SUBTYPE_BUDDY_ICON    PURPLE_SUBTYPE_BUDDY_ICON
-#define GAIM_SUBTYPE_CONNECTION    PURPLE_SUBTYPE_CONNECTION
-#define GAIM_SUBTYPE_CONVERSATION  PURPLE_SUBTYPE_CONVERSATION
-#define GAIM_SUBTYPE_PLUGIN        PURPLE_SUBTYPE_PLUGIN
-#define GAIM_SUBTYPE_BLIST_NODE    PURPLE_SUBTYPE_BLIST_NODE
-#define GAIM_SUBTYPE_CIPHER        PURPLE_SUBTYPE_CIPHER
-#define GAIM_SUBTYPE_STATUS        PURPLE_SUBTYPE_STATUS
-#define GAIM_SUBTYPE_LOG           PURPLE_SUBTYPE_LOG
-#define GAIM_SUBTYPE_XFER          PURPLE_SUBTYPE_XFER
-#define GAIM_SUBTYPE_SAVEDSTATUS   PURPLE_SUBTYPE_SAVEDSTATUS
-#define GAIM_SUBTYPE_XMLNODE       PURPLE_SUBTYPE_XMLNODE
-#define GAIM_SUBTYPE_USERINFO      PURPLE_SUBTYPE_USERINFO
-#define GaimSubType  PurpleSubType
-
-#define GaimValue  PurpleValue
-
-#define gaim_value_new                purple_value_new
-#define gaim_value_new_outgoing       purple_value_new_outgoing
-#define gaim_value_destroy            purple_value_destroy
-#define gaim_value_dup                purple_value_dup
-#define gaim_value_purple_buddy_icon_get_extensionget_type           purple_value_get_type
-#define gaim_value_get_subtype        purple_value_get_subtype
-#define gaim_value_get_specific_type  purple_value_get_specific_type
-#define gaim_value_is_outgoing        purple_value_is_outgoing
-#define gaim_value_set_char           purple_value_set_char
-#define gaim_value_set_uchar          purple_value_set_uchar
-#define gaim_value_set_boolean        purple_value_set_boolean
-#define gaim_value_set_short          purple_value_set_short
-#define gaim_value_set_ushort         purple_value_set_ushort
-#define gaim_value_set_int            purple_value_set_int
-#define gaim_value_set_uint           purple_value_set_uint
-#define gaim_value_set_long           purple_value_set_long
-#define gaim_value_set_ulong          purple_value_set_ulong
-#define gaim_value_set_int64          purple_value_set_int64
-#define gaim_value_set_uint64         purple_value_set_uint64
-#define gaim_value_set_string         purple_value_set_string
-#define gaim_value_set_object         purple_value_set_object
-#define gaim_value_set_pointer        purple_value_set_pointer
-#define gaim_value_set_enum           purple_value_set_enum
-#define gaim_value_set_boxed          purple_value_set_boxed
-#define gaim_value_get_char           purple_value_get_char
-#define gaim_value_get_uchar          purple_value_get_uchar
-#define gaim_value_get_boolean        purple_value_get_boolean
-#define gaim_value_get_short          purple_value_get_short
-#define gaim_value_get_ushort         purple_value_get_ushort
-#define gaim_value_get_int            purple_value_get_int
-#define gaim_value_get_uint           purple_value_get_uint
-#define gaim_value_get_long           purple_value_get_long
-#define gaim_value_get_ulong          purple_value_get_ulong
-#define gaim_value_get_int64          purple_value_get_int64
-#define gaim_value_get_uint64         purple_value_get_uint64
-#define gaim_value_get_string         purple_value_get_string
-#define gaim_value_get_object         purple_value_get_object
-#define gaim_value_get_pointer        purple_value_get_pointer
-#define gaim_value_get_enum           purple_value_get_enum
-#define gaim_value_get_boxed          purple_value_get_boxed
-
-/* from version.h */
-
-#define GAIM_MAJOR_VERSION  PURPLE_MAJOR_VERSION
-#define GAIM_MINOR_VERSION  PURPLE_MINOR_VERSION
-#define GAIM_MICRO_VERSION  PURPLE_MICRO_VERSION
-
-#define GAIM_VERSION_CHECK  PURPLE_VERSION_CHECK
-
-/* from whiteboard.h */
-
-#define GaimWhiteboardPrplOps  PurpleWhiteboardPrplOps
-#define GaimWhiteboard         PurpleWhiteboard
-#define GaimWhiteboardUiOps    PurpleWhiteboardUiOps
-
-#define gaim_whiteboard_set_ui_ops    purple_whiteboard_set_ui_ops
-#define gaim_whiteboard_set_prpl_ops  purple_whiteboard_set_prpl_ops
-
-#define gaim_whiteboard_create             purple_whiteboard_create
-#define gaim_whiteboard_destroy            purple_whiteboard_destroy
-#define gaim_whiteboard_start              purple_whiteboard_start
-#define gaim_whiteboard_get_session        purple_whiteboard_get_session
-#define gaim_whiteboard_draw_list_destroy  purple_whiteboard_draw_list_destroy
-#define gaim_whiteboard_get_dimensions     purple_whiteboard_get_dimensions
-#define gaim_whiteboard_set_dimensions     purple_whiteboard_set_dimensions
-#define gaim_whiteboard_draw_point         purple_whiteboard_draw_point
-#define gaim_whiteboard_send_draw_list     purple_whiteboard_send_draw_list
-#define gaim_whiteboard_draw_line          purple_whiteboard_draw_line
-#define gaim_whiteboard_clear              purple_whiteboard_clear
-#define gaim_whiteboard_send_clear         purple_whiteboard_send_clear
-#define gaim_whiteboard_send_brush         purple_whiteboard_send_brush
-#define gaim_whiteboard_get_brush          purple_whiteboard_get_brush
-#define gaim_whiteboard_set_brush          purple_whiteboard_set_brush
-
-/* for static plugins */
-#define gaim_init_ssl_plugin			purple_init_ssl_plugin
-#define gaim_init_ssl_openssl_plugin	purple_init_ssl_openssl_plugin
-#define gaim_init_ssl_gnutls_plugin		purple_init_ssl_gnutls_plugin
-#define gaim_init_gg_plugin				purple_init_gg_plugin
-#define gaim_init_jabber_plugin			purple_init_jabber_plugin
-#define gaim_init_sametime_plugin		purple_init_sametime_plugin
-#define gaim_init_msn_plugin			purple_init_msn_plugin
-#define gaim_init_novell_plugin			purple_init_novell_plugin
-#define gaim_init_qq_plugin				purple_init_qq_plugin
-#define gaim_init_simple_plugin			purple_init_simple_plugin
-#define gaim_init_yahoo_plugin			purple_init_yahoo_plugin
-#define gaim_init_zephyr_plugin			purple_init_zephyr_plugin
-#define gaim_init_aim_plugin			purple_init_aim_plugin
-#define gaim_init_icq_plugin			purple_init_icq_plugin
-
-#endif /* _GAIM_COMPAT_H_ */
--- a/libpurple/gconf/Makefile.am	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/gconf/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -2,6 +2,7 @@
 
 EXTRA_DIST = purple.schemas.in
 
+if INSTALL_I18N
 schema_in_files = purple.schemas.in
 schema_DATA = $(schema_in_files:.schemas.in=.schemas)
 @INTLTOOL_SCHEMAS_RULE@
@@ -12,4 +13,6 @@
 		grep -v "^WARNING: failed to install schema" | grep -v "^Attached schema" 1>&2
 else
 install-data-local:
-endif
+endif #GCONF_SCHEMAS_INSTALL
+
+endif #INSTALL_I18N
--- a/libpurple/idle.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/idle.h	Wed Jun 13 19:30:27 2012 -0400
@@ -26,6 +26,8 @@
 #ifndef _PURPLE_IDLE_H_
 #define _PURPLE_IDLE_H_
 
+#include <time.h>
+
 /**
  * Idle UI operations.
  */
@@ -39,9 +41,7 @@
 	void (*_purple_reserved4)(void);
 } PurpleIdleUiOps;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Idle API                                                        */
@@ -95,8 +95,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_IDLE_H_ */
--- a/libpurple/imgstore.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/imgstore.c	Wed Jun 13 19:30:27 2012 -0400
@@ -25,7 +25,6 @@
  *
 */
 
-#include <glib.h>
 #include "internal.h"
 
 #include "dbus-maybe.h"
--- a/libpurple/imgstore.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/imgstore.h	Wed Jun 13 19:30:27 2012 -0400
@@ -34,9 +34,7 @@
  */
 typedef struct _PurpleStoredImage PurpleStoredImage;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Add an image to the store.
@@ -68,7 +66,6 @@
  * @param path  The path to the image.
  *
  * @return  The stored image.
- * @since 2.X.X
  */
 PurpleStoredImage *
 purple_imgstore_new_from_file(const char *path);
@@ -207,8 +204,6 @@
  */
 void purple_imgstore_uninit(void);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_IMGSTORE_H_ */
--- a/libpurple/internal.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/internal.h	Wed Jun 13 19:30:27 2012 -0400
@@ -118,36 +118,13 @@
 # include <unistd.h>
 #endif
 
-/* MAXPATHLEN should only be used with readlink() on glib < 2.4.0.  For
- * anything else, use g_file_read_link() or other dynamic functions.  This is
- * important because Hurd has no hard limits on path length. */
-#if !GLIB_CHECK_VERSION(2,4,0)
-# ifndef MAXPATHLEN
-#  ifdef PATH_MAX
-#   define MAXPATHLEN PATH_MAX
-#  else
-#   define MAXPATHLEN 1024
-#  endif
-# endif
-#endif
-
 #ifndef HOST_NAME_MAX
 # define HOST_NAME_MAX 255
 #endif
 
 #include <glib.h>
-#if !GLIB_CHECK_VERSION(2,4,0)
-#	define G_MAXUINT32 ((guint32) 0xffffffff)
-#endif
 
-#ifndef G_MAXSIZE
-#	if GLIB_SIZEOF_LONG == 8
-#		define G_MAXSIZE ((gsize) 0xffffffffffffffff)
-#	else
-#		define G_MAXSIZE ((gsize) 0xffffffff)
-#	endif
-#endif
-
+/* This wasn't introduced until Glib 2.14 :( */
 #ifndef G_MAXSSIZE
 #	if GLIB_SIZEOF_LONG == 8
 #		define G_MAXSSIZE ((gssize) 0x7fffffffffffffff)
@@ -156,80 +133,12 @@
 #	endif
 #endif
 
-#if GLIB_CHECK_VERSION(2,6,0)
-#	include <glib/gstdio.h>
-#endif
-
-#if !GLIB_CHECK_VERSION(2,6,0)
-#	define g_freopen freopen
-#	define g_fopen fopen
-#	define g_rmdir rmdir
-#	define g_remove remove
-#	define g_unlink unlink
-#	define g_lstat lstat
-#	define g_stat stat
-#	define g_mkdir mkdir
-#	define g_rename rename
-#	define g_open open
-#endif
-
-#if !GLIB_CHECK_VERSION(2,8,0) && !defined _WIN32
-#	define g_access access
-#endif
-
-#if !GLIB_CHECK_VERSION(2,10,0)
-#	define g_slice_new(type) g_new(type, 1)
-#	define g_slice_new0(type) g_new0(type, 1)
-#	define g_slice_free(type, mem) g_free(mem)
-#endif
+#include <glib/gstdio.h>
 
 #ifdef _WIN32
 #include "win32dep.h"
 #endif
 
-/* ugly ugly ugly */
-/* This is a workaround for the fact that G_GINT64_MODIFIER and G_GSIZE_FORMAT
- * are only defined in Glib >= 2.4 */
-#ifndef G_GINT64_MODIFIER
-#	if GLIB_SIZEOF_LONG == 8
-#		define G_GINT64_MODIFIER "l"
-#	else
-#		define G_GINT64_MODIFIER "ll"
-#	endif
-#endif
-
-#ifndef G_GSIZE_MODIFIER
-#	if GLIB_SIZEOF_LONG == 8
-#		define G_GSIZE_MODIFIER "l"
-#	else
-#		define G_GSIZE_MODIFIER ""
-#	endif
-#endif
-
-#ifndef G_GSIZE_FORMAT
-#	if GLIB_SIZEOF_LONG == 8
-#		define G_GSIZE_FORMAT "lu"
-#	else
-#		define G_GSIZE_FORMAT "u"
-#	endif
-#endif
-
-#ifndef G_GSSIZE_FORMAT
-#	if GLIB_SIZEOF_LONG == 8
-#		define G_GSSIZE_FORMAT "li"
-#	else
-#		define G_GSSIZE_FORMAT "i"
-#	endif
-#endif
-
-#ifndef G_GNUC_NULL_TERMINATED
-#	if     __GNUC__ >= 4
-#		define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
-#	else
-#		define G_GNUC_NULL_TERMINATED
-#	endif
-#endif
-
 #ifdef HAVE_CONFIG_H
 #if SIZEOF_TIME_T == 4
 #	define PURPLE_TIME_T_MODIFIER "lu"
@@ -242,44 +151,6 @@
 
 #include <glib-object.h>
 
-#ifndef G_DEFINE_TYPE
-#define G_DEFINE_TYPE(TypeName, type_name, TYPE_PARENT) \
-\
-static void     type_name##_init              (TypeName        *self); \
-static void     type_name##_class_init        (TypeName##Class *klass); \
-static gpointer type_name##_parent_class = NULL; \
-static void     type_name##_class_intern_init (gpointer klass) \
-{ \
-  type_name##_parent_class = g_type_class_peek_parent (klass); \
-  type_name##_class_init ((TypeName##Class*) klass); \
-} \
-\
-GType \
-type_name##_get_type (void) \
-{ \
-  static GType g_define_type_id = 0; \
-  if (G_UNLIKELY (g_define_type_id == 0)) \
-    { \
-      g_define_type_id = \
-        g_type_register_static_simple (TYPE_PARENT, \
-                                       g_intern_static_string (#TypeName), \
-                                       sizeof (TypeName##Class), \
-                                       (GClassInitFunc)type_name##_class_intern_init, \
-                                       sizeof (TypeName), \
-                                       (GInstanceInitFunc)type_name##_init, \
-                                       (GTypeFlags) 0); \
-    }					\
-  return g_define_type_id;		\
-} /* closes type_name##_get_type() */
-
-#endif
-
-/* Safer ways to work with static buffers. When using non-static
- * buffers, either use g_strdup_* functions (preferred) or use
- * g_strlcpy/g_strlcpy directly. */
-#define purple_strlcpy(dest, src) g_strlcpy(dest, src, sizeof(dest))
-#define purple_strlcat(dest, src) g_strlcat(dest, src, sizeof(dest))
-
 #define PURPLE_WEBSITE "http://pidgin.im/"
 #define PURPLE_DEVEL_WEBSITE "http://developer.pidgin.im/"
 
@@ -299,12 +170,6 @@
 void
 _purple_buddy_icons_blist_loaded_cb(void);
 
-/* This is for the purple_core_migrate() code to tell the buddy
- * icon subsystem about the old icons directory so it can
- * migrate any icons in use. */
-void
-_purple_buddy_icon_set_old_icons_dir(const char *dirname);
-
 /**
  * Creates a connection to the specified account and either connects
  * or attempts to register a new account.  If you are logging in,
--- a/libpurple/log.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/log.c	Wed Jun 13 19:30:27 2012 -0400
@@ -302,7 +302,7 @@
 			}
 		}
 
-		score = (gint)score_double;
+		score = (gint) ceil(score_double);
 		g_hash_table_replace(logsize_users_decayed, lu, GINT_TO_POINTER(score));
 	}
 	return score;
@@ -1103,7 +1103,7 @@
 			/* Find the account for username in the list of accounts for protocol. */
 			username_unescaped = purple_unescape_filename(username);
 			for (account_iter = g_list_first(accounts) ; account_iter != NULL ; account_iter = account_iter->next) {
-				if (purple_strequal(((PurpleAccount *)account_iter->data)->username, username_unescaped)) {
+				if (purple_strequal(purple_account_get_username((PurpleAccount *)account_iter->data), username_unescaped)) {
 					account = account_iter->data;
 					break;
 				}
@@ -1157,6 +1157,7 @@
 			g_dir_close(username_dir);
 		}
 		g_free(protocol_path);
+		g_list_free(accounts);
 		g_dir_close(protocol_dir);
 	}
 	g_free(log_path);
@@ -1375,6 +1376,7 @@
 	char *image_corrected_msg;
 	char *date;
 	char *header;
+	char *escaped_from;
 	PurplePlugin *plugin = purple_find_prpl(purple_account_get_protocol_id(log->account));
 	PurpleLogCommonLoggerData *data = log->logger_data;
 	gsize written = 0;
@@ -1413,6 +1415,8 @@
 	if(!data->file)
 		return 0;
 
+	escaped_from = g_markup_escape_text(from, -1);
+
 	image_corrected_msg = convert_image_tags(log, message);
 	purple_markup_html_to_xhtml(image_corrected_msg, &msg_fixed, NULL);
 
@@ -1434,34 +1438,35 @@
 			written += fprintf(data->file, "<font color=\"#FF0000\"><font size=\"2\">(%s)</font><b> %s</b></font><br/>\n", date, msg_fixed);
 		else if (type & PURPLE_MESSAGE_WHISPER)
 			written += fprintf(data->file, "<font color=\"#6C2585\"><font size=\"2\">(%s)</font><b> %s:</b></font> %s<br/>\n",
-					date, from, msg_fixed);
+					date, escaped_from, msg_fixed);
 		else if (type & PURPLE_MESSAGE_AUTO_RESP) {
 			if (type & PURPLE_MESSAGE_SEND)
-				written += fprintf(data->file, _("<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b></font> %s<br/>\n"), date, from, msg_fixed);
+				written += fprintf(data->file, _("<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b></font> %s<br/>\n"), date, escaped_from, msg_fixed);
 			else if (type & PURPLE_MESSAGE_RECV)
-				written += fprintf(data->file, _("<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b></font> %s<br/>\n"), date, from, msg_fixed);
+				written += fprintf(data->file, _("<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s &lt;AUTO-REPLY&gt;:</b></font> %s<br/>\n"), date, escaped_from, msg_fixed);
 		} else if (type & PURPLE_MESSAGE_RECV) {
 			if(purple_message_meify(msg_fixed, -1))
 				written += fprintf(data->file, "<font color=\"#062585\"><font size=\"2\">(%s)</font> <b>***%s</b></font> %s<br/>\n",
-						date, from, msg_fixed);
+						date, escaped_from, msg_fixed);
 			else
 				written += fprintf(data->file, "<font color=\"#A82F2F\"><font size=\"2\">(%s)</font> <b>%s:</b></font> %s<br/>\n",
-						date, from, msg_fixed);
+						date, escaped_from, msg_fixed);
 		} else if (type & PURPLE_MESSAGE_SEND) {
 			if(purple_message_meify(msg_fixed, -1))
 				written += fprintf(data->file, "<font color=\"#062585\"><font size=\"2\">(%s)</font> <b>***%s</b></font> %s<br/>\n",
-						date, from, msg_fixed);
+						date, escaped_from, msg_fixed);
 			else
 				written += fprintf(data->file, "<font color=\"#16569E\"><font size=\"2\">(%s)</font> <b>%s:</b></font> %s<br/>\n",
-						date, from, msg_fixed);
+						date, escaped_from, msg_fixed);
 		} else {
 			purple_debug_error("log", "Unhandled message type.\n");
 			written += fprintf(data->file, "<font size=\"2\">(%s)</font><b> %s:</b></font> %s<br/>\n",
-						date, from, msg_fixed);
+						date, escaped_from, msg_fixed);
 		}
 	}
 	g_free(date);
 	g_free(msg_fixed);
+	g_free(escaped_from);
 	fflush(data->file);
 
 	return written;
@@ -1677,7 +1682,6 @@
 	struct tm tm;
 	char month[4];
 	struct old_logger_data *data = NULL;
-	char *newlog;
 	int logfound = 0;
 	int lastoff = 0;
 	int newlen;
@@ -1779,7 +1783,7 @@
 	}
 
 	while (fgets(buf, BUF_LONG, file)) {
-		if ((newlog = strstr(buf, "---- New C"))) {
+		if (strstr(buf, "---- New C") != NULL) {
 			int length;
 			int offset;
 			char convostart[32];
@@ -1834,7 +1838,7 @@
 
 			g_snprintf(convostart, length, "%s", temp);
 			memset(&tm, 0, sizeof(tm));
-			sscanf(convostart, "%*s %s %d %d:%d:%d %d",
+			sscanf(convostart, "%*s %3s %d %d:%d:%d %d",
 			       month, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tm.tm_year);
 			/* Ugly hack, in case current locale is not English */
 			if (purple_strequal(month, "Jan")) {
--- a/libpurple/log.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/log.h	Wed Jun 13 19:30:27 2012 -0400
@@ -181,9 +181,7 @@
 	 * IMPORTANT: Update that code if you add members here. */
 };
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /***************************************/
 /** @name Log Functions                */
@@ -301,8 +299,6 @@
  * @param name                The name of the log
  * @param account             The account
  * @return                    The activity score
- *
- * @since 2.6.0
  */
 int purple_log_get_activity_score(PurpleLogType type, const char *name, PurpleAccount *account);
 
@@ -576,8 +572,6 @@
 /*@}*/
 
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_LOG_H_ */
--- a/libpurple/marshallers.list	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/marshallers.list	Wed Jun 13 19:30:27 2012 -0400
@@ -1,5 +1,8 @@
 VOID:POINTER,POINTER,OBJECT
 BOOLEAN:OBJECT,POINTER,STRING
 VOID:STRING,STRING
+VOID:STRING,STRING,DOUBLE
 VOID:ENUM,STRING,STRING
 VOID:ENUM,STRING,STRING,BOOLEAN
+VOID:FLAGS,FLAGS
+VOID:STRING,STRING,OBJECT,OBJECT
--- a/libpurple/media-gst.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/media-gst.h	Wed Jun 13 19:30:27 2012 -0400
@@ -21,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
 #ifndef _PURPLE_MEDIA_GST_H_
@@ -32,8 +32,6 @@
 
 #include <gst/gst.h>
 
-G_BEGIN_DECLS
-
 #define PURPLE_TYPE_MEDIA_ELEMENT_TYPE           (purple_media_element_type_get_type())
 #define PURPLE_TYPE_MEDIA_ELEMENT_INFO           (purple_media_element_info_get_type())
 #define PURPLE_MEDIA_ELEMENT_INFO(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfo))
@@ -42,7 +40,7 @@
 #define PURPLE_IS_MEDIA_ELEMENT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_ELEMENT_INFO))
 #define PURPLE_MEDIA_ELEMENT_INFO_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfo))
 
-/** @copydoc _PurpleMediaElementInfo */
+/** An opaque structure representing an audio/video source/sink. */
 typedef struct _PurpleMediaElementInfo PurpleMediaElementInfo;
 typedef struct _PurpleMediaElementInfoClass PurpleMediaElementInfoClass;
 typedef GstElement *(*PurpleMediaElementCreateCallback)(PurpleMedia *media,
@@ -73,16 +71,12 @@
 	PURPLE_MEDIA_ELEMENT_SINK = 1 << 10,		/** can be set as an active sink */
 } PurpleMediaElementType;
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Gets the element type's GType.
  *
  * @return The element type's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_element_type_get_type(void);
 
@@ -90,8 +84,6 @@
  * Gets the element info's GType.
  *
  * @return The element info's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_element_info_get_type(void);
 
@@ -102,8 +94,6 @@
  * @param sess_id The session id of the session to get the source from.
  *
  * @return The source retrieved.
- *
- * @since 2.6.0
  */
 GstElement *purple_media_get_src(PurpleMedia *media, const gchar *sess_id);
 
@@ -115,8 +105,6 @@
  * @param participant Optionally, the participant of the stream to get the tee from.
  *
  * @return The GstTee element from the chosen session/stream.
- *
- * @since 2.6.0
  */
 GstElement *purple_media_get_tee(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant);
@@ -128,8 +116,6 @@
  * @param manager The media manager to get the pipeline from.
  *
  * @return The pipeline.
- *
- * @since 2.6.0
  */
 GstElement *purple_media_manager_get_pipeline(PurpleMediaManager *manager);
 
@@ -138,8 +124,9 @@
  *
  * @param manager The media manager to use to obtain the source/sink.
  * @param type The type of source/sink to get.
- *
- * @since 2.6.0
+ * @param media The media call this element is requested for.
+ * @param session_id The id of the session this element is requested for or NULL.
+ * @param participant The remote user this element is requested for or NULL.
  */
 GstElement *purple_media_manager_get_element(PurpleMediaManager *manager,
 		PurpleMediaSessionType type, PurpleMedia *media,
@@ -156,6 +143,27 @@
 PurpleMediaElementInfo *purple_media_manager_get_active_element(
 		PurpleMediaManager *manager, PurpleMediaElementType type);
 
+/**
+ * Reduces media formats supported by the video source to given set.
+ *
+ * Useful to force negotiation of smaller picture resolution more suitable for
+ * use with particular codec and communication protocol without rescaling.
+ *
+ * @param manager The media manager to set the media formats.
+ * @param caps Set of allowed media formats.
+ */
+void purple_media_manager_set_video_caps(PurpleMediaManager *manager,
+		GstCaps *caps);
+
+/**
+ * Returns current set of media formats limiting the output from video source.
+ *
+ * @param manager The media manager to get the media formats from.
+ *
+ * @return @c GstCaps limiting the video source's formats.
+ */
+GstCaps *purple_media_manager_get_video_caps(PurpleMediaManager *manager);
+
 gchar *purple_media_element_info_get_id(PurpleMediaElementInfo *info);
 gchar *purple_media_element_info_get_name(PurpleMediaElementInfo *info);
 PurpleMediaElementType purple_media_element_info_get_element_type(
@@ -164,10 +172,6 @@
 		PurpleMediaElementInfo *info, PurpleMedia *media,
 		const gchar *session_id, const gchar *participant);
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif  /* _PURPLE_MEDIA_GST_H_ */
--- a/libpurple/media.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/media.c	Wed Jun 13 19:30:27 2012 -0400
@@ -21,30 +21,25 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-
-#include <string.h>
-
 #include "internal.h"
 
 #include "account.h"
 #include "media.h"
+#include "media/backend-iface.h"
 #include "mediamanager.h"
-#include "network.h"
 
 #include "debug.h"
 
 #ifdef USE_GSTREAMER
+#include "media/backend-fs2.h"
 #include "marshallers.h"
 #include "media-gst.h"
 #endif
 
 #ifdef USE_VV
 
-#include <gst/farsight/fs-conference-iface.h>
-#include <gst/farsight/fs-element-added-notifier.h>
-
 /** @copydoc _PurpleMediaSession */
 typedef struct _PurpleMediaSession PurpleMediaSession;
 /** @copydoc _PurpleMediaStream */
@@ -53,14 +48,6 @@
 typedef struct _PurpleMediaClass PurpleMediaClass;
 /** @copydoc _PurpleMediaPrivate */
 typedef struct _PurpleMediaPrivate PurpleMediaPrivate;
-/** @copydoc _PurpleMediaCandidateClass */
-typedef struct _PurpleMediaCandidateClass PurpleMediaCandidateClass;
-/** @copydoc _PurpleMediaCandidatePrivate */
-typedef struct _PurpleMediaCandidatePrivate PurpleMediaCandidatePrivate;
-/** @copydoc _PurpleMediaCodecClass */
-typedef struct _PurpleMediaCodecClass PurpleMediaCodecClass;
-/** @copydoc _PurpleMediaCodecPrivate */
-typedef struct _PurpleMediaCodecPrivate PurpleMediaCodecPrivate;
 
 /** The media class */
 struct _PurpleMediaClass
@@ -79,10 +66,6 @@
 {
 	gchar *id;
 	PurpleMedia *media;
-	GstElement *src;
-	GstElement *tee;
-	FsSession *session;
-
 	PurpleMediaSessionType type;
 	gboolean initiator;
 };
@@ -91,9 +74,6 @@
 {
 	PurpleMediaSession *session;
 	gchar *participant;
-	FsStream *stream;
-	GstElement *src;
-	GstElement *tee;
 
 	GList *local_candidates;
 	GList *remote_candidates;
@@ -104,8 +84,6 @@
 
 	GList *active_local_candidates;
 	GList *active_remote_candidates;
-
-	guint connected_cb_id;
 };
 #endif
 
@@ -114,16 +92,14 @@
 #ifdef USE_VV
 	PurpleMediaManager *manager;
 	PurpleAccount *account;
-	FsConference *conference;
+	PurpleMediaBackend *backend;
+	gchar *conference_type;
 	gboolean initiator;
 	gpointer prpl_data;
 
 	GHashTable *sessions;	/* PurpleMediaSession table */
-	GHashTable *participants; /* FsParticipant table */
-
+	GList *participants;
 	GList *streams;		/* PurpleMediaStream table */
-
-	GstElement *confbin;
 #else
 	gpointer dummy;
 #endif
@@ -131,8 +107,6 @@
 
 #ifdef USE_VV
 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
-#define PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidatePrivate))
-#define PURPLE_MEDIA_CODEC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodecPrivate))
 
 static void purple_media_class_init (PurpleMediaClass *klass);
 static void purple_media_init (PurpleMedia *media);
@@ -141,25 +115,29 @@
 static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
 static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
 
-static void purple_media_new_local_candidate_cb(FsStream *stream,
-		FsCandidate *local_candidate, PurpleMediaSession *session);
-static void purple_media_candidates_prepared_cb(FsStream *stream,
-		PurpleMediaSession *session);
-static void purple_media_candidate_pair_established_cb(FsStream *stream,
-		FsCandidate *native_candidate, FsCandidate *remote_candidate,
-		PurpleMediaSession *session);
-static gboolean media_bus_call(GstBus *bus,
-		GstMessage *msg, PurpleMedia *media);
+static void purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *participant,
+		PurpleMediaCandidate *candidate, PurpleMedia *media);
+static void purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *name, PurpleMedia *media);
+static void purple_media_candidate_pair_established_cb(
+		PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *name,
+		PurpleMediaCandidate *local_candidate,
+		PurpleMediaCandidate *remote_candidate,
+		PurpleMedia *media);
+static void purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, PurpleMedia *media);
 
 static GObjectClass *parent_class = NULL;
 
 
 
 enum {
-	ERROR,
-	ACCEPTED,
+	S_ERROR,
 	CANDIDATES_PREPARED,
 	CODECS_CHANGED,
+	LEVEL,
 	NEW_CANDIDATE,
 	STATE_CHANGED,
 	STREAM_INFO,
@@ -170,46 +148,15 @@
 enum {
 	PROP_0,
 	PROP_MANAGER,
+	PROP_BACKEND,
 	PROP_ACCOUNT,
-	PROP_CONFERENCE,
+	PROP_CONFERENCE_TYPE,
 	PROP_INITIATOR,
 	PROP_PRPL_DATA,
 };
 #endif
 
 
-/*
- * PurpleMediaElementType
- */
-
-GType
-purple_media_session_type_get_type()
-{
-	static GType type = 0;
-	if (type == 0) {
-		static const GFlagsValue values[] = {
-			{ PURPLE_MEDIA_NONE,
-				"PURPLE_MEDIA_NONE", "none" },
-			{ PURPLE_MEDIA_RECV_AUDIO,
-				"PURPLE_MEDIA_RECV_AUDIO", "recv-audio" },
-			{ PURPLE_MEDIA_SEND_AUDIO,
-				"PURPLE_MEDIA_SEND_AUDIO", "send-audio" },
-			{ PURPLE_MEDIA_RECV_VIDEO,
-				"PURPLE_MEDIA_RECV_VIDEO", "recv-video" },
-			{ PURPLE_MEDIA_SEND_VIDEO,
-				"PURPLE_MEDIA_SEND_VIDEO", "send-audio" },
-			{ PURPLE_MEDIA_AUDIO,
-				"PURPLE_MEDIA_AUDIO", "audio" },
-			{ PURPLE_MEDIA_VIDEO,
-				"PURPLE_MEDIA_VIDEO", "video" },
-			{ 0, NULL, NULL }
-		};
-		type = g_flags_register_static(
-				"PurpleMediaSessionType", values);
-	}
-	return type;
-}
-
 GType
 purple_media_get_type()
 {
@@ -237,59 +184,13 @@
 #endif
 }
 
-GType
-purple_media_state_changed_get_type()
-{
-	static GType type = 0;
-	if (type == 0) {
-		static const GEnumValue values[] = {
-			{ PURPLE_MEDIA_STATE_NEW,
-				"PURPLE_MEDIA_STATE_NEW", "new" },
-			{ PURPLE_MEDIA_STATE_CONNECTED,
-				"PURPLE_MEDIA_STATE_CONNECTED", "connected" },
-			{ PURPLE_MEDIA_STATE_END,
-				"PURPLE_MEDIA_STATE_END", "end" },
-			{ 0, NULL, NULL }
-		};
-		type = g_enum_register_static("PurpleMediaState", values);
-	}
-	return type;
-}
-
-GType
-purple_media_info_type_get_type()
-{
-	static GType type = 0;
-	if (type == 0) {
-		static const GEnumValue values[] = {
-			{ PURPLE_MEDIA_INFO_HANGUP,
-					"PURPLE_MEDIA_INFO_HANGUP", "hangup" },
-			{ PURPLE_MEDIA_INFO_ACCEPT,
-					"PURPLE_MEDIA_INFO_ACCEPT", "accept" },
-			{ PURPLE_MEDIA_INFO_REJECT,
-					"PURPLE_MEDIA_INFO_REJECT", "reject" },
-			{ PURPLE_MEDIA_INFO_MUTE,
-					"PURPLE_MEDIA_INFO_MUTE", "mute" },
-			{ PURPLE_MEDIA_INFO_UNMUTE,
-					"PURPLE_MEDIA_INFO_UNMUTE", "unmute" },
-			{ PURPLE_MEDIA_INFO_HOLD,
-					"PURPLE_MEDIA_INFO_HOLD", "hold" },
-			{ PURPLE_MEDIA_INFO_UNHOLD,
-					"PURPLE_MEDIA_INFO_HOLD", "unhold" },
-			{ 0, NULL, NULL }
-		};
-		type = g_enum_register_static("PurpleMediaInfoType", values);
-	}
-	return type;
-}
-
 #ifdef USE_VV
 static void
 purple_media_class_init (PurpleMediaClass *klass)
 {
 	GObjectClass *gobject_class = (GObjectClass*)klass;
 	parent_class = g_type_class_peek_parent(klass);
-	
+
 	gobject_class->dispose = purple_media_dispose;
 	gobject_class->finalize = purple_media_finalize;
 	gobject_class->set_property = purple_media_set_property;
@@ -302,18 +203,30 @@
 			PURPLE_TYPE_MEDIA_MANAGER,
 			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
 
+	/*
+	 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
+	 * like interfaces because they "aren't GObjects"
+	 */
+	g_object_class_install_property(gobject_class, PROP_BACKEND,
+			g_param_spec_object("backend",
+			"Purple Media Backend",
+			"The backend object this media object uses.",
+			G_TYPE_OBJECT,
+			G_PARAM_READABLE));
+
 	g_object_class_install_property(gobject_class, PROP_ACCOUNT,
 			g_param_spec_pointer("account",
 			"PurpleAccount",
 			"The account this media session is on.",
 			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
 
-	g_object_class_install_property(gobject_class, PROP_CONFERENCE,
-			g_param_spec_object("conference",
-			"Farsight conference",
-			"The FsConference associated with this media.",
-			FS_TYPE_CONFERENCE,
-			G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+	g_object_class_install_property(gobject_class, PROP_CONFERENCE_TYPE,
+			g_param_spec_string("conference-type",
+			"Conference Type",
+			"The type of conference that this media object "
+			"has been created to provide.",
+			NULL,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
 
 	g_object_class_install_property(gobject_class, PROP_INITIATOR,
 			g_param_spec_boolean("initiator",
@@ -328,14 +241,10 @@
 			"Data the prpl plugin set on the media session.",
 			G_PARAM_READWRITE));
 
-	purple_media_signals[ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass),
+	purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 g_cclosure_marshal_VOID__STRING,
 					 G_TYPE_NONE, 1, G_TYPE_STRING);
-	purple_media_signals[ACCEPTED] = g_signal_new("accepted", G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-					 purple_smarshal_VOID__STRING_STRING,
-					 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
 	purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 purple_smarshal_VOID__STRING_STRING,
@@ -345,6 +254,11 @@
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 g_cclosure_marshal_VOID__STRING,
 					 G_TYPE_NONE, 1, G_TYPE_STRING);
+	purple_media_signals[LEVEL] = g_signal_new("level", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 purple_smarshal_VOID__STRING_STRING_DOUBLE,
+					 G_TYPE_NONE, 3, G_TYPE_STRING,
+					 G_TYPE_STRING, G_TYPE_DOUBLE);
 	purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
@@ -377,21 +291,19 @@
 	if (stream == NULL)
 		return;
 
-	/* Remove the connected_cb timeout */
-	if (stream->connected_cb_id != 0)
-		purple_timeout_remove(stream->connected_cb_id);
-
 	g_free(stream->participant);
 
 	if (stream->local_candidates)
-		fs_candidate_list_destroy(stream->local_candidates);
+		purple_media_candidate_list_free(stream->local_candidates);
 	if (stream->remote_candidates)
-		fs_candidate_list_destroy(stream->remote_candidates);
+		purple_media_candidate_list_free(stream->remote_candidates);
 
 	if (stream->active_local_candidates)
-		fs_candidate_list_destroy(stream->active_local_candidates);
+		purple_media_candidate_list_free(
+				stream->active_local_candidates);
 	if (stream->active_remote_candidates)
-		fs_candidate_list_destroy(stream->active_remote_candidates);
+		purple_media_candidate_list_free(
+				stream->active_remote_candidates);
 
 	g_free(stream);
 }
@@ -410,56 +322,17 @@
 purple_media_dispose(GObject *media)
 {
 	PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
-	GList *iter = NULL;
 
 	purple_debug_info("media","purple_media_dispose\n");
 
 	purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media));
 
-	if (priv->confbin) {
-		gst_element_set_locked_state(priv->confbin, TRUE);
-		gst_element_set_state(GST_ELEMENT(priv->confbin),
-				GST_STATE_NULL);
-		gst_bin_remove(GST_BIN(purple_media_manager_get_pipeline(
-				priv->manager)), priv->confbin);
-		priv->confbin = NULL;
-		priv->conference = NULL;
-	}
-
-	for (iter = priv->streams; iter; iter = g_list_next(iter)) {
-		PurpleMediaStream *stream = iter->data;
-		if (stream->stream) {
-			g_object_unref(stream->stream);
-			stream->stream = NULL;
-		}
-	}
-
-	if (priv->sessions) {
-		GList *sessions = g_hash_table_get_values(priv->sessions);
-		for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
-			PurpleMediaSession *session = sessions->data;
-			if (session->session) {
-				g_object_unref(session->session);
-				session->session = NULL;
-			}
-		}
-	}
-
-	if (priv->participants) {
-		GList *participants = g_hash_table_get_values(priv->participants);
-		for (; participants; participants = g_list_delete_link(participants, participants))
-			g_object_unref(participants->data);
+	if (priv->backend) {
+		g_object_unref(priv->backend);
+		priv->backend = NULL;
 	}
 
 	if (priv->manager) {
-		GstElement *pipeline = purple_media_manager_get_pipeline(
-				priv->manager);
-		GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
-		g_signal_handlers_disconnect_matched(G_OBJECT(bus),
-				G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
-				0, 0, 0, media_bus_call, media);
-		gst_object_unref(bus);
-
 		g_object_unref(priv->manager);
 		priv->manager = NULL;
 	}
@@ -476,6 +349,10 @@
 	for (; priv->streams; priv->streams = g_list_delete_link(priv->streams, priv->streams))
 		purple_media_stream_free(priv->streams->data);
 
+	for (; priv->participants; priv->participants = g_list_delete_link(
+			priv->participants, priv->participants))
+		g_free(priv->participants->data);
+
 	if (priv->sessions) {
 		GList *sessions = g_hash_table_get_values(priv->sessions);
 		for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
@@ -488,36 +365,6 @@
 }
 
 static void
-purple_media_setup_pipeline(PurpleMedia *media)
-{
-	GstBus *bus;
-	gchar *name;
-	GstElement *pipeline;
-
-	if (media->priv->conference == NULL || media->priv->manager == NULL)
-		return;
-
-	pipeline = purple_media_manager_get_pipeline(media->priv->manager);
-
-	name = g_strdup_printf("conf_%p",
-			media->priv->conference);
-	media->priv->confbin = gst_bin_new(name);
-	g_free(name);
-
-	bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
-	g_signal_connect(G_OBJECT(bus), "message",
-			G_CALLBACK(media_bus_call), media);
-	gst_object_unref(bus);
-
-	gst_bin_add(GST_BIN(pipeline),
-			media->priv->confbin);
-	gst_bin_add(GST_BIN(media->priv->confbin),
-			GST_ELEMENT(media->priv->conference));
-	gst_element_set_state(GST_ELEMENT(media->priv->confbin),
-			GST_STATE_PLAYING);
-}
-
-static void
 purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
 	PurpleMedia *media;
@@ -527,30 +374,49 @@
 
 	switch (prop_id) {
 		case PROP_MANAGER:
-			media->priv->manager = g_value_get_object(value);
-			g_object_ref(media->priv->manager);
-
-			purple_media_setup_pipeline(media);
+			media->priv->manager = g_value_dup_object(value);
 			break;
 		case PROP_ACCOUNT:
 			media->priv->account = g_value_get_pointer(value);
 			break;
-		case PROP_CONFERENCE: {
-			if (media->priv->conference)
-				gst_object_unref(media->priv->conference);
-			media->priv->conference = g_value_get_object(value);
-			gst_object_ref(media->priv->conference);
-
-			purple_media_setup_pipeline(media);
+		case PROP_CONFERENCE_TYPE:
+			media->priv->conference_type =
+					g_value_dup_string(value);
+			media->priv->backend = g_object_new(
+					purple_media_manager_get_backend_type(
+					purple_media_manager_get()),
+					"conference-type",
+					media->priv->conference_type,
+					"media", media,
+					NULL);
+			g_signal_connect(media->priv->backend,
+					"active-candidate-pair",
+					G_CALLBACK(
+					purple_media_candidate_pair_established_cb),
+					media);
+			g_signal_connect(media->priv->backend,
+					"candidates-prepared",
+					G_CALLBACK(
+					purple_media_candidates_prepared_cb),
+					media);
+			g_signal_connect(media->priv->backend,
+					"codecs-changed",
+					G_CALLBACK(
+					purple_media_codecs_changed_cb),
+					media);
+			g_signal_connect(media->priv->backend,
+					"new-candidate",
+					G_CALLBACK(
+					purple_media_new_local_candidate_cb),
+					media);
 			break;
-		}
 		case PROP_INITIATOR:
 			media->priv->initiator = g_value_get_boolean(value);
 			break;
 		case PROP_PRPL_DATA:
 			media->priv->prpl_data = g_value_get_pointer(value);
 			break;
-		default:	
+		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
 	}
@@ -561,18 +427,22 @@
 {
 	PurpleMedia *media;
 	g_return_if_fail(PURPLE_IS_MEDIA(object));
-	
+
 	media = PURPLE_MEDIA(object);
 
 	switch (prop_id) {
 		case PROP_MANAGER:
 			g_value_set_object(value, media->priv->manager);
 			break;
+		case PROP_BACKEND:
+			g_value_set_object(value, media->priv->backend);
+			break;
 		case PROP_ACCOUNT:
 			g_value_set_pointer(value, media->priv->account);
 			break;
-		case PROP_CONFERENCE:
-			g_value_set_object(value, media->priv->conference);
+		case PROP_CONFERENCE_TYPE:
+			g_value_set_string(value,
+					media->priv->conference_type);
 			break;
 		case PROP_INITIATOR:
 			g_value_set_boolean(value, media->priv->initiator);
@@ -580,1133 +450,13 @@
 		case PROP_PRPL_DATA:
 			g_value_set_pointer(value, media->priv->prpl_data);
 			break;
-		default:	
-			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);	
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
 	}
 
 }
-#endif
 
-/*
- * PurpleMediaCandidateType
- */
-
-GType
-purple_media_candidate_type_get_type()
-{
-	static GType type = 0;
-	if (type == 0) {
-		static const GEnumValue values[] = {
-			{ PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
-					"PURPLE_MEDIA_CANDIDATE_TYPE_HOST",
-					"host" },
-			{ PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
-					"PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX",
-					"srflx" },
-			{ PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX,
-					"PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX",
-					"prflx" },
-			{ PURPLE_MEDIA_CANDIDATE_TYPE_RELAY,
-					"PPURPLE_MEDIA_CANDIDATE_TYPE_RELAY",
-					"relay" },
-			{ PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST,
-					"PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST",
-					"multicast" },
-			{ 0, NULL, NULL }
-		};
-		type = g_enum_register_static("PurpleMediaCandidateType",
-				values);
-	}
-	return type;
-}
-
-/*
- * PurpleMediaNetworkProtocol
- */
-
-GType
-purple_media_network_protocol_get_type()
-{
-	static GType type = 0;
-	if (type == 0) {
-		static const GEnumValue values[] = {
-			{ PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
-					"PURPLE_MEDIA_NETWORK_PROTOCOL_UDP",
-					"udp" },
-			{ PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
-					"PURPLE_MEDIA_NETWORK_PROTOCOL_TCP",
-					"tcp" },
-			{ 0, NULL, NULL }
-		};
-		type = g_enum_register_static("PurpleMediaNetworkProtocol",
-				values);
-	}
-	return type;
-}
-
-/*
- * PurpleMediaCandidate
- */
-
-struct _PurpleMediaCandidateClass
-{
-	GObjectClass parent_class;
-};
-
-struct _PurpleMediaCandidate
-{
-	GObject parent;
-};
-
-#ifdef USE_VV
-struct _PurpleMediaCandidatePrivate
-{
-	gchar *foundation;
-	guint component_id;
-	gchar *ip;
-	guint16 port;
-	gchar *base_ip;
-	guint16 base_port;
-	PurpleMediaNetworkProtocol proto;
-	guint32 priority;
-	PurpleMediaCandidateType type;
-	gchar *username;
-	gchar *password;
-	guint ttl;
-};
-
-enum {
-	PROP_CANDIDATE_0,
-	PROP_FOUNDATION,
-	PROP_COMPONENT_ID,
-	PROP_IP,
-	PROP_PORT,
-	PROP_BASE_IP,
-	PROP_BASE_PORT,
-	PROP_PROTOCOL,
-	PROP_PRIORITY,
-	PROP_TYPE,
-	PROP_USERNAME,
-	PROP_PASSWORD,
-	PROP_TTL,
-};
-
-static void
-purple_media_candidate_init(PurpleMediaCandidate *info)
-{
-	PurpleMediaCandidatePrivate *priv =
-			PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info);
-	priv->foundation = NULL;
-	priv->component_id = 0;
-	priv->ip = NULL;
-	priv->port = 0;
-	priv->base_ip = NULL;
-	priv->proto = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
-	priv->priority = 0;
-	priv->type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
-	priv->username = NULL;
-	priv->password = NULL;
-	priv->ttl = 0;
-}
-
-static void
-purple_media_candidate_finalize(GObject *info)
-{
-	PurpleMediaCandidatePrivate *priv =
-			PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info);
-
-	g_free(priv->foundation);
-	g_free(priv->ip);
-	g_free(priv->base_ip);
-	g_free(priv->username);
-	g_free(priv->password);
-}
-
-static void
-purple_media_candidate_set_property (GObject *object, guint prop_id,
-		const GValue *value, GParamSpec *pspec)
-{
-	PurpleMediaCandidatePrivate *priv;
-	g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object));
-
-	priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object);
-
-	switch (prop_id) {
-		case PROP_FOUNDATION:
-			g_free(priv->foundation);
-			priv->foundation = g_value_dup_string(value);
-			break;
-		case PROP_COMPONENT_ID:
-			priv->component_id = g_value_get_uint(value);
-			break;
-		case PROP_IP:
-			g_free(priv->ip);
-			priv->ip = g_value_dup_string(value);
-			break;
-		case PROP_PORT:
-			priv->port = g_value_get_uint(value);
-			break;
-		case PROP_BASE_IP:
-			g_free(priv->base_ip);
-			priv->base_ip = g_value_dup_string(value);
-			break;
-		case PROP_BASE_PORT:
-			priv->base_port = g_value_get_uint(value);
-			break;
-		case PROP_PROTOCOL:
-			priv->proto = g_value_get_enum(value);
-			break;
-		case PROP_PRIORITY:
-			priv->priority = g_value_get_uint(value);
-			break;
-		case PROP_TYPE:
-			priv->type = g_value_get_enum(value);
-			break;
-		case PROP_USERNAME:
-			g_free(priv->username);
-			priv->username = g_value_dup_string(value);
-			break;
-		case PROP_PASSWORD:
-			g_free(priv->password);
-			priv->password = g_value_dup_string(value);
-			break;
-		case PROP_TTL:
-			priv->ttl = g_value_get_uint(value);
-			break;
-		default:	
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(
-					object, prop_id, pspec);
-			break;
-	}
-}
-
-static void
-purple_media_candidate_get_property (GObject *object, guint prop_id,
-		GValue *value, GParamSpec *pspec)
-{
-	PurpleMediaCandidatePrivate *priv;
-	g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object));
-	
-	priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object);
-
-	switch (prop_id) {
-		case PROP_FOUNDATION:
-			g_value_set_string(value, priv->foundation);
-			break;
-		case PROP_COMPONENT_ID:
-			g_value_set_uint(value, priv->component_id);
-			break;
-		case PROP_IP:
-			g_value_set_string(value, priv->ip);
-			break;
-		case PROP_PORT:
-			g_value_set_uint(value, priv->port);
-			break;
-		case PROP_BASE_IP:
-			g_value_set_string(value, priv->base_ip);
-			break;
-		case PROP_BASE_PORT:
-			g_value_set_uint(value, priv->base_port);
-			break;
-		case PROP_PROTOCOL:
-			g_value_set_enum(value, priv->proto);
-			break;
-		case PROP_PRIORITY:
-			g_value_set_uint(value, priv->priority);
-			break;
-		case PROP_TYPE:
-			g_value_set_enum(value, priv->type);
-			break;
-		case PROP_USERNAME:
-			g_value_set_string(value, priv->username);
-			break;
-		case PROP_PASSWORD:
-			g_value_set_string(value, priv->password);
-			break;
-		case PROP_TTL:
-			g_value_set_uint(value, priv->ttl);
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(
-					object, prop_id, pspec);
-			break;
-	}
-}
-
-static void
-purple_media_candidate_class_init(PurpleMediaCandidateClass *klass)
-{
-	GObjectClass *gobject_class = (GObjectClass*)klass;
-	
-	gobject_class->finalize = purple_media_candidate_finalize;
-	gobject_class->set_property = purple_media_candidate_set_property;
-	gobject_class->get_property = purple_media_candidate_get_property;
-
-	g_object_class_install_property(gobject_class, PROP_FOUNDATION,
-			g_param_spec_string("foundation",
-			"Foundation",
-			"The foundation of the candidate.",
-			NULL,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_COMPONENT_ID,
-			g_param_spec_uint("component-id",
-			"Component ID",
-			"The component id of the candidate.",
-			0, G_MAXUINT, 0,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_IP,
-			g_param_spec_string("ip",
-			"IP Address",
-			"The IP address of the candidate.",
-			NULL,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_PORT,
-			g_param_spec_uint("port",
-			"Port",
-			"The port of the candidate.",
-			0, G_MAXUINT16, 0,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_BASE_IP,
-			g_param_spec_string("base-ip",
-			"Base IP",
-			"The internal IP address of the candidate.",
-			NULL,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_BASE_PORT,
-			g_param_spec_uint("base-port",
-			"Base Port",
-			"The internal port of the candidate.",
-			0, G_MAXUINT16, 0,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_PROTOCOL,
-			g_param_spec_enum("protocol",
-			"Protocol",
-			"The protocol of the candidate.",
-			PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL,
-			PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_PRIORITY,
-			g_param_spec_uint("priority",
-			"Priority",
-			"The priority of the candidate.",
-			0, G_MAXUINT32, 0,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_TYPE,
-			g_param_spec_enum("type",
-			"Type",
-			"The type of the candidate.",
-			PURPLE_TYPE_MEDIA_CANDIDATE_TYPE,
-			PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_USERNAME,
-			g_param_spec_string("username",
-			"Username",
-			"The username used to connect to the candidate.",
-			NULL,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_PASSWORD,
-			g_param_spec_string("password",
-			"Password",
-			"The password use to connect to the candidate.",
-			NULL,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_TTL,
-			g_param_spec_uint("ttl",
-			"TTL",
-			"The TTL of the candidate.",
-			0, G_MAXUINT, 0,
-			G_PARAM_READWRITE));
-
-	g_type_class_add_private(klass, sizeof(PurpleMediaCandidatePrivate));
-}
-
-G_DEFINE_TYPE(PurpleMediaCandidate,
-		purple_media_candidate, G_TYPE_OBJECT);
-#else
-GType
-purple_media_candidate_get_type()
-{
-	return G_TYPE_NONE;
-}
-#endif
-
-PurpleMediaCandidate *
-purple_media_candidate_new(const gchar *foundation, guint component_id,
-		PurpleMediaCandidateType type,
-		PurpleMediaNetworkProtocol proto,
-		const gchar *ip, guint port)
-{
-	return g_object_new(PURPLE_TYPE_MEDIA_CANDIDATE,
-			"foundation", foundation,
-			"component-id", component_id,
-			"type", type,
-			"protocol", proto,
-			"ip", ip,
-			"port", port, NULL);
-}
-
-static PurpleMediaCandidate *
-purple_media_candidate_copy(PurpleMediaCandidate *candidate)
-{
-#ifdef USE_VV
-	PurpleMediaCandidatePrivate *priv;
-	PurpleMediaCandidate *new_candidate;
-
-	if (candidate == NULL)
-		return NULL;
-
-	priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate);
-
-	new_candidate = purple_media_candidate_new(priv->foundation,
-			priv->component_id, priv->type, priv->proto,
-			priv->ip, priv->port);
-	g_object_set(new_candidate,
-			"base-ip", priv->base_ip,
-			"base-port", priv->base_port,
-			"priority", priv->priority,
-			"username", priv->username,
-			"password", priv->password,
-			"ttl", priv->ttl, NULL);
-	return new_candidate;
-#else
-	return NULL;
-#endif
-}
-
-#ifdef USE_VV
-static FsCandidate *
-purple_media_candidate_to_fs(PurpleMediaCandidate *candidate)
-{
-	PurpleMediaCandidatePrivate *priv;
-	FsCandidate *fscandidate;
-
-	if (candidate == NULL)
-		return NULL;
-
-	priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate);
-
-	fscandidate = fs_candidate_new(priv->foundation,
-			priv->component_id, priv->type,
-			priv->proto, priv->ip, priv->port);
-
-	fscandidate->base_ip = g_strdup(priv->base_ip);
-	fscandidate->base_port = priv->base_port;
-	fscandidate->priority = priv->priority;
-	fscandidate->username = g_strdup(priv->username);
-	fscandidate->password = g_strdup(priv->password);
-	fscandidate->ttl = priv->ttl;
-	return fscandidate;
-}
-
-static PurpleMediaCandidate *
-purple_media_candidate_from_fs(FsCandidate *fscandidate)
-{
-	PurpleMediaCandidate *candidate;
-
-	if (fscandidate == NULL)
-		return NULL;
-
-	candidate = purple_media_candidate_new(fscandidate->foundation,
-		fscandidate->component_id, fscandidate->type,
-		fscandidate->proto, fscandidate->ip, fscandidate->port);
-	g_object_set(candidate,
-			"base-ip", fscandidate->base_ip,
-			"base-port", fscandidate->base_port,
-			"priority", fscandidate->priority,
-			"username", fscandidate->username,
-			"password", fscandidate->password,
-			"ttl", fscandidate->ttl, NULL);
-	return candidate;
-}
-
-static GList *
-purple_media_candidate_list_from_fs(GList *candidates)
-{
-	GList *new_list = NULL;
-
-	for (; candidates; candidates = g_list_next(candidates)) {
-		new_list = g_list_prepend(new_list,
-				purple_media_candidate_from_fs(
-				candidates->data));
-	}
-
-	new_list = g_list_reverse(new_list);
-	return new_list;
-}
-
-static GList *
-purple_media_candidate_list_to_fs(GList *candidates)
-{
-	GList *new_list = NULL;
-
-	for (; candidates; candidates = g_list_next(candidates)) {
-		new_list = g_list_prepend(new_list,
-				purple_media_candidate_to_fs(
-				candidates->data));
-	}
-
-	new_list = g_list_reverse(new_list);
-	return new_list;
-}
-#endif
-
-GList *
-purple_media_candidate_list_copy(GList *candidates)
-{
-	GList *new_list = NULL;
-
-	for (; candidates; candidates = g_list_next(candidates)) {
-		new_list = g_list_prepend(new_list,
-				purple_media_candidate_copy(candidates->data));
-	}
-
-	new_list = g_list_reverse(new_list);
-	return new_list;
-}
-
-void
-purple_media_candidate_list_free(GList *candidates)
-{
-	for (; candidates; candidates =
-			g_list_delete_link(candidates, candidates)) {
-		g_object_unref(candidates->data);
-	}
-}
-
-gchar *
-purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate)
-{
-	gchar *foundation;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
-	g_object_get(candidate, "foundation", &foundation, NULL);
-	return foundation;
-}
-
-guint
-purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate)
-{
-	guint component_id;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
-	g_object_get(candidate, "component-id", &component_id, NULL);
-	return component_id;
-}
-
-gchar *
-purple_media_candidate_get_ip(PurpleMediaCandidate *candidate)
-{
-	gchar *ip;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
-	g_object_get(candidate, "ip", &ip, NULL);
-	return ip;
-}
-
-guint16
-purple_media_candidate_get_port(PurpleMediaCandidate *candidate)
-{
-	guint port;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
-	g_object_get(candidate, "port", &port, NULL);
-	return port;
-}
-
-gchar *
-purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate)
-{
-	gchar *base_ip;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
-	g_object_get(candidate, "base-ip", &base_ip, NULL);
-	return base_ip;
-}
-
-guint16
-purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate)
-{
-	guint base_port;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
-	g_object_get(candidate, "base_port", &base_port, NULL);
-	return base_port;
-}
-
-PurpleMediaNetworkProtocol
-purple_media_candidate_get_protocol(PurpleMediaCandidate *candidate)
-{
-	PurpleMediaNetworkProtocol protocol;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate),
-			PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
-	g_object_get(candidate, "protocol", &protocol, NULL);
-	return protocol;
-}
-
-guint32
-purple_media_candidate_get_priority(PurpleMediaCandidate *candidate)
-{
-	guint priority;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
-	g_object_get(candidate, "priority", &priority, NULL);
-	return priority;
-}
-
-PurpleMediaCandidateType
-purple_media_candidate_get_candidate_type(PurpleMediaCandidate *candidate)
-{
-	PurpleMediaCandidateType type;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate),
-			PURPLE_MEDIA_CANDIDATE_TYPE_HOST);
-	g_object_get(candidate, "type", &type, NULL);
-	return type;
-}
-
-gchar *
-purple_media_candidate_get_username(PurpleMediaCandidate *candidate)
-{
-	gchar *username;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
-	g_object_get(candidate, "username", &username, NULL);
-	return username;
-}
-
-gchar *
-purple_media_candidate_get_password(PurpleMediaCandidate *candidate)
-{
-	gchar *password;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
-	g_object_get(candidate, "password", &password, NULL);
-	return password;
-}
-
-guint
-purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate)
-{
-	guint ttl;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
-	g_object_get(candidate, "ttl", &ttl, NULL);
-	return ttl;
-}
-
-#ifdef USE_VV
-static FsMediaType
-purple_media_to_fs_media_type(PurpleMediaSessionType type)
-{
-	if (type & PURPLE_MEDIA_AUDIO)
-		return FS_MEDIA_TYPE_AUDIO;
-	else if (type & PURPLE_MEDIA_VIDEO)
-		return FS_MEDIA_TYPE_VIDEO;
-	else
-		return 0;
-}
-
-static FsStreamDirection
-purple_media_to_fs_stream_direction(PurpleMediaSessionType type)
-{
-	if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO ||
-			(type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO)
-		return FS_DIRECTION_BOTH;
-	else if ((type & PURPLE_MEDIA_SEND_AUDIO) ||
-			(type & PURPLE_MEDIA_SEND_VIDEO))
-		return FS_DIRECTION_SEND;
-	else if ((type & PURPLE_MEDIA_RECV_AUDIO) ||
-			(type & PURPLE_MEDIA_RECV_VIDEO))
-		return FS_DIRECTION_RECV;
-	else
-		return FS_DIRECTION_NONE;
-}
-
-static PurpleMediaSessionType
-purple_media_from_fs(FsMediaType type, FsStreamDirection direction)
-{
-	PurpleMediaSessionType result = PURPLE_MEDIA_NONE;
-	if (type == FS_MEDIA_TYPE_AUDIO) {
-		if (direction & FS_DIRECTION_SEND)
-			result |= PURPLE_MEDIA_SEND_AUDIO;
-		if (direction & FS_DIRECTION_RECV)
-			result |= PURPLE_MEDIA_RECV_AUDIO;
-	} else if (type == FS_MEDIA_TYPE_VIDEO) {
-		if (direction & FS_DIRECTION_SEND)
-			result |= PURPLE_MEDIA_SEND_VIDEO;
-		if (direction & FS_DIRECTION_RECV)
-			result |= PURPLE_MEDIA_RECV_VIDEO;
-	}
-	return result;
-}
-#endif
-
-/*
- * PurpleMediaCodec
- */
-
-struct _PurpleMediaCodecClass
-{
-	GObjectClass parent_class;
-};
-
-struct _PurpleMediaCodec
-{
-	GObject parent;
-};
-
-#ifdef USE_VV
-struct _PurpleMediaCodecPrivate
-{
-	gint id;
-	char *encoding_name;
-	PurpleMediaSessionType media_type;
-	guint clock_rate;
-	guint channels;
-	GList *optional_params;
-};
-
-enum {
-	PROP_CODEC_0,
-	PROP_ID,
-	PROP_ENCODING_NAME,
-	PROP_MEDIA_TYPE,
-	PROP_CLOCK_RATE,
-	PROP_CHANNELS,
-	PROP_OPTIONAL_PARAMS,
-};
-
-static void
-purple_media_codec_init(PurpleMediaCodec *info)
-{
-	PurpleMediaCodecPrivate *priv =
-			PURPLE_MEDIA_CODEC_GET_PRIVATE(info);
-	priv->encoding_name = NULL;
-	priv->optional_params = NULL;
-}
-
-static void
-purple_media_codec_finalize(GObject *info)
-{
-	PurpleMediaCodecPrivate *priv =
-			PURPLE_MEDIA_CODEC_GET_PRIVATE(info);
-	g_free(priv->encoding_name);
-	for (; priv->optional_params; priv->optional_params =
-			g_list_delete_link(priv->optional_params,
-			priv->optional_params)) {
-		g_free(priv->optional_params->data);
-	}
-}
-
-static void
-purple_media_codec_set_property (GObject *object, guint prop_id,
-		const GValue *value, GParamSpec *pspec)
-{
-	PurpleMediaCodecPrivate *priv;
-	g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object));
-
-	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object);
-
-	switch (prop_id) {
-		case PROP_ID:
-			priv->id = g_value_get_uint(value);
-			break;
-		case PROP_ENCODING_NAME:
-			g_free(priv->encoding_name);
-			priv->encoding_name = g_value_dup_string(value);
-			break;
-		case PROP_MEDIA_TYPE:
-			priv->media_type = g_value_get_flags(value);
-			break;
-		case PROP_CLOCK_RATE:
-			priv->clock_rate = g_value_get_uint(value);
-			break;
-		case PROP_CHANNELS:
-			priv->channels = g_value_get_uint(value);
-			break;
-		case PROP_OPTIONAL_PARAMS:
-			priv->optional_params = g_value_get_pointer(value);
-			break;
-		default:	
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(
-					object, prop_id, pspec);
-			break;
-	}
-}
-
-static void
-purple_media_codec_get_property (GObject *object, guint prop_id,
-		GValue *value, GParamSpec *pspec)
-{
-	PurpleMediaCodecPrivate *priv;
-	g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object));
-	
-	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object);
-
-	switch (prop_id) {
-		case PROP_ID:
-			g_value_set_uint(value, priv->id);
-			break;
-		case PROP_ENCODING_NAME:
-			g_value_set_string(value, priv->encoding_name);
-			break;
-		case PROP_MEDIA_TYPE:
-			g_value_set_flags(value, priv->media_type);
-			break;
-		case PROP_CLOCK_RATE:
-			g_value_set_uint(value, priv->clock_rate);
-			break;
-		case PROP_CHANNELS:
-			g_value_set_uint(value, priv->channels);
-			break;
-		case PROP_OPTIONAL_PARAMS:
-			g_value_set_pointer(value, priv->optional_params);
-			break;
-		default:	
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(
-					object, prop_id, pspec);
-			break;
-	}
-}
-
-static void
-purple_media_codec_class_init(PurpleMediaCodecClass *klass)
-{
-	GObjectClass *gobject_class = (GObjectClass*)klass;
-	
-	gobject_class->finalize = purple_media_codec_finalize;
-	gobject_class->set_property = purple_media_codec_set_property;
-	gobject_class->get_property = purple_media_codec_get_property;
-
-	g_object_class_install_property(gobject_class, PROP_ID,
-			g_param_spec_uint("id",
-			"ID",
-			"The numeric identifier of the codec.",
-			0, G_MAXUINT, 0,
-			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_ENCODING_NAME,
-			g_param_spec_string("encoding-name",
-			"Encoding Name",
-			"The name of the codec.",
-			NULL,
-			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE,
-			g_param_spec_flags("media-type",
-			"Media Type",
-			"Whether this is an audio of video codec.",
-			PURPLE_TYPE_MEDIA_SESSION_TYPE,
-			PURPLE_MEDIA_NONE,
-			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_CLOCK_RATE,
-			g_param_spec_uint("clock-rate",
-			"Create Callback",
-			"The function called to create this element.",
-			0, G_MAXUINT, 0,
-			G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_CHANNELS,
-			g_param_spec_uint("channels",
-			"Channels",
-			"The number of channels in this codec.",
-			0, G_MAXUINT, 0,
-			G_PARAM_READWRITE));
-	g_object_class_install_property(gobject_class, PROP_OPTIONAL_PARAMS,
-			g_param_spec_pointer("optional-params",
-			"Optional Params",
-			"A list of optional parameters for the codec.",
-			G_PARAM_READWRITE));
-
-	g_type_class_add_private(klass, sizeof(PurpleMediaCodecPrivate));
-}
-
-G_DEFINE_TYPE(PurpleMediaCodec,
-		purple_media_codec, G_TYPE_OBJECT);
-#else
-GType
-purple_media_codec_get_type()
-{
-	return G_TYPE_NONE;
-}
-#endif
-
-guint
-purple_media_codec_get_id(PurpleMediaCodec *codec)
-{
-	guint id;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0);
-	g_object_get(codec, "id", &id, NULL);
-	return id;
-}
-
-gchar *
-purple_media_codec_get_encoding_name(PurpleMediaCodec *codec)
-{
-	gchar *name;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL);
-	g_object_get(codec, "encoding-name", &name, NULL);
-	return name;
-}
-
-guint
-purple_media_codec_get_clock_rate(PurpleMediaCodec *codec)
-{
-	guint clock_rate;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0);
-	g_object_get(codec, "clock-rate", &clock_rate, NULL);
-	return clock_rate;
-}
-
-guint
-purple_media_codec_get_channels(PurpleMediaCodec *codec)
-{
-	guint channels;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0);
-	g_object_get(codec, "channels", &channels, NULL);
-	return channels;
-}
-
-GList *
-purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec)
-{
-	GList *optional_params;
-	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL);
-	g_object_get(codec, "optional-params", &optional_params, NULL);
-	return optional_params;
-}
-
-void
-purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
-		const gchar *name, const gchar *value)
-{
-#ifdef USE_VV
-	PurpleMediaCodecPrivate *priv;
-	PurpleKeyValuePair *new_param;
-
-	g_return_if_fail(codec != NULL);
-	g_return_if_fail(name != NULL && value != NULL);
-
-	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
-
-	new_param = g_new0(PurpleKeyValuePair, 1);
-	new_param->key = g_strdup(name);
-	new_param->value = g_strdup(value);
-	priv->optional_params = g_list_append(
-			priv->optional_params, new_param);
-#endif
-}
-
-void
-purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
-		PurpleKeyValuePair *param)
-{
-#ifdef USE_VV
-	PurpleMediaCodecPrivate *priv;
-
-	g_return_if_fail(codec != NULL && param != NULL);
-
-	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
-
-	g_free(param->key);
-	g_free(param->value);
-	g_free(param);
-
-	priv->optional_params =
-			g_list_remove(priv->optional_params, param);
-#endif
-}
-
-PurpleKeyValuePair *
-purple_media_codec_get_optional_parameter(PurpleMediaCodec *codec,
-		const gchar *name, const gchar *value)
-{
-#ifdef USE_VV
-	PurpleMediaCodecPrivate *priv;
-	GList *iter;
-
-	g_return_val_if_fail(codec != NULL, NULL);
-	g_return_val_if_fail(name != NULL, NULL);
-
-	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
-
-	for (iter = priv->optional_params; iter; iter = g_list_next(iter)) {
-		PurpleKeyValuePair *param = iter->data;
-		if (!g_ascii_strcasecmp(param->key, name) &&
-				(value == NULL ||
-				!g_ascii_strcasecmp(param->value, value)))
-			return param;
-	}
-#endif
-
-	return NULL;
-}
-
-PurpleMediaCodec *
-purple_media_codec_new(int id, const char *encoding_name,
-		PurpleMediaSessionType media_type, guint clock_rate)
-{
-	PurpleMediaCodec *codec =
-			g_object_new(PURPLE_TYPE_MEDIA_CODEC,
-			"id", id,
-			"encoding_name", encoding_name,
-			"media_type", media_type,
-			"clock-rate", clock_rate, NULL);
-	return codec;
-}
-
-static PurpleMediaCodec *
-purple_media_codec_copy(PurpleMediaCodec *codec)
-{
-#ifdef USE_VV
-	PurpleMediaCodecPrivate *priv;
-	PurpleMediaCodec *new_codec;
-	GList *iter;
-
-	if (codec == NULL)
-		return NULL;
-
-	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
-
-	new_codec = purple_media_codec_new(priv->id, priv->encoding_name,
-			priv->media_type, priv->clock_rate);
-	g_object_set(codec, "channels", priv->channels, NULL);
-
-	for (iter = priv->optional_params; iter; iter = g_list_next(iter)) {
-		PurpleKeyValuePair *param =
-				(PurpleKeyValuePair*)iter->data;
-		purple_media_codec_add_optional_parameter(new_codec,
-				param->key, param->value);
-	}
-
-	return new_codec;
-#else
-	return NULL;
-#endif
-}
-
-#ifdef USE_VV
-static FsCodec *
-purple_media_codec_to_fs(const PurpleMediaCodec *codec)
-{
-	PurpleMediaCodecPrivate *priv;
-	FsCodec *new_codec;
-	GList *iter;
-
-	if (codec == NULL)
-		return NULL;
-
-	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
-
-	new_codec = fs_codec_new(priv->id, priv->encoding_name,
-			purple_media_to_fs_media_type(priv->media_type),
-			priv->clock_rate);
-	new_codec->channels = priv->channels;
-
-	for (iter = priv->optional_params; iter; iter = g_list_next(iter)) {
-		PurpleKeyValuePair *param = (PurpleKeyValuePair*)iter->data;
-		fs_codec_add_optional_parameter(new_codec,
-				param->key, param->value);
-	}
-
-	return new_codec;
-}
-
-static PurpleMediaCodec *
-purple_media_codec_from_fs(const FsCodec *codec)
-{
-	PurpleMediaCodec *new_codec;
-	GList *iter;
-
-	if (codec == NULL)
-		return NULL;
-
-	new_codec = purple_media_codec_new(codec->id, codec->encoding_name,
-			purple_media_from_fs(codec->media_type,
-			FS_DIRECTION_BOTH), codec->clock_rate);
-	g_object_set(new_codec, "channels", codec->channels, NULL);
-
-	for (iter = codec->optional_params; iter; iter = g_list_next(iter)) {
-		FsCodecParameter *param = (FsCodecParameter*)iter->data;
-		purple_media_codec_add_optional_parameter(new_codec,
-				param->name, param->value);
-	}
-
-	return new_codec;
-}
-#endif
-
-gchar *
-purple_media_codec_to_string(const PurpleMediaCodec *codec)
-{
-#ifdef USE_VV
-	FsCodec *fscodec = purple_media_codec_to_fs(codec);
-	gchar *str = fs_codec_to_string(fscodec);
-	fs_codec_destroy(fscodec);
-	return str;
-#else
-	return g_strdup("");
-#endif
-}
-
-#ifdef USE_VV
-static GList *
-purple_media_codec_list_from_fs(GList *codecs)
-{
-	GList *new_list = NULL;
-
-	for (; codecs; codecs = g_list_next(codecs)) {
-		new_list = g_list_prepend(new_list,
-				purple_media_codec_from_fs(
-				codecs->data));
-	}
-
-	new_list = g_list_reverse(new_list);
-	return new_list;
-}
-
-static GList *
-purple_media_codec_list_to_fs(GList *codecs)
-{
-	GList *new_list = NULL;
-
-	for (; codecs; codecs = g_list_next(codecs)) {
-		new_list = g_list_prepend(new_list,
-				purple_media_codec_to_fs(
-				codecs->data));
-	}
-
-	new_list = g_list_reverse(new_list);
-	return new_list;
-}
-#endif
-
-GList *
-purple_media_codec_list_copy(GList *codecs)
-{
-	GList *new_list = NULL;
-
-	for (; codecs; codecs = g_list_next(codecs)) {
-		new_list = g_list_prepend(new_list,
-				purple_media_codec_copy(codecs->data));
-	}
-
-	new_list = g_list_reverse(new_list);
-	return new_list;
-}
-
-void
-purple_media_codec_list_free(GList *codecs)
-{
-	for (; codecs; codecs =
-			g_list_delete_link(codecs, codecs)) {
-		g_object_unref(codecs->data);
-	}
-}
-
-#ifdef USE_VV
 static PurpleMediaSession*
 purple_media_get_session(PurpleMedia *media, const gchar *sess_id)
 {
@@ -1715,14 +465,6 @@
 			g_hash_table_lookup(media->priv->sessions, sess_id) : NULL;
 }
 
-static FsParticipant*
-purple_media_get_participant(PurpleMedia *media, const gchar *name)
-{
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	return (FsParticipant*) (media->priv->participants) ?
-			g_hash_table_lookup(media->priv->participants, name) : NULL;
-}
-
 static PurpleMediaStream*
 purple_media_get_stream(PurpleMedia *media, const gchar *session, const gchar *participant)
 {
@@ -1748,7 +490,7 @@
 {
 	GList *streams;
 	GList *ret = NULL;
-	
+
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
 
 	streams = media->priv->streams;
@@ -1773,63 +515,33 @@
 
 	if (!media->priv->sessions) {
 		purple_debug_info("media", "Creating hash table for sessions\n");
-		media->priv->sessions = g_hash_table_new(g_str_hash, g_str_equal);
+		media->priv->sessions = g_hash_table_new_full(g_str_hash, g_str_equal,
+		                                              g_free, NULL);
 	}
 	g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
 }
 
+#if 0
 static gboolean
 purple_media_remove_session(PurpleMedia *media, PurpleMediaSession *session)
 {
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
 	return g_hash_table_remove(media->priv->sessions, session->id);
 }
-
-static FsParticipant *
-purple_media_add_participant(PurpleMedia *media, const gchar *name)
-{
-	FsParticipant *participant;
-	GError *err = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-
-	participant = purple_media_get_participant(media, name);
-
-	if (participant)
-		return participant;
-
-	participant = fs_conference_new_participant(media->priv->conference,
-						    (gchar*)name, &err);
-
-	if (err) {
-		purple_debug_error("media", "Error creating participant: %s\n",
-				   err->message);
-		g_error_free(err);
-		return NULL;
-	}
-
-	if (!media->priv->participants) {
-		purple_debug_info("media", "Creating hash table for participants\n");
-		media->priv->participants = g_hash_table_new_full(g_str_hash,
-				g_str_equal, g_free, NULL);
-	}
-
-	g_hash_table_insert(media->priv->participants, g_strdup(name), participant);
-
-	return participant;
-}
+#endif
 
 static PurpleMediaStream *
-purple_media_insert_stream(PurpleMediaSession *session, const gchar *name, FsStream *stream)
+purple_media_insert_stream(PurpleMediaSession *session,
+		const gchar *name, gboolean initiator)
 {
 	PurpleMediaStream *media_stream;
-	
+
 	g_return_val_if_fail(session != NULL, NULL);
 
 	media_stream = g_new0(PurpleMediaStream, 1);
-	media_stream->stream = stream;
 	media_stream->participant = g_strdup(name);
 	media_stream->session = session;
+	media_stream->initiator = initiator;
 
 	session->media->priv->streams =
 			g_list_append(session->media->priv->streams, media_stream);
@@ -1839,7 +551,7 @@
 
 static void
 purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
-				     FsCandidate *candidate)
+				     PurpleMediaCandidate *candidate)
 {
 	PurpleMediaStream *stream;
 
@@ -1862,205 +574,25 @@
 #endif
 }
 
-#ifdef USE_VV
-static void 
-purple_media_set_src(PurpleMedia *media, const gchar *sess_id, GstElement *src)
-{
-	PurpleMediaSession *session;
-	GstPad *sinkpad;
-	GstPad *srcpad;
-
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-	g_return_if_fail(GST_IS_ELEMENT(src));
-
-	session = purple_media_get_session(media, sess_id);
-
-	if (session == NULL) {
-		purple_debug_warning("media", "purple_media_set_src: trying"
-				" to set src on non-existent session\n");
-		return;
-	}
-
-	if (session->src)
-		gst_object_unref(session->src);
-	session->src = src;
-	gst_element_set_locked_state(session->src, TRUE);
-
-	session->tee = gst_element_factory_make("tee", NULL);
-	gst_bin_add(GST_BIN(session->media->priv->confbin), session->tee);
-
-	/* This supposedly isn't necessary, but it silences some warnings */
-	if (GST_ELEMENT_PARENT(session->media->priv->confbin)
-			== GST_ELEMENT_PARENT(session->src)) {
-		GstPad *pad = gst_element_get_static_pad(session->tee, "sink");
-		GstPad *ghost = gst_ghost_pad_new(NULL, pad);
-		gst_object_unref(pad);
-		gst_pad_set_active(ghost, TRUE);
-		gst_element_add_pad(session->media->priv->confbin, ghost);
-	}
-
-	gst_element_link(session->src, session->media->priv->confbin);
-	gst_element_set_state(session->tee, GST_STATE_PLAYING);
-
-	g_object_get(session->session, "sink-pad", &sinkpad, NULL);
-	srcpad = gst_element_get_request_pad(session->tee, "src%d");
-	purple_debug_info("media", "connecting pad: %s\n", 
-			  gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK
-			  ? "success" : "failure");
-	gst_element_set_locked_state(session->src, FALSE);
-	gst_object_unref(session->src);
-}
-#endif
-
 #ifdef USE_GSTREAMER
 GstElement *
 purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
 {
 #ifdef USE_VV
-	PurpleMediaSession *session;
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	session = purple_media_get_session(media, sess_id);
-	return (session != NULL) ? session->src : NULL;
+
+	if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
+		return purple_media_backend_fs2_get_src(
+				PURPLE_MEDIA_BACKEND_FS2(
+				media->priv->backend), sess_id);
+
+	g_return_val_if_reached(NULL);
 #else
 	return NULL;
 #endif
 }
 #endif /* USE_GSTREAMER */
 
-#ifdef USE_VV
-static PurpleMediaSession *
-purple_media_session_from_fs_stream(PurpleMedia *media, FsStream *stream)
-{
-	FsSession *fssession;
-	GList *values;
-
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	g_return_val_if_fail(FS_IS_STREAM(stream), NULL);
-
-	g_object_get(stream, "session", &fssession, NULL);
-
-	values = g_hash_table_get_values(media->priv->sessions);
-
-	for (; values; values = g_list_delete_link(values, values)) {
-		PurpleMediaSession *session = values->data;
-
-		if (session->session == fssession) {
-			g_list_free(values);
-			g_object_unref(fssession);
-			return session;
-		}
-	}
-
-	g_object_unref(fssession);
-	return NULL;
-}
-
-static gboolean
-media_bus_call(GstBus *bus, GstMessage *msg, PurpleMedia *media)
-{
-	switch(GST_MESSAGE_TYPE(msg)) {
-		case GST_MESSAGE_ELEMENT: {
-			if (!FS_IS_CONFERENCE(GST_MESSAGE_SRC(msg)) ||
-					!PURPLE_IS_MEDIA(media) ||
-					media->priv->conference !=
-					FS_CONFERENCE(GST_MESSAGE_SRC(msg)))
-				break;
-
-			if (gst_structure_has_name(msg->structure, "farsight-error")) {
-				FsError error_no;
-				gst_structure_get_enum(msg->structure, "error-no",
-						FS_TYPE_ERROR, (gint*)&error_no);
-				/*
-				 * Unknown CName is only a problem for the
-				 * multicast transmitter which isn't used.
-				 */
-				if (error_no != FS_ERROR_UNKNOWN_CNAME)
-					purple_debug_error("media", "farsight-error: %i: %s\n", error_no,
-						  	gst_structure_get_string(msg->structure, "error-msg"));
-			} else if (gst_structure_has_name(msg->structure,
-					"farsight-new-local-candidate")) {
-				FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream"));
-				FsCandidate *local_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "candidate"));
-				PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream);
-				purple_media_new_local_candidate_cb(stream, local_candidate, session);
-			} else if (gst_structure_has_name(msg->structure,
-					"farsight-local-candidates-prepared")) {
-				FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream"));
-				PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream);
-				purple_media_candidates_prepared_cb(stream, session);
-			} else if (gst_structure_has_name(msg->structure,
-					"farsight-new-active-candidate-pair")) {
-				FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream"));
-				FsCandidate *local_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "local-candidate"));
-				FsCandidate *remote_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "remote-candidate"));
-				PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream);
-				purple_media_candidate_pair_established_cb(stream, local_candidate, remote_candidate, session);
-			} else if (gst_structure_has_name(msg->structure,
-					"farsight-recv-codecs-changed")) {
-				GList *codecs = g_value_get_boxed(gst_structure_get_value(msg->structure, "codecs"));
-				FsCodec *codec = codecs->data;
-				purple_debug_info("media", "farsight-recv-codecs-changed: %s\n", codec->encoding_name);
-				
-			} else if (gst_structure_has_name(msg->structure,
-					"farsight-component-state-changed")) {
-				FsStreamState fsstate = g_value_get_enum(gst_structure_get_value(msg->structure, "state"));
-				guint component = g_value_get_uint(gst_structure_get_value(msg->structure, "component"));
-				const gchar *state;
-				switch (fsstate) {
-					case FS_STREAM_STATE_FAILED:
-						state = "FAILED";
-						break;
-					case FS_STREAM_STATE_DISCONNECTED:
-						state = "DISCONNECTED";
-						break;
-					case FS_STREAM_STATE_GATHERING:
-						state = "GATHERING";
-						break;
-					case FS_STREAM_STATE_CONNECTING:
-						state = "CONNECTING";
-						break;
-					case FS_STREAM_STATE_CONNECTED:
-						state = "CONNECTED";
-						break;
-					case FS_STREAM_STATE_READY:
-						state = "READY";
-						break;
-					default:
-						state = "UNKNOWN";
-						break;
-				}
-				purple_debug_info("media", "farsight-component-state-changed: component: %u state: %s\n", component, state);
-			} else if (gst_structure_has_name(msg->structure,
-					"farsight-send-codec-changed")) {
-				FsCodec *codec = g_value_get_boxed(gst_structure_get_value(msg->structure, "codec"));
-				gchar *codec_str = fs_codec_to_string(codec);
-				purple_debug_info("media", "farsight-send-codec-changed: codec: %s\n", codec_str);
-				g_free(codec_str);
-			} else if (gst_structure_has_name(msg->structure,
-					"farsight-codecs-changed")) {
-				GList *sessions = g_hash_table_get_values(PURPLE_MEDIA(media)->priv->sessions);
-				FsSession *fssession = g_value_get_object(gst_structure_get_value(msg->structure, "session"));
-				for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
-					PurpleMediaSession *session = sessions->data;
-					if (session->session == fssession) {
-						gchar *session_id = g_strdup(session->id);
-						g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, session_id);
-						g_free(session_id);
-						g_list_free(sessions);
-						break;
-					}
-				}
-			}
-			break;
-		}
-		default:
-			break;
-	}
-
-	return TRUE;
-}
-#endif
-
 PurpleAccount *
 purple_media_get_account(PurpleMedia *media)
 {
@@ -2110,7 +642,7 @@
 	va_end(args);
 
 	purple_debug_error("media", "%s\n", message);
-	g_signal_emit(media, purple_media_signals[ERROR], 0, message);
+	g_signal_emit(media, purple_media_signals[S_ERROR], 0, message);
 
 	g_free(message);
 #endif
@@ -2121,12 +653,99 @@
 		const gchar *session_id, const gchar *participant)
 {
 #ifdef USE_VV
+	GList *iter, *sessions = NULL, *participants = NULL;
+
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
-	if (session_id == NULL && participant == NULL) {
+
+	iter = purple_media_get_streams(media, session_id, participant);
+
+	/* Free matching streams */
+	for (; iter; iter = g_list_delete_link(iter, iter)) {
+		PurpleMediaStream *stream = iter->data;
+
+		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
+				0, PURPLE_MEDIA_STATE_END,
+				stream->session->id, stream->participant);
+
+		media->priv->streams =
+				g_list_remove(media->priv->streams, stream);
+
+		if (g_list_find(sessions, stream->session) == NULL)
+			sessions = g_list_prepend(sessions, stream->session);
+
+		if (g_list_find_custom(participants, stream->participant,
+				(GCompareFunc)strcmp) == NULL)
+			participants = g_list_prepend(participants,
+					g_strdup(stream->participant));
+
+		purple_media_stream_free(stream);
+	}
+
+	iter = media->priv->streams;
+
+	/* Reduce to list of sessions to remove */
+	for (; iter; iter = g_list_next(iter)) {
+		PurpleMediaStream *stream = iter->data;
+
+		sessions = g_list_remove(sessions, stream->session);
+	}
+
+	/* Free sessions with no streams left */
+	for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
+		PurpleMediaSession *session = sessions->data;
+
+		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
+				0, PURPLE_MEDIA_STATE_END,
+				session->id, NULL);
+
+		g_hash_table_remove(media->priv->sessions, session->id);
+		purple_media_session_free(session);
+	}
+
+	iter = media->priv->streams;
+
+	/* Reduce to list of participants to remove */
+	for (; iter; iter = g_list_next(iter)) {
+		PurpleMediaStream *stream = iter->data;
+		GList *tmp;
+
+		tmp = g_list_find_custom(participants,
+				stream->participant, (GCompareFunc)strcmp);
+
+		if (tmp != NULL) {
+			g_free(tmp->data);
+			participants = g_list_delete_link(participants,	tmp);
+		}
+	}
+
+	/* Remove participants with no streams left (just emit the signal) */
+	for (; participants; participants =
+			g_list_delete_link(participants, participants)) {
+		gchar *participant = participants->data;
+		GList *link = g_list_find_custom(media->priv->participants,
+				participant, (GCompareFunc)strcmp);
+
+		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
+				0, PURPLE_MEDIA_STATE_END,
+				NULL, participant);
+
+		if (link != NULL) {
+			g_free(link->data);
+			media->priv->participants = g_list_delete_link(
+					media->priv->participants, link);
+		}
+
+		g_free(participant);
+	}
+
+	/* Free the conference if no sessions left */
+	if (media->priv->sessions != NULL &&
+			g_hash_table_size(media->priv->sessions) == 0) {
 		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
 				0, PURPLE_MEDIA_STATE_END,
 				NULL, NULL);
 		g_object_unref(media);
+		return;
 	}
 #endif
 }
@@ -2140,6 +759,72 @@
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
 
 	if (type == PURPLE_MEDIA_INFO_ACCEPT) {
+		GList *streams, *sessions = NULL, *participants = NULL;
+
+		g_return_if_fail(PURPLE_IS_MEDIA(media));
+
+		streams = purple_media_get_streams(media,
+				session_id, participant);
+
+		/* Emit stream acceptance */
+		for (; streams; streams =
+				g_list_delete_link(streams, streams)) {
+			PurpleMediaStream *stream = streams->data;
+
+			stream->accepted = TRUE;
+
+			g_signal_emit(media,
+					purple_media_signals[STREAM_INFO],
+					0, type, stream->session->id,
+					stream->participant, local);
+
+			if (g_list_find(sessions, stream->session) == NULL)
+				sessions = g_list_prepend(sessions,
+						stream->session);
+
+			if (g_list_find_custom(participants,
+					stream->participant,
+					(GCompareFunc)strcmp) == NULL)
+				participants = g_list_prepend(participants,
+						g_strdup(stream->participant));
+		}
+
+		/* Emit session acceptance */
+		for (; sessions; sessions =
+				g_list_delete_link(sessions, sessions)) {
+			PurpleMediaSession *session = sessions->data;
+
+			if (purple_media_accepted(media, session->id, NULL))
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0,
+						PURPLE_MEDIA_INFO_ACCEPT,
+						session->id, NULL, local);
+		}
+
+		/* Emit participant acceptance */
+		for (; participants; participants = g_list_delete_link(
+				participants, participants)) {
+			gchar *participant = participants->data;
+
+			if (purple_media_accepted(media, NULL, participant))
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0,
+						PURPLE_MEDIA_INFO_ACCEPT,
+						NULL, participant, local);
+
+			g_free(participant);
+		}
+
+		/* Emit conference acceptance */
+		if (purple_media_accepted(media, NULL, NULL))
+			g_signal_emit(media,
+					purple_media_signals[STREAM_INFO],
+					0, PURPLE_MEDIA_INFO_ACCEPT,
+					NULL, NULL, local);
+
+		return;
+	} else if (type == PURPLE_MEDIA_INFO_HANGUP ||
+			type == PURPLE_MEDIA_INFO_REJECT) {
 		GList *streams;
 
 		g_return_if_fail(PURPLE_IS_MEDIA(media));
@@ -2147,251 +832,230 @@
 		streams = purple_media_get_streams(media,
 				session_id, participant);
 
+		/* Emit for stream */
 		for (; streams; streams =
 				g_list_delete_link(streams, streams)) {
 			PurpleMediaStream *stream = streams->data;
-			g_object_set(G_OBJECT(stream->stream), "direction",
-					purple_media_to_fs_stream_direction(
-					stream->session->type), NULL);
-			stream->accepted = TRUE;
+
+			g_signal_emit(media,
+					purple_media_signals[STREAM_INFO],
+					0, type, stream->session->id,
+					stream->participant, local);
 		}
 
-		g_signal_emit(media, purple_media_signals[ACCEPTED],
-				0, NULL, NULL);
-	} else if (local == TRUE && (type == PURPLE_MEDIA_INFO_MUTE ||
-			type == PURPLE_MEDIA_INFO_UNMUTE)) {
-		GList *sessions;
-		gboolean active = (type == PURPLE_MEDIA_INFO_MUTE);
+		if (session_id != NULL && participant != NULL) {
+			/* Everything that needs to be emitted has been */
+		} else if (session_id == NULL && participant == NULL) {
+			/* Emit for everything in the conference */
+			GList *sessions = NULL;
+			GList *participants = media->priv->participants;
 
-		g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-		if (session_id == NULL)
-			sessions = g_hash_table_get_values(
+			if (media->priv->sessions != NULL)
+				sessions = g_hash_table_get_values(
 					media->priv->sessions);
-		else
-			sessions = g_list_prepend(NULL,
-					purple_media_get_session(
-					media, session_id));
+
+			/* Emit for sessions */
+			for (; sessions; sessions = g_list_delete_link(
+					sessions, sessions)) {
+				PurpleMediaSession *session = sessions->data;
 
-		purple_debug_info("media", "Turning mute %s\n",
-				active ? "on" : "off");
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type,
+						session->id, NULL, local);
+			}
+
+			/* Emit for participants */
+			for (; participants; participants =
+					g_list_next(participants)) {
+				gchar *participant = participants->data;
+
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type,
+						NULL, participant, local);
+			}
 
-		for (; sessions; sessions = g_list_delete_link(
-				sessions, sessions)) {
-			PurpleMediaSession *session = sessions->data;
-			if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
-				GstElement *volume = gst_bin_get_by_name(
-						GST_BIN(session->src),
-						"purpleaudioinputvolume");
-				g_object_set(volume, "mute", active, NULL);
+			/* Emit for conference */
+			g_signal_emit(media,
+					purple_media_signals[STREAM_INFO],
+					0, type, NULL, NULL, local);
+		} else if (session_id != NULL) {
+			/* Emit just the specific session */
+			PurpleMediaSession *session =
+					purple_media_get_session(
+					media, session_id);
+
+			if (session == NULL) {
+				purple_debug_warning("media",
+						"Couldn't find session"
+						" to hangup/reject.\n");
+			} else {
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type,
+						session->id, NULL, local);
+			}
+		} else if (participant != NULL) {
+			/* Emit just the specific participant */
+			if (!g_list_find_custom(media->priv->participants,
+					participant, (GCompareFunc)strcmp)) {
+				purple_debug_warning("media",
+						"Couldn't find participant"
+						" to hangup/reject.\n");
+			} else {
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type, NULL,
+						participant, local);
 			}
 		}
+
+		purple_media_end(media, session_id, participant);
+		return;
 	}
 
 	g_signal_emit(media, purple_media_signals[STREAM_INFO],
 			0, type, session_id, participant, local);
+#endif
+}
 
-	if (type == PURPLE_MEDIA_INFO_HANGUP ||
-			type == PURPLE_MEDIA_INFO_REJECT) {
-		purple_media_end(media, session_id, participant);
-	}
+void
+purple_media_set_params(PurpleMedia *media,
+		guint num_params, GParameter *params)
+{
+#ifdef USE_VV
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+
+	purple_media_backend_set_params(media->priv->backend, num_params, params);
 #endif
 }
 
+const gchar **
+purple_media_get_available_params(PurpleMedia *media)
+{
+	static const gchar *NULL_ARRAY[] = { NULL };
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL_ARRAY);
+
+	return purple_media_backend_get_available_params(media->priv->backend);
+#else
+	return NULL_ARRAY;
+#endif
+}
+
+gboolean
+purple_media_param_is_supported(PurpleMedia *media, const gchar *param)
+{
+#ifdef USE_VV
+	const gchar **params;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+	g_return_val_if_fail(param != NULL, FALSE);
+
+	params = purple_media_backend_get_available_params(media->priv->backend);
+	for (; *params != NULL; ++params)
+		if (!strcmp(*params, param))
+			return TRUE;
+#endif
+	return FALSE;
+}
+
 #ifdef USE_VV
 static void
-purple_media_new_local_candidate_cb(FsStream *stream,
-				    FsCandidate *local_candidate,
-				    PurpleMediaSession *session)
+purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *participant,
+		PurpleMediaCandidate *candidate, PurpleMedia *media)
 {
-	gchar *name;
-	FsParticipant *participant;
-	PurpleMediaCandidate *candidate;
-
-	g_return_if_fail(FS_IS_STREAM(stream));
-	g_return_if_fail(session != NULL);
+	PurpleMediaSession *session =
+			purple_media_get_session(media, sess_id);
 
-	purple_debug_info("media", "got new local candidate: %s\n", local_candidate->foundation);
-	g_object_get(stream, "participant", &participant, NULL);
-	g_object_get(participant, "cname", &name, NULL);
-	g_object_unref(participant);
-
-	purple_media_insert_local_candidate(session, name, fs_candidate_copy(local_candidate));
+	purple_media_insert_local_candidate(session, participant,
+			purple_media_candidate_copy(candidate));
 
-	candidate = purple_media_candidate_from_fs(local_candidate);
 	g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
-		      0, session->id, name, candidate);
-	g_object_unref(candidate);
-
-	g_free(name);
+		      0, session->id, participant, candidate);
 }
 
 static void
-purple_media_candidates_prepared_cb(FsStream *stream, PurpleMediaSession *session)
+purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *name, PurpleMedia *media)
 {
-	gchar *name;
-	FsParticipant *participant;
 	PurpleMediaStream *stream_data;
 
-	g_return_if_fail(FS_IS_STREAM(stream));
-	g_return_if_fail(session != NULL);
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
 
-	g_object_get(stream, "participant", &participant, NULL);
-	g_object_get(participant, "cname", &name, NULL);
-	g_object_unref(participant);
-
-	stream_data = purple_media_get_stream(session->media, session->id, name);
+	stream_data = purple_media_get_stream(media, sess_id, name);
 	stream_data->candidates_prepared = TRUE;
 
-	g_signal_emit(session->media,
-			purple_media_signals[CANDIDATES_PREPARED],
-			0, session->id, name);
-
-	g_free(name);
+	g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED],
+			0, sess_id, name);
 }
 
 /* callback called when a pair of transport candidates (local and remote)
  * has been established */
 static void
-purple_media_candidate_pair_established_cb(FsStream *fsstream,
-					   FsCandidate *native_candidate,
-					   FsCandidate *remote_candidate,
-					   PurpleMediaSession *session)
+purple_media_candidate_pair_established_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *name,
+		PurpleMediaCandidate *local_candidate,
+		PurpleMediaCandidate *remote_candidate,
+		PurpleMedia *media)
 {
-	gchar *name;
-	FsParticipant *participant;
 	PurpleMediaStream *stream;
 	GList *iter;
-
-	g_return_if_fail(FS_IS_STREAM(fsstream));
-	g_return_if_fail(session != NULL);
+	guint id;
 
-	g_object_get(fsstream, "participant", &participant, NULL);
-	g_object_get(participant, "cname", &name, NULL);
-	g_object_unref(participant);
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
 
-	stream = purple_media_get_stream(session->media, session->id, name);
+	stream = purple_media_get_stream(media, sess_id, name);
+	id = purple_media_candidate_get_component_id(local_candidate);
 
 	iter = stream->active_local_candidates;
 	for(; iter; iter = g_list_next(iter)) {
-		FsCandidate *c = iter->data;
-		if (native_candidate->component_id == c->component_id) {
-			fs_candidate_destroy(c);
+		PurpleMediaCandidate *c = iter->data;
+		if (id == purple_media_candidate_get_component_id(c)) {
+			g_object_unref(c);
 			stream->active_local_candidates =
 					g_list_delete_link(iter, iter);
 			stream->active_local_candidates = g_list_prepend(
 					stream->active_local_candidates,
-					fs_candidate_copy(native_candidate));
+					purple_media_candidate_copy(
+					local_candidate));
 			break;
 		}
 	}
 	if (iter == NULL)
 		stream->active_local_candidates = g_list_prepend(
 				stream->active_local_candidates,
-				fs_candidate_copy(native_candidate));
+				purple_media_candidate_copy(
+				local_candidate));
+
+	id = purple_media_candidate_get_component_id(local_candidate);
 
 	iter = stream->active_remote_candidates;
 	for(; iter; iter = g_list_next(iter)) {
-		FsCandidate *c = iter->data;
-		if (native_candidate->component_id == c->component_id) {
-			fs_candidate_destroy(c);
+		PurpleMediaCandidate *c = iter->data;
+		if (id == purple_media_candidate_get_component_id(c)) {
+			g_object_unref(c);
 			stream->active_remote_candidates =
 					g_list_delete_link(iter, iter);
 			stream->active_remote_candidates = g_list_prepend(
 					stream->active_remote_candidates,
-					fs_candidate_copy(remote_candidate));
+					purple_media_candidate_copy(
+					remote_candidate));
 			break;
 		}
 	}
 	if (iter == NULL)
 		stream->active_remote_candidates = g_list_prepend(
 				stream->active_remote_candidates,
-				fs_candidate_copy(remote_candidate));
+				purple_media_candidate_copy(
+				remote_candidate));
 
 	purple_debug_info("media", "candidate pair established\n");
 }
 
-static gboolean
-purple_media_connected_cb(PurpleMediaStream *stream)
-{
-	g_return_val_if_fail(stream != NULL, FALSE);
-
-	stream->connected_cb_id = 0;
-
-	purple_media_manager_create_output_window(
-			stream->session->media->priv->manager,
-			stream->session->media,
-			stream->session->id, stream->participant);
-
-	g_signal_emit(stream->session->media,
-			purple_media_signals[STATE_CHANGED],
-			0, PURPLE_MEDIA_STATE_CONNECTED,
-			stream->session->id, stream->participant);
-	return FALSE;
-}
-
 static void
-purple_media_src_pad_added_cb(FsStream *fsstream, GstPad *srcpad,
-			      FsCodec *codec, PurpleMediaStream *stream)
+purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, PurpleMedia *media)
 {
-	PurpleMediaPrivate *priv;
-	GstPad *sinkpad;
-
-	g_return_if_fail(FS_IS_STREAM(fsstream));
-	g_return_if_fail(stream != NULL);
-
-	priv = stream->session->media->priv;
-
-	if (stream->src == NULL) {
-		GstElement *sink = NULL;
-
-		if (codec->media_type == FS_MEDIA_TYPE_AUDIO) {
-			/*
-			 * Should this instead be:
-			 *  audioconvert ! audioresample ! liveadder !
-			 *   audioresample ! audioconvert ! realsink
-			 */
-			stream->src = gst_element_factory_make(
-					"liveadder", NULL);
-			sink = purple_media_manager_get_element(priv->manager,
-					PURPLE_MEDIA_RECV_AUDIO,
-					stream->session->media,
-					stream->session->id,
-					stream->participant);
-		} else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) {
-			stream->src = gst_element_factory_make(
-					"fsfunnel", NULL);
-			sink = gst_element_factory_make(
-					"fakesink", NULL);
-			g_object_set(G_OBJECT(sink), "async", FALSE, NULL);
-		}
-		stream->tee = gst_element_factory_make("tee", NULL);
-		gst_bin_add_many(GST_BIN(priv->confbin),
-				stream->src, stream->tee, sink, NULL);
-		gst_element_sync_state_with_parent(sink);
-		gst_element_sync_state_with_parent(stream->tee);
-		gst_element_sync_state_with_parent(stream->src);
-		gst_element_link_many(stream->src, stream->tee, sink, NULL);
-	}
-
-	sinkpad = gst_element_get_request_pad(stream->src, "sink%d");
-	gst_pad_link(srcpad, sinkpad);
-	gst_object_unref(sinkpad);
-
-	stream->connected_cb_id = purple_timeout_add(0,
-			(GSourceFunc)purple_media_connected_cb, stream);
-}
-
-static void
-purple_media_element_added_cb(FsElementAddedNotifier *self,
-		GstBin *bin, GstElement *element, gpointer user_data)
-{
-	/*
-	 * Hack to make H264 work with Gmail video.
-	 */
-	if (!strncmp(GST_ELEMENT_NAME(element), "x264", 4)) {
-		g_object_set(GST_OBJECT(element), "cabac", FALSE, NULL);
-	}
+	g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, sess_id);
 }
 #endif  /* USE_VV */
 
@@ -2403,97 +1067,21 @@
 {
 #ifdef USE_VV
 	PurpleMediaSession *session;
-	FsParticipant *participant = NULL;
 	PurpleMediaStream *stream = NULL;
-	FsMediaType media_type = purple_media_to_fs_media_type(type);
-	FsStreamDirection type_direction =
-			purple_media_to_fs_stream_direction(type);
-	gboolean is_nice = !strcmp(transmitter, "nice");
 
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
 
+	if (!purple_media_backend_add_stream(media->priv->backend,
+			sess_id, who, type, initiator, transmitter,
+			num_params, params)) {
+		purple_debug_error("media", "Error adding stream.\n");
+		return FALSE;
+	}
+
 	session = purple_media_get_session(media, sess_id);
 
 	if (!session) {
-		GError *err = NULL;
-		GList *codec_conf = NULL, *iter = NULL;
-		gchar *filename = NULL;
-		PurpleMediaSessionType session_type;
-		GstElement *src = NULL;
-
 		session = g_new0(PurpleMediaSession, 1);
-
-		session->session = fs_conference_new_session(
-				media->priv->conference, media_type, &err);
-
-		if (err != NULL) {
-			purple_media_error(media, "Error creating session: %s\n", err->message);
-			g_error_free(err);
-			g_free(session);
-			return FALSE;
-		}
-
-		filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
-		codec_conf = fs_codec_list_from_keyfile(filename, &err);
-		g_free(filename);
-
-		if (err != NULL) {
-			if (err->code == 4)
-				purple_debug_info("media", "Couldn't read "
-						"fs-codec.conf: %s\n",
-						err->message);
-			else
-				purple_debug_error("media", "Error reading "
-						"fs-codec.conf: %s\n",
-						err->message);
-			g_error_free(err);
-		}
-
-		/*
-		 * Add SPEEX if the configuration file doesn't exist or
-		 * there isn't a speex entry.
-		 */
-		for (iter = codec_conf; iter; iter = g_list_next(iter)) {
-			FsCodec *codec = iter->data;
-			if (!g_ascii_strcasecmp(codec->encoding_name, "speex"))
-				break;
-		}
-
-		if (iter == NULL) {
-			codec_conf = g_list_prepend(codec_conf,
-					fs_codec_new(FS_CODEC_ID_ANY,
-					"SPEEX", FS_MEDIA_TYPE_AUDIO, 8000));
-			codec_conf = g_list_prepend(codec_conf,
-					fs_codec_new(FS_CODEC_ID_ANY,
-					"SPEEX", FS_MEDIA_TYPE_AUDIO, 16000));
-		}
-
-		fs_session_set_codec_preferences(session->session, codec_conf, NULL);
-
-		/*
-		 * Removes a 5-7 second delay before
-		 * receiving the src-pad-added signal.
-		 * Only works for non-multicast FsRtpSessions.
-		 */
-		if (is_nice || !strcmp(transmitter, "rawudp"))
-			g_object_set(G_OBJECT(session->session),
-					"no-rtcp-timeout", 0, NULL);
-
-		/*
-		 * Hack to make x264 work with Gmail video.
-		 */
-		if (is_nice && !strcmp(sess_id, "google-video")) {
-			FsElementAddedNotifier *notifier =
-					fs_element_added_notifier_new();
-			g_signal_connect(G_OBJECT(notifier), "element-added",
-					G_CALLBACK(purple_media_element_added_cb),
-					stream);
-			fs_element_added_notifier_add(notifier,
-					GST_BIN(media->priv->conference));
-		}
-
-		fs_codec_list_destroy(codec_conf);
-
 		session->id = g_strdup(sess_id);
 		session->media = media;
 		session->type = type;
@@ -2503,146 +1091,23 @@
 		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
 				0, PURPLE_MEDIA_STATE_NEW,
 				session->id, NULL);
-
-		session_type = purple_media_from_fs(media_type,
-				FS_DIRECTION_SEND);
-		src = purple_media_manager_get_element(
-				media->priv->manager, session_type,
-				media, session->id, who);
-		if (!GST_IS_ELEMENT(src)) {
-			purple_debug_error("media",
-					"Error creating src for session %s\n",
-					session->id);
-			purple_media_end(media, session->id, NULL);
-			return FALSE;
-		}
-
-		purple_media_set_src(media, session->id, src);
-		gst_element_set_state(session->src, GST_STATE_PLAYING);
-
-		purple_media_manager_create_output_window(
-				media->priv->manager,
-				session->media,
-				session->id, NULL);
-	}
-
-	if (!(participant = purple_media_add_participant(media, who))) {
-		purple_media_remove_session(media, session);
-		g_free(session);
-		return FALSE;
-	} else {
-		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_NEW,
-				NULL, who);
 	}
 
-	stream = purple_media_get_stream(media, sess_id, who);
-
-	if (!stream) {
-		GError *err = NULL;
-		FsStream *fsstream = NULL;
-		const gchar *stun_ip = purple_network_get_stun_ip();
-		const gchar *turn_ip = purple_network_get_turn_ip();
-
-		if (stun_ip || turn_ip) {
-			guint new_num_params = 
-					(stun_ip && is_nice) && turn_ip ?
-					num_params + 2 : num_params + 1;
-			guint next_param_index = num_params;
-			GParameter *param = g_new0(GParameter, new_num_params);
-			memcpy(param, params, sizeof(GParameter) * num_params);
-
-			if (stun_ip) {
-				purple_debug_info("media", 
-					"setting property stun-ip on new stream: %s\n", stun_ip);
-
-				param[next_param_index].name = "stun-ip";
-				g_value_init(&param[next_param_index].value, G_TYPE_STRING);
-				g_value_set_string(&param[next_param_index].value, stun_ip);
-				next_param_index++;
-			}
-
-			if (turn_ip && is_nice) {
-				GValueArray *relay_info = g_value_array_new(0);
-				GValue value;
-				gint turn_port = 
-					purple_prefs_get_int("/purple/network/turn_port");
-				const gchar *username =
-					purple_prefs_get_string("/purple/network/turn_username");
-				const gchar *password =
-					purple_prefs_get_string("/purple/network/turn_password");
-				GstStructure *turn_setup = gst_structure_new("relay-info",
-					"ip", G_TYPE_STRING, turn_ip, 
-					"port", G_TYPE_UINT, turn_port,
-					"username", G_TYPE_STRING, username,
-					"password", G_TYPE_STRING, password,
-					NULL);
+	if (!g_list_find_custom(media->priv->participants,
+			who, (GCompareFunc)strcmp)) {
+		media->priv->participants = g_list_prepend(
+				media->priv->participants, g_strdup(who));
 
-				if (turn_setup) {
-					memset(&value, 0, sizeof(GValue));
-					g_value_init(&value, GST_TYPE_STRUCTURE);
-					gst_value_set_structure(&value, turn_setup);
-					relay_info = g_value_array_append(relay_info, &value);
-					gst_structure_free(turn_setup);
-
-					purple_debug_info("media",
-						"setting property relay-info on new stream\n");
-					param[next_param_index].name = "relay-info";
-					g_value_init(&param[next_param_index].value, 
-						G_TYPE_VALUE_ARRAY);
-					g_value_set_boxed(&param[next_param_index].value,
-						relay_info);
-					g_value_array_free(relay_info);
-				} else {
-					purple_debug_error("media", "Error relay info");
-					g_object_unref(participant);
-					g_hash_table_remove(media->priv->participants, who);
-					purple_media_remove_session(media, session);
-					g_free(session);
-					return FALSE;
-				}
-			}
+		g_signal_emit_by_name(media, "state-changed",
+				PURPLE_MEDIA_STATE_NEW, NULL, who);
+	}
 
-			fsstream = fs_session_new_stream(session->session,
-					participant, type_direction &
-					FS_DIRECTION_RECV, transmitter,
-					new_num_params, param, &err);
-			g_free(param);
-		} else {
-			fsstream = fs_session_new_stream(session->session,
-					participant, type_direction &
-					FS_DIRECTION_RECV, transmitter,
-					num_params, params, &err);
-		}
-
-		if (err) {
-			purple_debug_error("media", "Error creating stream: %s\n",
-					   err->message);
-			g_error_free(err);
-			g_object_unref(participant);
-			g_hash_table_remove(media->priv->participants, who);
-			purple_media_remove_session(media, session);
-			g_free(session);
-			return FALSE;
-		}
-
-		stream = purple_media_insert_stream(session, who, fsstream);
-		stream->initiator = initiator;
-
-		/* callback for source pad added (new stream source ready) */
-		g_signal_connect(G_OBJECT(fsstream),
-				 "src-pad-added", G_CALLBACK(purple_media_src_pad_added_cb), stream);
+	if (purple_media_get_stream(media, sess_id, who) == NULL) {
+		stream = purple_media_insert_stream(session, who, initiator);
 
 		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
 				0, PURPLE_MEDIA_STATE_NEW,
 				session->id, who);
-	} else {
-		if (purple_media_to_fs_stream_direction(stream->session->type)
-				!= type_direction) {
-			/* change direction */
-			g_object_set(stream->stream, "direction",
-					type_direction, NULL);
-		}
 	}
 
 	return TRUE;
@@ -2677,22 +1142,9 @@
 purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
 {
 #ifdef USE_VV
-	GList *fscodecs;
-	GList *codecs;
-	PurpleMediaSession *session;
-
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
 
-	session = purple_media_get_session(media, sess_id);
-
-	if (session == NULL)
-		return NULL;
-
-	g_object_get(G_OBJECT(session->session),
-		     "codecs", &fscodecs, NULL);
-	codecs = purple_media_codec_list_from_fs(fscodecs);
-	fs_codec_list_destroy(fscodecs);
-	return codecs;
+	return purple_media_backend_get_codecs(media->priv->backend, sess_id);
 #else
 	return NULL;
 #endif
@@ -2703,11 +1155,10 @@
                                   const gchar *participant)
 {
 #ifdef USE_VV
-	PurpleMediaStream *stream;
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	stream = purple_media_get_stream(media, sess_id, participant);
-	return stream ? purple_media_candidate_list_from_fs(
-			stream->local_candidates) : NULL;
+
+	return purple_media_backend_get_local_candidates(media->priv->backend,
+			sess_id, participant);
 #else
 	return NULL;
 #endif
@@ -2720,7 +1171,6 @@
 {
 #ifdef USE_VV
 	PurpleMediaStream *stream;
-	GError *err = NULL;
 
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
 	stream = purple_media_get_stream(media, sess_id, participant);
@@ -2729,30 +1179,19 @@
 		purple_debug_error("media",
 				"purple_media_add_remote_candidates: "
 				"couldn't find stream %s %s.\n",
-				sess_id, participant);
+				sess_id ? sess_id : "(null)",
+				participant ? participant : "(null)");
 		return;
 	}
 
 	stream->remote_candidates = g_list_concat(stream->remote_candidates,
-			purple_media_candidate_list_to_fs(remote_candidates));
-
-	fs_stream_set_remote_candidates(stream->stream,
-			stream->remote_candidates, &err);
+			purple_media_candidate_list_copy(remote_candidates));
 
-	if (err) {
-		purple_debug_error("media", "Error adding remote"
-				" candidates: %s\n", err->message);
-		g_error_free(err);
-	}
+	purple_media_backend_add_remote_candidates(media->priv->backend,
+			sess_id, participant, remote_candidates);
 #endif
 }
 
-#if 0
-/*
- * These two functions aren't being used and I'd rather not lock in the API
- * until they are needed. If they ever are.
- */
-
 GList *
 purple_media_get_active_local_candidates(PurpleMedia *media,
 		const gchar *sess_id, const gchar *participant)
@@ -2761,7 +1200,7 @@
 	PurpleMediaStream *stream;
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
 	stream = purple_media_get_stream(media, sess_id, participant);
-	return purple_media_candidate_list_from_fs(
+	return purple_media_candidate_list_copy(
 			stream->active_local_candidates);
 #else
 	return NULL;
@@ -2776,42 +1215,22 @@
 	PurpleMediaStream *stream;
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
 	stream = purple_media_get_stream(media, sess_id, participant);
-	return purple_media_candidate_list_from_fs(
+	return purple_media_candidate_list_copy(
 			stream->active_remote_candidates);
 #else
 	return NULL;
 #endif
 }
-#endif
 
 gboolean
 purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
                                const gchar *participant, GList *codecs)
 {
 #ifdef USE_VV
-	PurpleMediaStream *stream;
-	FsStream *fsstream;
-	GList *fscodecs;
-	GError *err = NULL;
-
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-	stream = purple_media_get_stream(media, sess_id, participant);
-
-	if (stream == NULL)
-		return FALSE;
 
-	fsstream = stream->stream;
-	fscodecs = purple_media_codec_list_to_fs(codecs);
-	fs_stream_set_remote_codecs(fsstream, fscodecs, &err);
-	fs_codec_list_destroy(fscodecs);
-
-	if (err) {
-		purple_debug_error("media", "Error setting remote codecs: %s\n",
-				   err->message);
-		g_error_free(err);
-		return FALSE;
-	}
-	return TRUE;
+	return purple_media_backend_set_remote_codecs(media->priv->backend,
+			sess_id, participant, codecs);
 #else
 	return FALSE;
 #endif
@@ -2848,27 +1267,10 @@
 purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
 {
 #ifdef USE_VV
-	PurpleMediaSession *session;
-	FsCodec *fscodec;
-	GError *err = NULL;
-
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
 
-	session = purple_media_get_session(media, sess_id);
-
-	if (session != NULL)
-		return FALSE;
-
-	fscodec = purple_media_codec_to_fs(codec);
-	fs_session_set_send_codec(session->session, fscodec, &err);
-	fs_codec_destroy(fscodec);
-
-	if (err) {
-		purple_debug_error("media", "Error setting send codec\n");
-		g_error_free(err);
-		return FALSE;
-	}
-	return TRUE;
+	return purple_media_backend_set_send_codec(
+			media->priv->backend, sess_id, codec);
 #else
 	return FALSE;
 #endif
@@ -2878,31 +1280,10 @@
 purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
 {
 #ifdef USE_VV
-	gboolean ret;
-
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
 
-	if (sess_id != NULL) {
-		PurpleMediaSession *session;
-		session = purple_media_get_session(media, sess_id);
-
-		if (session == NULL)
-			return FALSE;
-
-		g_object_get(session->session, "codecs-ready", &ret, NULL);
-	} else {
-		GList *values = g_hash_table_get_values(media->priv->sessions);
-		for (; values; values = g_list_delete_link(values, values)) {
-			PurpleMediaSession *session = values->data;
-			g_object_get(session->session,
-					"codecs-ready", &ret, NULL);
-			if (ret == FALSE)
-				break;
-		}
-		if (values != NULL)
-			g_list_free(values);
-	}
-	return ret;
+	return purple_media_backend_codecs_ready(
+			media->priv->backend, sess_id);
 #else
 	return FALSE;
 #endif
@@ -2978,26 +1359,13 @@
 		const gchar *session_id, double level)
 {
 #ifdef USE_VV
-	GList *sessions;
-
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	if (session_id == NULL)
-		sessions = g_hash_table_get_values(media->priv->sessions);
-	else
-		sessions = g_list_append(NULL,
-				purple_media_get_session(media, session_id));
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
 
-	for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
-		PurpleMediaSession *session = sessions->data;
-
-		if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
-			GstElement *volume = gst_bin_get_by_name(
-					GST_BIN(session->src),
-					"purpleaudioinputvolume");
-			g_object_set(volume, "volume", level, NULL);
-		}
-	}
+	purple_media_backend_fs2_set_input_volume(
+			PURPLE_MEDIA_BACKEND_FS2(
+			media->priv->backend),
+			session_id, level);
 #endif
 }
 
@@ -3006,40 +1374,13 @@
 		double level)
 {
 #ifdef USE_VV
-	GList *streams;
-
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	streams = purple_media_get_streams(media,
-			session_id, participant);
-
-	for (; streams; streams = g_list_delete_link(streams, streams)) {
-		PurpleMediaStream *stream = streams->data;
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
 
-		if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO) {
-			GstElement *tee = stream->tee;
-			GstIterator *iter = gst_element_iterate_src_pads(tee);
-			GstPad *sinkpad;
-			while (gst_iterator_next(iter, (gpointer)&sinkpad)
-					 == GST_ITERATOR_OK) {
-				GstPad *peer = gst_pad_get_peer(sinkpad);
-				GstElement *volume;
-
-				if (peer == NULL) {
-					gst_object_unref(sinkpad);
-					continue;
-				}
-					
-				volume = gst_bin_get_by_name(GST_BIN(
-						GST_OBJECT_PARENT(peer)),
-						"purpleaudiooutputvolume");
-				g_object_set(volume, "volume", level, NULL);
-				gst_object_unref(peer);
-				gst_object_unref(sinkpad);
-			}
-			gst_iterator_free(iter);
-		}
-	}
+	purple_media_backend_fs2_set_output_volume(
+			PURPLE_MEDIA_BACKEND_FS2(
+			media->priv->backend),
+			session_id, participant, level);
 #endif
 }
 
@@ -3087,16 +1428,11 @@
 #ifdef USE_VV
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
 
-	if (session_id != NULL && participant == NULL) {
-		PurpleMediaSession *session =
-				purple_media_get_session(media, session_id);
-		return (session != NULL) ? session->tee : NULL;
-	} else if (session_id != NULL && participant != NULL) {
-		PurpleMediaStream *stream =
-				purple_media_get_stream(media,
+	if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
+		return purple_media_backend_fs2_get_tee(
+				PURPLE_MEDIA_BACKEND_FS2(
+				media->priv->backend),
 				session_id, participant);
-		return (stream != NULL) ? stream->tee : NULL;
-	}
 	g_return_val_if_reached(NULL);
 #else
 	return NULL;
--- a/libpurple/media.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/media.h	Wed Jun 13 19:30:27 2012 -0400
@@ -21,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
 #ifndef _PURPLE_MEDIA_H_
@@ -30,23 +30,10 @@
 #include <glib.h>
 #include <glib-object.h>
 
-G_BEGIN_DECLS
-
-#define PURPLE_TYPE_MEDIA_CANDIDATE           (purple_media_candidate_get_type())
-#define PURPLE_MEDIA_CANDIDATE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate))
-#define PURPLE_MEDIA_CANDIDATE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate))
-#define PURPLE_IS_MEDIA_CANDIDATE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_CANDIDATE))
-#define PURPLE_IS_MEDIA_CANDIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_CANDIDATE))
-#define PURPLE_MEDIA_CANDIDATE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate))
+#include "media/candidate.h"
+#include "media/codec.h"
+#include "media/enum-types.h"
 
-#define PURPLE_TYPE_MEDIA_CODEC           (purple_media_codec_get_type())
-#define PURPLE_MEDIA_CODEC(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec))
-#define PURPLE_MEDIA_CODEC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec))
-#define PURPLE_IS_MEDIA_CODEC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_CODEC))
-#define PURPLE_IS_MEDIA_CODEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_CODEC))
-#define PURPLE_MEDIA_CODEC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec))
-
-#define PURPLE_TYPE_MEDIA_SESSION_TYPE (purple_media_session_type_get_type())
 #define PURPLE_TYPE_MEDIA            (purple_media_get_type())
 #define PURPLE_MEDIA(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA, PurpleMedia))
 #define PURPLE_MEDIA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA, PurpleMediaClass))
@@ -54,310 +41,27 @@
 #define PURPLE_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA))
 #define PURPLE_MEDIA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA, PurpleMediaClass))
 
-#define PURPLE_TYPE_MEDIA_CANDIDATE_TYPE (purple_media_candidate_type_get_type())
-#define PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL (purple_media_network_protocol_get_type())
-#define PURPLE_MEDIA_TYPE_STATE      (purple_media_state_changed_get_type())
-#define PURPLE_MEDIA_TYPE_INFO_TYPE	(purple_media_info_type_get_type())
-
-/** @copydoc _PurpleMedia */
+/** An opaque structure representing a media call. */
 typedef struct _PurpleMedia PurpleMedia;
-/** @copydoc _PurpleMediaCandidate */
-typedef struct _PurpleMediaCandidate PurpleMediaCandidate;
-/** @copydoc _PurpleMediaCodec */
-typedef struct _PurpleMediaCodec PurpleMediaCodec;
-
-/** Media caps */
-typedef enum {
-	PURPLE_MEDIA_CAPS_NONE = 0,
-	PURPLE_MEDIA_CAPS_AUDIO = 1,
-	PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION = 1 << 1,
-	PURPLE_MEDIA_CAPS_VIDEO = 1 << 2,
-	PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION = 1 << 3,
-	PURPLE_MEDIA_CAPS_AUDIO_VIDEO = 1 << 4,
-	PURPLE_MEDIA_CAPS_MODIFY_SESSION = 1 << 5,
-	PURPLE_MEDIA_CAPS_CHANGE_DIRECTION = 1 << 6,
-} PurpleMediaCaps;
-
-/** Media session types */
-typedef enum {
-	PURPLE_MEDIA_NONE	= 0,
-	PURPLE_MEDIA_RECV_AUDIO = 1 << 0,
-	PURPLE_MEDIA_SEND_AUDIO = 1 << 1,
-	PURPLE_MEDIA_RECV_VIDEO = 1 << 2,
-	PURPLE_MEDIA_SEND_VIDEO = 1 << 3,
-	PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO,
-	PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO
-} PurpleMediaSessionType;
-
-/** Media state-changed types */
-typedef enum {
-	PURPLE_MEDIA_STATE_NEW = 0,
-	PURPLE_MEDIA_STATE_CONNECTED,
-	PURPLE_MEDIA_STATE_END,
-} PurpleMediaState;
-
-/** Media info types */
-typedef enum {
-	PURPLE_MEDIA_INFO_HANGUP = 0,
-	PURPLE_MEDIA_INFO_ACCEPT,
-	PURPLE_MEDIA_INFO_REJECT,
-	PURPLE_MEDIA_INFO_MUTE,
-	PURPLE_MEDIA_INFO_UNMUTE,
-	PURPLE_MEDIA_INFO_HOLD,
-	PURPLE_MEDIA_INFO_UNHOLD,
-} PurpleMediaInfoType;
-
-typedef enum {
-	PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
-	PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
-	PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX,
-	PURPLE_MEDIA_CANDIDATE_TYPE_RELAY,
-	PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST,
-} PurpleMediaCandidateType;
-
-typedef enum {
-	PURPLE_MEDIA_COMPONENT_NONE = 0,
-	PURPLE_MEDIA_COMPONENT_RTP = 1,
-	PURPLE_MEDIA_COMPONENT_RTCP = 2,
-} PurpleMediaComponentType;
-
-typedef enum {
-	PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
-	PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
-} PurpleMediaNetworkProtocol;
 
 #include "signals.h"
 #include "util.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Gets the media session type's GType
- *
- * @return The media session type's GType.
- *
- * @since 2.6.0
- */
-GType purple_media_session_type_get_type(void);
-
-/**
- * Gets the media candidate type's GType
- *
- * @return The media candidate type's GType.
- *
- * @since 2.6.0
- */
-GType purple_media_candidate_type_get_type(void);
-
-/**
- * Gets the media network protocol's GType
- *
- * @return The media network protocol's GType.
- *
- * @since 2.6.0
- */
-GType purple_media_network_protocol_get_type(void);
+G_BEGIN_DECLS
 
 /**
  * Gets the media class's GType
  *
  * @return The media class's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_get_type(void);
 
 /**
- * Gets the type of the state-changed enum
- *
- * @return The state-changed enum's GType
- *
- * @since 2.6.0
- */
-GType purple_media_state_changed_get_type(void);
-
-/**
- * Gets the type of the info type enum
- *
- * @return The info type enum's GType
- *
- * @since 2.6.0
- */
-GType purple_media_info_type_get_type(void);
-
-/**
- * Gets the type of the media candidate structure.
- *
- * @return The media canditate's GType
- *
- * @since 2.6.0
- */
-GType purple_media_candidate_get_type(void);
-
-/**
- * Creates a PurpleMediaCandidate instance.
- *
- * @param foundation The foundation of the candidate.
- * @param component_id The component this candidate is for.
- * @param type The type of candidate.
- * @param proto The protocol this component is for.
- * @param ip The IP address of this component.
- * @param port The network port.
- *
- * @return The newly created PurpleMediaCandidate instance.
- *
- * @since 2.6.0
- */
-PurpleMediaCandidate *purple_media_candidate_new(
-		const gchar *foundation, guint component_id,
-		PurpleMediaCandidateType type,
-		PurpleMediaNetworkProtocol proto,
-		const gchar *ip, guint port);
-
-/**
- * Copies a GList of PurpleMediaCandidate and its contents.
- *
- * @param candidates The list of candidates to be copied.
- *
- * @return The copy of the GList.
- *
- * @since 2.6.0
- */
-GList *purple_media_candidate_list_copy(GList *candidates);
-
-/**
- * Frees a GList of PurpleMediaCandidate and its contents.
- *
- * @param candidates The list of candidates to be freed.
- *
- * @since 2.6.0
- */
-void purple_media_candidate_list_free(GList *candidates);
-
-gchar *purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate);
-guint purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate);
-gchar *purple_media_candidate_get_ip(PurpleMediaCandidate *candidate);
-guint16 purple_media_candidate_get_port(PurpleMediaCandidate *candidate);
-gchar *purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate);
-guint16 purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate);
-PurpleMediaNetworkProtocol purple_media_candidate_get_protocol(
-		PurpleMediaCandidate *candidate);
-guint32 purple_media_candidate_get_priority(PurpleMediaCandidate *candidate);
-PurpleMediaCandidateType purple_media_candidate_get_candidate_type(
-		PurpleMediaCandidate *candidate);
-gchar *purple_media_candidate_get_username(PurpleMediaCandidate *candidate);
-gchar *purple_media_candidate_get_password(PurpleMediaCandidate *candidate);
-guint purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate);
-
-/**
- * Gets the type of the media codec structure.
- *
- * @return The media codec's GType
- *
- * @since 2.6.0
- */
-GType purple_media_codec_get_type(void);
-
-/**
- * Creates a new PurpleMediaCodec instance.
- *
- * @param id Codec identifier.
- * @param encoding_name Name of the media type this encodes.
- * @param media_type PurpleMediaSessionType of this codec.
- * @param clock_rate The clock rate this codec encodes at, if applicable.
- *
- * @return The newly created PurpleMediaCodec.
- *
- * @since 2.6.0
- */
-PurpleMediaCodec *purple_media_codec_new(int id, const char *encoding_name,
-		PurpleMediaSessionType media_type, guint clock_rate);
-
-guint purple_media_codec_get_id(PurpleMediaCodec *codec);
-gchar *purple_media_codec_get_encoding_name(PurpleMediaCodec *codec);
-guint purple_media_codec_get_clock_rate(PurpleMediaCodec *codec);
-guint purple_media_codec_get_channels(PurpleMediaCodec *codec);
-GList *purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec);
-
-/**
- * Creates a string representation of the codec.
- *
- * @param codec The codec to create the string of.
- *
- * @return The new string representation.
- *
- * @since 2.6.0
- */
-gchar *purple_media_codec_to_string(const PurpleMediaCodec *codec);
-
-/**
- * Adds an optional parameter to the codec.
- *
- * @param codec The codec to add the parameter to.
- * @param name The name of the parameter to add.
- * @param value The value of the parameter to add.
- *
- * @since 2.6.0
- */
-void purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
-		const gchar *name, const gchar *value);
-
-/**
- * Removes an optional parameter from the codec.
- *
- * @param codec The codec to remove the parameter from.
- * @param param A pointer to the parameter to remove.
- *
- * @since 2.6.0
- */
-void purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
-		PurpleKeyValuePair *param);
-
-/**
- * Gets an optional parameter based on the values given.
- *
- * @param codec The codec to find the parameter in.
- * @param name The name of the parameter to search for.
- * @param value The value to search for or NULL.
- *
- * @return The value found or NULL.
- *
- * @since 2.6.0
- */
-PurpleKeyValuePair *purple_media_codec_get_optional_parameter(
-		PurpleMediaCodec *codec, const gchar *name,
-		const gchar *value);
-
-/**
- * Copies a GList of PurpleMediaCodec and its contents.
- *
- * @param codecs The list of codecs to be copied.
- *
- * @return The copy of the GList.
- *
- * @since 2.6.0
- */
-GList *purple_media_codec_list_copy(GList *codecs);
-
-/**
- * Frees a GList of PurpleMediaCodec and its contents.
- *
- * @param codecs The list of codecs to be freed.
- *
- * @since 2.6.0
- */
-void purple_media_codec_list_free(GList *codecs);
-
-/**
  * Gets a list of session IDs.
  *
  * @param media The media session from which to retrieve session IDs.
  *
  * @return GList of session IDs. The caller must free the list.
- *
- * @since 2.6.0
  */
 GList *purple_media_get_session_ids(PurpleMedia *media);
 
@@ -367,8 +71,6 @@
  * @param media The media session to retrieve the account from.
  *
  * @return The account retrieved.
- *
- * @since 2.6.0
  */
 PurpleAccount *purple_media_get_account(PurpleMedia *media);
 
@@ -378,8 +80,6 @@
  * @param media The media session to retrieve the prpl data from.
  *
  * @return The prpl data retrieved.
- *
- * @since 2.6.0
  */
 gpointer purple_media_get_prpl_data(PurpleMedia *media);
 
@@ -388,8 +88,6 @@
  *
  * @param media The media session to set the prpl data on.
  * @param prpl_data The data to set on the media session.
- *
- * @since 2.6.0
  */
 void purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data);
 
@@ -399,8 +97,6 @@
  * @param media The media object to set the state on.
  * @param error The format of the error message to send in the signal.
  * @param ... The arguments to plug into the format.
- *
- * @since 2.6.0
  */
 void purple_media_error(PurpleMedia *media, const gchar *error, ...);
 
@@ -410,8 +106,6 @@
  * @param media The media object with which to end streams.
  * @param session_id The session to end streams on.
  * @param participant The participant to end streams with.
- *
- * @since 2.6.0
  */
 void purple_media_end(PurpleMedia *media, const gchar *session_id,
 		const gchar *participant);
@@ -424,14 +118,52 @@
  * @param session_id The id of the session of the stream being signaled.
  * @param participant The participant of the stream being signaled.
  * @param local TRUE if the info originated locally, FALSE if on the remote end.
- *
- * @since 2.6.0
  */
 void purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
 		const gchar *session_id, const gchar *participant,
 		gboolean local);
 
 /**
+ * Sets various optional parameters of the media call.
+ *
+ * Currently supported are:
+ *   - "sdes-cname"    : The CNAME for the RTP sessions
+ *   - "sdes-name"     : Real name used to describe the source in SDES messages
+ *   - "sdes-tool"     : The TOOL to put in SDES messages
+ *   - "sdes-email"    : Email address to put in SDES messages
+ *   - "sdes-location" : The LOCATION to put in SDES messages
+ *   - "sdes-note"     : The NOTE to put in SDES messages
+ *   - "sdes-phone"    : The PHONE to put in SDES messages
+ *
+ * @param media The media object to set the parameters on.
+ * @param num_params The number of parameters to pass
+ * @param params Array of @c GParameter to pass
+ */
+void purple_media_set_params(PurpleMedia *media,
+		guint num_params, GParameter *params);
+
+/**
+ * Gets the list of optional parameters supported by the media backend.
+ *
+ * The list is owned by the @c PurpleMedia internals and should NOT be freed.
+ *
+ * @param media The media object
+ *
+ * @return NULL-terminated array of names of supported parameters.
+ */
+const gchar **purple_media_get_available_params(PurpleMedia *media);
+
+/**
+ * Checks if given optional parameter is supported by the media backend.
+ *
+ * @param media The media object
+ * @param param name of parameter
+ *
+ * @return @c TRUE if backend recognizes the parameter, @c FALSE otherwise.
+ */
+gboolean purple_media_param_is_supported(PurpleMedia *media, const gchar *param);
+
+/**
  * Adds a stream to a session.
  *
  * It only adds a stream to one audio session or video session as
@@ -447,8 +179,6 @@
  * @param params The parameters to pass to Farsight.
  *
  * @return @c TRUE The stream was added successfully, @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
 		const gchar *who, PurpleMediaSessionType type,
@@ -462,8 +192,6 @@
  * @param sess_id The session id of the session to get the type from.
  *
  * @return The retreived session type.
- *
- * @since 2.6.0
  */
 PurpleMediaSessionType purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id);
 
@@ -473,8 +201,6 @@
  * @param media The media object to get the manager instance from.
  *
  * @return The PurpleMediaManager instance retrieved.
- *
- * @since 2.6.0
  */
 struct _PurpleMediaManager *purple_media_get_manager(PurpleMedia *media);
 
@@ -485,8 +211,6 @@
  * @param sess_id The session id of the session to get the codecs from.
  *
  * @return The retreieved codecs.
- *
- * @since 2.6.0
  */
 GList *purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id);
 
@@ -497,8 +221,6 @@
  * @param sess_id The session id of the session find the stream in.
  * @param participant The name of the remote user to add the candidates for.
  * @param remote_candidates The remote candidates to add.
- *
- * @since 2.6.0
  */
 void purple_media_add_remote_candidates(PurpleMedia *media,
 					const gchar *sess_id,
@@ -511,19 +233,11 @@
  * @param media The media object to find the session in.
  * @param sess_id The session id of the session to find the stream in.
  * @param participant The name of the remote user to get the candidates from.
- *
- * @since 2.6.0
  */
 GList *purple_media_get_local_candidates(PurpleMedia *media,
 					 const gchar *sess_id,
 					 const gchar *participant);
 
-#if 0
-/*
- * These two functions aren't being used and I'd rather not lock in the API
- * until they are needed. If they ever are.
- */
-
 /**
  * Gets the active local candidates for the stream.
  *
@@ -549,7 +263,6 @@
  */
 GList *purple_media_get_active_remote_candidates(PurpleMedia *media,
 		const gchar *sess_id, const gchar *participant);
-#endif
 
 /**
  * Sets remote candidates from the stream.
@@ -557,10 +270,9 @@
  * @param media The media object to find the session in.
  * @param sess_id The session id of the session find the stream in.
  * @param participant The name of the remote user to set the candidates from.
+ * @param codecs The list of remote codecs to set.
  *
  * @return @c TRUE The codecs were set successfully, or @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
 					const gchar *participant, GList *codecs);
@@ -573,8 +285,6 @@
  * @param participant The remote user to check for.
  *
  * @return @c TRUE All streams for the given session_id/participant combination have candidates prepared, @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_candidates_prepared(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant);
@@ -587,8 +297,6 @@
  * @param codec The codec to set the session to stream.
  *
  * @return @c TRUE The codec was successfully changed, or @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec);
 
@@ -599,8 +307,6 @@
  * @param sess_id The session id of the session to check.
  *
  * @return @c TRUE The codecs are ready, or @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id);
 
@@ -612,8 +318,6 @@
  * @param participant The participant of the stream to check.
  *
  * @return TRUE if the local user is the stream's initator, else FALSE.
- *
- * @since 2.6.0
  */
 gboolean purple_media_is_initiator(PurpleMedia *media,
 		const gchar *sess_id, const gchar *participant);
@@ -626,8 +330,6 @@
  * @param participant The participant to check.
  *
  * @return @c TRUE The selected streams have been accepted, or @c FALSE otherwise.
- *
- * @since 2.6.0
  */
 gboolean purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
 		const gchar *participant);
@@ -638,8 +340,6 @@
  * @param media The media object the sessions are in.
  * @param session_id The session to select (if any).
  * @param level The level to set the volume to.
- *
- * @since 2.6.0
  */
 void purple_media_set_input_volume(PurpleMedia *media, const gchar *session_id, double level);
 
@@ -650,8 +350,6 @@
  * @param session_id The session to limit the streams to (if any).
  * @param participant The participant to limit the streams to (if any).
  * @param level The level to set the volume to.
- *
- * @since 2.6.0
  */
 void purple_media_set_output_volume(PurpleMedia *media, const gchar *session_id,
 		const gchar *participant, double level);
@@ -665,8 +363,6 @@
  * @param window_id The window id use for embedding the video in.
  *
  * @return An id to reference the output window.
- *
- * @since 2.6.0
  */
 gulong purple_media_set_output_window(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant,
@@ -676,15 +372,9 @@
  * Removes all output windows from a given media session.
  *
  * @param media The instance to remove all output windows from.
- *
- * @since 2.6.0
  */
 void purple_media_remove_output_windows(PurpleMedia *media);
 
-#ifdef __cplusplus
-}
-#endif
-
 G_END_DECLS
 
 #endif  /* _PURPLE_MEDIA_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/backend-fs2.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,2292 @@
+/**
+ * @file backend-fs2.c Farsight 2 backend for media 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 "internal.h"
+
+#include "backend-fs2.h"
+
+#ifdef USE_VV
+#include "backend-iface.h"
+#include "debug.h"
+#include "network.h"
+#include "media-gst.h"
+
+#include <gst/farsight/fs-conference-iface.h>
+#include <gst/farsight/fs-element-added-notifier.h>
+
+/** @copydoc _PurpleMediaBackendFs2Class */
+typedef struct _PurpleMediaBackendFs2Class PurpleMediaBackendFs2Class;
+/** @copydoc _PurpleMediaBackendFs2Private */
+typedef struct _PurpleMediaBackendFs2Private PurpleMediaBackendFs2Private;
+/** @copydoc _PurpleMediaBackendFs2Session */
+typedef struct _PurpleMediaBackendFs2Session PurpleMediaBackendFs2Session;
+/** @copydoc _PurpleMediaBackendFs2Stream */
+typedef struct _PurpleMediaBackendFs2Stream PurpleMediaBackendFs2Stream;
+
+#define PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj) \
+		(G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+		PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2Private))
+
+static void purple_media_backend_iface_init(PurpleMediaBackendIface *iface);
+
+static gboolean
+gst_bus_cb(GstBus *bus, GstMessage *msg, PurpleMediaBackendFs2 *self);
+static void
+state_changed_cb(PurpleMedia *media, PurpleMediaState state,
+		gchar *sid, gchar *name, PurpleMediaBackendFs2 *self);
+static void
+stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
+		gchar *sid, gchar *name, gboolean local,
+		PurpleMediaBackendFs2 *self);
+
+static gboolean purple_media_backend_fs2_add_stream(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *who,
+		PurpleMediaSessionType type, gboolean initiator,
+		const gchar *transmitter,
+		guint num_params, GParameter *params);
+static void purple_media_backend_fs2_add_remote_candidates(
+		PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *remote_candidates);
+static gboolean purple_media_backend_fs2_codecs_ready(PurpleMediaBackend *self,
+		const gchar *sess_id);
+static GList *purple_media_backend_fs2_get_codecs(PurpleMediaBackend *self,
+		const gchar *sess_id);
+static GList *purple_media_backend_fs2_get_local_candidates(
+		PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant);
+static gboolean purple_media_backend_fs2_set_remote_codecs(
+		PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *codecs);
+static gboolean purple_media_backend_fs2_set_send_codec(
+		PurpleMediaBackend *self, const gchar *sess_id,
+		PurpleMediaCodec *codec);
+static void purple_media_backend_fs2_set_params(PurpleMediaBackend *self,
+		guint num_params, GParameter *params);
+static const gchar **purple_media_backend_fs2_get_available_params(void);
+
+static void free_stream(PurpleMediaBackendFs2Stream *stream);
+static void free_session(PurpleMediaBackendFs2Session *session);
+
+struct _PurpleMediaBackendFs2Class
+{
+	GObjectClass parent_class;
+};
+
+struct _PurpleMediaBackendFs2
+{
+	GObject parent;
+};
+
+G_DEFINE_TYPE_WITH_CODE(PurpleMediaBackendFs2, purple_media_backend_fs2,
+		G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(
+		PURPLE_TYPE_MEDIA_BACKEND, purple_media_backend_iface_init));
+
+struct _PurpleMediaBackendFs2Stream
+{
+	PurpleMediaBackendFs2Session *session;
+	gchar *participant;
+	FsStream *stream;
+
+	GstElement *src;
+	GstElement *tee;
+	GstElement *volume;
+	GstElement *level;
+	GstElement *fakesink;
+	GstElement *queue;
+
+	GList *local_candidates;
+	GList *remote_candidates;
+
+	guint connected_cb_id;
+};
+
+struct _PurpleMediaBackendFs2Session
+{
+	PurpleMediaBackendFs2 *backend;
+	gchar *id;
+	FsSession *session;
+
+	GstElement *src;
+	GstElement *tee;
+	GstElement *srcvalve;
+
+	GstPad *srcpad;
+
+	PurpleMediaSessionType type;
+};
+
+struct _PurpleMediaBackendFs2Private
+{
+	PurpleMedia *media;
+	GstElement *confbin;
+	FsConference *conference;
+	gchar *conference_type;
+
+	GHashTable *sessions;
+	GHashTable *participants;
+
+	GList *streams;
+
+	gdouble silence_threshold;
+};
+
+enum {
+	PROP_0,
+	PROP_CONFERENCE_TYPE,
+	PROP_MEDIA,
+};
+
+static void
+purple_media_backend_fs2_init(PurpleMediaBackendFs2 *self)
+{}
+
+static gboolean
+event_probe_cb(GstPad *srcpad, GstEvent *event, gboolean release_pad)
+{
+	if (GST_EVENT_TYPE(event) == GST_EVENT_CUSTOM_DOWNSTREAM
+		&& gst_event_has_name(event, "purple-unlink-tee")) {
+
+		const GstStructure *s = gst_event_get_structure(event);
+
+		gst_pad_unlink(srcpad, gst_pad_get_peer(srcpad));
+
+		gst_pad_remove_event_probe(srcpad,
+			g_value_get_uint(gst_structure_get_value(s, "handler-id")));
+
+		if (g_value_get_boolean(gst_structure_get_value(s, "release-pad")))
+			gst_element_release_request_pad(GST_ELEMENT_PARENT(srcpad), srcpad);
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+unlink_teepad_dynamic(GstPad *srcpad, gboolean release_pad)
+{
+	guint id = gst_pad_add_event_probe(srcpad, G_CALLBACK(event_probe_cb), NULL);
+
+	if (GST_IS_GHOST_PAD(srcpad))
+		srcpad = gst_ghost_pad_get_target(GST_GHOST_PAD(srcpad));
+
+	gst_element_send_event(gst_pad_get_parent_element(srcpad),
+		gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM,
+			gst_structure_new("purple-unlink-tee",
+				"release-pad", G_TYPE_BOOLEAN, release_pad,
+				"handler-id", G_TYPE_UINT, id,
+				NULL)));
+}
+
+static void
+purple_media_backend_fs2_dispose(GObject *obj)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj);
+	GList *iter = NULL;
+
+	purple_debug_info("backend-fs2", "purple_media_backend_fs2_dispose\n");
+
+	if (priv->confbin) {
+		GstElement *pipeline;
+
+		pipeline = purple_media_manager_get_pipeline(
+				purple_media_get_manager(priv->media));
+
+		/* All connections to media sources should be blocked before confbin is
+		 * removed, to prevent freezing of any other simultaneously running
+		 * media calls. */
+		if (priv->sessions) {
+			GList *sessions = g_hash_table_get_values(priv->sessions);
+			for (; sessions; sessions =
+					g_list_delete_link(sessions, sessions)) {
+				PurpleMediaBackendFs2Session *session = sessions->data;
+				if (session->srcpad) {
+					unlink_teepad_dynamic(session->srcpad, FALSE);
+					gst_object_unref(session->srcpad);
+					session->srcpad = NULL;
+				}
+			}
+		}
+
+		gst_element_set_locked_state(priv->confbin, TRUE);
+		gst_element_set_state(GST_ELEMENT(priv->confbin),
+				GST_STATE_NULL);
+
+		if (pipeline) {
+			GstBus *bus;
+			gst_bin_remove(GST_BIN(pipeline), priv->confbin);
+			bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
+			g_signal_handlers_disconnect_matched(G_OBJECT(bus),
+					G_SIGNAL_MATCH_FUNC |
+					G_SIGNAL_MATCH_DATA,
+					0, 0, 0, gst_bus_cb, obj);
+			gst_object_unref(bus);
+		} else {
+			purple_debug_warning("backend-fs2", "Unable to "
+					"properly dispose the conference. "
+					"Couldn't get the pipeline.\n");
+		}
+
+		priv->confbin = NULL;
+		priv->conference = NULL;
+
+	}
+
+	if (priv->sessions) {
+		GList *sessions = g_hash_table_get_values(priv->sessions);
+
+		for (; sessions; sessions =
+				g_list_delete_link(sessions, sessions)) {
+			PurpleMediaBackendFs2Session *session =
+					sessions->data;
+
+			if (session->session) {
+				g_object_unref(session->session);
+				session->session = NULL;
+			}
+		}
+	}
+
+	if (priv->participants) {
+		g_hash_table_destroy(priv->participants);
+		priv->participants = NULL;
+	}
+
+	for (iter = priv->streams; iter; iter = g_list_next(iter)) {
+		PurpleMediaBackendFs2Stream *stream = iter->data;
+		if (stream->stream) {
+			g_object_unref(stream->stream);
+			stream->stream = NULL;
+		}
+	}
+
+	if (priv->media) {
+		g_object_remove_weak_pointer(G_OBJECT(priv->media),
+				(gpointer*)&priv->media);
+		priv->media = NULL;
+	}
+
+	G_OBJECT_CLASS(purple_media_backend_fs2_parent_class)->dispose(obj);
+}
+
+static void
+purple_media_backend_fs2_finalize(GObject *obj)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj);
+
+	purple_debug_info("backend-fs2", "purple_media_backend_fs2_finalize\n");
+
+	g_free(priv->conference_type);
+
+	for (; priv->streams; priv->streams =
+			g_list_delete_link(priv->streams, priv->streams)) {
+		PurpleMediaBackendFs2Stream *stream = priv->streams->data;
+		free_stream(stream);
+	}
+
+	if (priv->sessions) {
+		GList *sessions = g_hash_table_get_values(priv->sessions);
+
+		for (; sessions; sessions =
+				g_list_delete_link(sessions, sessions)) {
+			PurpleMediaBackendFs2Session *session =
+					sessions->data;
+			free_session(session);
+		}
+
+		g_hash_table_destroy(priv->sessions);
+	}
+
+	G_OBJECT_CLASS(purple_media_backend_fs2_parent_class)->finalize(obj);
+}
+
+static void
+purple_media_backend_fs2_set_property(GObject *object, guint prop_id,
+		const GValue *value, GParamSpec *pspec)
+{
+	PurpleMediaBackendFs2Private *priv;
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object));
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(object);
+
+	switch (prop_id) {
+		case PROP_CONFERENCE_TYPE:
+			priv->conference_type = g_value_dup_string(value);
+			break;
+		case PROP_MEDIA:
+			priv->media = g_value_get_object(value);
+
+			if (priv->media == NULL)
+				break;
+
+			g_object_add_weak_pointer(G_OBJECT(priv->media),
+					(gpointer*)&priv->media);
+
+			g_signal_connect(G_OBJECT(priv->media),
+					"state-changed",
+					G_CALLBACK(state_changed_cb),
+					PURPLE_MEDIA_BACKEND_FS2(object));
+			g_signal_connect(G_OBJECT(priv->media), "stream-info",
+					G_CALLBACK(stream_info_cb),
+					PURPLE_MEDIA_BACKEND_FS2(object));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(
+					object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_media_backend_fs2_get_property(GObject *object, guint prop_id,
+		GValue *value, GParamSpec *pspec)
+{
+	PurpleMediaBackendFs2Private *priv;
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object));
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(object);
+
+	switch (prop_id) {
+		case PROP_CONFERENCE_TYPE:
+			g_value_set_string(value, priv->conference_type);
+			break;
+		case PROP_MEDIA:
+			g_value_set_object(value, priv->media);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(
+					object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_media_backend_fs2_class_init(PurpleMediaBackendFs2Class *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+
+	gobject_class->dispose = purple_media_backend_fs2_dispose;
+	gobject_class->finalize = purple_media_backend_fs2_finalize;
+	gobject_class->set_property = purple_media_backend_fs2_set_property;
+	gobject_class->get_property = purple_media_backend_fs2_get_property;
+
+	g_object_class_override_property(gobject_class, PROP_CONFERENCE_TYPE,
+			"conference-type");
+	g_object_class_override_property(gobject_class, PROP_MEDIA, "media");
+
+	g_type_class_add_private(klass, sizeof(PurpleMediaBackendFs2Private));
+}
+
+static void
+purple_media_backend_iface_init(PurpleMediaBackendIface *iface)
+{
+	iface->add_stream = purple_media_backend_fs2_add_stream;
+	iface->add_remote_candidates =
+			purple_media_backend_fs2_add_remote_candidates;
+	iface->codecs_ready = purple_media_backend_fs2_codecs_ready;
+	iface->get_codecs = purple_media_backend_fs2_get_codecs;
+	iface->get_local_candidates =
+			purple_media_backend_fs2_get_local_candidates;
+	iface->set_remote_codecs = purple_media_backend_fs2_set_remote_codecs;
+	iface->set_send_codec = purple_media_backend_fs2_set_send_codec;
+	iface->set_params = purple_media_backend_fs2_set_params;
+	iface->get_available_params = purple_media_backend_fs2_get_available_params;
+}
+
+static FsMediaType
+session_type_to_fs_media_type(PurpleMediaSessionType type)
+{
+	if (type & PURPLE_MEDIA_AUDIO)
+		return FS_MEDIA_TYPE_AUDIO;
+	else if (type & PURPLE_MEDIA_VIDEO)
+		return FS_MEDIA_TYPE_VIDEO;
+	else
+		return 0;
+}
+
+static FsStreamDirection
+session_type_to_fs_stream_direction(PurpleMediaSessionType type)
+{
+	if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO ||
+			(type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO)
+		return FS_DIRECTION_BOTH;
+	else if ((type & PURPLE_MEDIA_SEND_AUDIO) ||
+			(type & PURPLE_MEDIA_SEND_VIDEO))
+		return FS_DIRECTION_SEND;
+	else if ((type & PURPLE_MEDIA_RECV_AUDIO) ||
+			(type & PURPLE_MEDIA_RECV_VIDEO))
+		return FS_DIRECTION_RECV;
+	else
+		return FS_DIRECTION_NONE;
+}
+
+static PurpleMediaSessionType
+session_type_from_fs(FsMediaType type, FsStreamDirection direction)
+{
+	PurpleMediaSessionType result = PURPLE_MEDIA_NONE;
+	if (type == FS_MEDIA_TYPE_AUDIO) {
+		if (direction & FS_DIRECTION_SEND)
+			result |= PURPLE_MEDIA_SEND_AUDIO;
+		if (direction & FS_DIRECTION_RECV)
+			result |= PURPLE_MEDIA_RECV_AUDIO;
+	} else if (type == FS_MEDIA_TYPE_VIDEO) {
+		if (direction & FS_DIRECTION_SEND)
+			result |= PURPLE_MEDIA_SEND_VIDEO;
+		if (direction & FS_DIRECTION_RECV)
+			result |= PURPLE_MEDIA_RECV_VIDEO;
+	}
+	return result;
+}
+
+static FsCandidate *
+candidate_to_fs(PurpleMediaCandidate *candidate)
+{
+	FsCandidate *fscandidate;
+	gchar *foundation;
+	guint component_id;
+	gchar *ip;
+	guint port;
+	gchar *base_ip;
+	guint base_port;
+	PurpleMediaNetworkProtocol proto;
+	guint32 priority;
+	PurpleMediaCandidateType type;
+	gchar *username;
+	gchar *password;
+	guint ttl;
+
+	if (candidate == NULL)
+		return NULL;
+
+	g_object_get(G_OBJECT(candidate),
+			"foundation", &foundation,
+			"component-id", &component_id,
+			"ip", &ip,
+			"port", &port,
+			"base-ip", &base_ip,
+			"base-port", &base_port,
+			"protocol", &proto,
+			"priority", &priority,
+			"type", &type,
+			"username", &username,
+			"password", &password,
+			"ttl", &ttl,
+			NULL);
+
+	fscandidate = fs_candidate_new(foundation,
+			component_id, type,
+			proto, ip, port);
+
+	fscandidate->base_ip = base_ip;
+	fscandidate->base_port = base_port;
+	fscandidate->priority = priority;
+	fscandidate->username = username;
+	fscandidate->password = password;
+	fscandidate->ttl = ttl;
+
+	g_free(foundation);
+	g_free(ip);
+	return fscandidate;
+}
+
+static GList *
+candidate_list_to_fs(GList *candidates)
+{
+	GList *new_list = NULL;
+
+	for (; candidates; candidates = g_list_next(candidates)) {
+		new_list = g_list_prepend(new_list,
+				candidate_to_fs(candidates->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+static PurpleMediaCandidate *
+candidate_from_fs(FsCandidate *fscandidate)
+{
+	PurpleMediaCandidate *candidate;
+
+	if (fscandidate == NULL)
+		return NULL;
+
+	candidate = purple_media_candidate_new(fscandidate->foundation,
+		fscandidate->component_id, fscandidate->type,
+		fscandidate->proto, fscandidate->ip, fscandidate->port);
+	g_object_set(candidate,
+			"base-ip", fscandidate->base_ip,
+			"base-port", fscandidate->base_port,
+			"priority", fscandidate->priority,
+			"username", fscandidate->username,
+			"password", fscandidate->password,
+			"ttl", fscandidate->ttl, NULL);
+	return candidate;
+}
+
+static GList *
+candidate_list_from_fs(GList *candidates)
+{
+	GList *new_list = NULL;
+
+	for (; candidates; candidates = g_list_next(candidates)) {
+		new_list = g_list_prepend(new_list,
+			candidate_from_fs(candidates->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+static FsCodec *
+codec_to_fs(const PurpleMediaCodec *codec)
+{
+	FsCodec *new_codec;
+	gint id;
+	char *encoding_name;
+	PurpleMediaSessionType media_type;
+	guint clock_rate;
+	guint channels;
+	GList *iter;
+
+	if (codec == NULL)
+		return NULL;
+
+	g_object_get(G_OBJECT(codec),
+			"id", &id,
+			"encoding-name", &encoding_name,
+			"media-type", &media_type,
+			"clock-rate", &clock_rate,
+			"channels", &channels,
+			"optional-params", &iter,
+			NULL);
+
+	new_codec = fs_codec_new(id, encoding_name,
+			session_type_to_fs_media_type(media_type),
+			clock_rate);
+	new_codec->channels = channels;
+
+	for (; iter; iter = g_list_next(iter)) {
+		PurpleKeyValuePair *param = (PurpleKeyValuePair*)iter->data;
+		fs_codec_add_optional_parameter(new_codec,
+				param->key, param->value);
+	}
+
+	g_free(encoding_name);
+	return new_codec;
+}
+
+static PurpleMediaCodec *
+codec_from_fs(const FsCodec *codec)
+{
+	PurpleMediaCodec *new_codec;
+	GList *iter;
+
+	if (codec == NULL)
+		return NULL;
+
+	new_codec = purple_media_codec_new(codec->id, codec->encoding_name,
+			session_type_from_fs(codec->media_type,
+			FS_DIRECTION_BOTH), codec->clock_rate);
+	g_object_set(new_codec, "channels", codec->channels, NULL);
+
+	for (iter = codec->optional_params; iter; iter = g_list_next(iter)) {
+		FsCodecParameter *param = (FsCodecParameter*)iter->data;
+		purple_media_codec_add_optional_parameter(new_codec,
+				param->name, param->value);
+	}
+
+	return new_codec;
+}
+
+static GList *
+codec_list_from_fs(GList *codecs)
+{
+	GList *new_list = NULL;
+
+	for (; codecs; codecs = g_list_next(codecs)) {
+		new_list = g_list_prepend(new_list,
+				codec_from_fs(codecs->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+static GList *
+codec_list_to_fs(GList *codecs)
+{
+	GList *new_list = NULL;
+
+	for (; codecs; codecs = g_list_next(codecs)) {
+		new_list = g_list_prepend(new_list,
+				codec_to_fs(codecs->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+static PurpleMediaBackendFs2Session *
+get_session(PurpleMediaBackendFs2 *self, const gchar *sess_id)
+{
+	PurpleMediaBackendFs2Private *priv;
+	PurpleMediaBackendFs2Session *session = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL);
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+
+	if (priv->sessions != NULL)
+		session = g_hash_table_lookup(priv->sessions, sess_id);
+
+	return session;
+}
+
+static FsParticipant *
+get_participant(PurpleMediaBackendFs2 *self, const gchar *name)
+{
+	PurpleMediaBackendFs2Private *priv;
+	FsParticipant *participant = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL);
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+
+	if (priv->participants != NULL)
+		participant = g_hash_table_lookup(priv->participants, name);
+
+	return participant;
+}
+
+static PurpleMediaBackendFs2Stream *
+get_stream(PurpleMediaBackendFs2 *self,
+		const gchar *sess_id, const gchar *name)
+{
+	PurpleMediaBackendFs2Private *priv;
+	GList *streams;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL);
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	streams = priv->streams;
+
+	for (; streams; streams = g_list_next(streams)) {
+		PurpleMediaBackendFs2Stream *stream = streams->data;
+		if (!strcmp(stream->session->id, sess_id) &&
+				!strcmp(stream->participant, name))
+			return stream;
+	}
+
+	return NULL;
+}
+
+static GList *
+get_streams(PurpleMediaBackendFs2 *self,
+		const gchar *sess_id, const gchar *name)
+{
+	PurpleMediaBackendFs2Private *priv;
+	GList *streams, *ret = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL);
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	streams = priv->streams;
+
+	for (; streams; streams = g_list_next(streams)) {
+		PurpleMediaBackendFs2Stream *stream = streams->data;
+
+		if (sess_id != NULL && strcmp(stream->session->id, sess_id))
+			continue;
+		else if (name != NULL && strcmp(stream->participant, name))
+			continue;
+		else
+			ret = g_list_prepend(ret, stream);
+	}
+
+	ret = g_list_reverse(ret);
+	return ret;
+}
+
+static PurpleMediaBackendFs2Session *
+get_session_from_fs_stream(PurpleMediaBackendFs2 *self, FsStream *stream)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	FsSession *fssession;
+	GList *values;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL);
+	g_return_val_if_fail(FS_IS_STREAM(stream), NULL);
+
+	g_object_get(stream, "session", &fssession, NULL);
+
+	values = g_hash_table_get_values(priv->sessions);
+
+	for (; values; values = g_list_delete_link(values, values)) {
+		PurpleMediaBackendFs2Session *session = values->data;
+
+		if (session->session == fssession) {
+			g_list_free(values);
+			g_object_unref(fssession);
+			return session;
+		}
+	}
+
+	g_object_unref(fssession);
+	return 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);
+	value = gst_value_list_get_value(list, 0);
+	value_db = g_value_get_double(value);
+	percent = pow(10, value_db / 20);
+	return (percent > 1.0) ? 1.0 : percent;
+}
+
+static void
+gst_handle_message_element(GstBus *bus, GstMessage *msg,
+		PurpleMediaBackendFs2 *self)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
+	static guint level_id = 0;
+
+	if (level_id == 0)
+		level_id = g_signal_lookup("level", PURPLE_TYPE_MEDIA);
+
+	if (gst_structure_has_name(msg->structure, "level")) {
+		GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
+		gchar *name;
+		gchar *participant = NULL;
+		PurpleMediaBackendFs2Session *session = NULL;
+		gdouble percent;
+
+		if (!PURPLE_IS_MEDIA(priv->media) ||
+				GST_ELEMENT_PARENT(src) != priv->confbin)
+			return;
+
+		name = gst_element_get_name(src);
+
+		if (!strncmp(name, "sendlevel_", 10)) {
+			session = get_session(self, name+10);
+			if (priv->silence_threshold > 0) {
+				percent = gst_msg_db_to_percent(msg, "decay");
+				g_object_set(session->srcvalve,
+						"drop", (percent < priv->silence_threshold), NULL);
+			}
+		}
+
+		g_free(name);
+
+		if (!g_signal_has_handler_pending(priv->media, level_id, 0, FALSE))
+			return;
+
+		if (!session) {
+			GList *iter = priv->streams;
+			PurpleMediaBackendFs2Stream *stream;
+			for (; iter; iter = g_list_next(iter)) {
+				stream = iter->data;
+				if (stream->level == src) {
+					session = stream->session;
+					participant = stream->participant;
+					break;
+				}
+			}
+		}
+
+		if (!session)
+			return;
+
+		percent = gst_msg_db_to_percent(msg, "rms");
+
+		g_signal_emit(priv->media, level_id, 0,
+				session->id, participant, percent);
+		return;
+	}
+
+	if (!FS_IS_CONFERENCE(src) || !PURPLE_IS_MEDIA_BACKEND(self) ||
+			priv->conference != FS_CONFERENCE(src))
+		return;
+
+	if (gst_structure_has_name(msg->structure, "farsight-error")) {
+		FsError error_no;
+		gst_structure_get_enum(msg->structure, "error-no",
+				FS_TYPE_ERROR, (gint*)&error_no);
+		switch (error_no) {
+			case FS_ERROR_NO_CODECS:
+				purple_media_error(priv->media, _("No codecs"
+						" found. Install some"
+						" GStreamer codecs found"
+						" in GStreamer plugins"
+						" packages."));
+				purple_media_end(priv->media, NULL, NULL);
+				break;
+			case FS_ERROR_NO_CODECS_LEFT:
+				purple_media_error(priv->media, _("No codecs"
+						" left. Your codec"
+						" preferences in"
+						" fs-codecs.conf are too"
+						" strict."));
+				purple_media_end(priv->media, NULL, NULL);
+				break;
+			case FS_ERROR_UNKNOWN_CNAME:
+			/*
+			 * Unknown CName is only a problem for the
+			 * multicast transmitter which isn't used.
+			 * It is also deprecated.
+			 */
+				break;
+			default:
+				purple_debug_error("backend-fs2",
+						"farsight-error: %i: %s\n",
+						error_no,
+					  	gst_structure_get_string(
+						msg->structure, "error-msg"));
+				break;
+		}
+
+		if (FS_ERROR_IS_FATAL(error_no)) {
+			purple_media_error(priv->media, _("A non-recoverable "
+					"Farsight2 error has occurred."));
+			purple_media_end(priv->media, NULL, NULL);
+		}
+	} else if (gst_structure_has_name(msg->structure,
+			"farsight-new-local-candidate")) {
+		const GValue *value;
+		FsStream *stream;
+		FsCandidate *local_candidate;
+		PurpleMediaCandidate *candidate;
+		FsParticipant *participant;
+		PurpleMediaBackendFs2Session *session;
+		PurpleMediaBackendFs2Stream *media_stream;
+		gchar *name;
+
+		value = gst_structure_get_value(msg->structure, "stream");
+		stream = g_value_get_object(value);
+		value = gst_structure_get_value(msg->structure, "candidate");
+		local_candidate = g_value_get_boxed(value);
+
+		session = get_session_from_fs_stream(self, stream);
+
+		purple_debug_info("backend-fs2",
+				"got new local candidate: %s\n",
+				local_candidate->foundation);
+
+		g_object_get(stream, "participant", &participant, NULL);
+		g_object_get(participant, "cname", &name, NULL);
+		g_object_unref(participant);
+
+		media_stream = get_stream(self, session->id, name);
+		media_stream->local_candidates = g_list_append(
+				media_stream->local_candidates,
+				fs_candidate_copy(local_candidate));
+
+		candidate = candidate_from_fs(local_candidate);
+		g_signal_emit_by_name(self, "new-candidate",
+				session->id, name, candidate);
+		g_object_unref(candidate);
+	} else if (gst_structure_has_name(msg->structure,
+			"farsight-local-candidates-prepared")) {
+		const GValue *value;
+		FsStream *stream;
+		FsParticipant *participant;
+		PurpleMediaBackendFs2Session *session;
+		gchar *name;
+
+		value = gst_structure_get_value(msg->structure, "stream");
+		stream = g_value_get_object(value);
+		session = get_session_from_fs_stream(self, stream);
+
+		g_object_get(stream, "participant", &participant, NULL);
+		g_object_get(participant, "cname", &name, NULL);
+		g_object_unref(participant);
+
+		g_signal_emit_by_name(self, "candidates-prepared",
+				session->id, name);
+	} else if (gst_structure_has_name(msg->structure,
+			"farsight-new-active-candidate-pair")) {
+		const GValue *value;
+		FsStream *stream;
+		FsCandidate *local_candidate;
+		FsCandidate *remote_candidate;
+		FsParticipant *participant;
+		PurpleMediaBackendFs2Session *session;
+		PurpleMediaCandidate *lcandidate, *rcandidate;
+		gchar *name;
+
+		value = gst_structure_get_value(msg->structure, "stream");
+		stream = g_value_get_object(value);
+		value = gst_structure_get_value(msg->structure,
+				"local-candidate");
+		local_candidate = g_value_get_boxed(value);
+		value = gst_structure_get_value(msg->structure,
+				"remote-candidate");
+		remote_candidate = g_value_get_boxed(value);
+
+		g_object_get(stream, "participant", &participant, NULL);
+		g_object_get(participant, "cname", &name, NULL);
+		g_object_unref(participant);
+
+		session = get_session_from_fs_stream(self, stream);
+
+		lcandidate = candidate_from_fs(local_candidate);
+		rcandidate = candidate_from_fs(remote_candidate);
+
+		g_signal_emit_by_name(self, "active-candidate-pair",
+				session->id, name, lcandidate, rcandidate);
+
+		g_object_unref(lcandidate);
+		g_object_unref(rcandidate);
+	} else if (gst_structure_has_name(msg->structure,
+			"farsight-recv-codecs-changed")) {
+		const GValue *value;
+		GList *codecs;
+		FsCodec *codec;
+
+		value = gst_structure_get_value(msg->structure, "codecs");
+		codecs = g_value_get_boxed(value);
+		codec = codecs->data;
+
+		purple_debug_info("backend-fs2",
+				"farsight-recv-codecs-changed: %s\n",
+				codec->encoding_name);
+	} else if (gst_structure_has_name(msg->structure,
+			"farsight-component-state-changed")) {
+		const GValue *value;
+		FsStreamState fsstate;
+		guint component;
+		const gchar *state;
+
+		value = gst_structure_get_value(msg->structure, "state");
+		fsstate = g_value_get_enum(value);
+		value = gst_structure_get_value(msg->structure, "component");
+		component = g_value_get_uint(value);
+
+		switch (fsstate) {
+			case FS_STREAM_STATE_FAILED:
+				state = "FAILED";
+				break;
+			case FS_STREAM_STATE_DISCONNECTED:
+				state = "DISCONNECTED";
+				break;
+			case FS_STREAM_STATE_GATHERING:
+				state = "GATHERING";
+				break;
+			case FS_STREAM_STATE_CONNECTING:
+				state = "CONNECTING";
+				break;
+			case FS_STREAM_STATE_CONNECTED:
+				state = "CONNECTED";
+				break;
+			case FS_STREAM_STATE_READY:
+				state = "READY";
+				break;
+			default:
+				state = "UNKNOWN";
+				break;
+		}
+
+		purple_debug_info("backend-fs2",
+				"farsight-component-state-changed: "
+				"component: %u state: %s\n",
+				component, state);
+	} else if (gst_structure_has_name(msg->structure,
+			"farsight-send-codec-changed")) {
+		const GValue *value;
+		FsCodec *codec;
+		gchar *codec_str;
+
+		value = gst_structure_get_value(msg->structure, "codec");
+		codec = g_value_get_boxed(value);
+		codec_str = fs_codec_to_string(codec);
+
+		purple_debug_info("backend-fs2",
+				"farsight-send-codec-changed: codec: %s\n",
+				codec_str);
+
+		g_free(codec_str);
+	} else if (gst_structure_has_name(msg->structure,
+			"farsight-codecs-changed")) {
+		const GValue *value;
+		FsSession *fssession;
+		GList *sessions;
+
+		value = gst_structure_get_value(msg->structure, "session");
+		fssession = g_value_get_object(value);
+		sessions = g_hash_table_get_values(priv->sessions);
+
+		for (; sessions; sessions =
+				g_list_delete_link(sessions, sessions)) {
+			PurpleMediaBackendFs2Session *session = sessions->data;
+			gchar *session_id;
+
+			if (session->session != fssession)
+				continue;
+
+			session_id = g_strdup(session->id);
+			g_signal_emit_by_name(self, "codecs-changed",
+					session_id);
+			g_free(session_id);
+			g_list_free(sessions);
+			break;
+		}
+	}
+}
+
+static void
+gst_handle_message_error(GstBus *bus, GstMessage *msg,
+		PurpleMediaBackendFs2 *self)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(msg));
+	GstElement *lastElement = NULL;
+	GList *sessions;
+
+	GError *error = NULL;
+	gchar *debug_msg = NULL;
+
+	gst_message_parse_error(msg, &error, &debug_msg);
+	purple_debug_error("backend-fs2", "gst error %s\ndebugging: %s\n",
+			error->message, debug_msg);
+
+	g_error_free(error);
+	g_free(debug_msg);
+
+	while (element && !GST_IS_PIPELINE(element)) {
+		if (element == priv->confbin)
+			break;
+
+		lastElement = element;
+		element = GST_ELEMENT_PARENT(element);
+	}
+
+	if (!element || !GST_IS_PIPELINE(element))
+		return;
+
+	sessions = purple_media_get_session_ids(priv->media);
+
+	for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
+		if (purple_media_get_src(priv->media, sessions->data)
+				!= lastElement)
+			continue;
+
+		if (purple_media_get_session_type(priv->media, sessions->data)
+				& PURPLE_MEDIA_AUDIO)
+			purple_media_error(priv->media,
+					_("Error with your microphone"));
+		else
+			purple_media_error(priv->media,
+					_("Error with your webcam"));
+
+		break;
+	}
+
+	g_list_free(sessions);
+
+	purple_media_error(priv->media, _("Conference error"));
+	purple_media_end(priv->media, NULL, NULL);
+}
+
+static gboolean
+gst_bus_cb(GstBus *bus, GstMessage *msg, PurpleMediaBackendFs2 *self)
+{
+	switch(GST_MESSAGE_TYPE(msg)) {
+		case GST_MESSAGE_ELEMENT:
+			gst_handle_message_element(bus, msg, self);
+			break;
+		case GST_MESSAGE_ERROR:
+			gst_handle_message_error(bus, msg, self);
+			break;
+		default:
+			break;
+	}
+
+	return TRUE;
+}
+
+static void
+remove_element(GstElement *element)
+{
+	if (element) {
+		gst_element_set_locked_state(element, TRUE);
+		gst_element_set_state(element, GST_STATE_NULL);
+		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element)), element);
+	}
+}
+
+static void
+state_changed_cb(PurpleMedia *media, PurpleMediaState state,
+		gchar *sid, gchar *name, PurpleMediaBackendFs2 *self)
+{
+	if (state == PURPLE_MEDIA_STATE_END) {
+		PurpleMediaBackendFs2Private *priv =
+				PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+
+		if (sid && name) {
+			PurpleMediaBackendFs2Stream *stream = get_stream(self, sid, name);
+			gst_object_unref(stream->stream);
+
+			priv->streams = g_list_remove(priv->streams, stream);
+
+			remove_element(stream->src);
+			remove_element(stream->tee);
+			remove_element(stream->volume);
+			remove_element(stream->level);
+			remove_element(stream->fakesink);
+			remove_element(stream->queue);
+
+			free_stream(stream);
+		} else if (sid && !name) {
+			PurpleMediaBackendFs2Session *session = get_session(self, sid);
+			GstPad *pad;
+
+			g_object_get(session->session, "sink-pad", &pad, NULL);
+			gst_pad_unlink(GST_PAD_PEER(pad), pad);
+			gst_object_unref(pad);
+
+			gst_object_unref(session->session);
+			g_hash_table_remove(priv->sessions, session->id);
+
+			pad = gst_pad_get_peer(session->srcpad);
+			gst_element_remove_pad(GST_ELEMENT_PARENT(pad), pad);
+			gst_object_unref(pad);
+			gst_object_unref(session->srcpad);
+
+			remove_element(session->srcvalve);
+			remove_element(session->tee);
+
+			free_session(session);
+		}
+
+		purple_media_manager_remove_output_windows(
+				purple_media_get_manager(media), media, sid, name);
+	}
+}
+
+static void
+stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
+		gchar *sid, gchar *name, gboolean local,
+		PurpleMediaBackendFs2 *self)
+{
+	if (type == PURPLE_MEDIA_INFO_ACCEPT && sid != NULL && name != NULL) {
+		PurpleMediaBackendFs2Stream *stream =
+				get_stream(self, sid, name);
+		GError *err = NULL;
+
+		g_object_set(G_OBJECT(stream->stream), "direction",
+				session_type_to_fs_stream_direction(
+				stream->session->type), NULL);
+
+		if (stream->remote_candidates == NULL ||
+				purple_media_is_initiator(media, sid, name))
+			return;
+
+		fs_stream_set_remote_candidates(stream->stream,
+				stream->remote_candidates, &err);
+
+		if (err == NULL)
+			return;
+
+		purple_debug_error("backend-fs2", "Error adding "
+				"remote candidates: %s\n",
+				err->message);
+		g_error_free(err);
+	} else if (local == TRUE && (type == PURPLE_MEDIA_INFO_MUTE ||
+			type == PURPLE_MEDIA_INFO_UNMUTE)) {
+		PurpleMediaBackendFs2Private *priv =
+				PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+		gboolean active = (type == PURPLE_MEDIA_INFO_MUTE);
+		GList *sessions;
+
+		if (sid == NULL)
+			sessions = g_hash_table_get_values(priv->sessions);
+		else
+			sessions = g_list_prepend(NULL,
+					get_session(self, sid));
+
+		purple_debug_info("media", "Turning mute %s\n",
+				active ? "on" : "off");
+
+		for (; sessions; sessions = g_list_delete_link(
+				sessions, sessions)) {
+			PurpleMediaBackendFs2Session *session =
+					sessions->data;
+
+			if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
+				gchar *name = g_strdup_printf("volume_%s",
+						session->id);
+				GstElement *volume = gst_bin_get_by_name(
+						GST_BIN(priv->confbin), name);
+				g_free(name);
+				g_object_set(volume, "mute", active, NULL);
+			}
+		}
+	} else if (local == TRUE && (type == PURPLE_MEDIA_INFO_HOLD ||
+			type == PURPLE_MEDIA_INFO_UNHOLD)) {
+		gboolean active = (type == PURPLE_MEDIA_INFO_HOLD);
+		GList *streams = get_streams(self, sid, name);
+		for (; streams; streams =
+				g_list_delete_link(streams, streams)) {
+			PurpleMediaBackendFs2Stream *stream = streams->data;
+			if (stream->session->type & PURPLE_MEDIA_SEND_AUDIO) {
+				g_object_set(stream->stream, "direction",
+						session_type_to_fs_stream_direction(
+						stream->session->type & ((active) ?
+						~PURPLE_MEDIA_SEND_AUDIO :
+						PURPLE_MEDIA_AUDIO)), NULL);
+			}
+		}
+	} else if (local == TRUE && (type == PURPLE_MEDIA_INFO_PAUSE ||
+			type == PURPLE_MEDIA_INFO_UNPAUSE)) {
+		gboolean active = (type == PURPLE_MEDIA_INFO_PAUSE);
+		GList *streams = get_streams(self, sid, name);
+		for (; streams; streams =
+				g_list_delete_link(streams, streams)) {
+			PurpleMediaBackendFs2Stream *stream = streams->data;
+			if (stream->session->type & PURPLE_MEDIA_SEND_VIDEO) {
+				g_object_set(stream->stream, "direction",
+						session_type_to_fs_stream_direction(
+						stream->session->type & ((active) ?
+						~PURPLE_MEDIA_SEND_VIDEO :
+						PURPLE_MEDIA_VIDEO)), NULL);
+			}
+		}
+	}
+}
+
+static gboolean
+init_conference(PurpleMediaBackendFs2 *self)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	GstElement *pipeline;
+	GstBus *bus;
+	gchar *name;
+
+	priv->conference = FS_CONFERENCE(
+			gst_element_factory_make(priv->conference_type, NULL));
+
+	if (priv->conference == NULL) {
+		purple_debug_error("backend-fs2", "Conference == NULL\n");
+		return FALSE;
+	}
+
+	if (purple_account_get_silence_suppression(
+				purple_media_get_account(priv->media)))
+		priv->silence_threshold = purple_prefs_get_int(
+				"/purple/media/audio/silence_threshold") / 100.0;
+	else
+		priv->silence_threshold = 0;
+
+	pipeline = purple_media_manager_get_pipeline(
+			purple_media_get_manager(priv->media));
+
+	if (pipeline == NULL) {
+		purple_debug_error("backend-fs2",
+				"Couldn't retrieve pipeline.\n");
+		return FALSE;
+	}
+
+	name = g_strdup_printf("conf_%p", priv->conference);
+	priv->confbin = gst_bin_new(name);
+	if (priv->confbin == NULL) {
+		purple_debug_error("backend-fs2",
+				"Couldn't create confbin.\n");
+		return FALSE;
+	}
+
+	g_free(name);
+
+	bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
+	if (bus == NULL) {
+		purple_debug_error("backend-fs2",
+				"Couldn't get the pipeline's bus.\n");
+		return FALSE;
+	}
+
+	g_signal_connect(G_OBJECT(bus), "message",
+			G_CALLBACK(gst_bus_cb), self);
+	gst_object_unref(bus);
+
+	if (!gst_bin_add(GST_BIN(pipeline),
+			GST_ELEMENT(priv->confbin))) {
+		purple_debug_error("backend-fs2", "Couldn't add confbin "
+				"element to the pipeline\n");
+		return FALSE;
+	}
+
+	if (!gst_bin_add(GST_BIN(priv->confbin),
+			GST_ELEMENT(priv->conference))) {
+		purple_debug_error("backend-fs2", "Couldn't add conference "
+				"element to the confbin\n");
+		return FALSE;
+	}
+
+	if (gst_element_set_state(GST_ELEMENT(priv->confbin),
+			GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
+		purple_debug_error("backend-fs2",
+				"Failed to start conference.\n");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+gst_element_added_cb(FsElementAddedNotifier *self,
+		GstBin *bin, GstElement *element, gpointer user_data)
+{
+	/*
+	 * Hack to make H264 work with Gmail video.
+	 */
+	if (!strncmp(GST_ELEMENT_NAME(element), "x264", 4)) {
+		g_object_set(GST_OBJECT(element), "cabac", FALSE, NULL);
+	}
+}
+
+static gboolean
+create_src(PurpleMediaBackendFs2 *self, const gchar *sess_id,
+		PurpleMediaSessionType type)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	PurpleMediaBackendFs2Session *session;
+	PurpleMediaSessionType session_type;
+	FsMediaType media_type = session_type_to_fs_media_type(type);
+	FsStreamDirection type_direction =
+			session_type_to_fs_stream_direction(type);
+	GstElement *src;
+	GstPad *sinkpad, *srcpad;
+	GstPad *ghost = NULL;
+
+	if ((type_direction & FS_DIRECTION_SEND) == 0)
+		return TRUE;
+
+	session_type = session_type_from_fs(
+			media_type, FS_DIRECTION_SEND);
+	src = purple_media_manager_get_element(
+			purple_media_get_manager(priv->media),
+			session_type, priv->media, sess_id, NULL);
+
+	if (!GST_IS_ELEMENT(src)) {
+		purple_debug_error("backend-fs2",
+				"Error creating src for session %s\n",
+				sess_id);
+		return FALSE;
+	}
+
+	session = get_session(self, sess_id);
+
+	if (session == NULL) {
+		purple_debug_warning("backend-fs2",
+				"purple_media_set_src: trying to set"
+				" src on non-existent session\n");
+		return FALSE;
+	}
+
+	if (session->src)
+		gst_object_unref(session->src);
+
+	session->src = src;
+	gst_element_set_locked_state(session->src, TRUE);
+
+	session->tee = gst_element_factory_make("tee", NULL);
+	gst_bin_add(GST_BIN(priv->confbin), session->tee);
+
+	/* This supposedly isn't necessary, but it silences some warnings */
+	if (GST_ELEMENT_PARENT(priv->confbin)
+			== GST_ELEMENT_PARENT(session->src)) {
+		GstPad *pad = gst_element_get_static_pad(session->tee, "sink");
+		ghost = gst_ghost_pad_new(NULL, pad);
+		gst_object_unref(pad);
+		gst_pad_set_active(ghost, TRUE);
+		gst_element_add_pad(priv->confbin, ghost);
+	}
+
+	gst_element_set_state(session->tee, GST_STATE_PLAYING);
+	gst_element_link(session->src, priv->confbin);
+	if (ghost)
+		session->srcpad = gst_pad_get_peer(ghost);
+
+	g_object_get(session->session, "sink-pad", &sinkpad, NULL);
+	if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
+		gchar *name = g_strdup_printf("volume_%s", session->id);
+		GstElement *level;
+		GstElement *volume = gst_element_factory_make("volume", name);
+		double input_volume = purple_prefs_get_int(
+				"/purple/media/audio/volume/input")/10.0;
+		g_free(name);
+		name = g_strdup_printf("sendlevel_%s", session->id);
+		level = gst_element_factory_make("level", name);
+		g_free(name);
+		session->srcvalve = gst_element_factory_make("valve", NULL);
+		gst_bin_add(GST_BIN(priv->confbin), volume);
+		gst_bin_add(GST_BIN(priv->confbin), level);
+		gst_bin_add(GST_BIN(priv->confbin), session->srcvalve);
+		gst_element_set_state(level, GST_STATE_PLAYING);
+		gst_element_set_state(volume, GST_STATE_PLAYING);
+		gst_element_set_state(session->srcvalve, GST_STATE_PLAYING);
+		gst_element_link(level, session->srcvalve);
+		gst_element_link(volume, level);
+		gst_element_link(session->tee, volume);
+		srcpad = gst_element_get_static_pad(session->srcvalve, "src");
+		g_object_set(volume, "volume", input_volume, NULL);
+	} else {
+		srcpad = gst_element_get_request_pad(session->tee, "src%d");
+	}
+
+	purple_debug_info("backend-fs2", "connecting pad: %s\n",
+			  gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK
+			  ? "success" : "failure");
+	gst_element_set_locked_state(session->src, FALSE);
+	gst_object_unref(session->src);
+	gst_object_unref(sinkpad);
+
+	gst_element_set_state(session->src, GST_STATE_PLAYING);
+
+	purple_media_manager_create_output_window(purple_media_get_manager(
+			priv->media), priv->media, sess_id, NULL);
+
+	return TRUE;
+}
+
+static gboolean
+create_session(PurpleMediaBackendFs2 *self, const gchar *sess_id,
+		PurpleMediaSessionType type, gboolean initiator,
+		const gchar *transmitter)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	PurpleMediaBackendFs2Session *session;
+	GError *err = NULL;
+	GList *codec_conf = NULL, *iter = NULL;
+	gchar *filename = NULL;
+	gboolean is_nice = !strcmp(transmitter, "nice");
+
+	session = g_new0(PurpleMediaBackendFs2Session, 1);
+
+	session->session = fs_conference_new_session(priv->conference,
+			session_type_to_fs_media_type(type), &err);
+
+	if (err != NULL) {
+		purple_media_error(priv->media,
+				_("Error creating session: %s"),
+				err->message);
+		g_error_free(err);
+		g_free(session);
+		return FALSE;
+	}
+
+	filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
+	codec_conf = fs_codec_list_from_keyfile(filename, &err);
+	g_free(filename);
+
+	if (err != NULL) {
+		if (err->code == 4)
+			purple_debug_info("backend-fs2", "Couldn't read "
+					"fs-codec.conf: %s\n",
+					err->message);
+		else
+			purple_debug_error("backend-fs2", "Error reading "
+					"fs-codec.conf: %s\n",
+					err->message);
+		g_error_free(err);
+	}
+
+	/*
+	 * Add SPEEX if the configuration file doesn't exist or
+	 * there isn't a speex entry.
+	 */
+	for (iter = codec_conf; iter; iter = g_list_next(iter)) {
+		FsCodec *codec = iter->data;
+		if (!g_ascii_strcasecmp(codec->encoding_name, "speex"))
+			break;
+	}
+
+	if (iter == NULL) {
+		codec_conf = g_list_prepend(codec_conf,
+				fs_codec_new(FS_CODEC_ID_ANY,
+				"SPEEX", FS_MEDIA_TYPE_AUDIO, 8000));
+		codec_conf = g_list_prepend(codec_conf,
+				fs_codec_new(FS_CODEC_ID_ANY,
+				"SPEEX", FS_MEDIA_TYPE_AUDIO, 16000));
+	}
+
+	fs_session_set_codec_preferences(session->session, codec_conf, NULL);
+	fs_codec_list_destroy(codec_conf);
+
+	/*
+	 * Removes a 5-7 second delay before
+	 * receiving the src-pad-added signal.
+	 * Only works for non-multicast FsRtpSessions.
+	 */
+	if (is_nice || !strcmp(transmitter, "rawudp"))
+		g_object_set(G_OBJECT(session->session),
+				"no-rtcp-timeout", 0, NULL);
+
+	/*
+	 * Hack to make x264 work with Gmail video.
+	 */
+	if (is_nice && !strcmp(sess_id, "google-video")) {
+		FsElementAddedNotifier *notifier =
+				fs_element_added_notifier_new();
+		g_signal_connect(G_OBJECT(notifier), "element-added",
+				G_CALLBACK(gst_element_added_cb), NULL);
+		fs_element_added_notifier_add(notifier,
+				GST_BIN(priv->conference));
+	}
+
+	session->id = g_strdup(sess_id);
+	session->backend = self;
+	session->type = type;
+
+	if (!priv->sessions) {
+		purple_debug_info("backend-fs2",
+				"Creating hash table for sessions\n");
+		priv->sessions = g_hash_table_new_full(g_str_hash, g_str_equal,
+		                                       g_free, NULL);
+	}
+
+	g_hash_table_insert(priv->sessions, g_strdup(session->id), session);
+
+	if (!create_src(self, sess_id, type)) {
+		purple_debug_info("backend-fs2", "Error creating the src\n");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+free_session(PurpleMediaBackendFs2Session *session)
+{
+	g_free(session->id);
+	g_free(session);
+}
+
+static gboolean
+create_participant(PurpleMediaBackendFs2 *self, const gchar *name)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	FsParticipant *participant;
+	GError *err = NULL;
+
+	participant = fs_conference_new_participant(
+			priv->conference, name, &err);
+
+	if (err) {
+		purple_debug_error("backend-fs2",
+				"Error creating participant: %s\n",
+				err->message);
+		g_error_free(err);
+		return FALSE;
+	}
+
+	if (!priv->participants) {
+		purple_debug_info("backend-fs2",
+				"Creating hash table for participants\n");
+		priv->participants = g_hash_table_new_full(g_str_hash,
+				g_str_equal, g_free, g_object_unref);
+	}
+
+	g_hash_table_insert(priv->participants, g_strdup(name), participant);
+
+	return TRUE;
+}
+
+static gboolean
+src_pad_added_cb_cb(PurpleMediaBackendFs2Stream *stream)
+{
+	PurpleMediaBackendFs2Private *priv;
+
+	g_return_val_if_fail(stream != NULL, FALSE);
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(stream->session->backend);
+	stream->connected_cb_id = 0;
+
+	purple_media_manager_create_output_window(
+			purple_media_get_manager(priv->media), priv->media,
+			stream->session->id, stream->participant);
+
+	g_signal_emit_by_name(priv->media, "state-changed",
+			PURPLE_MEDIA_STATE_CONNECTED,
+			stream->session->id, stream->participant);
+	return FALSE;
+}
+
+static void
+src_pad_added_cb(FsStream *fsstream, GstPad *srcpad,
+		FsCodec *codec, PurpleMediaBackendFs2Stream *stream)
+{
+	PurpleMediaBackendFs2Private *priv;
+	GstPad *sinkpad;
+
+	g_return_if_fail(FS_IS_STREAM(fsstream));
+	g_return_if_fail(stream != NULL);
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(stream->session->backend);
+
+	if (stream->src == NULL) {
+		GstElement *sink = NULL;
+
+		if (codec->media_type == FS_MEDIA_TYPE_AUDIO) {
+			double output_volume = purple_prefs_get_int(
+					"/purple/media/audio/volume/output")/10.0;
+			/*
+			 * Should this instead be:
+			 *  audioconvert ! audioresample ! liveadder !
+			 *   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);
+			sink = purple_media_manager_get_element(
+					purple_media_get_manager(priv->media),
+					PURPLE_MEDIA_RECV_AUDIO, priv->media,
+					stream->session->id,
+					stream->participant);
+			gst_bin_add(GST_BIN(priv->confbin), stream->queue);
+			gst_bin_add(GST_BIN(priv->confbin), stream->volume);
+			gst_bin_add(GST_BIN(priv->confbin), stream->level);
+			gst_bin_add(GST_BIN(priv->confbin), sink);
+			gst_element_set_state(sink, GST_STATE_PLAYING);
+			gst_element_set_state(stream->level, GST_STATE_PLAYING);
+			gst_element_set_state(stream->volume, GST_STATE_PLAYING);
+			gst_element_set_state(stream->queue, GST_STATE_PLAYING);
+			gst_element_link(stream->level, sink);
+			gst_element_link(stream->volume, stream->level);
+			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);
+			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);
+			stream->fakesink = sink;
+		}
+		stream->tee = gst_element_factory_make("tee", NULL);
+		gst_bin_add_many(GST_BIN(priv->confbin),
+				stream->src, stream->tee, NULL);
+		gst_element_set_state(stream->tee, GST_STATE_PLAYING);
+		gst_element_set_state(stream->src, GST_STATE_PLAYING);
+		gst_element_link_many(stream->src, stream->tee, sink, NULL);
+	}
+
+	sinkpad = gst_element_get_request_pad(stream->src, "sink%d");
+	gst_pad_link(srcpad, sinkpad);
+	gst_object_unref(sinkpad);
+
+	stream->connected_cb_id = purple_timeout_add(0,
+			(GSourceFunc)src_pad_added_cb_cb, stream);
+}
+
+static GValueArray *
+append_relay_info(GValueArray *relay_info, const gchar *ip, gint port,
+	const gchar *username, const gchar *password, const gchar *type)
+{
+	GValue value;
+	GstStructure *turn_setup = gst_structure_new("relay-info",
+				"ip", G_TYPE_STRING, ip,
+				"port", G_TYPE_UINT, port,
+				"username", G_TYPE_STRING, username,
+				"password", G_TYPE_STRING, password,
+	            "relay-type", G_TYPE_STRING, type,
+				NULL);
+
+	if (turn_setup) {
+		memset(&value, 0, sizeof(GValue));
+		g_value_init(&value, GST_TYPE_STRUCTURE);
+		gst_value_set_structure(&value, turn_setup);
+		relay_info = g_value_array_append(relay_info, &value);
+		gst_structure_free(turn_setup);
+	}
+
+	return relay_info;
+}
+
+static gboolean
+create_stream(PurpleMediaBackendFs2 *self,
+		const gchar *sess_id, const gchar *who,
+		PurpleMediaSessionType type, gboolean initiator,
+		const gchar *transmitter,
+		guint num_params, GParameter *params)
+{
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	GError *err = NULL;
+	FsStream *fsstream = NULL;
+	const gchar *stun_ip = purple_network_get_stun_ip();
+	const gchar *turn_ip = purple_network_get_turn_ip();
+	guint _num_params = num_params;
+	GParameter *_params = g_new0(GParameter, num_params + 3);
+	FsStreamDirection type_direction =
+			session_type_to_fs_stream_direction(type);
+	PurpleMediaBackendFs2Session *session;
+	PurpleMediaBackendFs2Stream *stream;
+	FsParticipant *participant;
+	/* check if the prpl has already specified a relay-info
+	  we need to do this to allow them to override when using non-standard
+	  TURN modes, like Google f.ex. */
+	gboolean got_turn_from_prpl = FALSE;
+	int i;
+
+	for (i = 0 ; i < num_params ; i++) {
+		if (purple_strequal(params[i].name, "relay-info")) {
+			got_turn_from_prpl = TRUE;
+			break;
+		}
+	}
+
+	memcpy(_params, params, sizeof(GParameter) * num_params);
+
+	/* set the controlling mode parameter */
+	_params[_num_params].name = "controlling-mode";
+	g_value_init(&_params[_num_params].value, G_TYPE_BOOLEAN);
+	g_value_set_boolean(&_params[_num_params].value, initiator);
+	++_num_params;
+
+	if (stun_ip) {
+		purple_debug_info("backend-fs2",
+			"Setting stun-ip on new stream: %s\n", stun_ip);
+
+		_params[_num_params].name = "stun-ip";
+		g_value_init(&_params[_num_params].value, G_TYPE_STRING);
+		g_value_set_string(&_params[_num_params].value, stun_ip);
+		++_num_params;
+	}
+
+	if (turn_ip && !strcmp("nice", transmitter) && !got_turn_from_prpl) {
+		GValueArray *relay_info = g_value_array_new(0);
+		gint port;
+		const gchar *username =	purple_prefs_get_string(
+				"/purple/network/turn_username");
+		const gchar *password = purple_prefs_get_string(
+				"/purple/network/turn_password");
+
+		/* UDP */
+		port = purple_prefs_get_int("/purple/network/turn_port");
+		if (port > 0) {
+			relay_info = append_relay_info(relay_info, turn_ip, port, username,
+				password, "udp");
+		}
+		
+		/* TCP */
+		port = purple_prefs_get_int("/purple/network/turn_port_tcp");
+		if (port > 0) {
+			relay_info = append_relay_info(relay_info, turn_ip, port, username,
+				password, "tcp");
+		}
+
+		/* TURN over SSL is only supported by libnice for Google's "psuedo" SSL mode
+			at this time */
+
+		purple_debug_info("backend-fs2",
+			"Setting relay-info on new stream\n");
+		_params[_num_params].name = "relay-info";
+		g_value_init(&_params[_num_params].value,
+			G_TYPE_VALUE_ARRAY);
+		g_value_set_boxed(&_params[_num_params].value,
+			relay_info);
+		g_value_array_free(relay_info);
+		_num_params++;
+	}
+
+	session = get_session(self, sess_id);
+
+	if (session == NULL) {
+		purple_debug_error("backend-fs2",
+				"Couldn't find session to create stream.\n");
+		return FALSE;
+	}
+
+	participant = get_participant(self, who);
+
+	if (participant == NULL) {
+		purple_debug_error("backend-fs2", "Couldn't find "
+				"participant to create stream.\n");
+		return FALSE;
+	}
+
+	fsstream = fs_session_new_stream(session->session, participant,
+			initiator == TRUE ? type_direction :
+			(type_direction & FS_DIRECTION_RECV), transmitter,
+			_num_params, _params, &err);
+	g_free(_params);
+
+	if (fsstream == NULL) {
+		if (err) {
+			purple_debug_error("backend-fs2",
+					"Error creating stream: %s\n",
+					err && err->message ?
+					err->message : "NULL");
+			g_error_free(err);
+		} else
+			purple_debug_error("backend-fs2",
+					"Error creating stream\n");
+		return FALSE;
+	}
+
+	stream = g_new0(PurpleMediaBackendFs2Stream, 1);
+	stream->participant = g_strdup(who);
+	stream->session = session;
+	stream->stream = fsstream;
+
+	priv->streams =	g_list_append(priv->streams, stream);
+
+	g_signal_connect(G_OBJECT(fsstream), "src-pad-added",
+			G_CALLBACK(src_pad_added_cb), stream);
+
+	return TRUE;
+}
+
+static void
+free_stream(PurpleMediaBackendFs2Stream *stream)
+{
+	/* Remove the connected_cb timeout */
+	if (stream->connected_cb_id != 0)
+		purple_timeout_remove(stream->connected_cb_id);
+
+	g_free(stream->participant);
+
+	if (stream->local_candidates)
+		fs_candidate_list_destroy(stream->local_candidates);
+
+	if (stream->remote_candidates)
+		fs_candidate_list_destroy(stream->remote_candidates);
+
+	g_free(stream);
+}
+
+static gboolean
+purple_media_backend_fs2_add_stream(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *who,
+		PurpleMediaSessionType type, gboolean initiator,
+		const gchar *transmitter,
+		guint num_params, GParameter *params)
+{
+	PurpleMediaBackendFs2 *backend = PURPLE_MEDIA_BACKEND_FS2(self);
+	PurpleMediaBackendFs2Private *priv =
+			PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(backend);
+	PurpleMediaBackendFs2Stream *stream;
+
+	if (priv->conference == NULL && !init_conference(backend)) {
+		purple_debug_error("backend-fs2",
+				"Error initializing the conference.\n");
+		return FALSE;
+	}
+
+	if (get_session(backend, sess_id) == NULL &&
+			!create_session(backend, sess_id, type,
+			initiator, transmitter)) {
+		purple_debug_error("backend-fs2",
+				"Error creating the session.\n");
+		return FALSE;
+	}
+
+	if (get_participant(backend, who) == NULL &&
+			!create_participant(backend, who)) {
+		purple_debug_error("backend-fs2",
+				"Error creating the participant.\n");
+		return FALSE;
+	}
+
+	stream = get_stream(backend, sess_id, who);
+
+	if (stream != NULL) {
+		FsStreamDirection type_direction =
+				session_type_to_fs_stream_direction(type);
+
+		if (session_type_to_fs_stream_direction(
+				stream->session->type) != type_direction) {
+			/* change direction */
+			g_object_set(stream->stream, "direction",
+					type_direction, NULL);
+		}
+	} else if (!create_stream(backend, sess_id, who, type,
+			initiator, transmitter, num_params, params)) {
+		purple_debug_error("backend-fs2",
+				"Error creating the stream.\n");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+purple_media_backend_fs2_add_remote_candidates(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *remote_candidates)
+{
+	PurpleMediaBackendFs2Private *priv;
+	PurpleMediaBackendFs2Stream *stream;
+	GError *err = NULL;
+
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self));
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+	stream = get_stream(PURPLE_MEDIA_BACKEND_FS2(self),
+			sess_id, participant);
+
+	if (stream == NULL) {
+		purple_debug_error("backend-fs2",
+				"purple_media_add_remote_candidates: "
+				"couldn't find stream %s %s.\n",
+				sess_id ? sess_id : "(null)",
+				participant ? participant : "(null)");
+		return;
+	}
+
+	stream->remote_candidates = g_list_concat(stream->remote_candidates,
+			candidate_list_to_fs(remote_candidates));
+
+	if (purple_media_is_initiator(priv->media, sess_id, participant) ||
+			purple_media_accepted(
+			priv->media, sess_id, participant)) {
+		fs_stream_set_remote_candidates(stream->stream,
+				stream->remote_candidates, &err);
+
+		if (err) {
+			purple_debug_error("backend-fs2", "Error adding remote"
+					" candidates: %s\n", err->message);
+			g_error_free(err);
+		}
+	}
+}
+
+static gboolean
+purple_media_backend_fs2_codecs_ready(PurpleMediaBackend *self,
+		const gchar *sess_id)
+{
+	PurpleMediaBackendFs2Private *priv;
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE);
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+
+	if (sess_id != NULL) {
+		PurpleMediaBackendFs2Session *session = get_session(
+				PURPLE_MEDIA_BACKEND_FS2(self), sess_id);
+
+		if (session == NULL)
+			return FALSE;
+
+		if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
+				PURPLE_MEDIA_SEND_VIDEO))
+			g_object_get(session->session,
+					"codecs-ready", &ret, NULL);
+		else
+			ret = TRUE;
+	} else {
+		GList *values = g_hash_table_get_values(priv->sessions);
+
+		for (; values; values = g_list_delete_link(values, values)) {
+			PurpleMediaBackendFs2Session *session = values->data;
+			if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
+					PURPLE_MEDIA_SEND_VIDEO))
+				g_object_get(session->session,
+						"codecs-ready", &ret, NULL);
+			else
+				ret = TRUE;
+
+			if (ret == FALSE)
+				break;
+		}
+
+		if (values != NULL)
+			g_list_free(values);
+	}
+
+	return ret;
+}
+
+static GList *
+purple_media_backend_fs2_get_codecs(PurpleMediaBackend *self,
+		const gchar *sess_id)
+{
+	PurpleMediaBackendFs2Session *session;
+	GList *fscodecs;
+	GList *codecs;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL);
+
+	session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id);
+
+	if (session == NULL)
+		return NULL;
+
+	g_object_get(G_OBJECT(session->session),
+		     "codecs", &fscodecs, NULL);
+	codecs = codec_list_from_fs(fscodecs);
+	fs_codec_list_destroy(fscodecs);
+
+	return codecs;
+}
+
+static GList *
+purple_media_backend_fs2_get_local_candidates(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant)
+{
+	PurpleMediaBackendFs2Stream *stream;
+	GList *candidates = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL);
+
+	stream = get_stream(PURPLE_MEDIA_BACKEND_FS2(self),
+			sess_id, participant);
+
+	if (stream != NULL)
+		candidates = candidate_list_from_fs(
+				stream->local_candidates);
+	return candidates;
+}
+
+static gboolean
+purple_media_backend_fs2_set_remote_codecs(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *codecs)
+{
+	PurpleMediaBackendFs2Stream *stream;
+	GList *fscodecs;
+	GError *err = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE);
+	stream = get_stream(PURPLE_MEDIA_BACKEND_FS2(self),
+			sess_id, participant);
+
+	if (stream == NULL)
+		return FALSE;
+
+	fscodecs = codec_list_to_fs(codecs);
+	fs_stream_set_remote_codecs(stream->stream, fscodecs, &err);
+	fs_codec_list_destroy(fscodecs);
+
+	if (err) {
+		purple_debug_error("backend-fs2",
+				"Error setting remote codecs: %s\n",
+				err->message);
+		g_error_free(err);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+purple_media_backend_fs2_set_send_codec(PurpleMediaBackend *self,
+		const gchar *sess_id, PurpleMediaCodec *codec)
+{
+	PurpleMediaBackendFs2Session *session;
+	FsCodec *fscodec;
+	GError *err = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE);
+
+	session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id);
+
+	if (session == NULL)
+		return FALSE;
+
+	fscodec = codec_to_fs(codec);
+	fs_session_set_send_codec(session->session, fscodec, &err);
+	fs_codec_destroy(fscodec);
+
+	if (err) {
+		purple_debug_error("media", "Error setting send codec\n");
+		g_error_free(err);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+purple_media_backend_fs2_set_params(PurpleMediaBackend *self,
+		guint num_params, GParameter *params)
+{
+	PurpleMediaBackendFs2Private *priv;
+	const gchar **supported = purple_media_backend_fs2_get_available_params();
+	const gchar **p;
+	guint i;
+
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self));
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+
+	if (priv->conference == NULL &&
+		!init_conference(PURPLE_MEDIA_BACKEND_FS2(self))) {
+		purple_debug_error("backend-fs2",
+				"Error initializing the conference.\n");
+		return;
+	}
+
+	for (i = 0; i != num_params; ++i) {
+		for (p = supported; *p != NULL; ++p) {
+			if (!strcmp(params[i].name, *p)) {
+				g_object_set(priv->conference,
+						params[i].name, g_value_get_string(&params[i].value),
+						NULL);
+				break;
+			}
+		}
+	}
+}
+
+static const gchar **
+purple_media_backend_fs2_get_available_params(void)
+{
+	static const gchar *supported_params[] = {
+		"sdes-cname", "sdes-email", "sdes-location", "sdes-name", "sdes-note",
+		"sdes-phone", "sdes-tool", NULL
+	};
+
+	return supported_params;
+}
+#else
+GType
+purple_media_backend_fs2_get_type(void)
+{
+	return G_TYPE_NONE;
+}
+#endif /* USE_VV */
+
+#ifdef USE_GSTREAMER
+GstElement *
+purple_media_backend_fs2_get_src(PurpleMediaBackendFs2 *self,
+		const gchar *sess_id)
+{
+#ifdef USE_VV
+	PurpleMediaBackendFs2Session *session = get_session(self, sess_id);
+	return session != NULL ? session->src : NULL;
+#else
+	return NULL;
+#endif
+}
+
+GstElement *
+purple_media_backend_fs2_get_tee(PurpleMediaBackendFs2 *self,
+		const gchar *sess_id, const gchar *who)
+{
+#ifdef USE_VV
+	if (sess_id != NULL && who == NULL) {
+		PurpleMediaBackendFs2Session *session =
+				get_session(self, sess_id);
+		return (session != NULL) ? session->tee : NULL;
+	} else if (sess_id != NULL && who != NULL) {
+		PurpleMediaBackendFs2Stream *stream =
+				get_stream(self, sess_id, who);
+		return (stream != NULL) ? stream->tee : NULL;
+	}
+
+#endif /* USE_VV */
+	g_return_val_if_reached(NULL);
+}
+
+void
+purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2 *self,
+		const gchar *sess_id, double level)
+{
+#ifdef USE_VV
+	PurpleMediaBackendFs2Private *priv;
+	GList *sessions;
+
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self));
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+
+	purple_prefs_set_int("/purple/media/audio/volume/input", level);
+
+	if (sess_id == NULL)
+		sessions = g_hash_table_get_values(priv->sessions);
+	else
+		sessions = g_list_append(NULL, get_session(self, sess_id));
+
+	for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
+		PurpleMediaBackendFs2Session *session = sessions->data;
+
+		if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
+			gchar *name = g_strdup_printf("volume_%s",
+					session->id);
+			GstElement *volume = gst_bin_get_by_name(
+					GST_BIN(priv->confbin), name);
+			g_free(name);
+			g_object_set(volume, "volume", level/10.0, NULL);
+		}
+	}
+#endif /* USE_VV */
+}
+
+void
+purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2 *self,
+		const gchar *sess_id, const gchar *who, double level)
+{
+#ifdef USE_VV
+	GList *streams;
+
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self));
+
+	purple_prefs_set_int("/purple/media/audio/volume/output", level);
+
+	streams = get_streams(self, sess_id, who);
+
+	for (; streams; streams = g_list_delete_link(streams, streams)) {
+		PurpleMediaBackendFs2Stream *stream = streams->data;
+
+		if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO
+				&& GST_IS_ELEMENT(stream->volume)) {
+			g_object_set(stream->volume, "volume",
+					level/10.0, NULL);
+		}
+	}
+#endif /* USE_VV */
+}
+#endif /* USE_GSTREAMER */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/backend-fs2.h	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,77 @@
+/**
+ * @file backend-fs2.h Farsight 2 backend for media 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
+ */
+
+/*
+ * This file should not yet be part of libpurple's API.
+ * It should remain internal only for now.
+ */
+
+#ifndef _MEDIA_BACKEND_FS2_H_
+#define _MEDIA_BACKEND_FS2_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_MEDIA_BACKEND_FS2            (purple_media_backend_fs2_get_type())
+#define PURPLE_IS_MEDIA_BACKEND_FS2(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_BACKEND_FS2))
+#define PURPLE_IS_MEDIA_BACKEND_FS2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_BACKEND_FS2))
+#define PURPLE_MEDIA_BACKEND_FS2(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2))
+#define PURPLE_MEDIA_BACKEND_FS2_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2))
+#define PURPLE_MEDIA_BACKEND_FS2_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2))
+
+/** An opaque structure representing the Farsight 2 media backend. */
+typedef struct _PurpleMediaBackendFs2 PurpleMediaBackendFs2;
+
+/**
+ * Gets the type of the Farsight 2 media backend object.
+ *
+ * @return The Farsight 2 media backend's GType
+ */
+GType purple_media_backend_fs2_get_type(void);
+
+#ifdef USE_GSTREAMER
+/*
+ * Temporary function in order to be able to test while
+ * integrating with PurpleMedia
+ */
+#include <gst/gst.h>
+GstElement *purple_media_backend_fs2_get_src(
+		PurpleMediaBackendFs2 *self,
+		const gchar *sess_id);
+GstElement *purple_media_backend_fs2_get_tee(
+		PurpleMediaBackendFs2 *self,
+		const gchar *sess_id, const gchar *who);
+void purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2 *self,
+		const gchar *sess_id, double level);
+void purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2 *self,
+		const gchar *sess_id, const gchar *who, double level);
+/* end tmp */
+#endif /* USE_GSTREAMER */
+
+G_END_DECLS
+
+#endif /* _MEDIA_BACKEND_FS2_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/backend-iface.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,211 @@
+/**
+ * @file backend-iface.c Interface for media backend
+ * @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 "backend-iface.h"
+
+#include "marshallers.h"
+
+enum {
+	S_ERROR,
+	CANDIDATES_PREPARED,
+	CODECS_CHANGED,
+	NEW_CANDIDATE,
+	ACTIVE_CANDIDATE_PAIR,
+	LAST_SIGNAL
+};
+
+static guint purple_media_backend_signals[LAST_SIGNAL] = {0};
+
+static void
+purple_media_backend_base_init(gpointer iface)
+{
+	static gboolean is_initialized = FALSE;
+
+	if (is_initialized)
+		return;
+
+	g_object_interface_install_property(iface,
+			g_param_spec_string("conference-type",
+			"Conference Type",
+			"The type of conference that this backend "
+			"has been created to provide.",
+			NULL,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+	g_object_interface_install_property(iface,
+			g_param_spec_object("media",
+			"Purple Media",
+			"The media object that this backend is bound to.",
+			PURPLE_TYPE_MEDIA,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+	purple_media_backend_signals[S_ERROR] =
+			g_signal_new("error", G_TYPE_FROM_CLASS(iface),
+			G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+			g_cclosure_marshal_VOID__STRING,
+			G_TYPE_NONE, 1, G_TYPE_STRING);
+	purple_media_backend_signals[CANDIDATES_PREPARED] =
+			g_signal_new("candidates-prepared",
+			G_TYPE_FROM_CLASS(iface),
+			G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+			purple_smarshal_VOID__STRING_STRING,
+			G_TYPE_NONE, 2, G_TYPE_STRING,
+			G_TYPE_STRING);
+	purple_media_backend_signals[CODECS_CHANGED] =
+			g_signal_new("codecs-changed",
+			G_TYPE_FROM_CLASS(iface),
+			G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+			g_cclosure_marshal_VOID__STRING,
+			G_TYPE_NONE, 1, G_TYPE_STRING);
+	purple_media_backend_signals[NEW_CANDIDATE] =
+			g_signal_new("new-candidate",
+			G_TYPE_FROM_CLASS(iface),
+			G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+			purple_smarshal_VOID__POINTER_POINTER_OBJECT,
+			G_TYPE_NONE, 3, G_TYPE_POINTER,
+			G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE);
+	purple_media_backend_signals[ACTIVE_CANDIDATE_PAIR] =
+			g_signal_new("active-candidate-pair",
+			G_TYPE_FROM_CLASS(iface),
+			G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+			purple_smarshal_VOID__STRING_STRING_OBJECT_OBJECT,
+			G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING,
+			PURPLE_TYPE_MEDIA_CANDIDATE,
+			PURPLE_TYPE_MEDIA_CANDIDATE);
+
+	is_initialized = TRUE;
+}
+
+GType
+purple_media_backend_get_type(void)
+{
+	static GType iface_type = 0;
+	if (iface_type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PurpleMediaBackendIface),
+			purple_media_backend_base_init,
+			NULL,
+			NULL,
+			NULL,
+			NULL,
+			0,
+			0,
+			NULL,
+			NULL
+		};
+
+		iface_type = g_type_register_static (G_TYPE_INTERFACE,
+				"PurpleMediaBackend", &info, 0);
+	}
+
+	return iface_type;
+}
+
+gboolean
+purple_media_backend_add_stream(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *who,
+		PurpleMediaSessionType type, gboolean initiator,
+		const gchar *transmitter,
+		guint num_params, GParameter *params)
+{
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE);
+	return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->add_stream(self,
+			sess_id, who, type, initiator, transmitter,
+			num_params, params);
+}
+
+void
+purple_media_backend_add_remote_candidates(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *remote_candidates)
+{
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND(self));
+	PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->add_remote_candidates(self,
+			sess_id, participant, remote_candidates);
+}
+
+gboolean
+purple_media_backend_codecs_ready(PurpleMediaBackend *self,
+		const gchar *sess_id)
+{
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE);
+	return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->codecs_ready(self,
+			sess_id);
+}
+
+GList *
+purple_media_backend_get_codecs(PurpleMediaBackend *self,
+		const gchar *sess_id)
+{
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), NULL);
+	return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->get_codecs(self,
+			sess_id);
+}
+
+GList *
+purple_media_backend_get_local_candidates(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant)
+{
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), NULL);
+	return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->
+			get_local_candidates(self,
+			sess_id, participant);
+}
+
+gboolean
+purple_media_backend_set_remote_codecs(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *codecs)
+{
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE);
+	return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->set_remote_codecs(
+			self, sess_id, participant, codecs);
+}
+
+gboolean
+purple_media_backend_set_send_codec(PurpleMediaBackend *self,
+		const gchar *sess_id, PurpleMediaCodec *codec)
+{
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE);
+	return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->set_send_codec(self,
+			sess_id, codec);
+}
+
+void
+purple_media_backend_set_params(PurpleMediaBackend *self,
+		guint num_params, GParameter *params)
+{
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND(self));
+	PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->set_params(self, num_params, params);
+}
+
+const gchar **
+purple_media_backend_get_available_params(PurpleMediaBackend *self)
+{
+	static const gchar *NULL_ARRAY[] = { NULL };
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), NULL_ARRAY);
+	return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->get_available_params();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/backend-iface.h	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,204 @@
+/**
+ * @file backend-iface.h Interface for media backends
+ * @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 _MEDIA_BACKEND_IFACE_H_
+#define _MEDIA_BACKEND_IFACE_H_
+
+#include "codec.h"
+#include "enum-types.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_MEDIA_BACKEND		(purple_media_backend_get_type())
+#define PURPLE_IS_MEDIA_BACKEND(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_BACKEND))
+#define PURPLE_MEDIA_BACKEND(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_BACKEND, PurpleMediaBackend))
+#define PURPLE_MEDIA_BACKEND_GET_INTERFACE(inst)(G_TYPE_INSTANCE_GET_INTERFACE((inst), PURPLE_TYPE_MEDIA_BACKEND, PurpleMediaBackendIface))
+
+/** A placeholder to represent any media backend */
+typedef struct _PurpleMediaBackend PurpleMediaBackend;
+/** A structure to derive media backends from. */
+typedef struct _PurpleMediaBackendIface PurpleMediaBackendIface;
+
+struct _PurpleMediaBackendIface
+{
+	GTypeInterface parent_iface; /**< The parent iface class */
+
+	/** Implementable functions called with purple_media_backend_* */
+	gboolean (*add_stream) (PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *who,
+		PurpleMediaSessionType type, gboolean initiator,
+		const gchar *transmitter,
+		guint num_params, GParameter *params);
+	void (*add_remote_candidates) (PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *remote_candidates);
+	gboolean (*codecs_ready) (PurpleMediaBackend *self,
+		const gchar *sess_id);
+	GList *(*get_codecs) (PurpleMediaBackend *self,
+		const gchar *sess_id);
+	GList *(*get_local_candidates) (PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant);
+	gboolean (*set_remote_codecs) (PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *codecs);
+	gboolean (*set_send_codec) (PurpleMediaBackend *self,
+		const gchar *sess_id, PurpleMediaCodec *codec);
+	void (*set_params) (PurpleMediaBackend *self,
+		guint num_params, GParameter *params);
+	const gchar **(*get_available_params) (void);
+};
+
+/**
+ * Gets the media backend's GType.
+ *
+ * @return The media backend's GType.
+ */
+GType purple_media_backend_get_type(void);
+
+/**
+ * Creates and adds a stream to the media backend.
+ *
+ * @param self The backend to add the stream to.
+ * @param sess_id The session id of the stream to add.
+ * @param who The remote participant of the stream to add.
+ * @param type The media type and direction of the stream to add.
+ * @param initiator True if the local user initiated the stream.
+ * @param transmitter The string id of the tranmsitter to use.
+ * @param num_params The number of parameters in the param parameter.
+ * @param params The additional parameters to pass when creating the stream.
+ *
+ * @return True if the stream was successfully created, othewise False.
+ */
+gboolean purple_media_backend_add_stream(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *who,
+		PurpleMediaSessionType type, gboolean initiator,
+		const gchar *transmitter,
+		guint num_params, GParameter *params);
+
+/**
+ * Add remote candidates to a stream.
+ *
+ * @param self The backend the stream is in.
+ * @param sess_id The session id associated with the stream.
+ * @param participant The participant associated with the stream.
+ * @param remote_candidates The list of remote candidates to add.
+ */
+void purple_media_backend_add_remote_candidates(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *remote_candidates);
+
+/**
+ * Get whether or not a session's codecs are ready.
+ *
+ * A codec is ready if all of the attributes and additional
+ * parameters have been collected.
+ *
+ * @param self The media backend the session is in.
+ * @param sess_id The session id of the session to check.
+ *
+ * @return True if the codecs are ready, otherwise False.
+ */
+gboolean purple_media_backend_codecs_ready(PurpleMediaBackend *self,
+		const gchar *sess_id);
+
+/**
+ * Gets the codec intersection list for a session.
+ *
+ * The intersection list consists of all codecs that are compatible
+ * between the local and remote software.
+ *
+ * @param self The media backend the session is in.
+ * @param sess_id The session id of the session to use.
+ *
+ * @return The codec intersection list.
+ */
+GList *purple_media_backend_get_codecs(PurpleMediaBackend *self,
+		const gchar *sess_id);
+
+/**
+ * Gets the list of local candidates for a stream.
+ *
+ * @param self The media backend the stream is in.
+ * @param sess_id The session id associated with the stream.
+ * @param particilant The participant associated with the stream.
+ *
+ * @return The list of local candidates.
+ */
+GList *purple_media_backend_get_local_candidates(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant);
+
+/**
+ * Sets the remote codecs on a stream.
+ *
+ * @param self The media backend the stream is in.
+ * @param sess_id The session id the stream is associated with.
+ * @param participant The participant the stream is associated with.
+ * @param codecs The list of remote codecs to set.
+ *
+ * @return True if the remote codecs were set successfully, otherwise False.
+ */
+gboolean purple_media_backend_set_remote_codecs(PurpleMediaBackend *self,
+		const gchar *sess_id, const gchar *participant,
+		GList *codecs);
+
+/**
+ * Sets which codec format to send media content in for a session.
+ *
+ * @param self The media backend the session is in.
+ * @param sess_id The session id of the session to set the codec for.
+ * @param codec The codec to set.
+ *
+ * @return True if set successfully, otherwise False.
+ */
+gboolean purple_media_backend_set_send_codec(PurpleMediaBackend *self,
+		const gchar *sess_id, PurpleMediaCodec *codec);
+
+/**
+ * Sets various optional parameters of the media backend.
+ *
+ * @param self The media backend to set the parameters on.
+ * @param num_params The number of parameters to pass to backend
+ * @param params Array of @c GParameter to pass to backend
+ */
+void purple_media_backend_set_params(PurpleMediaBackend *self,
+		guint num_params, GParameter *params);
+
+/**
+ * Gets the list of optional parameters supported by the media backend.
+ *
+ * The list should NOT be freed.
+ *
+ * @param self The media backend
+ *
+ * @return NULL-terminated array of names of supported parameters.
+ */
+const gchar **purple_media_backend_get_available_params(PurpleMediaBackend *self);
+
+G_END_DECLS
+
+#endif /* _MEDIA_BACKEND_IFACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/candidate.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,495 @@
+/**
+ * @file candidate.c Candidate for Media 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 "candidate.h"
+
+/** @copydoc _PurpleMediaCandidateClass */
+typedef struct _PurpleMediaCandidateClass PurpleMediaCandidateClass;
+/** @copydoc _PurpleMediaCandidatePrivate */
+typedef struct _PurpleMediaCandidatePrivate PurpleMediaCandidatePrivate;
+
+#define PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(obj) \
+		(G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+		PURPLE_TYPE_MEDIA_CANDIDATE, \
+		PurpleMediaCandidatePrivate))
+
+
+struct _PurpleMediaCandidateClass
+{
+	GObjectClass parent_class;
+};
+
+struct _PurpleMediaCandidate
+{
+	GObject parent;
+};
+
+G_DEFINE_TYPE(PurpleMediaCandidate, purple_media_candidate, G_TYPE_OBJECT);
+
+struct _PurpleMediaCandidatePrivate
+{
+	gchar *foundation;
+	guint component_id;
+	gchar *ip;
+	guint16 port;
+	gchar *base_ip;
+	guint16 base_port;
+	PurpleMediaNetworkProtocol proto;
+	guint32 priority;
+	PurpleMediaCandidateType type;
+	gchar *username;
+	gchar *password;
+	guint ttl;
+};
+
+enum {
+	PROP_CANDIDATE_0,
+	PROP_FOUNDATION,
+	PROP_COMPONENT_ID,
+	PROP_IP,
+	PROP_PORT,
+	PROP_BASE_IP,
+	PROP_BASE_PORT,
+	PROP_PROTOCOL,
+	PROP_PRIORITY,
+	PROP_TYPE,
+	PROP_USERNAME,
+	PROP_PASSWORD,
+	PROP_TTL,
+};
+
+static void
+purple_media_candidate_init(PurpleMediaCandidate *info)
+{
+	PurpleMediaCandidatePrivate *priv =
+			PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info);
+	priv->foundation = NULL;
+	priv->component_id = 0;
+	priv->ip = NULL;
+	priv->port = 0;
+	priv->base_ip = NULL;
+	priv->proto = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
+	priv->priority = 0;
+	priv->type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
+	priv->username = NULL;
+	priv->password = NULL;
+	priv->ttl = 0;
+}
+
+static void
+purple_media_candidate_finalize(GObject *info)
+{
+	PurpleMediaCandidatePrivate *priv =
+			PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info);
+
+	g_free(priv->foundation);
+	g_free(priv->ip);
+	g_free(priv->base_ip);
+	g_free(priv->username);
+	g_free(priv->password);
+}
+
+static void
+purple_media_candidate_set_property (GObject *object, guint prop_id,
+		const GValue *value, GParamSpec *pspec)
+{
+	PurpleMediaCandidatePrivate *priv;
+	g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object));
+
+	priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object);
+
+	switch (prop_id) {
+		case PROP_FOUNDATION:
+			g_free(priv->foundation);
+			priv->foundation = g_value_dup_string(value);
+			break;
+		case PROP_COMPONENT_ID:
+			priv->component_id = g_value_get_uint(value);
+			break;
+		case PROP_IP:
+			g_free(priv->ip);
+			priv->ip = g_value_dup_string(value);
+			break;
+		case PROP_PORT:
+			priv->port = g_value_get_uint(value);
+			break;
+		case PROP_BASE_IP:
+			g_free(priv->base_ip);
+			priv->base_ip = g_value_dup_string(value);
+			break;
+		case PROP_BASE_PORT:
+			priv->base_port = g_value_get_uint(value);
+			break;
+		case PROP_PROTOCOL:
+			priv->proto = g_value_get_enum(value);
+			break;
+		case PROP_PRIORITY:
+			priv->priority = g_value_get_uint(value);
+			break;
+		case PROP_TYPE:
+			priv->type = g_value_get_enum(value);
+			break;
+		case PROP_USERNAME:
+			g_free(priv->username);
+			priv->username = g_value_dup_string(value);
+			break;
+		case PROP_PASSWORD:
+			g_free(priv->password);
+			priv->password = g_value_dup_string(value);
+			break;
+		case PROP_TTL:
+			priv->ttl = g_value_get_uint(value);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(
+					object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_media_candidate_get_property (GObject *object, guint prop_id,
+		GValue *value, GParamSpec *pspec)
+{
+	PurpleMediaCandidatePrivate *priv;
+	g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object));
+
+	priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object);
+
+	switch (prop_id) {
+		case PROP_FOUNDATION:
+			g_value_set_string(value, priv->foundation);
+			break;
+		case PROP_COMPONENT_ID:
+			g_value_set_uint(value, priv->component_id);
+			break;
+		case PROP_IP:
+			g_value_set_string(value, priv->ip);
+			break;
+		case PROP_PORT:
+			g_value_set_uint(value, priv->port);
+			break;
+		case PROP_BASE_IP:
+			g_value_set_string(value, priv->base_ip);
+			break;
+		case PROP_BASE_PORT:
+			g_value_set_uint(value, priv->base_port);
+			break;
+		case PROP_PROTOCOL:
+			g_value_set_enum(value, priv->proto);
+			break;
+		case PROP_PRIORITY:
+			g_value_set_uint(value, priv->priority);
+			break;
+		case PROP_TYPE:
+			g_value_set_enum(value, priv->type);
+			break;
+		case PROP_USERNAME:
+			g_value_set_string(value, priv->username);
+			break;
+		case PROP_PASSWORD:
+			g_value_set_string(value, priv->password);
+			break;
+		case PROP_TTL:
+			g_value_set_uint(value, priv->ttl);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(
+					object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_media_candidate_class_init(PurpleMediaCandidateClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+
+	gobject_class->finalize = purple_media_candidate_finalize;
+	gobject_class->set_property = purple_media_candidate_set_property;
+	gobject_class->get_property = purple_media_candidate_get_property;
+
+	g_object_class_install_property(gobject_class, PROP_FOUNDATION,
+			g_param_spec_string("foundation",
+			"Foundation",
+			"The foundation of the candidate.",
+			NULL,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_COMPONENT_ID,
+			g_param_spec_uint("component-id",
+			"Component ID",
+			"The component id of the candidate.",
+			0, G_MAXUINT, 0,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_IP,
+			g_param_spec_string("ip",
+			"IP Address",
+			"The IP address of the candidate.",
+			NULL,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_PORT,
+			g_param_spec_uint("port",
+			"Port",
+			"The port of the candidate.",
+			0, G_MAXUINT16, 0,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_BASE_IP,
+			g_param_spec_string("base-ip",
+			"Base IP",
+			"The internal IP address of the candidate.",
+			NULL,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_BASE_PORT,
+			g_param_spec_uint("base-port",
+			"Base Port",
+			"The internal port of the candidate.",
+			0, G_MAXUINT16, 0,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_PROTOCOL,
+			g_param_spec_enum("protocol",
+			"Protocol",
+			"The protocol of the candidate.",
+			PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL,
+			PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_PRIORITY,
+			g_param_spec_uint("priority",
+			"Priority",
+			"The priority of the candidate.",
+			0, G_MAXUINT32, 0,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_TYPE,
+			g_param_spec_enum("type",
+			"Type",
+			"The type of the candidate.",
+			PURPLE_TYPE_MEDIA_CANDIDATE_TYPE,
+			PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_USERNAME,
+			g_param_spec_string("username",
+			"Username",
+			"The username used to connect to the candidate.",
+			NULL,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_PASSWORD,
+			g_param_spec_string("password",
+			"Password",
+			"The password use to connect to the candidate.",
+			NULL,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_TTL,
+			g_param_spec_uint("ttl",
+			"TTL",
+			"The TTL of the candidate.",
+			0, G_MAXUINT, 0,
+			G_PARAM_READWRITE));
+
+	g_type_class_add_private(klass, sizeof(PurpleMediaCandidatePrivate));
+}
+
+PurpleMediaCandidate *
+purple_media_candidate_new(const gchar *foundation, guint component_id,
+		PurpleMediaCandidateType type,
+		PurpleMediaNetworkProtocol proto,
+		const gchar *ip, guint port)
+{
+	return g_object_new(PURPLE_TYPE_MEDIA_CANDIDATE,
+			"foundation", foundation,
+			"component-id", component_id,
+			"type", type,
+			"protocol", proto,
+			"ip", ip,
+			"port", port, NULL);
+}
+
+PurpleMediaCandidate *
+purple_media_candidate_copy(PurpleMediaCandidate *candidate)
+{
+	PurpleMediaCandidatePrivate *priv;
+	PurpleMediaCandidate *new_candidate;
+
+	if (candidate == NULL)
+		return NULL;
+
+	priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate);
+
+	new_candidate = purple_media_candidate_new(priv->foundation,
+			priv->component_id, priv->type, priv->proto,
+			priv->ip, priv->port);
+	g_object_set(new_candidate,
+			"base-ip", priv->base_ip,
+			"base-port", priv->base_port,
+			"priority", priv->priority,
+			"username", priv->username,
+			"password", priv->password,
+			"ttl", priv->ttl, NULL);
+	return new_candidate;
+}
+
+GList *
+purple_media_candidate_list_copy(GList *candidates)
+{
+	GList *new_list = NULL;
+
+	for (; candidates; candidates = g_list_next(candidates)) {
+		new_list = g_list_prepend(new_list,
+				purple_media_candidate_copy(candidates->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+void
+purple_media_candidate_list_free(GList *candidates)
+{
+	for (; candidates; candidates =
+			g_list_delete_link(candidates, candidates)) {
+		g_object_unref(candidates->data);
+	}
+}
+
+gchar *
+purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate)
+{
+	gchar *foundation;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
+	g_object_get(candidate, "foundation", &foundation, NULL);
+	return foundation;
+}
+
+guint
+purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate)
+{
+	guint component_id;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
+	g_object_get(candidate, "component-id", &component_id, NULL);
+	return component_id;
+}
+
+gchar *
+purple_media_candidate_get_ip(PurpleMediaCandidate *candidate)
+{
+	gchar *ip;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
+	g_object_get(candidate, "ip", &ip, NULL);
+	return ip;
+}
+
+guint16
+purple_media_candidate_get_port(PurpleMediaCandidate *candidate)
+{
+	guint port;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
+	g_object_get(candidate, "port", &port, NULL);
+	return port;
+}
+
+gchar *
+purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate)
+{
+	gchar *base_ip;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
+	g_object_get(candidate, "base-ip", &base_ip, NULL);
+	return base_ip;
+}
+
+guint16
+purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate)
+{
+	guint base_port;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
+	g_object_get(candidate, "base_port", &base_port, NULL);
+	return base_port;
+}
+
+PurpleMediaNetworkProtocol
+purple_media_candidate_get_protocol(PurpleMediaCandidate *candidate)
+{
+	PurpleMediaNetworkProtocol protocol;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate),
+			PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
+	g_object_get(candidate, "protocol", &protocol, NULL);
+	return protocol;
+}
+
+guint32
+purple_media_candidate_get_priority(PurpleMediaCandidate *candidate)
+{
+	guint priority;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
+	g_object_get(candidate, "priority", &priority, NULL);
+	return priority;
+}
+
+PurpleMediaCandidateType
+purple_media_candidate_get_candidate_type(PurpleMediaCandidate *candidate)
+{
+	PurpleMediaCandidateType type;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate),
+			PURPLE_MEDIA_CANDIDATE_TYPE_HOST);
+	g_object_get(candidate, "type", &type, NULL);
+	return type;
+}
+
+gchar *
+purple_media_candidate_get_username(PurpleMediaCandidate *candidate)
+{
+	gchar *username;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
+	g_object_get(candidate, "username", &username, NULL);
+	return username;
+}
+
+gchar *
+purple_media_candidate_get_password(PurpleMediaCandidate *candidate)
+{
+	gchar *password;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
+	g_object_get(candidate, "password", &password, NULL);
+	return password;
+}
+
+guint
+purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate)
+{
+	guint ttl;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
+	g_object_get(candidate, "ttl", &ttl, NULL);
+	return ttl;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/candidate.h	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,218 @@
+/**
+ * @file candidate.h Candidate for Media 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_MEDIA_CANDIDATE_H_
+#define _PURPLE_MEDIA_CANDIDATE_H_
+
+#include "enum-types.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_MEDIA_CANDIDATE            (purple_media_candidate_get_type())
+#define PURPLE_IS_MEDIA_CANDIDATE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_CANDIDATE))
+#define PURPLE_IS_MEDIA_CANDIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_CANDIDATE))
+#define PURPLE_MEDIA_CANDIDATE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate))
+#define PURPLE_MEDIA_CANDIDATE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate))
+#define PURPLE_MEDIA_CANDIDATE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate))
+
+/** An opaque structure representing a network candidate (IP Address and port pair). */
+typedef struct _PurpleMediaCandidate PurpleMediaCandidate;
+
+/**
+ * Gets the type of the media candidate structure.
+ *
+ * @return The media canditate's GType
+ */
+GType purple_media_candidate_get_type(void);
+
+/**
+ * Creates a PurpleMediaCandidate instance.
+ *
+ * @param foundation The foundation of the candidate.
+ * @param component_id The component this candidate is for.
+ * @param type The type of candidate.
+ * @param proto The protocol this component is for.
+ * @param ip The IP address of this component.
+ * @param port The network port.
+ *
+ * @return The newly created PurpleMediaCandidate instance.
+ */
+PurpleMediaCandidate *purple_media_candidate_new(
+		const gchar *foundation, guint component_id,
+		PurpleMediaCandidateType type,
+		PurpleMediaNetworkProtocol proto,
+		const gchar *ip, guint port);
+
+/**
+ * Copies a PurpleMediaCandidate.
+ *
+ * @param candidate The candidate to copy.
+ *
+ * @return The copy of the PurpleMediaCandidate.
+ */
+PurpleMediaCandidate *purple_media_candidate_copy(
+		PurpleMediaCandidate *candidate);
+
+/**
+ * Copies a GList of PurpleMediaCandidate and its contents.
+ *
+ * @param candidates The list of candidates to be copied.
+ *
+ * @return The copy of the GList.
+ */
+GList *purple_media_candidate_list_copy(GList *candidates);
+
+/**
+ * Frees a GList of PurpleMediaCandidate and its contents.
+ *
+ * @param candidates The list of candidates to be freed.
+ */
+void purple_media_candidate_list_free(GList *candidates);
+
+/**
+ * Gets the foundation (identifier) from the candidate.
+ *
+ * @param candidate The candidate to get the foundation from.
+ *
+ * @return The foundation.
+ */
+gchar *purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the component id (rtp or rtcp)
+ *
+ * @param candidate The candidate to get the compnent id from.
+ *
+ * @return The component id.
+ */
+guint purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the IP address.
+ *
+ * @param candidate The candidate to get the IP address from.
+ *
+ * @return The IP address.
+ */
+gchar *purple_media_candidate_get_ip(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the port.
+ *
+ * @param candidate The candidate to get the port from.
+ *
+ * @return The port.
+ */
+guint16 purple_media_candidate_get_port(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the base (internal) IP address.
+ *
+ * This can be NULL.
+ *
+ * @param candidate The candidate to get the base IP address from.
+ *
+ * @return The base IP address.
+ */
+gchar *purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the base (internal) port.
+ *
+ * Invalid if the base IP is NULL.
+ *
+ * @param candidate The candidate to get the base port.
+ *
+ * @return The base port.
+ */
+guint16 purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the protocol (TCP or UDP).
+ *
+ * @param candidate The candidate to get the protocol from.
+ *
+ * @return The protocol.
+ */
+PurpleMediaNetworkProtocol purple_media_candidate_get_protocol(
+		PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the priority.
+ *
+ * @param candidate The candidate to get the priority from.
+ *
+ * @return The priority.
+ */
+guint32 purple_media_candidate_get_priority(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the candidate type.
+ *
+ * @param candidate The candidate to get the candidate type from.
+ *
+ * @return The candidate type.
+ */
+PurpleMediaCandidateType purple_media_candidate_get_candidate_type(
+		PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the username.
+ *
+ * This can be NULL. It depends on the transmission type.
+ *
+ * @param The candidate to get the username from.
+ *
+ * @return The username.
+ */
+gchar *purple_media_candidate_get_username(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the password.
+ *
+ * This can be NULL. It depends on the transmission type.
+ *
+ * @param The candidate to get the password from.
+ *
+ * @return The password.
+ */
+gchar *purple_media_candidate_get_password(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the TTL.
+ *
+ * @param The candidate to get the TTL from.
+ *
+ * @return The TTL.
+ */
+guint purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate);
+
+G_END_DECLS
+
+#endif  /* _PURPLE_MEDIA_CANDIDATE_H_ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/codec.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,421 @@
+/**
+ * @file codec.c Codec for Media 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 "codec.h"
+
+/** @copydoc _PurpleMediaCodecClass */
+typedef struct _PurpleMediaCodecClass PurpleMediaCodecClass;
+/** @copydoc _PurpleMediaCodecPrivate */
+typedef struct _PurpleMediaCodecPrivate PurpleMediaCodecPrivate;
+
+#define PURPLE_MEDIA_CODEC_GET_PRIVATE(obj) \
+		(G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+		PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodecPrivate))
+
+struct _PurpleMediaCodecClass
+{
+	GObjectClass parent_class;
+};
+
+struct _PurpleMediaCodec
+{
+	GObject parent;
+};
+
+G_DEFINE_TYPE(PurpleMediaCodec, purple_media_codec, G_TYPE_OBJECT);
+
+struct _PurpleMediaCodecPrivate
+{
+	gint id;
+	char *encoding_name;
+	PurpleMediaSessionType media_type;
+	guint clock_rate;
+	guint channels;
+	GList *optional_params;
+};
+
+enum {
+	PROP_CODEC_0,
+	PROP_ID,
+	PROP_ENCODING_NAME,
+	PROP_MEDIA_TYPE,
+	PROP_CLOCK_RATE,
+	PROP_CHANNELS,
+	PROP_OPTIONAL_PARAMS,
+};
+
+static void
+purple_media_codec_init(PurpleMediaCodec *info)
+{
+	PurpleMediaCodecPrivate *priv =
+			PURPLE_MEDIA_CODEC_GET_PRIVATE(info);
+	priv->encoding_name = NULL;
+	priv->optional_params = NULL;
+}
+
+static void
+purple_media_codec_finalize(GObject *info)
+{
+	PurpleMediaCodecPrivate *priv =
+			PURPLE_MEDIA_CODEC_GET_PRIVATE(info);
+	g_free(priv->encoding_name);
+	for (; priv->optional_params; priv->optional_params =
+			g_list_delete_link(priv->optional_params, priv->optional_params)) {
+		PurpleKeyValuePair *param = priv->optional_params->data;
+		g_free(param->key);
+		g_free(param->value);
+		g_free(param);
+	}
+}
+
+static void
+purple_media_codec_set_property (GObject *object, guint prop_id,
+		const GValue *value, GParamSpec *pspec)
+{
+	PurpleMediaCodecPrivate *priv;
+	g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object));
+
+	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object);
+
+	switch (prop_id) {
+		case PROP_ID:
+			priv->id = g_value_get_uint(value);
+			break;
+		case PROP_ENCODING_NAME:
+			g_free(priv->encoding_name);
+			priv->encoding_name = g_value_dup_string(value);
+			break;
+		case PROP_MEDIA_TYPE:
+			priv->media_type = g_value_get_flags(value);
+			break;
+		case PROP_CLOCK_RATE:
+			priv->clock_rate = g_value_get_uint(value);
+			break;
+		case PROP_CHANNELS:
+			priv->channels = g_value_get_uint(value);
+			break;
+		case PROP_OPTIONAL_PARAMS:
+			priv->optional_params = g_value_get_pointer(value);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(
+					object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_media_codec_get_property (GObject *object, guint prop_id,
+		GValue *value, GParamSpec *pspec)
+{
+	PurpleMediaCodecPrivate *priv;
+	g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object));
+
+	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object);
+
+	switch (prop_id) {
+		case PROP_ID:
+			g_value_set_uint(value, priv->id);
+			break;
+		case PROP_ENCODING_NAME:
+			g_value_set_string(value, priv->encoding_name);
+			break;
+		case PROP_MEDIA_TYPE:
+			g_value_set_flags(value, priv->media_type);
+			break;
+		case PROP_CLOCK_RATE:
+			g_value_set_uint(value, priv->clock_rate);
+			break;
+		case PROP_CHANNELS:
+			g_value_set_uint(value, priv->channels);
+			break;
+		case PROP_OPTIONAL_PARAMS:
+			g_value_set_pointer(value, priv->optional_params);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(
+					object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_media_codec_class_init(PurpleMediaCodecClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+
+	gobject_class->finalize = purple_media_codec_finalize;
+	gobject_class->set_property = purple_media_codec_set_property;
+	gobject_class->get_property = purple_media_codec_get_property;
+
+	g_object_class_install_property(gobject_class, PROP_ID,
+			g_param_spec_uint("id",
+			"ID",
+			"The numeric identifier of the codec.",
+			0, G_MAXUINT, 0,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_ENCODING_NAME,
+			g_param_spec_string("encoding-name",
+			"Encoding Name",
+			"The name of the codec.",
+			NULL,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE,
+			g_param_spec_flags("media-type",
+			"Media Type",
+			"Whether this is an audio of video codec.",
+			PURPLE_TYPE_MEDIA_SESSION_TYPE,
+			PURPLE_MEDIA_NONE,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_CLOCK_RATE,
+			g_param_spec_uint("clock-rate",
+			"Create Callback",
+			"The function called to create this element.",
+			0, G_MAXUINT, 0,
+			G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_CHANNELS,
+			g_param_spec_uint("channels",
+			"Channels",
+			"The number of channels in this codec.",
+			0, G_MAXUINT, 0,
+			G_PARAM_READWRITE));
+	g_object_class_install_property(gobject_class, PROP_OPTIONAL_PARAMS,
+			g_param_spec_pointer("optional-params",
+			"Optional Params",
+			"A list of optional parameters for the codec.",
+			G_PARAM_READWRITE));
+
+	g_type_class_add_private(klass, sizeof(PurpleMediaCodecPrivate));
+}
+
+PurpleMediaCodec *
+purple_media_codec_new(int id, const char *encoding_name,
+		PurpleMediaSessionType media_type, guint clock_rate)
+{
+	PurpleMediaCodec *codec =
+			g_object_new(PURPLE_TYPE_MEDIA_CODEC,
+			"id", id,
+			"encoding_name", encoding_name,
+			"media_type", media_type,
+			"clock-rate", clock_rate, NULL);
+	return codec;
+}
+
+guint
+purple_media_codec_get_id(PurpleMediaCodec *codec)
+{
+	guint id;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0);
+	g_object_get(codec, "id", &id, NULL);
+	return id;
+}
+
+gchar *
+purple_media_codec_get_encoding_name(PurpleMediaCodec *codec)
+{
+	gchar *name;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL);
+	g_object_get(codec, "encoding-name", &name, NULL);
+	return name;
+}
+
+guint
+purple_media_codec_get_clock_rate(PurpleMediaCodec *codec)
+{
+	guint clock_rate;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0);
+	g_object_get(codec, "clock-rate", &clock_rate, NULL);
+	return clock_rate;
+}
+
+guint
+purple_media_codec_get_channels(PurpleMediaCodec *codec)
+{
+	guint channels;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0);
+	g_object_get(codec, "channels", &channels, NULL);
+	return channels;
+}
+
+GList *
+purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec)
+{
+	GList *optional_params;
+	g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL);
+	g_object_get(codec, "optional-params", &optional_params, NULL);
+	return optional_params;
+}
+
+void
+purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
+		const gchar *name, const gchar *value)
+{
+	PurpleMediaCodecPrivate *priv;
+	PurpleKeyValuePair *new_param;
+
+	g_return_if_fail(codec != NULL);
+	g_return_if_fail(name != NULL && value != NULL);
+
+	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
+
+	new_param = g_new0(PurpleKeyValuePair, 1);
+	new_param->key = g_strdup(name);
+	new_param->value = g_strdup(value);
+	priv->optional_params = g_list_append(
+			priv->optional_params, new_param);
+}
+
+void
+purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
+		PurpleKeyValuePair *param)
+{
+	PurpleMediaCodecPrivate *priv;
+
+	g_return_if_fail(codec != NULL && param != NULL);
+
+	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
+
+	g_free(param->key);
+	g_free(param->value);
+
+	priv->optional_params =
+			g_list_remove(priv->optional_params, param);
+	g_free(param);
+}
+
+PurpleKeyValuePair *
+purple_media_codec_get_optional_parameter(PurpleMediaCodec *codec,
+		const gchar *name, const gchar *value)
+{
+	PurpleMediaCodecPrivate *priv;
+	GList *iter;
+
+	g_return_val_if_fail(codec != NULL, NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
+
+	for (iter = priv->optional_params; iter; iter = g_list_next(iter)) {
+		PurpleKeyValuePair *param = iter->data;
+		if (!g_ascii_strcasecmp(param->key, name) &&
+				(value == NULL ||
+				!g_ascii_strcasecmp(param->value, value)))
+			return param;
+	}
+
+	return NULL;
+}
+
+PurpleMediaCodec *
+purple_media_codec_copy(PurpleMediaCodec *codec)
+{
+	PurpleMediaCodecPrivate *priv;
+	PurpleMediaCodec *new_codec;
+	GList *iter;
+
+	if (codec == NULL)
+		return NULL;
+
+	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
+
+	new_codec = purple_media_codec_new(priv->id, priv->encoding_name,
+			priv->media_type, priv->clock_rate);
+	g_object_set(codec, "channels", priv->channels, NULL);
+
+	for (iter = priv->optional_params; iter; iter = g_list_next(iter)) {
+		PurpleKeyValuePair *param =
+				(PurpleKeyValuePair*)iter->data;
+		purple_media_codec_add_optional_parameter(new_codec,
+				param->key, param->value);
+	}
+
+	return new_codec;
+}
+
+GList *
+purple_media_codec_list_copy(GList *codecs)
+{
+	GList *new_list = NULL;
+
+	for (; codecs; codecs = g_list_next(codecs)) {
+		new_list = g_list_prepend(new_list,
+				purple_media_codec_copy(codecs->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+void
+purple_media_codec_list_free(GList *codecs)
+{
+	for (; codecs; codecs =
+			g_list_delete_link(codecs, codecs)) {
+		g_object_unref(codecs->data);
+	}
+}
+
+gchar *
+purple_media_codec_to_string(const PurpleMediaCodec *codec)
+{
+	PurpleMediaCodecPrivate *priv;
+	GString *string = NULL;
+	GList *item;
+	gchar *charstring;
+	const gchar *media_type_str = NULL;
+
+	if (codec == NULL)
+		return g_strdup("(NULL)");
+
+	priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
+
+	string = g_string_new("");
+
+	if (priv->media_type & PURPLE_MEDIA_AUDIO)
+		media_type_str = "audio";
+	else if (priv->media_type & PURPLE_MEDIA_VIDEO)
+		media_type_str = "video";
+
+	g_string_printf(string, "%d: %s %s clock:%d channels:%d", priv->id,
+			media_type_str, priv->encoding_name,
+			priv->clock_rate, priv->channels);
+
+	for (item = priv->optional_params; item; item = g_list_next (item)) {
+		PurpleKeyValuePair *param = item->data;
+		g_string_append_printf (string, " %s=%s",
+				param->key, (gchar *)param->value);
+	}
+
+	charstring = string->str;
+	g_string_free (string, FALSE);
+
+	return charstring;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/codec.h	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,186 @@
+/**
+ * @file codec.h Codec for Media 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_MEDIA_CODEC_H_
+#define _PURPLE_MEDIA_CODEC_H_
+
+#include "enum-types.h"
+
+/** An opaque structure representing an audio or video codec. */
+typedef struct _PurpleMediaCodec PurpleMediaCodec;
+
+#include "../util.h"
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_MEDIA_CODEC            (purple_media_codec_get_type())
+#define PURPLE_IS_MEDIA_CODEC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_CODEC))
+#define PURPLE_IS_MEDIA_CODEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_CODEC))
+#define PURPLE_MEDIA_CODEC(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec))
+#define PURPLE_MEDIA_CODEC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec))
+#define PURPLE_MEDIA_CODEC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec))
+
+
+/**
+ * Gets the type of the media codec structure.
+ *
+ * @return The media codec's GType
+ */
+GType purple_media_codec_get_type(void);
+
+/**
+ * Creates a new PurpleMediaCodec instance.
+ *
+ * @param id Codec identifier.
+ * @param encoding_name Name of the media type this encodes.
+ * @param media_type PurpleMediaSessionType of this codec.
+ * @param clock_rate The clock rate this codec encodes at, if applicable.
+ *
+ * @return The newly created PurpleMediaCodec.
+ */
+PurpleMediaCodec *purple_media_codec_new(int id, const char *encoding_name,
+		PurpleMediaSessionType media_type, guint clock_rate);
+
+/**
+ * Gets the codec id.
+ *
+ * @param The codec to get the id from.
+ *
+ * @return The codec id.
+ */
+guint purple_media_codec_get_id(PurpleMediaCodec *codec);
+
+/**
+ * Gets the encoding name.
+ *
+ * @param The codec to get the encoding name from.
+ *
+ * @return The encoding name.
+ */
+gchar *purple_media_codec_get_encoding_name(PurpleMediaCodec *codec);
+
+/**
+ * Gets the clock rate.
+ *
+ * @param The codec to get the clock rate from.
+ *
+ * @return The clock rate.
+ */
+guint purple_media_codec_get_clock_rate(PurpleMediaCodec *codec);
+
+/**
+ * Gets the number of channels.
+ *
+ * @param The codec to get the number of channels from.
+ *
+ * @return The number of channels.
+ */
+guint purple_media_codec_get_channels(PurpleMediaCodec *codec);
+
+/**
+ * Gets a list of the optional parameters.
+ *
+ * The list consists of PurpleKeyValuePair's.
+ *
+ * @param The codec to get the optional parameters from.
+ *
+ * @return The list of optional parameters. The list is owned by the codec and
+ *         should not be freed.
+ */
+GList *purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec);
+
+/**
+ * Adds an optional parameter to the codec.
+ *
+ * @param codec The codec to add the parameter to.
+ * @param name The name of the parameter to add.
+ * @param value The value of the parameter to add.
+ */
+void purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
+		const gchar *name, const gchar *value);
+
+/**
+ * Removes an optional parameter from the codec.
+ *
+ * @param codec The codec to remove the parameter from.
+ * @param param A pointer to the parameter to remove.
+ */
+void purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
+		PurpleKeyValuePair *param);
+
+/**
+ * Gets an optional parameter based on the values given.
+ *
+ * @param codec The codec to find the parameter in.
+ * @param name The name of the parameter to search for.
+ * @param value The value to search for or NULL.
+ *
+ * @return The value found or NULL.
+ */
+PurpleKeyValuePair *purple_media_codec_get_optional_parameter(
+		PurpleMediaCodec *codec, const gchar *name,
+		const gchar *value);
+
+/**
+ * Copies a PurpleMediaCodec object.
+ *
+ * @param codec The codec to copy.
+ *
+ * @return The copy of the codec.
+ */
+PurpleMediaCodec *purple_media_codec_copy(PurpleMediaCodec *codec);
+
+/**
+ * Copies a GList of PurpleMediaCodec and its contents.
+ *
+ * @param codecs The list of codecs to be copied.
+ *
+ * @return The copy of the GList.
+ */
+GList *purple_media_codec_list_copy(GList *codecs);
+
+/**
+ * Frees a GList of PurpleMediaCodec and its contents.
+ *
+ * @param codecs The list of codecs to be freed.
+ */
+void purple_media_codec_list_free(GList *codecs);
+
+/**
+ * Creates a string representation of the codec.
+ *
+ * @param codec The codec to create the string of.
+ *
+ * @return The new string representation.
+ */
+gchar *purple_media_codec_to_string(const PurpleMediaCodec *codec);
+
+G_END_DECLS
+
+#endif  /* _PURPLE_MEDIA_CODEC_H_ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/enum-types.c	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,213 @@
+/**
+ * @file enum-types.c Enum types for Media 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 "enum-types.h"
+
+/*
+ * PurpleMediaCandidateType
+ */
+
+GType
+purple_media_candidate_type_get_type()
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GEnumValue values[] = {
+			{ PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
+					"PURPLE_MEDIA_CANDIDATE_TYPE_HOST",
+					"host" },
+			{ PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
+					"PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX",
+					"srflx" },
+			{ PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX,
+					"PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX",
+					"prflx" },
+			{ PURPLE_MEDIA_CANDIDATE_TYPE_RELAY,
+					"PURPLE_MEDIA_CANDIDATE_TYPE_RELAY",
+					"relay" },
+			{ PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST,
+					"PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST",
+					"multicast" },
+			{ 0, NULL, NULL }
+		};
+		type = g_enum_register_static("PurpleMediaCandidateType",
+				values);
+	}
+	return type;
+}
+
+/*
+ * PurpleMediaCaps
+ */
+
+GType
+purple_media_caps_get_type()
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GEnumValue values[] = {
+			{ PURPLE_MEDIA_CAPS_NONE,
+					"PURPLE_MEDIA_CAPS_NONE", "none" },
+			{ PURPLE_MEDIA_CAPS_AUDIO,
+					"PURPLE_MEDIA_CAPS_AUDIO", "audio" },
+			{ PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION,
+					"PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION",
+					"audio-single-direction" },
+			{ PURPLE_MEDIA_CAPS_VIDEO,
+					"PURPLE_MEDIA_CAPS_VIDEO", "video" },
+			{ PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION,
+					"PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION",
+					"video-single-direction" },
+			{ PURPLE_MEDIA_CAPS_AUDIO_VIDEO,
+					"PURPLE_MEDIA_CAPS_AUDIO_VIDEO",
+					"audio-video" },
+			{ PURPLE_MEDIA_CAPS_MODIFY_SESSION,
+					"PURPLE_MEDIA_CAPS_MODIFY_SESSION",
+					"modify-session" },
+			{ PURPLE_MEDIA_CAPS_CHANGE_DIRECTION,
+					"PURPLE_MEDIA_CAPS_CHANGE_DIRECTION",
+					"change-direction" },
+			{ 0, NULL, NULL }
+		};
+		type = g_enum_register_static("PurpleMediaCaps", values);
+	}
+	return type;
+}
+
+/*
+ * PurpleMediaInfoType
+ */
+
+GType
+purple_media_info_type_get_type()
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GEnumValue values[] = {
+			{ PURPLE_MEDIA_INFO_HANGUP,
+					"PURPLE_MEDIA_INFO_HANGUP", "hangup" },
+			{ PURPLE_MEDIA_INFO_ACCEPT,
+					"PURPLE_MEDIA_INFO_ACCEPT", "accept" },
+			{ PURPLE_MEDIA_INFO_REJECT,
+					"PURPLE_MEDIA_INFO_REJECT", "reject" },
+			{ PURPLE_MEDIA_INFO_MUTE,
+					"PURPLE_MEDIA_INFO_MUTE", "mute" },
+			{ PURPLE_MEDIA_INFO_UNMUTE,
+					"PURPLE_MEDIA_INFO_UNMUTE", "unmute" },
+			{ PURPLE_MEDIA_INFO_PAUSE,
+					"PURPLE_MEDIA_INFO_PAUSE", "pause" },
+			{ PURPLE_MEDIA_INFO_UNPAUSE,
+					"PURPLE_MEDIA_INFO_UNPAUSE", "unpause" },
+			{ PURPLE_MEDIA_INFO_HOLD,
+					"PURPLE_MEDIA_INFO_HOLD", "hold" },
+			{ PURPLE_MEDIA_INFO_UNHOLD,
+					"PURPLE_MEDIA_INFO_HOLD", "unhold" },
+			{ 0, NULL, NULL }
+		};
+		type = g_enum_register_static("PurpleMediaInfoType", values);
+	}
+	return type;
+}
+
+/*
+ * PurpleMediaNetworkProtocol
+ */
+
+GType
+purple_media_network_protocol_get_type()
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GEnumValue values[] = {
+			{ PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
+					"PURPLE_MEDIA_NETWORK_PROTOCOL_UDP",
+					"udp" },
+			{ PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
+					"PURPLE_MEDIA_NETWORK_PROTOCOL_TCP",
+					"tcp" },
+			{ 0, NULL, NULL }
+		};
+		type = g_enum_register_static("PurpleMediaNetworkProtocol",
+				values);
+	}
+	return type;
+}
+
+/*
+ * PurpleMediaSessionType
+ */
+
+GType
+purple_media_session_type_get_type()
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GFlagsValue values[] = {
+			{ PURPLE_MEDIA_NONE,
+				"PURPLE_MEDIA_NONE", "none" },
+			{ PURPLE_MEDIA_RECV_AUDIO,
+				"PURPLE_MEDIA_RECV_AUDIO", "recv-audio" },
+			{ PURPLE_MEDIA_SEND_AUDIO,
+				"PURPLE_MEDIA_SEND_AUDIO", "send-audio" },
+			{ PURPLE_MEDIA_RECV_VIDEO,
+				"PURPLE_MEDIA_RECV_VIDEO", "recv-video" },
+			{ PURPLE_MEDIA_SEND_VIDEO,
+				"PURPLE_MEDIA_SEND_VIDEO", "send-audio" },
+			{ PURPLE_MEDIA_AUDIO,
+				"PURPLE_MEDIA_AUDIO", "audio" },
+			{ PURPLE_MEDIA_VIDEO,
+				"PURPLE_MEDIA_VIDEO", "video" },
+			{ 0, NULL, NULL }
+		};
+		type = g_flags_register_static(
+				"PurpleMediaSessionType", values);
+	}
+	return type;
+}
+
+/*
+ * PurpleMediaState
+ */
+
+GType
+purple_media_state_changed_get_type()
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GEnumValue values[] = {
+			{ PURPLE_MEDIA_STATE_NEW,
+				"PURPLE_MEDIA_STATE_NEW", "new" },
+			{ PURPLE_MEDIA_STATE_CONNECTED,
+				"PURPLE_MEDIA_STATE_CONNECTED", "connected" },
+			{ PURPLE_MEDIA_STATE_END,
+				"PURPLE_MEDIA_STATE_END", "end" },
+			{ 0, NULL, NULL }
+		};
+		type = g_enum_register_static("PurpleMediaState", values);
+	}
+	return type;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/enum-types.h	Wed Jun 13 19:30:27 2012 -0400
@@ -0,0 +1,150 @@
+/**
+ * @file enum-types.h Enum types for Media 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_MEDIA_ENUM_TYPES_H_
+#define _PURPLE_MEDIA_ENUM_TYPES_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define PURPLE_TYPE_MEDIA_CANDIDATE_TYPE   (purple_media_candidate_type_get_type())
+#define PURPLE_MEDIA_TYPE_CAPS	           (purple_media_caps_get_type())
+#define PURPLE_MEDIA_TYPE_INFO_TYPE	   (purple_media_info_type_get_type())
+#define PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL (purple_media_network_protocol_get_type())
+#define PURPLE_TYPE_MEDIA_SESSION_TYPE     (purple_media_session_type_get_type())
+#define PURPLE_MEDIA_TYPE_STATE            (purple_media_state_changed_get_type())
+
+/** Media candidate types */
+typedef enum {
+	PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
+	PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
+	PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX,
+	PURPLE_MEDIA_CANDIDATE_TYPE_RELAY,
+	PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST,
+} PurpleMediaCandidateType;
+
+/** Media caps */
+typedef enum {
+	PURPLE_MEDIA_CAPS_NONE = 0,
+	PURPLE_MEDIA_CAPS_AUDIO = 1,
+	PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION = 1 << 1,
+	PURPLE_MEDIA_CAPS_VIDEO = 1 << 2,
+	PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION = 1 << 3,
+	PURPLE_MEDIA_CAPS_AUDIO_VIDEO = 1 << 4,
+	PURPLE_MEDIA_CAPS_MODIFY_SESSION = 1 << 5,
+	PURPLE_MEDIA_CAPS_CHANGE_DIRECTION = 1 << 6,
+} PurpleMediaCaps;
+
+/** Media component types */
+typedef enum {
+	PURPLE_MEDIA_COMPONENT_NONE = 0,
+	PURPLE_MEDIA_COMPONENT_RTP = 1,
+	PURPLE_MEDIA_COMPONENT_RTCP = 2,
+} PurpleMediaComponentType;
+
+/** Media info types */
+typedef enum {
+	PURPLE_MEDIA_INFO_HANGUP = 0,
+	PURPLE_MEDIA_INFO_ACCEPT,
+	PURPLE_MEDIA_INFO_REJECT,
+	PURPLE_MEDIA_INFO_MUTE,
+	PURPLE_MEDIA_INFO_UNMUTE,
+	PURPLE_MEDIA_INFO_PAUSE,
+	PURPLE_MEDIA_INFO_UNPAUSE,
+	PURPLE_MEDIA_INFO_HOLD,
+	PURPLE_MEDIA_INFO_UNHOLD,
+} PurpleMediaInfoType;
+
+/** Media network protocols */
+typedef enum {
+	PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
+	PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
+} PurpleMediaNetworkProtocol;
+
+/** Media session types */
+typedef enum {
+	PURPLE_MEDIA_NONE	= 0,
+	PURPLE_MEDIA_RECV_AUDIO = 1 << 0,
+	PURPLE_MEDIA_SEND_AUDIO = 1 << 1,
+	PURPLE_MEDIA_RECV_VIDEO = 1 << 2,
+	PURPLE_MEDIA_SEND_VIDEO = 1 << 3,
+	PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO,
+	PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO
+} PurpleMediaSessionType;
+
+/** Media state-changed types */
+typedef enum {
+	PURPLE_MEDIA_STATE_NEW = 0,
+	PURPLE_MEDIA_STATE_CONNECTED,
+	PURPLE_MEDIA_STATE_END,
+} PurpleMediaState;
+
+/**
+ * Gets the media candidate type's GType
+ *
+ * @return The media candidate type's GType.
+ */
+GType purple_media_candidate_type_get_type(void);
+
+/**
+ * Gets the type of the media caps flags
+ *
+ * @return The media caps flags' GType
+ */
+GType purple_media_caps_get_type(void);
+
+/**
+ * Gets the type of the info type enum
+ *
+ * @return The info type enum's GType
+ */
+GType purple_media_info_type_get_type(void);
+
+/**
+ * Gets the media network protocol's GType
+ *
+ * @return The media network protocol's GType.
+ */
+GType purple_media_network_protocol_get_type(void);
+
+/**
+ * Gets the media session type's GType
+ *
+ * @return The media session type's GType.
+ */
+GType purple_media_session_type_get_type(void);
+
+/**
+ * Gets the type of the state-changed enum
+ *
+ * @return The state-changed enum's GType
+ */
+GType purple_media_state_changed_get_type(void);
+
+G_END_DECLS
+
+#endif  /* _PURPLE_MEDIA_ENUM_TYPES_ */
--- a/libpurple/mediamanager.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/mediamanager.c	Wed Jun 13 19:30:27 2012 -0400
@@ -21,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
 #include "internal.h"
@@ -37,8 +37,9 @@
 #endif
 
 #ifdef USE_VV
+#include <media/backend-fs2.h>
 
-#include <gst/farsight/fs-conference-iface.h>
+#include <gst/farsight/fs-element-added-notifier.h>
 #include <gst/interfaces/xoverlay.h>
 
 /** @copydoc _PurpleMediaManagerPrivate */
@@ -79,6 +80,8 @@
 	GList *elements;
 	GList *output_windows;
 	gulong next_output_window_id;
+	GType backend_type;
+	GstCaps *video_caps;
 
 	PurpleMediaElementInfo *video_src;
 	PurpleMediaElementInfo *video_sink;
@@ -99,6 +102,7 @@
 
 enum {
 	INIT_MEDIA,
+	UI_CAPS_CHANGED,
 	LAST_SIGNAL
 };
 static guint purple_media_manager_signals[LAST_SIGNAL] = {0};
@@ -137,7 +141,7 @@
 {
 	GObjectClass *gobject_class = (GObjectClass*)klass;
 	parent_class = g_type_class_peek_parent(klass);
-	
+
 	gobject_class->finalize = purple_media_manager_finalize;
 
 	purple_media_manager_signals[INIT_MEDIA] = g_signal_new ("init-media",
@@ -147,6 +151,15 @@
 		purple_smarshal_BOOLEAN__OBJECT_POINTER_STRING,
 		G_TYPE_BOOLEAN, 3, PURPLE_TYPE_MEDIA,
 		G_TYPE_POINTER, G_TYPE_STRING);
+
+	purple_media_manager_signals[UI_CAPS_CHANGED] = g_signal_new ("ui-caps-changed",
+		G_TYPE_FROM_CLASS (klass),
+		G_SIGNAL_RUN_LAST,
+		0, NULL, NULL,
+		purple_smarshal_VOID__FLAGS_FLAGS,
+		G_TYPE_NONE, 2, PURPLE_MEDIA_TYPE_CAPS,
+		PURPLE_MEDIA_TYPE_CAPS);
+
 	g_type_class_add_private(klass, sizeof(PurpleMediaManagerPrivate));
 }
 
@@ -156,6 +169,16 @@
 	media->priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media);
 	media->priv->medias = NULL;
 	media->priv->next_output_window_id = 1;
+#ifdef USE_VV
+	media->priv->backend_type = PURPLE_TYPE_MEDIA_BACKEND_FS2;
+#endif
+
+	purple_prefs_add_none("/purple/media");
+	purple_prefs_add_none("/purple/media/audio");
+	purple_prefs_add_int("/purple/media/audio/silence_threshold", 5);
+	purple_prefs_add_none("/purple/media/audio/volume");
+	purple_prefs_add_int("/purple/media/audio/volume/input", 10);
+	purple_prefs_add_int("/purple/media/audio/volume/output", 10);
 }
 
 static void
@@ -170,6 +193,8 @@
 			g_list_delete_link(priv->elements, priv->elements)) {
 		g_object_unref(priv->elements->data);
 	}
+	if (priv->video_caps)
+		gst_caps_unref(priv->video_caps);
 	parent_class->finalize(media);
 }
 #endif
@@ -229,6 +254,10 @@
 	g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
 
 	if (manager->priv->pipeline == NULL) {
+		FsElementAddedNotifier *notifier;
+		gchar *filename;
+		GError *err = NULL;
+		GKeyFile *keyfile;
 		GstBus *bus;
 		manager->priv->pipeline = gst_pipeline_new(NULL);
 
@@ -241,6 +270,38 @@
 				gst_bus_sync_signal_handler, NULL);
 		gst_object_unref(bus);
 
+		filename = g_build_filename(purple_user_dir(),
+				"fs-element.conf", NULL);
+		keyfile = g_key_file_new();
+		if (!g_key_file_load_from_file(keyfile, filename,
+				G_KEY_FILE_NONE, &err)) {
+			if (err->code == 4)
+				purple_debug_info("mediamanager",
+						"Couldn't read "
+						"fs-element.conf: %s\n",
+						err->message);
+			else
+				purple_debug_error("mediamanager",
+						"Error reading "
+						"fs-element.conf: %s\n",
+						err->message);
+			g_error_free(err);
+		}
+		g_free(filename);
+
+		/* Hack to make alsasrc stop messing up audio timestamps */
+		if (!g_key_file_has_key(keyfile,
+				"alsasrc", "slave-method", NULL)) {
+			g_key_file_set_integer(keyfile,
+					"alsasrc", "slave-method", 2);
+		}
+
+		notifier = fs_element_added_notifier_new();
+		fs_element_added_notifier_add(notifier,
+				GST_BIN(manager->priv->pipeline));
+		fs_element_added_notifier_set_properties_from_keyfile(
+				notifier, keyfile);
+
 		gst_element_set_state(manager->priv->pipeline,
 				GST_STATE_PLAYING);
 	}
@@ -261,34 +322,15 @@
 {
 #ifdef USE_VV
 	PurpleMedia *media;
-	FsConference *conference = FS_CONFERENCE(gst_element_factory_make(conference_type, NULL));
-	GstStateChangeReturn ret;
 	gboolean signal_ret;
 
-	if (conference == NULL) {
-		purple_conv_present_error(remote_user, account,
-					  _("Error creating conference."));
-		purple_debug_error("media", "Conference == NULL\n");
-		return NULL;
-	}
-
 	media = PURPLE_MEDIA(g_object_new(purple_media_get_type(),
 			     "manager", manager,
 			     "account", account,
-			     "conference", conference,
+			     "conference-type", conference_type,
 			     "initiator", initiator,
 			     NULL));
 
-	ret = gst_element_set_state(GST_ELEMENT(conference), GST_STATE_PLAYING);
-
-	if (ret == GST_STATE_CHANGE_FAILURE) {
-		purple_conv_present_error(remote_user, account,
-					  _("Error creating conference."));
-		purple_debug_error("media", "Failed to start conference.\n");
-		g_object_unref(media);
-		return NULL;
-	}
-
 	g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0,
 			media, account, remote_user, &signal_ret);
 
@@ -356,15 +398,20 @@
 	GstElement *parent = GST_ELEMENT_PARENT(pad);
 	GstIterator *iter;
 	GstPad *remaining_pad;
+	GstIteratorResult result;
 
 	gst_element_release_request_pad(GST_ELEMENT_PARENT(pad), pad);
-	iter = gst_element_iterate_pads(parent);
+
+	iter = gst_element_iterate_src_pads(parent);
 
-	if (gst_iterator_next(iter, (gpointer)&remaining_pad)
-			== GST_ITERATOR_DONE) {
+	result = gst_iterator_next(iter, (gpointer)&remaining_pad);
+
+	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) {
+		gst_object_unref(remaining_pad);
 	}
 
 	gst_iterator_free(iter);
@@ -372,6 +419,43 @@
 #endif
 
 #ifdef USE_GSTREAMER
+
+void
+purple_media_manager_set_video_caps(PurpleMediaManager *manager, GstCaps *caps)
+{
+#ifdef USE_VV
+	if (manager->priv->video_caps)
+		gst_caps_unref(manager->priv->video_caps);
+
+	manager->priv->video_caps = caps;
+
+	if (manager->priv->pipeline && manager->priv->video_src) {
+		gchar *id = purple_media_element_info_get_id(manager->priv->video_src);
+		GstElement *src = gst_bin_get_by_name(GST_BIN(manager->priv->pipeline), id);
+
+		if (src) {
+			GstElement *capsfilter = gst_bin_get_by_name(GST_BIN(src), "prpl_video_caps");
+			g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
+		}
+
+		g_free(id);
+	}
+#endif
+}
+
+GstCaps *
+purple_media_manager_get_video_caps(PurpleMediaManager *manager)
+{
+#ifdef USE_VV
+	if (manager->priv->video_caps == NULL)
+		manager->priv->video_caps = gst_caps_from_string("video/x-raw-yuv,"
+			"width=[250,352], height=[200,288], framerate=[1/1,20/1]");
+	return manager->priv->video_caps;
+#else
+	return NULL;
+#endif
+}
+
 GstElement *
 purple_media_manager_get_element(PurpleMediaManager *manager,
 		PurpleMediaSessionType type, PurpleMedia *media,
@@ -414,7 +498,21 @@
 			bin = gst_bin_new(id);
 			tee = gst_element_factory_make("tee", "tee");
 			gst_bin_add_many(GST_BIN(bin), ret, tee, NULL);
-			gst_element_link(ret, tee);
+
+			if (type & PURPLE_MEDIA_SEND_VIDEO) {
+				GstElement *videoscale;
+				GstElement *capsfilter;
+
+				videoscale = gst_element_factory_make("videoscale", NULL);
+				capsfilter = gst_element_factory_make("capsfilter", "prpl_video_caps");
+
+				g_object_set(G_OBJECT(capsfilter),
+					"caps", purple_media_manager_get_video_caps(manager), NULL);
+
+				gst_bin_add_many(GST_BIN(bin), videoscale, capsfilter, NULL);
+				gst_element_link_many(ret, videoscale, capsfilter, tee, NULL);
+			} else
+				gst_element_link(ret, tee);
 
 			/*
 			 * This shouldn't be necessary, but it stops it from
@@ -426,7 +524,6 @@
 			gst_element_link(tee, fakesink);
 
 			ret = bin;
-			gst_element_set_locked_state(ret, TRUE);
 			gst_object_ref(ret);
 			gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(
 					manager)), ret);
@@ -667,7 +764,7 @@
 				(participant == ow->participant)) &&
 				!strcmp(session_id, ow->session_id)) {
 			GstBus *bus;
-			GstElement *queue;
+			GstElement *queue, *colorspace;
 			GstElement *tee = purple_media_get_tee(media,
 					session_id, participant);
 
@@ -676,6 +773,8 @@
 
 			queue = gst_element_factory_make(
 					"queue", NULL);
+			colorspace = gst_element_factory_make(
+					"ffmpegcolorspace", NULL);
 			ow->sink = purple_media_manager_get_element(
 					manager, PURPLE_MEDIA_RECV_VIDEO,
 					ow->media, ow->session_id,
@@ -696,7 +795,7 @@
 			}
 
 			gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)),
-					queue, ow->sink, NULL);
+					queue, colorspace, ow->sink, NULL);
 
 			bus = gst_pipeline_get_bus(GST_PIPELINE(
 					manager->priv->pipeline));
@@ -704,9 +803,11 @@
 					G_CALLBACK(window_id_cb), ow);
 			gst_object_unref(bus);
 
-			gst_element_sync_state_with_parent(ow->sink);
-			gst_element_link(queue, ow->sink);
-			gst_element_sync_state_with_parent(queue);
+			gst_element_set_state(ow->sink, GST_STATE_PLAYING);
+			gst_element_set_state(colorspace, 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(tee, queue);
 		}
 	}
@@ -775,8 +876,14 @@
 		GstPad *pad = gst_element_get_static_pad(
 				output_window->sink, "sink");
 		GstPad *peer = gst_pad_get_peer(pad);
-		GstElement *queue = GST_ELEMENT_PARENT(peer);
+		GstElement *colorspace = GST_ELEMENT_PARENT(peer), *queue;
 		gst_object_unref(pad);
+		gst_object_unref(peer);
+		pad = gst_element_get_static_pad(colorspace, "sink");
+		peer = gst_pad_get_peer(pad);
+		queue = GST_ELEMENT_PARENT(peer);
+		gst_object_unref(pad);
+		gst_object_unref(peer);
 		pad = gst_element_get_static_pad(queue, "sink");
 		peer = gst_pad_get_peer(pad);
 		gst_object_unref(pad);
@@ -785,6 +892,9 @@
 		gst_element_set_locked_state(queue, TRUE);
 		gst_element_set_state(queue, GST_STATE_NULL);
 		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(queue)), queue);
+		gst_element_set_locked_state(colorspace, TRUE);
+		gst_element_set_state(colorspace, GST_STATE_NULL);
+		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(colorspace)), colorspace);
 		gst_element_set_locked_state(output_window->sink, TRUE);
 		gst_element_set_state(output_window->sink, GST_STATE_NULL);
 		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(output_window->sink)),
@@ -835,8 +945,17 @@
 		PurpleMediaCaps caps)
 {
 #ifdef USE_VV
+	PurpleMediaCaps oldcaps;
+
 	g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager));
+
+	oldcaps = manager->priv->ui_caps;
 	manager->priv->ui_caps = caps;
+
+	if (caps != oldcaps)
+		g_signal_emit(manager,
+				purple_media_manager_signals[UI_CAPS_CHANGED],
+				0, caps, oldcaps);
 #endif
 }
 
@@ -852,6 +971,30 @@
 #endif
 }
 
+void
+purple_media_manager_set_backend_type(PurpleMediaManager *manager,
+		GType backend_type)
+{
+#ifdef USE_VV
+	g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager));
+
+	manager->priv->backend_type = backend_type;
+#endif
+}
+
+GType
+purple_media_manager_get_backend_type(PurpleMediaManager *manager)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager),
+			PURPLE_MEDIA_CAPS_NONE);
+
+	return manager->priv->backend_type;
+#else
+	return G_TYPE_NONE;
+#endif
+}
+
 #ifdef USE_GSTREAMER
 
 /*
@@ -983,7 +1126,7 @@
 		case PROP_CREATE_CB:
 			priv->create = g_value_get_pointer(value);
 			break;
-		default:	
+		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID(
 					object, prop_id, pspec);
 			break;
@@ -996,7 +1139,7 @@
 {
 	PurpleMediaElementInfoPrivate *priv;
 	g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object));
-	
+
 	priv = PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(object);
 
 	switch (prop_id) {
@@ -1012,7 +1155,7 @@
 		case PROP_CREATE_CB:
 			g_value_set_pointer(value, priv->create);
 			break;
-		default:	
+		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID(
 					object, prop_id, pspec);
 			break;
@@ -1023,7 +1166,7 @@
 purple_media_element_info_class_init(PurpleMediaElementInfoClass *klass)
 {
 	GObjectClass *gobject_class = (GObjectClass*)klass;
-	
+
 	gobject_class->finalize = purple_media_element_info_finalize;
 	gobject_class->set_property = purple_media_element_info_set_property;
 	gobject_class->get_property = purple_media_element_info_get_property;
--- a/libpurple/mediamanager.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/mediamanager.h	Wed Jun 13 19:30:27 2012 -0400
@@ -21,7 +21,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
 #ifndef _PURPLE_MEDIA_MANAGER_H_
@@ -30,16 +30,14 @@
 #include <glib.h>
 #include <glib-object.h>
 
-/** @copydoc _PurpleMediaManager */
+/** An opaque structure representing a group of (usually all) media calls. */
 typedef struct _PurpleMediaManager PurpleMediaManager;
-/** @copydoc _PurpleMediaManagerClass */
+/** The GObject class structure of the PurpleMediaManager object. */
 typedef struct _PurpleMediaManagerClass PurpleMediaManagerClass;
 
 #include "account.h"
 #include "media.h"
 
-G_BEGIN_DECLS
-
 #define PURPLE_TYPE_MEDIA_MANAGER            (purple_media_manager_get_type())
 #define PURPLE_MEDIA_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManager))
 #define PURPLE_MEDIA_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerClass))
@@ -47,9 +45,7 @@
 #define PURPLE_IS_MEDIA_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_MANAGER))
 #define PURPLE_MEDIA_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerClass))
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Media Manager API                                              */
@@ -60,8 +56,6 @@
  * Gets the media manager's GType.
  *
  * @return The media manager's GType.
- *
- * @since 2.6.0
  */
 GType purple_media_manager_get_type(void);
 
@@ -69,8 +63,6 @@
  * Gets the "global" media manager object. It's created if it doesn't already exist.
  *
  * @return The "global" instance of the media manager object.
- *
- * @since 2.6.0
  */
 PurpleMediaManager *purple_media_manager_get(void);
 
@@ -81,10 +73,9 @@
  * @param account The account to create the session on.
  * @param conference_type The conference type to feed into Farsight2.
  * @param remote_user The remote user to initiate the session with.
+ * @param initiator TRUE if the local user is the initiator of this media call, FALSE otherwise.
  *
  * @return A newly created media session.
- *
- * @since 2.6.0
  */
 PurpleMedia *purple_media_manager_create_media(PurpleMediaManager *manager,
 						PurpleAccount *account,
@@ -98,8 +89,6 @@
  * @param manager The media manager to get all of the sessions from.
  *
  * @return A list of all the media sessions.
- *
- * @since 2.6.0
  */
 GList *purple_media_manager_get_media(PurpleMediaManager *manager);
 
@@ -110,8 +99,6 @@
  * @param account The account the sessions are on.
  *
  * @return A list of the media sessions on the given account.
- *
- * @since 2.6.0
  */
 GList *purple_media_manager_get_media_by_account(
 		PurpleMediaManager *manager, PurpleAccount *account);
@@ -121,8 +108,6 @@
  *
  * @param manager The media manager to remove the media session from.
  * @param media The media session to remove.
- *
- * @since 2.6.0
  */
 void
 purple_media_manager_remove_media(PurpleMediaManager *manager,
@@ -139,8 +124,6 @@
  * @param participant The participant the output windows are registered with.
  *
  * @return TRUE if it succeeded, FALSE if it failed.
- *
- * @since 2.6.0
  */
 gboolean purple_media_manager_create_output_window(
 		PurpleMediaManager *manager, PurpleMedia *media,
@@ -156,8 +139,6 @@
  * @param window_id The window ID to embed the video in.
  *
  * @return A unique ID to the registered output window, 0 if it failed.
- *
- * @since 2.6.0
  */
 gulong purple_media_manager_set_output_window(PurpleMediaManager *manager,
 		PurpleMedia *media, const gchar *session_id,
@@ -170,8 +151,6 @@
  * @param output_window_id The ID of the output window.
  *
  * @return TRUE if it found the output window and was successful, else FALSE.
- *
- * @since 2.6.0
  */
 gboolean purple_media_manager_remove_output_window(
 		PurpleMediaManager *manager, gulong output_window_id);
@@ -183,8 +162,6 @@
  * @param media The media instance the output windows were registered for.
  * @param session_id The session the output windows were registered for.
  * @param participant The participant the output windows were registered for.
- *
- * @since 2.6.0
  */
 void purple_media_manager_remove_output_windows(
 		PurpleMediaManager *manager, PurpleMedia *media,
@@ -195,8 +172,6 @@
  *
  * @param manager The manager to set the caps on.
  * @param caps The caps to set.
- *
- * @since 2.6.0
  */
 void purple_media_manager_set_ui_caps(PurpleMediaManager *manager,
 		PurpleMediaCaps caps);
@@ -207,16 +182,28 @@
  * @param manager The manager to get caps from.
  *
  * @return caps The caps retrieved.
- *
- * @since 2.6.0
  */
 PurpleMediaCaps purple_media_manager_get_ui_caps(PurpleMediaManager *manager);
 
-/*}@*/
+/**
+ * Sets which media backend type media objects will use.
+ *
+ * @param manager The manager to set the caps on.
+ * @param backend_type The media backend type to use.
+ */
+void purple_media_manager_set_backend_type(PurpleMediaManager *manager,
+		GType backend_type);
 
-#ifdef __cplusplus
-}
-#endif
+/**
+ * Gets which media backend type media objects will use.
+ *
+ * @param manager The manager to get the media backend type from.
+ *
+ * @return The type of media backend type media objects will use.
+ */
+GType purple_media_manager_get_backend_type(PurpleMediaManager *manager);
+
+/*}@*/
 
 G_END_DECLS
 
--- a/libpurple/mime.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/mime.c	Wed Jun 13 19:30:27 2012 -0400
@@ -21,11 +21,6 @@
  * USA.
  */
 
-#include <stdio.h>
-#include <string.h>
-
-#include <glib.h>
-
 #include "internal.h"
 
 /* this should become "util.h" if we ever get this into purple proper */
@@ -441,6 +436,34 @@
 	g_free(bnd);
 }
 
+#define BOUNDARY "boundary="
+static char *
+parse_boundary(const char *ct)
+{
+	char *boundary_begin = g_strstr_len(ct, -1, BOUNDARY);
+	char *boundary_end;
+
+	if (!boundary_begin)
+		return NULL;
+
+	boundary_begin += sizeof(BOUNDARY) - 1;
+
+	if (*boundary_begin == '"') {
+		boundary_end = strchr(++boundary_begin, '"');
+		if (!boundary_end)
+			return NULL;
+	} else {
+		boundary_end = strchr(boundary_begin, ' ');
+		if (!boundary_end) {
+			boundary_end = strchr(boundary_begin, ';');
+			if (!boundary_end)
+				boundary_end = boundary_begin + strlen(boundary_begin);
+		}
+	}
+
+	return g_strndup(boundary_begin, boundary_end - boundary_begin);
+}
+#undef BOUNDARY
 
 PurpleMimeDocument *
 purple_mime_document_parsen(const char *buf, gsize len)
@@ -461,10 +484,11 @@
 
 	{
 		const char *ct = fields_get(&doc->fields, "content-type");
-		if(ct && purple_str_has_prefix(ct, "multipart")) {
-			char *bd = strrchr(ct, '=');
-			if(bd++) {
+		if (ct && purple_str_has_prefix(ct, "multipart")) {
+			char *bd = parse_boundary(ct);
+			if (bd) {
 				doc_parts_load(doc, bd, b, n);
+				g_free(bd);
 			}
 		}
 	}
--- a/libpurple/mime.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/mime.h	Wed Jun 13 19:30:27 2012 -0400
@@ -26,10 +26,6 @@
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * @file mime.h
  * @ingroup core
@@ -48,6 +44,8 @@
  */
 typedef struct _PurpleMimePart PurpleMimePart;
 
+G_BEGIN_DECLS
+
 /**
  * Allocate an empty MIME document.
  */
@@ -211,8 +209,6 @@
 
 void purple_mime_part_set_data(PurpleMimePart *part, const char *data);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif
--- a/libpurple/nat-pmp.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/nat-pmp.c	Wed Jun 13 19:30:27 2012 -0400
@@ -29,8 +29,8 @@
  * OF SUCH DAMAGE.
  */
 
+#include "internal.h"
 #include "nat-pmp.h"
-#include "internal.h"
 #include "debug.h"
 #include "signals.h"
 #include "network.h"
@@ -177,55 +177,56 @@
 default_gw()
 {
 	int mib[6];
-    size_t needed;
-    char *buf, *next, *lim;
-    struct rt_msghdr *rtm;
-    struct sockaddr *sa;
+	size_t needed;
+	char *buf, *next, *lim;
+	struct rt_msghdr *rtm;
+	struct sockaddr *sa;
 	struct sockaddr_in *sin = NULL;
-	gboolean found = FALSE;
 
-    mib[0] = CTL_NET;
-    mib[1] = PF_ROUTE; /* entire routing table or a subset of it */
-    mib[2] = 0; /* protocol number - always 0 */
-    mib[3] = 0; /* address family - 0 for all addres families */
-    mib[4] = NET_RT_DUMP;
-    mib[5] = 0;
+	mib[0] = CTL_NET;
+	mib[1] = PF_ROUTE; /* entire routing table or a subset of it */
+	mib[2] = 0; /* protocol number - always 0 */
+	mib[3] = 0; /* address family - 0 for all addres families */
+	mib[4] = NET_RT_DUMP;
+	mib[5] = 0;
 
 	/* Determine the buffer side needed to get the full routing table */
-    if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
 	{
 		purple_debug_warning("nat-pmp", "sysctl: net.route.0.0.dump estimate\n");
 		return NULL;
-    }
+	}
 
-    if (!(buf = malloc(needed)))
+	if (!(buf = malloc(needed)))
 	{
 		purple_debug_warning("nat-pmp", "Failed to malloc %" G_GSIZE_FORMAT "\n", needed);
 		return NULL;
-    }
+	}
 
 	/* Read the routing table into buf */
-    if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
 	{
+		free(buf);
 		purple_debug_warning("nat-pmp", "sysctl: net.route.0.0.dump\n");
 		return NULL;
-    }
+	}
 
-    lim = buf + needed;
+	lim = buf + needed;
 
-    for (next = buf; next < lim; next += rtm->rtm_msglen)
+	for (next = buf; next < lim; next += rtm->rtm_msglen)
 	{
 		rtm = (struct rt_msghdr *)next;
 		sa = (struct sockaddr *)(rtm + 1);
 
 		if (sa->sa_family == AF_INET)
 		{
-			sin = (struct sockaddr_in*) sa;
+			struct sockaddr_in *cursin = (struct sockaddr_in*) sa;
 
-			if ((rtm->rtm_flags & RTF_GATEWAY) && sin->sin_addr.s_addr == INADDR_ANY)
+			if ((rtm->rtm_flags & RTF_GATEWAY)
+			    && cursin->sin_addr.s_addr == INADDR_ANY)
 			{
 				/* We found the default route. Now get the destination address and netmask. */
-	            struct sockaddr *rti_info[RTAX_MAX];
+				struct sockaddr *rti_info[RTAX_MAX];
 				struct sockaddr addr, mask;
 
 				get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
@@ -251,15 +252,15 @@
 						memcpy(sin, rti_info[RTAX_GATEWAY], sizeof(struct sockaddr_in));
 
 						purple_debug_info("nat-pmp", "Found a default gateway\n");
-						found = TRUE;
 						break;
 					}
 				}
 			}
 		}
-    }
+	}
 
-	return (found ? sin : NULL);
+	free(buf);
+	return sin;
 }
 
 /*!
--- a/libpurple/nat-pmp.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/nat-pmp.h	Wed Jun 13 19:30:27 2012 -0400
@@ -41,6 +41,8 @@
 	PURPLE_PMP_TYPE_TCP
 } PurplePmpType;
 
+G_BEGIN_DECLS
+
 /**
  * Initialize nat-pmp
  */
@@ -54,24 +56,28 @@
 /**
  * Remove the NAT-PMP mapping for a specified type on a specified port
  *
- * @param type The PurplePmpType
+ * @param type        The PurplePmpType
  * @param privateport The private port on which we are listening locally
- * @param publicport The public port on which we are expecting a response
- * @param lifetime The lifetime of the mapping. It is recommended that this be PURPLE_PMP_LIFETIME.
+ * @param publicport  The public port on which we are expecting a response
+ * @param lifetime    The lifetime of the mapping. It is recommended that this
+ *                    be PURPLE_PMP_LIFETIME.
  *
- * @returns TRUE if succesful; FALSE if unsuccessful
+ * @returns TRUE if successful; FALSE if unsuccessful
  */
-gboolean purple_pmp_create_map(PurplePmpType type, unsigned short privateport, unsigned short publicport, int lifetime);
+gboolean purple_pmp_create_map(PurplePmpType type, unsigned short privateport,
+                               unsigned short publicport, int lifetime);
 
 /**
  * Remove the NAT-PMP mapping for a specified type on a specified port
  *
- * @param type The PurplePmpType
+ * @param type        The PurplePmpType
  * @param privateport The private port on which the mapping was previously made
  *
- * @returns TRUE if succesful; FALSE if unsuccessful
+ * @returns TRUE if successful; FALSE if unsuccessful
  */
 gboolean purple_pmp_destroy_map(PurplePmpType type, unsigned short privateport);
 
+G_END_DECLS
+
 #endif
 
--- a/libpurple/network.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/network.c	Wed Jun 13 19:30:27 2012 -0400
@@ -32,6 +32,9 @@
 #include <netinet/in.h>
 #include <net/if.h>
 #include <sys/ioctl.h>
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
 #else
 #include <nspapi.h>
 #endif
@@ -68,6 +71,10 @@
 #include <dbus/dbus-glib.h>
 #include <NetworkManager.h>
 
+#if !defined(NM_CHECK_VERSION)
+#define NM_CHECK_VERSION(x,y,z) 0
+#endif
+
 static DBusGConnection *nm_conn = NULL;
 static DBusGProxy *nm_proxy = NULL;
 static DBusGProxy *dbus_proxy = NULL;
@@ -95,6 +102,7 @@
 	PurpleNetworkListenCallback cb;
 	gpointer cb_data;
 	UPnPMappingAddRemove *mapping_data;
+	int timer;
 };
 
 #ifdef HAVE_NETWORKMANAGER
@@ -160,7 +168,7 @@
 	struct ifconf ifc;
 	struct ifreq *ifr;
 	struct sockaddr_in *sinptr;
-	guint32 lhost = htonl(127 * 256 * 256 * 256 + 1);
+	guint32 lhost = htonl((127 << 24) + 1); /* 127.0.0.1 */
 	long unsigned int add;
 	int source = fd;
 
@@ -200,6 +208,85 @@
 	return "0.0.0.0";
 }
 
+GList *
+purple_network_get_all_local_system_ips(void)
+{
+#if defined(HAVE_GETIFADDRS) && defined(HAVE_INET_NTOP)
+	GList *result = NULL;
+	struct ifaddrs *start, *ifa;
+	int ret;
+
+	ret = getifaddrs(&start);
+	if (ret < 0) {
+		purple_debug_warning("network",
+				"getifaddrs() failed: %s\n", g_strerror(errno));
+		return NULL;
+	}
+
+	for (ifa = start; ifa; ifa = ifa->ifa_next) {
+		int family = ifa->ifa_addr ? ifa->ifa_addr->sa_family : AF_UNSPEC;
+		char host[INET6_ADDRSTRLEN];
+		const char *tmp = NULL;
+
+		if ((family != AF_INET && family != AF_INET6) || ifa->ifa_flags & IFF_LOOPBACK)
+			continue;
+
+		if (family == AF_INET)
+			tmp = inet_ntop(family, &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, host, sizeof(host));
+		else {
+			struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)ifa->ifa_addr;
+			/* Peer-peer link-local communication is a big TODO.  I am not sure
+			 * how communicating link-local addresses is supposed to work, and
+			 * it seems like it would require attempting the cartesian product
+			 * of the local and remote interfaces to see if any match (eww).
+			 */
+			if (!IN6_IS_ADDR_LINKLOCAL(&sockaddr->sin6_addr))
+				tmp = inet_ntop(family, &sockaddr->sin6_addr, host, sizeof(host));
+		}
+		if (tmp != NULL)
+			result = g_list_prepend(result, g_strdup(tmp));
+	}
+
+	freeifaddrs(start);
+
+	return g_list_reverse(result);
+#else /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
+	GList *result = NULL;
+	int source = socket(PF_INET,SOCK_STREAM, 0);
+	char buffer[1024];
+	char *tmp;
+	struct ifconf ifc;
+	struct ifreq *ifr;
+
+	ifc.ifc_len = sizeof(buffer);
+	ifc.ifc_req = (struct ifreq *)buffer;
+	ioctl(source, SIOCGIFCONF, &ifc);
+	close(source);
+
+	tmp = buffer;
+	while (tmp < buffer + ifc.ifc_len) {
+		char dst[INET_ADDRSTRLEN];
+
+		ifr = (struct ifreq *)tmp;
+		tmp += HX_SIZE_OF_IFREQ(*ifr);
+
+		if (ifr->ifr_addr.sa_family == AF_INET) {
+			struct sockaddr_in *sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
+
+			inet_ntop(AF_INET, &sinptr->sin_addr, dst,
+				sizeof(dst));
+			purple_debug_info("network",
+				"found local i/f with address %s on IPv4\n", dst);
+			if (!purple_strequal(dst, "127.0.0.1")) {
+				result = g_list_append(result, g_strdup(dst));
+			}
+		}
+	}
+
+	return result;
+#endif /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
+}
+
 const char *
 purple_network_get_my_ip(int fd)
 {
@@ -267,17 +354,15 @@
 
 	if (success) {
 		/* add port mapping to hash table */
-		gint *key = g_new(gint, 1);
-		gint *value = g_new(gint, 1);
-		*key = purple_network_get_port_from_fd(listen_data->listenfd);
-		*value = listen_data->socket_type;
-		g_hash_table_insert(upnp_port_mappings, key, value);
+		gint key = purple_network_get_port_from_fd(listen_data->listenfd);
+		gint value = listen_data->socket_type;
+		g_hash_table_insert(upnp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
 	}
 
 	if (listen_data->cb)
 		listen_data->cb(listen_data->listenfd, listen_data->cb_data);
 
-	/* Clear the UPnP mapping data, since it's complete and purple_netweork_listen_cancel() will try to cancel
+	/* Clear the UPnP mapping data, since it's complete and purple_network_listen_cancel() will try to cancel
 	 * it otherwise. */
 	listen_data->mapping_data = NULL;
 	purple_network_listen_cancel(listen_data);
@@ -287,15 +372,16 @@
 purple_network_finish_pmp_map_cb(gpointer data)
 {
 	PurpleNetworkListenData *listen_data;
-	gint *key = g_new(gint, 1);
-	gint *value = g_new(gint, 1);
+	gint key;
+	gint value;
 
 	listen_data = data;
+	listen_data->timer = 0;
 
 	/* add port mapping to hash table */
-	*key = purple_network_get_port_from_fd(listen_data->listenfd);
-	*value = listen_data->socket_type;
-	g_hash_table_insert(nat_pmp_port_mappings, key, value);
+	key = purple_network_get_port_from_fd(listen_data->listenfd);
+	value = listen_data->socket_type;
+	g_hash_table_insert(nat_pmp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
 
 	if (listen_data->cb)
 		listen_data->cb(listen_data->listenfd, listen_data->cb_data);
@@ -305,14 +391,9 @@
 	return FALSE;
 }
 
-static gboolean listen_map_external = TRUE;
-void purple_network_listen_map_external(gboolean map_external)
-{
-	listen_map_external = map_external;
-}
-
 static PurpleNetworkListenData *
-purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
+purple_network_do_listen(unsigned short port, int socket_family, int socket_type, gboolean map_external,
+                             PurpleNetworkListenCallback cb, gpointer cb_data)
 {
 	int listenfd = -1;
 	int flags;
@@ -330,7 +411,7 @@
 	g_snprintf(serv, sizeof(serv), "%hu", port);
 	memset(&hints, 0, sizeof(struct addrinfo));
 	hints.ai_flags = AI_PASSIVE;
-	hints.ai_family = AF_UNSPEC;
+	hints.ai_family = socket_family;
 	hints.ai_socktype = socket_type;
 	errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
 	if (errnum != 0) {
@@ -354,7 +435,7 @@
 		if (listenfd < 0)
 			continue;
 		if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
-			purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno));
+			purple_debug_warning("network", "setsockopt(SO_REUSEADDR): %s\n", g_strerror(errno));
 		if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
 			break; /* success */
 		/* XXX - It is unclear to me (datallah) whether we need to be
@@ -369,6 +450,13 @@
 #else
 	struct sockaddr_in sockin;
 
+	if (socket_family != AF_INET && socket_family != AF_UNSPEC) {
+		purple_debug_warning("network", "Address family %d only "
+		                     "supported when built with getaddrinfo() "
+		                     "support\n", socket_family);
+		return NULL;
+	}
+
 	if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) {
 		purple_debug_warning("network", "socket: %s\n", g_strerror(errno));
 		return NULL;
@@ -410,11 +498,12 @@
 	listen_data->cb_data = cb_data;
 	listen_data->socket_type = socket_type;
 
-	if (!listen_map_external || !purple_prefs_get_bool("/purple/network/map_ports"))
+	if (!purple_socket_speaks_ipv4(listenfd) || !map_external ||
+			!purple_prefs_get_bool("/purple/network/map_ports"))
 	{
 		purple_debug_info("network", "Skipping external port mapping.\n");
 		/* The pmp_map_cb does what we want to do */
-		purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
+		listen_data->timer = purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
 	}
 	/* Attempt a NAT-PMP Mapping, which will return immediately */
 	else if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
@@ -422,7 +511,7 @@
 	{
 		purple_debug_info("network", "Created NAT-PMP mapping on port %i\n", actual_port);
 		/* We want to return listen_data now, and on the next run loop trigger the cb and destroy listen_data */
-		purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
+		listen_data->timer = purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
 	}
 	else
 	{
@@ -437,17 +526,21 @@
 }
 
 PurpleNetworkListenData *
-purple_network_listen(unsigned short port, int socket_type,
-		PurpleNetworkListenCallback cb, gpointer cb_data)
+purple_network_listen(unsigned short port, int socket_family, int socket_type,
+                             gboolean map_external, PurpleNetworkListenCallback cb,
+                             gpointer cb_data)
 {
 	g_return_val_if_fail(port != 0, NULL);
 
-	return purple_network_do_listen(port, socket_type, cb, cb_data);
+	return purple_network_do_listen(port, socket_family, socket_type, map_external,
+	                                cb, cb_data);
 }
 
 PurpleNetworkListenData *
 purple_network_listen_range(unsigned short start, unsigned short end,
-		int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
+                                   int socket_family, int socket_type, gboolean map_external,
+                                   PurpleNetworkListenCallback cb,
+                                   gpointer cb_data)
 {
 	PurpleNetworkListenData *ret = NULL;
 
@@ -460,7 +553,7 @@
 	}
 
 	for (; start <= end; start++) {
-		ret = purple_network_do_listen(start, socket_type, cb, cb_data);
+		ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, map_external, cb, cb_data);
 		if (ret != NULL)
 			break;
 	}
@@ -473,6 +566,9 @@
 	if (listen_data->mapping_data != NULL)
 		purple_upnp_cancel_port_mapping(listen_data->mapping_data);
 
+	if (listen_data->timer > 0)
+		purple_timeout_remove(listen_data->timer);
+
 	g_free(listen_data);
 }
 
@@ -577,7 +673,7 @@
 
 static gboolean _print_debug_msg(gpointer data) {
 	gchar *msg = data;
-	purple_debug_warning("network", msg);
+	purple_debug_warning("network", "%s", msg);
 	g_free(msg);
 	return FALSE;
 }
@@ -715,8 +811,20 @@
 			purple_debug_warning("network", "NetworkManager not active. Assuming connection exists.\n");
 	}
 
-	if (nm_state == NM_STATE_UNKNOWN || nm_state == NM_STATE_CONNECTED)
-		return TRUE;
+	switch (nm_state)
+	{
+		case NM_STATE_UNKNOWN:
+#if NM_CHECK_VERSION(0,8,992)
+		case NM_STATE_CONNECTED_LOCAL:
+		case NM_STATE_CONNECTED_SITE:
+		case NM_STATE_CONNECTED_GLOBAL:
+#else
+		case NM_STATE_CONNECTED:
+#endif
+			return TRUE;
+		default:
+			break;
+	}
 
 	return FALSE;
 
@@ -749,7 +857,13 @@
 
 	switch(state)
 	{
+#if NM_CHECK_VERSION(0,8,992)
+		case NM_STATE_CONNECTED_LOCAL:
+		case NM_STATE_CONNECTED_SITE:
+		case NM_STATE_CONNECTED_GLOBAL:
+#else
 		case NM_STATE_CONNECTED:
+#endif
 			/* Call res_init in case DNS servers have changed */
 			res_init();
 			/* update STUN IP in case we it changed (theoretically we could
@@ -759,13 +873,16 @@
 				purple_prefs_get_string("/purple/network/stun_server"));
 			purple_network_set_turn_server(
 				purple_prefs_get_string("/purple/network/turn_server"));
-			
+
 			if (ui_ops != NULL && ui_ops->network_connected != NULL)
 				ui_ops->network_connected();
 			break;
 		case NM_STATE_ASLEEP:
 		case NM_STATE_CONNECTING:
 		case NM_STATE_DISCONNECTED:
+#if NM_CHECK_VERSION(0,8,992)
+		case NM_STATE_DISCONNECTING:
+#endif
 			if (prev != NM_STATE_CONNECTED && prev != NM_STATE_UNKNOWN)
 				break;
 			if (ui_ops != NULL && ui_ops->network_disconnected != NULL)
@@ -822,10 +939,10 @@
 #endif
 
 static void
-purple_network_ip_lookup_cb(GSList *hosts, gpointer data, 
+purple_network_ip_lookup_cb(GSList *hosts, gpointer data,
 	const char *error_message)
 {
-	const gchar **ip = (const gchar **) data; 
+	const gchar **ip = (const gchar **) data;
 
 	if (error_message) {
 		purple_debug_error("network", "lookup of IP address failed: %s\n",
@@ -835,14 +952,14 @@
 	}
 
 	if (hosts && g_slist_next(hosts)) {
-		struct sockaddr *addr = g_slist_next(hosts)->data; 
+		struct sockaddr *addr = g_slist_next(hosts)->data;
 		char dst[INET6_ADDRSTRLEN];
-		
+
 		if (addr->sa_family == AF_INET6) {
-			inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr, 
+			inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr,
 				dst, sizeof(dst));
 		} else {
-			inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr, 
+			inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr,
 				dst, sizeof(dst));
 		}
 
@@ -864,10 +981,10 @@
 	if (stun_server && stun_server[0] != '\0') {
 		if (purple_network_is_available()) {
 			purple_debug_info("network", "running DNS query for STUN server\n");
-			purple_dnsquery_a(stun_server, 3478, purple_network_ip_lookup_cb,
+			purple_dnsquery_a(NULL, stun_server, 3478, purple_network_ip_lookup_cb,
 				&stun_ip);
 		} else {
-			purple_debug_info("network", 
+			purple_debug_info("network",
 				"network is unavailable, don't try to update STUN IP");
 		}
 	} else if (stun_ip) {
@@ -882,11 +999,11 @@
 	if (turn_server && turn_server[0] != '\0') {
 		if (purple_network_is_available()) {
 			purple_debug_info("network", "running DNS query for TURN server\n");
-			purple_dnsquery_a(turn_server, 
-				purple_prefs_get_int("/purple/network/turn_port"), 
+			purple_dnsquery_a(NULL, turn_server,
+				purple_prefs_get_int("/purple/network/turn_port"),
 				purple_network_ip_lookup_cb, &turn_ip);
 		} else {
-			purple_debug_info("network", 
+			purple_debug_info("network",
 				"network is unavailable, don't try to update TURN IP");
 		}
 	} else if (turn_ip) {
@@ -929,44 +1046,42 @@
 purple_network_upnp_mapping_remove(gpointer key, gpointer value,
 	gpointer user_data)
 {
-	gint port = (gint) *((gint *) key);
-	gint protocol = (gint) *((gint *) value);
+	gint port = GPOINTER_TO_INT(key);
+	gint protocol = GPOINTER_TO_INT(value);
 	purple_debug_info("network", "removing UPnP port mapping for port %d\n",
 		port);
-	purple_upnp_remove_port_mapping(port, 
-		protocol == SOCK_STREAM ? "TCP" : "UDP", 
+	purple_upnp_remove_port_mapping(port,
+		protocol == SOCK_STREAM ? "TCP" : "UDP",
 		purple_network_upnp_mapping_remove_cb, NULL);
-	g_hash_table_remove(upnp_port_mappings, key);
+	g_hash_table_remove(upnp_port_mappings, GINT_TO_POINTER(port));
 }
 
 static void
 purple_network_nat_pmp_mapping_remove(gpointer key, gpointer value,
 	gpointer user_data)
 {
-	gint port = (gint) *((gint *) key);
-	gint protocol = (gint) *((gint *) value);
+	gint port = GPOINTER_TO_INT(key);
+	gint protocol = GPOINTER_TO_INT(value);
 	purple_debug_info("network", "removing NAT-PMP port mapping for port %d\n",
 		port);
 	purple_pmp_destroy_map(
-		protocol == SOCK_STREAM ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP, 
+		protocol == SOCK_STREAM ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP,
 		port);
-	g_hash_table_remove(nat_pmp_port_mappings, key);
+	g_hash_table_remove(nat_pmp_port_mappings, GINT_TO_POINTER(port));
 }
 
 void
 purple_network_remove_port_mapping(gint fd)
 {
 	int port = purple_network_get_port_from_fd(fd);
-	gint *protocol = g_hash_table_lookup(upnp_port_mappings, &port);
+	gint protocol = GPOINTER_TO_INT(g_hash_table_lookup(upnp_port_mappings, GINT_TO_POINTER(port)));
 
 	if (protocol) {
-		purple_network_upnp_mapping_remove(&port, protocol, NULL);
-		g_hash_table_remove(upnp_port_mappings, protocol);
+		purple_network_upnp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
 	} else {
-		protocol = g_hash_table_lookup(nat_pmp_port_mappings, &port);
+		protocol = GPOINTER_TO_INT(g_hash_table_lookup(nat_pmp_port_mappings, GINT_TO_POINTER(port)));
 		if (protocol) {
-			purple_network_nat_pmp_mapping_remove(&port, protocol, NULL);
-			g_hash_table_remove(nat_pmp_port_mappings, protocol);
+			purple_network_nat_pmp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
 		}
 	}
 }
@@ -1024,6 +1139,7 @@
 	purple_prefs_add_string("/purple/network/stun_server", "");
 	purple_prefs_add_string("/purple/network/turn_server", "");
 	purple_prefs_add_int   ("/purple/network/turn_port", 3478);
+	purple_prefs_add_int	 ("/purple/network/turn_port_tcp", 3478);
 	purple_prefs_add_string("/purple/network/turn_username", "");
 	purple_prefs_add_string("/purple/network/turn_password", "");
 	purple_prefs_add_bool  ("/purple/network/auto_ip", TRUE);
@@ -1045,9 +1161,14 @@
 		                                     NM_DBUS_SERVICE,
 		                                     NM_DBUS_PATH,
 		                                     NM_DBUS_INTERFACE);
+		/* NM 0.6 signal */
 		dbus_g_proxy_add_signal(nm_proxy, "StateChange", G_TYPE_UINT, G_TYPE_INVALID);
 		dbus_g_proxy_connect_signal(nm_proxy, "StateChange",
 		                            G_CALLBACK(nm_state_change_cb), NULL, NULL);
+		/* NM 0.7 and later signal */
+		dbus_g_proxy_add_signal(nm_proxy, "StateChanged", G_TYPE_UINT, G_TYPE_INVALID);
+		dbus_g_proxy_connect_signal(nm_proxy, "StateChanged",
+		                            G_CALLBACK(nm_state_change_cb), NULL, NULL);
 
 		dbus_proxy = dbus_g_proxy_new_for_name(nm_conn,
 		                                       DBUS_SERVICE_DBUS,
@@ -1064,16 +1185,14 @@
 
 	purple_pmp_init();
 	purple_upnp_init();
-	
+
 	purple_network_set_stun_server(
 		purple_prefs_get_string("/purple/network/stun_server"));
 	purple_network_set_turn_server(
 		purple_prefs_get_string("/purple/network/turn_server"));
 
-	upnp_port_mappings = 
-		g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
-	nat_pmp_port_mappings =
-		g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free);
+	upnp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
+	nat_pmp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
 }
 
 
@@ -1084,6 +1203,7 @@
 #ifdef HAVE_NETWORKMANAGER
 	if (nm_proxy) {
 		dbus_g_proxy_disconnect_signal(nm_proxy, "StateChange", G_CALLBACK(nm_state_change_cb), NULL);
+		dbus_g_proxy_disconnect_signal(nm_proxy, "StateChanged", G_CALLBACK(nm_state_change_cb), NULL);
 		g_object_unref(G_OBJECT(nm_proxy));
 	}
 	if (dbus_proxy) {
@@ -1117,13 +1237,13 @@
 #endif
 	purple_signal_unregister(purple_network_get_handle(),
 							 "network-configuration-changed");
-	
+
 	if (stun_ip)
 		g_free(stun_ip);
 
 	g_hash_table_destroy(upnp_port_mappings);
 	g_hash_table_destroy(nat_pmp_port_mappings);
 
-	/* TODO: clean up remaining port mappings, note calling 
+	/* TODO: clean up remaining port mappings, note calling
 	 purple_upnp_remove_port_mapping from here doesn't quite work... */
 }
--- a/libpurple/network.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/network.h	Wed Jun 13 19:30:27 2012 -0400
@@ -26,9 +26,9 @@
 #ifndef _PURPLE_NETWORK_H_
 #define _PURPLE_NETWORK_H_
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <glib.h>
+
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Network API                                                     */
@@ -88,6 +88,16 @@
 const char *purple_network_get_local_system_ip(int fd);
 
 /**
+ * Returns all IP addresses of the local system.
+ *
+ * @note The caller must free this list.  If libpurple was built with
+ *       support for it, this function also enumerates IPv6 addresses.
+ *
+ * @return A list of local IP addresses.
+ */
+GList *purple_network_get_all_local_system_ips(void);
+
+/**
  * Returns the IP address that should be used anywhere a
  * public IP addresses is needed (listening for an incoming
  * file transfer, etc).
@@ -107,18 +117,6 @@
 const char *purple_network_get_my_ip(int fd);
 
 /**
- * Should calls to purple_network_listen() and purple_network_listen_range()
- * map the port externally using NAT-PMP or UPnP?
- * The default value is TRUE
- *
- * @param map_external Should the open port be mapped externally?
- * @deprecated In 3.0.0 a boolean will be added to the above functions to
- *             perform the same function.
- * @since 2.3.0
- */
-void purple_network_listen_map_external(gboolean map_external);
-
-/**
  * Attempts to open a listening port ONLY on the specified port number.
  * You probably want to use purple_network_listen_range() instead of this.
  * This function is useful, for example, if you wanted to write a telnet
@@ -127,13 +125,26 @@
  *
  * This opens a listening port. The caller will want to set up a watcher
  * of type PURPLE_INPUT_READ on the fd returned in cb. It will probably call
- * accept in the watcher callback, and then possibly remove the watcher and close
- * the listening socket, and add a new watcher on the new socket accept
+ * accept in the watcher callback, and then possibly remove the watcher and
+ * close the listening socket, and add a new watcher on the new socket accept
  * returned.
  *
+ * Libpurple does not currently do any port mapping (stateful firewall hole
+ * poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
+ * addresses, a mapping is done).
+ *
  * @param port The port number to bind to.  Must be greater than 0.
+ * @param socket_family The protocol family of the socket.  This should be
+ *                      AF_INET for IPv4 or AF_INET6 for IPv6.  IPv6 sockets
+ *                      may or may not be able to accept IPv4 connections
+ *                      based on the system configuration (use
+ *                      purple_socket_speaks_ipv4 to check).  If an IPv6
+ *                      socket doesn't accept V4-mapped addresses, you will
+ *                      need a second listener to support both v4 and v6.
  * @param socket_type The type of socket to open for listening.
  *   This will be either SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
+ * @param map_external Should the open port be mapped externally using
+ *           NAT-PNP or UPnP?  (default should be TRUE)
  * @param cb The callback to be invoked when the port to listen on is available.
  *           The file descriptor of the listening socket will be specified in
  *           this callback, or -1 if no socket could be established.
@@ -144,7 +155,8 @@
  *         socket to listen on.
  */
 PurpleNetworkListenData *purple_network_listen(unsigned short port,
-		int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data);
+	int socket_family, int socket_type, gboolean map_external,
+	PurpleNetworkListenCallback cb, gpointer cb_data);
 
 /**
  * Opens a listening port selected from a range of ports.  The range of
@@ -160,13 +172,26 @@
  * the listening socket, and add a new watcher on the new socket accept
  * returned.
  *
+ * Libpurple does not currently do any port mapping (stateful firewall hole
+ * poking) for IPv6-only listeners (if an IPv6 socket supports v4-mapped
+ * addresses, a mapping is done).
+ *
  * @param start The port number to bind to, or 0 to pick a random port.
  *              Users are allowed to override this arg in prefs.
  * @param end The highest possible port in the range of ports to listen on,
  *            or 0 to pick a random port.  Users are allowed to override this
  *            arg in prefs.
+ * @param socket_family The protocol family of the socket.  This should be
+ *                      AF_INET for IPv4 or AF_INET6 for IPv6.  IPv6 sockets
+ *                      may or may not be able to accept IPv4 connections
+ *                      based on the system configuration (use
+ *                      purple_socket_speaks_ipv4 to check).  If an IPv6
+ *                      socket doesn't accept V4-mapped addresses, you will
+ *                      need a second listener to support both v4 and v6.
  * @param socket_type The type of socket to open for listening.
  *   This will be either SOCK_STREAM for TCP or SOCK_DGRAM for UDP.
+ * @param map_external Should the open port be mapped externally using
+ *           NAT-PNP or UPnP?  (default should be TRUE)
  * @param cb The callback to be invoked when the port to listen on is available.
  *           The file descriptor of the listening socket will be specified in
  *           this callback, or -1 if no socket could be established.
@@ -176,16 +201,17 @@
  *         the pending listener, or NULL if unable to obtain a local
  *         socket to listen on.
  */
-PurpleNetworkListenData *purple_network_listen_range(unsigned short start,
-		unsigned short end, int socket_type,
-		PurpleNetworkListenCallback cb, gpointer cb_data);
+PurpleNetworkListenData *purple_network_listen_range(
+	unsigned short start, unsigned short end, int socket_family,
+	int socket_type, gboolean map_external,
+	PurpleNetworkListenCallback cb, gpointer cb_data);
 
 /**
  * This can be used to cancel any in-progress listener connection
  * by passing in the return value from either purple_network_listen()
  * or purple_network_listen_range().
  *
- * @param listen_data This listener attempt will be canceled and
+ * @param listen_data This listener attempt will be cancelled and
  *        the struct will be freed.
  */
 void purple_network_listen_cancel(PurpleNetworkListenData *listen_data);
@@ -213,8 +239,6 @@
  * This is what backs the --force-online command line argument in Pidgin,
  * for example.  This is useful for offline testing, especially when
  * combined with nullprpl.
- *
- * @since 2.6.0
  */
 void purple_network_force_online(void);
 
@@ -225,47 +249,42 @@
  */
 void *purple_network_get_handle(void);
 
-/**	
+/**
  * Update the STUN server IP given the host name
  * Will result in a DNS query being executed asynchronous
- * 
+ *
  * @param stun_server The host name of the STUN server to set
- * @since 2.6.0
  */
 void purple_network_set_stun_server(const gchar *stun_server);
-	
+
 /**
  * Get the IP address of the STUN server as a string representation
  *
  * @return the IP address
- * @since 2.6.0
  */
 const gchar *purple_network_get_stun_ip(void);
-	
-/**	
+
+/**
  * Update the TURN server IP given the host name
  * Will result in a DNS query being executed asynchronous
- * 
- * @param turn_server The host name of the STUN server to set
- * @since 2.6.0
+ *
+ * @param turn_server The host name of the TURN server to set
  */
 void purple_network_set_turn_server(const gchar *turn_server);
-	
+
 /**
- * Get the IP address of the STUN server as a string representation
+ * Get the IP address of the TURN server as a string representation
  *
  * @return the IP address
- * @since 2.6.0
  */
 const gchar *purple_network_get_turn_ip(void);
-		
+
 /**
  * Remove a port mapping (UPnP or NAT-PMP) associated with listening socket
  *
  * @param fd Socket to remove the port mapping for
- * @since 2.6.0
  */
-void purple_network_remove_port_mapping(gint fd);	
+void purple_network_remove_port_mapping(gint fd);
 
 /**
  * Convert a UTF-8 domain name to ASCII in accordance with the IDNA
@@ -282,7 +301,6 @@
  *                The caller is responsible for freeing this.
  * @returns       0 on success, -1 if the out is NULL, or an error code
  *                that currently corresponds to the Idna_rc enum in libidn.
- * @since 2.6.0
  */
 int purple_network_convert_idn_to_ascii(const gchar *in, gchar **out);
 
@@ -298,8 +316,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_NETWORK_H_ */
--- a/libpurple/notify.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/notify.c	Wed Jun 13 19:30:27 2012 -0400
@@ -53,7 +53,17 @@
 
 struct _PurpleNotifyUserInfo
 {
-	GList *user_info_entries;
+	GQueue entries;
+};
+
+/**
+ * Single column of a search result.
+ */
+struct _PurpleNotifySearchColumn
+{
+	char *title;           /**< Title of the column. */
+	gboolean visible;      /**< Should the column be visible to the user. Defaults to TRUE. */
+
 };
 
 void *
@@ -363,42 +373,31 @@
 
 	sc = g_new0(PurpleNotifySearchColumn, 1);
 	sc->title = g_strdup(title);
+	sc->visible = TRUE;
 
 	return sc;
 }
 
-guint
-purple_notify_searchresults_get_columns_count(PurpleNotifySearchResults *results)
+const char *purple_notify_searchresult_column_get_title(const PurpleNotifySearchColumn *column)
 {
-	g_return_val_if_fail(results != NULL, 0);
-
-	return g_list_length(results->columns);
-}
-
-guint
-purple_notify_searchresults_get_rows_count(PurpleNotifySearchResults *results)
-{
-	g_return_val_if_fail(results != NULL, 0);
-
-	return g_list_length(results->rows);
+	g_return_val_if_fail(column != NULL, NULL);
+	
+	return column->title;
 }
 
-char *
-purple_notify_searchresults_column_get_title(PurpleNotifySearchResults *results,
-										   unsigned int column_id)
+void purple_notify_searchresult_column_set_visible(PurpleNotifySearchColumn *column, gboolean visible)
 {
-	g_return_val_if_fail(results != NULL, NULL);
+	g_return_if_fail(column != NULL);
 
-	return ((PurpleNotifySearchColumn *)g_list_nth_data(results->columns, column_id))->title;
+	column->visible = visible;
 }
 
-GList *
-purple_notify_searchresults_row_get(PurpleNotifySearchResults *results,
-								  unsigned int row_id)
+gboolean
+purple_notify_searchresult_column_is_visible(const PurpleNotifySearchColumn *column)
 {
-	g_return_val_if_fail(results != NULL, NULL);
+	g_return_val_if_fail(column != NULL, FALSE);
 
-	return g_list_nth_data(results->rows, row_id);
+	return column->visible;
 }
 
 void *
@@ -472,7 +471,7 @@
 
 	user_info = g_new0(PurpleNotifyUserInfo, 1);
 	PURPLE_DBUS_REGISTER_POINTER(user_info, PurpleNotifyUserInfo);
-	user_info->user_info_entries = NULL;
+	g_queue_init(&user_info->entries);
 
 	return user_info;
 }
@@ -482,23 +481,23 @@
 {
 	GList *l;
 
-	for (l = user_info->user_info_entries; l != NULL; l = l->next) {
+	for (l = user_info->entries.head; l != NULL; l = l->next) {
 		PurpleNotifyUserInfoEntry *user_info_entry = l->data;
 
 		purple_notify_user_info_entry_destroy(user_info_entry);
 	}
 
-	g_list_free(user_info->user_info_entries);
+	g_queue_clear(&user_info->entries);
 	PURPLE_DBUS_UNREGISTER_POINTER(user_info);
 	g_free(user_info);
 }
 
-GList *
+GQueue *
 purple_notify_user_info_get_entries(PurpleNotifyUserInfo *user_info)
 {
 	g_return_val_if_fail(user_info != NULL, NULL);
 
-	return user_info->user_info_entries;
+	return &user_info->entries;
 }
 
 char *
@@ -509,7 +508,7 @@
 
 	text = g_string_new("");
 
-	for (l = user_info->user_info_entries; l != NULL; l = l->next) {
+	for (l = user_info->entries.head; l != NULL; l = l->next) {
 		PurpleNotifyUserInfoEntry *user_info_entry = l->data;
 		/* Add a newline before a section header */
 		if (user_info_entry->type == PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER)
@@ -593,21 +592,41 @@
 }
 
 void
-purple_notify_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
+purple_notify_user_info_add_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
 {
 	PurpleNotifyUserInfoEntry *entry;
 
 	entry = purple_notify_user_info_entry_new(label, value);
-	user_info->user_info_entries = g_list_append(user_info->user_info_entries, entry);
+	g_queue_push_tail(&user_info->entries, entry);
 }
 
 void
-purple_notify_user_info_prepend_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
+purple_notify_user_info_add_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
+{
+	gchar *escaped;
+
+	escaped = g_markup_escape_text(value, -1);
+	purple_notify_user_info_add_pair_html(user_info, label, escaped);
+	g_free(escaped);
+}
+
+void
+purple_notify_user_info_prepend_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
 {
 	PurpleNotifyUserInfoEntry *entry;
 
 	entry = purple_notify_user_info_entry_new(label, value);
-	user_info->user_info_entries = g_list_prepend(user_info->user_info_entries, entry);
+	g_queue_push_head(&user_info->entries, entry);
+}
+
+void
+purple_notify_user_info_prepend_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value)
+{
+	gchar *escaped;
+
+	escaped = g_markup_escape_text(value, -1);
+	purple_notify_user_info_prepend_pair_html(user_info, label, escaped);
+	g_free(escaped);
 }
 
 void
@@ -616,7 +635,7 @@
 	g_return_if_fail(user_info != NULL);
 	g_return_if_fail(entry != NULL);
 
-	user_info->user_info_entries = g_list_remove(user_info->user_info_entries, entry);
+	g_queue_remove(&user_info->entries, entry);
 }
 
 void
@@ -627,7 +646,7 @@
 	entry = purple_notify_user_info_entry_new(label, NULL);
 	entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER;
 
-	user_info->user_info_entries = g_list_append(user_info->user_info_entries, entry);
+	g_queue_push_tail(&user_info->entries, entry);
 }
 
 void
@@ -638,7 +657,7 @@
 	entry = purple_notify_user_info_entry_new(label, NULL);
 	entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER;
 
-	user_info->user_info_entries = g_list_prepend(user_info->user_info_entries, entry);
+	g_queue_push_head(&user_info->entries, entry);
 }
 
 void
@@ -649,7 +668,7 @@
 	entry = purple_notify_user_info_entry_new(NULL, NULL);
 	entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK;
 
-	user_info->user_info_entries = g_list_append(user_info->user_info_entries, entry);
+	g_queue_push_tail(&user_info->entries, entry);
 }
 
 void
@@ -660,17 +679,17 @@
 	entry = purple_notify_user_info_entry_new(NULL, NULL);
 	entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK;
 
-	user_info->user_info_entries = g_list_prepend(user_info->user_info_entries, entry);
+	g_queue_push_head(&user_info->entries, entry);
 }
 
 void
 purple_notify_user_info_remove_last_item(PurpleNotifyUserInfo *user_info)
 {
-	GList *last = g_list_last(user_info->user_info_entries);
-	if (last) {
-		purple_notify_user_info_entry_destroy(last->data);
-		user_info->user_info_entries = g_list_delete_link(user_info->user_info_entries, last);
-	}
+	PurpleNotifyUserInfoEntry *entry;
+
+	entry = g_queue_pop_tail(&user_info->entries);
+	if (entry)
+		purple_notify_user_info_entry_destroy(entry);
 }
 
 void *
--- a/libpurple/notify.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/notify.h	Wed Jun 13 19:30:27 2012 -0400
@@ -32,10 +32,13 @@
 #include <glib.h>
 
 typedef struct _PurpleNotifyUserInfoEntry	PurpleNotifyUserInfoEntry;
-typedef struct _PurpleNotifyUserInfo	PurpleNotifyUserInfo;
+typedef struct _PurpleNotifyUserInfo		PurpleNotifyUserInfo;
+/** @copydoc _PurpleNotifySearchColumn */
+typedef struct _PurpleNotifySearchColumn	PurpleNotifySearchColumn;
 
 #include "connection.h"
 
+
 /**
  * Notification close callbacks.
  */
@@ -106,14 +109,6 @@
 	PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER
 } PurpleNotifyUserInfoEntryType;
 
-/**
- * Single column of a search result.
- */
-typedef struct
-{
-	char *title; /**< Title of the column. */
-
-} PurpleNotifySearchColumn;
 
 
 /**
@@ -180,9 +175,7 @@
 } PurpleNotifyUiOps;
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 
 /**************************************************************************/
@@ -266,7 +259,8 @@
 PurpleNotifySearchResults *purple_notify_searchresults_new(void);
 
 /**
- * Returns a newly created search result column object.
+ * Returns a newly created search result column object.  The column defaults
+ * to being visible.
  *
  * @param title Title of the column. NOTE: Title will get g_strdup()ed.
  *
@@ -275,6 +269,32 @@
 PurpleNotifySearchColumn *purple_notify_searchresults_column_new(const char *title);
 
 /**
+ * Returns the title of the column
+ *
+ * @param column The search column object.
+ *
+ * @return The title of the column
+ */
+const char *purple_notify_searchresult_column_get_title(const PurpleNotifySearchColumn *column);
+
+/**
+ * Sets whether or not a search result column is visible.
+ *
+ * @param column  The search column object.
+ * @param visible TRUE if visible, or FALSE if not.
+ */
+void purple_notify_searchresult_column_set_visible(PurpleNotifySearchColumn *column, gboolean visible);
+
+/**
+ * Returns whether or not a search result column is visible.
+ *
+ * @param column The search column object.
+ *
+ * @return TRUE if the search result column is visible. FALSE otherwise.
+ */
+gboolean purple_notify_searchresult_column_is_visible(const PurpleNotifySearchColumn *column);
+
+/**
  * Adds a new column to the search result object.
  *
  * @param results The result object to which the column will be added.
@@ -292,92 +312,6 @@
 void purple_notify_searchresults_row_add(PurpleNotifySearchResults *results,
 									   GList *row);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
-/**
- * Returns a number of the rows in the search results object.
- *
- * @deprecated This function will be removed in Pidgin 3.0.0 unless
- *             there is sufficient demand to keep it.  Using this
- *             function encourages looping through the results
- *             inefficiently.  Instead of using this function you
- *             should iterate through the results using a loop
- *             similar to this:
- *                for (l = results->rows; l != NULL; l = l->next)
- *             If you really need to get the number of rows you
- *             can use g_list_length(results->rows).
- *
- * @param results The search results object.
- *
- * @return Number of the result rows.
- */
-guint purple_notify_searchresults_get_rows_count(PurpleNotifySearchResults *results);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
-/**
- * Returns a number of the columns in the search results object.
- *
- * @deprecated This function will be removed in Pidgin 3.0.0 unless
- *             there is sufficient demand to keep it.  Using this
- *             function encourages looping through the columns
- *             inefficiently.  Instead of using this function you
- *             should iterate through the columns using a loop
- *             similar to this:
- *                for (l = results->columns; l != NULL; l = l->next)
- *             If you really need to get the number of columns you
- *             can use g_list_length(results->columns).
- *
- * @param results The search results object.
- *
- * @return Number of the columns.
- */
-guint purple_notify_searchresults_get_columns_count(PurpleNotifySearchResults *results);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
-/**
- * Returns a row of the results from the search results object.
- *
- * @deprecated This function will be removed in Pidgin 3.0.0 unless
- *             there is sufficient demand to keep it.  Using this
- *             function encourages looping through the results
- *             inefficiently.  Instead of using this function you
- *             should iterate through the results using a loop
- *             similar to this:
- *                for (l = results->rows; l != NULL; l = l->next)
- *             If you really need to get the data for a particular
- *             row you can use g_list_nth_data(results->rows, row_id).
- *
- * @param results The search results object.
- * @param row_id  Index of the row to be returned.
- *
- * @return Row of the results.
- */
-GList *purple_notify_searchresults_row_get(PurpleNotifySearchResults *results,
-										 unsigned int row_id);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
-/**
- * Returns a title of the search results object's column.
- *
- * @deprecated This function will be removed in Pidgin 3.0.0 unless
- *             there is sufficient demand to keep it.  Using this
- *             function encourages looping through the columns
- *             inefficiently.  Instead of using this function you
- *             should iterate through the name of a particular
- *             column you can use
- *             g_list_nth_data(results->columns, row_id).
- *
- * @param results   The search results object.
- * @param column_id Index of the column.
- *
- * @return Title of the column.
- */
-char *purple_notify_searchresults_column_get_title(PurpleNotifySearchResults *results,
-												 unsigned int column_id);
-#endif
-
 /*@}*/
 
 /**************************************************************************/
@@ -506,20 +440,20 @@
  * Retrieve the array of PurpleNotifyUserInfoEntry objects from a
  * PurpleNotifyUserInfo
  *
- * This GList may be manipulated directly with normal GList functions such
- * as g_list_insert(). Only PurpleNotifyUserInfoEntry are allowed in the
- * list.  If a PurpleNotifyUserInfoEntry item is added to the list, it
- * should not be g_free()'d by the caller; PurpleNotifyUserInfo will g_free
- * it when destroyed.
+ * This GQueue may be manipulated directly with normal GQueue functions such
+ * as g_queue_push_tail(). Only PurpleNotifyUserInfoEntry are allowed in the
+ * queue.  If a PurpleNotifyUserInfoEntry item is added to the queue, it
+ * should not be freed by the caller; PurpleNotifyUserInfo will free it when
+ * destroyed.
  *
  * To remove a PurpleNotifyUserInfoEntry, use
- * purple_notify_user_info_remove_entry(). Do not use the GList directly.
+ * purple_notify_user_info_remove_entry(). Do not use the GQueue directly.
  *
  * @param user_info  The PurpleNotifyUserInfo
  *
- * @constreturn A GList of PurpleNotifyUserInfoEntry objects
+ * @constreturn A GQueue of PurpleNotifyUserInfoEntry objects.
  */
-GList *purple_notify_user_info_get_entries(PurpleNotifyUserInfo *user_info);
+GQueue *purple_notify_user_info_get_entries(PurpleNotifyUserInfo *user_info);
 
 /**
  * Create a textual representation of a PurpleNotifyUserInfo, separating
@@ -540,26 +474,32 @@
  *                   a colon.  If NULL, value will be displayed without a
  *                   label.
  * @param value      The value, which might be displayed by a UI after
- *                   the label.  If NULL, label will still be displayed;
- *                   the UI should then treat label as independent and not
+ *                   the label.  This should be valid HTML.  If you want
+ *                   to insert plaintext then use
+ *                   purple_notify_user_info_add_pair_plaintext(), instead.
+ *                   If this is NULL the label will still be displayed;
+ *                   the UI should treat label as independent and not
  *                   include a colon if it would otherwise.
  */
-void purple_notify_user_info_add_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
+void purple_notify_user_info_add_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
 
 /**
- * Prepend a label/value pair to a PurpleNotifyUserInfo object
- *
- * @param user_info  The PurpleNotifyUserInfo
- * @param label      A label, which for example might be displayed by a
- *                   UI with a colon after it ("Status:"). Do not include
- *                   a colon.  If NULL, value will be displayed without a
- *                   label.
- * @param value      The value, which might be displayed by a UI after
- *                   the label.  If NULL, label will still be displayed;
- *                   the UI should then treat label as independent and not
- *                   include a colon if it would otherwise.
+ * Like purple_notify_user_info_add_pair_html, but value should be plaintext
+ * and will be escaped using g_markup_escape_text().
  */
-void purple_notify_user_info_prepend_pair(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
+void purple_notify_user_info_add_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
+
+/**
+ * Like purple_notify_user_info_add_pair_html, but the pair is inserted
+ * at the beginning of the list.
+ */
+void purple_notify_user_info_prepend_pair_html(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
+
+/**
+ * Like purple_notify_user_info_prepend_pair_html, but value should be plaintext
+ * and will be escaped using g_markup_escape_text().
+ */
+void purple_notify_user_info_prepend_pair_plaintext(PurpleNotifyUserInfo *user_info, const char *label, const char *value);
 
 #if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_NOTIFY_C_)
 /**
@@ -582,9 +522,11 @@
  *
  * If added to a PurpleNotifyUserInfo object, this should not be free()'d,
  * as PurpleNotifyUserInfo will do so when destroyed.
- * purple_notify_user_info_add_pair() and
- * purple_notify_user_info_prepend_pair() are convenience methods for
- * creating entries and adding them to a PurpleNotifyUserInfo.
+ * purple_notify_user_info_add_pair_html(),
+ * purple_notify_user_info_add_pair_plaintext(),
+ * purple_notify_user_info_prepend_pair_html() and
+ * purple_notify_user_info_prepend_pair_plaintext() are convenience
+ * methods for creating entries and adding them to a PurpleNotifyUserInfo.
  *
  * @param label  A label, which for example might be displayed by a UI
  *               with a colon after it ("Status:"). Do not include a
@@ -609,7 +551,6 @@
  * Prepend a section break.  A UI might display this as a horizontal line.
  *
  * @param user_info  The PurpleNotifyUserInfo
- * @since 2.5.0
  */
 void purple_notify_user_info_prepend_section_break(PurpleNotifyUserInfo *user_info);
 
@@ -628,7 +569,6 @@
  *
  * @param user_info  The PurpleNotifyUserInfo
  * @param label      The name of the section
- * @since 2.5.0
  */
 void purple_notify_user_info_prepend_section_header(PurpleNotifyUserInfo *user_info, const char *label);
 
@@ -792,8 +732,6 @@
 /*@}*/
 
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_NOTIFY_H_ */
--- a/libpurple/ntlm.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/ntlm.c	Wed Jun 13 19:30:27 2012 -0400
@@ -24,8 +24,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
-#include <glib.h>
-#include <stdlib.h>
 #include "internal.h"
 
 #include "util.h"
@@ -113,14 +111,16 @@
 gchar *
 purple_ntlm_gen_type1(const gchar *hostname, const gchar *domain)
 {
-	int hostnamelen;
-	int domainlen;
+	int hostnamelen,host_off;
+	int domainlen,dom_off;
 	unsigned char *msg;
 	struct type1_message *tmsg;
 	gchar *tmp;
 
 	hostnamelen = strlen(hostname);
 	domainlen = strlen(domain);
+	host_off = sizeof(struct type1_message);
+	dom_off = sizeof(struct type1_message) + hostnamelen;
 	msg = g_malloc0(sizeof(struct type1_message) + hostnamelen + domainlen);
 	tmsg = (struct type1_message*)msg;
 	tmsg->protocol[0] = 'N';
@@ -134,11 +134,11 @@
 	tmsg->type      = GUINT32_TO_LE(0x00000001);
 	tmsg->flags     = GUINT32_TO_LE(0x0000b203);
 	tmsg->dom_len1  = tmsg->dom_len2 = GUINT16_TO_LE(domainlen);
-	tmsg->dom_off   = GUINT32_TO_LE(sizeof(struct type1_message) + hostnamelen);
+	tmsg->dom_off   = GUINT32_TO_LE(dom_off);
 	tmsg->host_len1 = tmsg->host_len2 = GUINT16_TO_LE(hostnamelen);
-	tmsg->host_off  = GUINT32_TO_LE(sizeof(struct type1_message));
-	memcpy(msg + tmsg->host_off, hostname, hostnamelen);
-	memcpy(msg + tmsg->dom_off, domain, domainlen);
+	tmsg->host_off  = GUINT32_TO_LE(host_off);
+	memcpy(msg + host_off, hostname, hostnamelen);
+	memcpy(msg + dom_off, domain, domainlen);
 
 	tmp = purple_base64_encode(msg, sizeof(struct type1_message) + hostnamelen + domainlen);
 	g_free(msg);
@@ -154,9 +154,14 @@
 	static guint8 nonce[8];
 
 	tmsg = (struct type2_message*)purple_base64_decode(type2, &retlen);
-	memcpy(nonce, tmsg->nonce, 8);
-	if (flags != NULL)
-		*flags = GUINT16_FROM_LE(tmsg->flags);
+	if (tmsg != NULL && retlen >= (sizeof(struct type2_message) - 1)) {
+		memcpy(nonce, tmsg->nonce, 8);
+		if (flags != NULL)
+			*flags = GUINT16_FROM_LE(tmsg->flags);
+	} else {
+		purple_debug_error("ntlm", "Unable to parse type2 message - returning empty nonce.\n");
+		memset(nonce, 0, 8);
+	}
 	g_free(tmsg);
 
 	return nonce;
--- a/libpurple/ntlm.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/ntlm.h	Wed Jun 13 19:30:27 2012 -0400
@@ -27,9 +27,7 @@
 #ifndef _PURPLE_NTLM_H
 #define _PURPLE_NTLM_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**
  * Generates the base64 encoded type 1 message needed for NTLM authentication
@@ -66,8 +64,6 @@
  */
 gchar *purple_ntlm_gen_type3(const gchar *username, const gchar *passw, const gchar *hostname, const gchar *domain, const guint8 *nonce, guint32 *flags);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_NTLM_H */
--- a/libpurple/plugin.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugin.c	Wed Jun 13 19:30:27 2012 -0400
@@ -33,6 +33,7 @@
 #include "request.h"
 #include "signals.h"
 #include "util.h"
+#include "valgrind.h"
 #include "version.h"
 
 typedef struct
@@ -63,13 +64,6 @@
 static GList *plugins_to_disable = NULL;
 #endif
 
-static void (*probe_cb)(void *) = NULL;
-static void *probe_cb_data = NULL;
-static void (*load_cb)(PurplePlugin *, void *) = NULL;
-static void *load_cb_data = NULL;
-static void (*unload_cb)(PurplePlugin *, void *) = NULL;
-static void *unload_cb_data = NULL;
-
 #ifdef PURPLE_PLUGINS
 
 static gboolean
@@ -253,11 +247,7 @@
 		 *
 		 * G_MODULE_BIND_LOCAL was added in glib 2.3.3.
 		 */
-#if GLIB_CHECK_VERSION(2,3,3)
 		plugin->handle = g_module_open(filename, G_MODULE_BIND_LOCAL);
-#else
-		plugin->handle = g_module_open(filename, 0);
-#endif
 
 		if (plugin->handle == NULL)
 		{
@@ -286,11 +276,7 @@
 				purple_debug_error("plugins", "%s is not loadable: %s\n",
 						 plugin->path, plugin->error);
 			}
-#if GLIB_CHECK_VERSION(2,3,3)
 			plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
-#else
-			plugin->handle = g_module_open(filename, G_MODULE_BIND_LAZY);
-#endif
 
 			if (plugin->handle == NULL)
 			{
@@ -622,9 +608,6 @@
 
 	plugin->loaded = TRUE;
 
-	if (load_cb != NULL)
-		load_cb(plugin, load_cb_data);
-
 	purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
 
 	return TRUE;
@@ -752,9 +735,6 @@
 	g_free(plugin->error);
 	plugin->error = NULL;
 
-	if (unload_cb != NULL)
-		unload_cb(plugin, unload_cb_data);
-
 	purple_signal_emit(purple_plugins_get_handle(), "plugin-unload", plugin);
 
 	purple_prefs_disconnect_by_handle(plugin);
@@ -875,7 +855,7 @@
 		 * it keeps all the plugins open, meaning that valgrind is able to
 		 * resolve symbol names in leak traces from plugins.
 		 */
-		if (!g_getenv("PURPLE_LEAKCHECK_HELP"))
+		if (!g_getenv("PURPLE_LEAKCHECK_HELP") && !RUNNING_ON_VALGRIND)
 		{
 			if (plugin->handle != NULL)
 				g_module_close(plugin->handle);
@@ -1446,10 +1426,6 @@
 													(GCompareFunc)compare_prpl);
 		}
 	}
-
-	if (probe_cb != NULL)
-		probe_cb(probe_cb_data);
-
 #endif /* PURPLE_PLUGINS */
 }
 
@@ -1520,50 +1496,6 @@
 #endif
 }
 
-void
-purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data)
-{
-	probe_cb = func;
-	probe_cb_data = data;
-}
-
-void
-purple_plugins_unregister_probe_notify_cb(void (*func)(void *))
-{
-	probe_cb = NULL;
-	probe_cb_data = NULL;
-}
-
-void
-purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
-									 void *data)
-{
-	load_cb = func;
-	load_cb_data = data;
-}
-
-void
-purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *))
-{
-	load_cb = NULL;
-	load_cb_data = NULL;
-}
-
-void
-purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
-									   void *data)
-{
-	unload_cb = func;
-	unload_cb_data = data;
-}
-
-void
-purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *, void *))
-{
-	unload_cb = NULL;
-	unload_cb_data = NULL;
-}
-
 PurplePlugin *
 purple_plugins_find_with_name(const char *name)
 {
--- a/libpurple/plugin.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugin.h	Wed Jun 13 19:30:27 2012 -0400
@@ -239,9 +239,7 @@
 #endif
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Plugin API                                                      */
@@ -317,8 +315,6 @@
  * startup" by excluding said plugins from the list of plugins to save.  The
  * UI needs to call purple_plugins_save_loaded() after calling this for it
  * to have any effect.
- *
- * @since 2.3.0
  */
 void purple_plugin_disable(PurplePlugin *plugin);
 
@@ -515,8 +511,6 @@
  * Returns a list of plugin search paths.
  *
  * @constreturn A list of searched paths.
- *
- * @since 2.6.0
  */
 GList *purple_plugins_get_search_paths(void);
 
@@ -566,72 +560,6 @@
  */
 gboolean purple_plugins_enabled(void);
 
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Registers a function that will be called when probing is finished.
- *
- * @param func The callback function.
- * @param data Data to pass to the callback.
- * @deprecated If you need this, ask for a plugin-probe signal to be added.
- */
-void purple_plugins_register_probe_notify_cb(void (*func)(void *), void *data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Unregisters a function that would be called when probing is finished.
- *
- * @param func The callback function.
- * @deprecated If you need this, ask for a plugin-probe signal to be added.
- */
-void purple_plugins_unregister_probe_notify_cb(void (*func)(void *));
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Registers a function that will be called when a plugin is loaded.
- *
- * @param func The callback function.
- * @param data Data to pass to the callback.
- * @deprecated Use the plugin-load signal instead.
- */
-void purple_plugins_register_load_notify_cb(void (*func)(PurplePlugin *, void *),
-										  void *data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Unregisters a function that would be called when a plugin is loaded.
- *
- * @param func The callback function.
- * @deprecated Use the plugin-load signal instead.
- */
-void purple_plugins_unregister_load_notify_cb(void (*func)(PurplePlugin *, void *));
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Registers a function that will be called when a plugin is unloaded.
- *
- * @param func The callback function.
- * @param data Data to pass to the callback.
- * @deprecated Use the plugin-unload signal instead.
- */
-void purple_plugins_register_unload_notify_cb(void (*func)(PurplePlugin *, void *),
-											void *data);
-#endif
-
-#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_PLUGIN_C_)
-/**
- * Unregisters a function that would be called when a plugin is unloaded.
- *
- * @param func The callback function.
- * @deprecated Use the plugin-unload signal instead.
- */
-void purple_plugins_unregister_unload_notify_cb(void (*func)(PurplePlugin *,
-														   void *));
-#endif
-
 /**
  * Finds a plugin with the specified name.
  *
@@ -733,8 +661,6 @@
  */
 void purple_plugin_action_free(PurplePluginAction *action);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_PLUGIN_H_ */
--- a/libpurple/pluginpref.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/pluginpref.h	Wed Jun 13 19:30:27 2012 -0400
@@ -50,9 +50,7 @@
 #include <glib.h>
 #include "prefs.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Plugin Preference API                                           */
@@ -262,8 +260,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_PLUGINPREF_H_ */
--- a/libpurple/plugins/Makefile.mingw	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/Makefile.mingw	Wed Jun 13 19:30:27 2012 -0400
@@ -55,7 +55,7 @@
 	$(MAKE) -C $(SSL_PLUGIN) -f $(MINGW_MAKEFILE) install
 	cp *.dll $(PURPLE_INSTALL_PLUGINS_DIR)
 
-.c.dll:
+%.dll: %.c $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H)
 	$(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@.o -c $<
 	$(CC) -shared $@.o $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@
 
--- a/libpurple/plugins/autoaccept.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/autoaccept.c	Wed Jun 13 19:30:27 2012 -0400
@@ -28,13 +28,7 @@
 
 /* System headers */
 #include <glib.h>
-#if GLIB_CHECK_VERSION(2,6,0)
-#	include <glib/gstdio.h>
-#else
-#	include <sys/types.h>
-#	include <sys/stat.h>
-#	define	g_mkdir mkdir
-#endif
+#include <glib/gstdio.h>
 
 /* Purple headers */
 #include <plugin.h>
@@ -49,9 +43,12 @@
 
 #define PREF_PREFIX		"/plugins/core/" PLUGIN_ID
 #define PREF_PATH		PREF_PREFIX "/path"
-#define PREF_STRANGER	PREF_PREFIX "/reject_stranger"
+#define PREF_STRANGER	PREF_PREFIX "/stranger"
 #define PREF_NOTIFY		PREF_PREFIX "/notify"
 #define PREF_NEWDIR     PREF_PREFIX "/newdir"
+#define PREF_ESCAPE     PREF_PREFIX "/escape"
+
+#define PREF_STRANGER_OLD PREF_PREFIX "/reject_stranger"
 
 typedef enum
 {
@@ -76,10 +73,10 @@
 auto_accept_complete_cb(PurpleXfer *xfer, PurpleXfer *my)
 {
 	if (xfer == my && purple_prefs_get_bool(PREF_NOTIFY) &&
-			!purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, xfer->who, xfer->account))
+			!purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, purple_xfer_get_remote_user(xfer), purple_xfer_get_account(xfer)))
 	{
 		char *message = g_strdup_printf(_("Autoaccepted file transfer of \"%s\" from \"%s\" completed."),
-					xfer->filename, xfer->who);
+					purple_xfer_get_filename(xfer), purple_xfer_get_remote_user(xfer));
 		purple_notify_info(NULL, _("Autoaccept complete"), message, NULL);
 		g_free(message);
 	}
@@ -94,25 +91,27 @@
 	char *filename;
 	char *dirname;
 
-	account = xfer->account;
-	node = PURPLE_BLIST_NODE(purple_find_buddy(account, xfer->who));
+    int accept_setting;
+
+	account = purple_xfer_get_account(xfer);
+	node = PURPLE_BLIST_NODE(purple_find_buddy(account, purple_xfer_get_remote_user(xfer)));
 
-	if (!node)
-	{
-		if (purple_prefs_get_bool(PREF_STRANGER))
-			xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL;
-		return;
+	/* If person is on buddy list, use the buddy setting; otherwise, use the
+	   stranger setting. */
+	if (node) {
+		node = purple_blist_node_get_parent(node);
+		g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(node));
+		accept_setting = purple_blist_node_get_int(node, "autoaccept");
+	} else {
+		accept_setting = purple_prefs_get_int(PREF_STRANGER);
 	}
 
-	node = purple_blist_node_get_parent(node);
-	g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(node));
-
-	pref = purple_prefs_get_string(PREF_PATH);
-	switch (purple_blist_node_get_int(node, "autoaccept"))
+	switch (accept_setting)
 	{
 		case FT_ASK:
 			break;
 		case FT_ACCEPT:
+            pref = purple_prefs_get_string(PREF_PATH);
 			if (ensure_path_exists(pref))
 			{
 				int count = 1;
@@ -122,7 +121,7 @@
 				gchar *ext;
 
 				if (purple_prefs_get_bool(PREF_NEWDIR))
-					dirname = g_build_filename(pref, purple_normalize(account, xfer->who), NULL);
+					dirname = g_build_filename(pref, purple_normalize(account, purple_xfer_get_remote_user(xfer)), NULL);
 				else
 					dirname = g_build_filename(pref, NULL);
 
@@ -132,7 +131,12 @@
 					break;
 				}
 
-				escape = purple_escape_filename(xfer->filename);
+				/* Escape filename (if escaping is turned on) */
+				if (purple_prefs_get_bool(PREF_ESCAPE)) {
+					escape = purple_escape_filename(purple_xfer_get_filename(xfer));
+				} else {
+					escape = purple_xfer_get_filename(xfer);
+				}
 				filename = g_build_filename(dirname, escape, NULL);
 
 				/* Split at the first dot, to avoid uniquifying "foo.tar.gz" to "foo.tar-2.gz" */
@@ -170,7 +174,7 @@
 								PURPLE_CALLBACK(auto_accept_complete_cb), xfer);
 			break;
 		case FT_REJECT:
-			xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL;
+			purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
 			break;
 	}
 }
@@ -193,7 +197,7 @@
 		node = purple_blist_node_get_parent(node);
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_CONTACT(node));
 
-	message = g_strdup_printf(_("When a file-transfer request arrives from %s"), 
+	message = g_strdup_printf(_("When a file-transfer request arrives from %s"),
 					purple_contact_get_alias((PurpleContact *)node));
 	purple_request_choice(plugin, _("Set Autoaccept Setting"), message,
 						NULL, purple_blist_node_get_int(node, "autoaccept"),
@@ -226,6 +230,17 @@
 static gboolean
 plugin_load(PurplePlugin *plugin)
 {
+	/* migrate the old pref (we should only care if the plugin is actually *used*) */
+	/*
+	 * TODO: We should eventually call purple_prefs_remove(PREFS_STRANGER_OLD)
+	 *       to clean up after ourselves, but we don't want to do it yet
+	 *       so that we don't break users who share a .purple directory
+	 *       between old libpurple clients and new libpurple clients.
+	 *                                             --Mark Doliner, 2011-01-03
+	 */
+	if(purple_prefs_get_bool(PREF_STRANGER_OLD))
+		purple_prefs_set_int(PREF_STRANGER, FT_REJECT);
+
 	purple_signal_connect(purple_xfers_get_handle(), "file-recv-request", plugin,
 						PURPLE_CALLBACK(file_recv_request_cb), plugin);
 	purple_signal_connect(purple_blist_get_handle(), "blist-node-extended-menu", plugin,
@@ -253,7 +268,12 @@
 	purple_plugin_pref_frame_add(frame, pref);
 
 	pref = purple_plugin_pref_new_with_name_and_label(PREF_STRANGER,
-					_("Automatically reject from users not in buddy list"));
+					_("When a file-transfer request arrives from a user who is\n"
+                      "*not* on your buddy list:"));
+	purple_plugin_pref_set_type(pref, PURPLE_PLUGIN_PREF_CHOICE);
+	purple_plugin_pref_add_choice(pref, _("Ask"), GINT_TO_POINTER(FT_ASK));
+	purple_plugin_pref_add_choice(pref, _("Auto Accept"), GINT_TO_POINTER(FT_ACCEPT));
+	purple_plugin_pref_add_choice(pref, _("Auto Reject"), GINT_TO_POINTER(FT_REJECT));
 	purple_plugin_pref_frame_add(frame, pref);
 
 	pref = purple_plugin_pref_new_with_name_and_label(PREF_NOTIFY,
@@ -265,6 +285,10 @@
 			_("Create a new directory for each user"));
 	purple_plugin_pref_frame_add(frame, pref);
 
+	pref = purple_plugin_pref_new_with_name_and_label(PREF_ESCAPE,
+			_("Escape the filenames"));
+	purple_plugin_pref_frame_add(frame, pref);
+
 	return frame;
 }
 
@@ -321,9 +345,10 @@
 	dirname = g_build_filename(purple_user_dir(), "autoaccept", NULL);
 	purple_prefs_add_none(PREF_PREFIX);
 	purple_prefs_add_string(PREF_PATH, dirname);
-	purple_prefs_add_bool(PREF_STRANGER, TRUE);
+	purple_prefs_add_int(PREF_STRANGER, FT_ASK);
 	purple_prefs_add_bool(PREF_NOTIFY, TRUE);
 	purple_prefs_add_bool(PREF_NEWDIR, TRUE);
+	purple_prefs_add_bool(PREF_ESCAPE, TRUE);
 	g_free(dirname);
 }
 
--- a/libpurple/plugins/codeinline.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/codeinline.c	Wed Jun 13 19:30:27 2012 -0400
@@ -62,7 +62,7 @@
 {
      PURPLE_PLUGIN_MAGIC,
      PURPLE_MAJOR_VERSION,
-     PURPLE_MINOR_VERSION,     
+     PURPLE_MINOR_VERSION,
      PURPLE_PLUGIN_STANDARD,
      NULL,
      0,
--- a/libpurple/plugins/filectl.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/filectl.c	Wed Jun 13 19:30:27 2012 -0400
@@ -40,12 +40,12 @@
 run_commands()
 {
 	struct stat finfo;
-	char filename[256];
+	char filename[MAXPATHLEN];
 	char buffer[1024];
 	char *command, *arg1, *arg2;
 	FILE *file;
 
-	sprintf(filename, "%s" G_DIR_SEPARATOR_S "control", purple_user_dir());
+	snprintf(filename, MAXPATHLEN, "%s" G_DIR_SEPARATOR_S "control", purple_user_dir());
 
 	file = g_fopen(filename, "r+");
 	while (fgets(buffer, sizeof(buffer), file)) {
@@ -144,9 +144,9 @@
 {
 	/* most of this was taken from Bash v2.04 by the FSF */
 	struct stat finfo;
-	char filename[256];
+	char filename[MAXPATHLEN];
 
-	sprintf(filename, "%s" G_DIR_SEPARATOR_S "control", purple_user_dir());
+	snprintf(filename, MAXPATHLEN, "%s" G_DIR_SEPARATOR_S "control", purple_user_dir());
 
 	if ((g_stat(filename, &finfo) == 0) && (finfo.st_size > 0))
 		run_commands();
@@ -160,9 +160,9 @@
 {
 	/* most of this was taken from Bash v2.04 by the FSF */
 	struct stat finfo;
-	char filename[256];
+	char filename[MAXPATHLEN];
 
-	sprintf(filename, "%s" G_DIR_SEPARATOR_S "control", purple_user_dir());
+	snprintf(filename, MAXPATHLEN, "%s" G_DIR_SEPARATOR_S "control", purple_user_dir());
 
 	if ((g_stat(filename, &finfo) == 0) && (finfo.st_size > 0))
 	{
--- a/libpurple/plugins/fortuneprofile.pl	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/fortuneprofile.pl	Wed Jun 13 19:30:27 2012 -0400
@@ -51,7 +51,7 @@
 	summary          => "Sets your AIM profile to a fortune (with a header and footer of your choice).",
 	description      => "Sets your AIM profile to a fortune (with a header and footer of your choice).",
 	author           => "Sean Egan <seanegan\@gmail.com>",
-	url              => "http://gaim.sf.net/",
+	url              => "http://pidgin.im/",
 
 	load             => "plugin_load"
 );
--- a/libpurple/plugins/idle.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/idle.c	Wed Jun 13 19:30:27 2012 -0400
@@ -104,15 +104,11 @@
 	PurpleAccount *acct = NULL;
 	GList *list, *iter;
 	int tm = purple_request_fields_get_integer(fields, "mins");
-	const char *prpl_id = NULL;
 
 	list = purple_accounts_get_all_active();
 	for(iter = list; iter; iter = iter->next) {
 		acct = (PurpleAccount *)(iter->data);
 
-		if(acct)
-			prpl_id = purple_account_get_protocol_id(acct);
-
 		if(acct && idleable_filter(acct)) {
 			purple_debug_misc("idle", "Idling %s.\n",
 					purple_account_get_username(acct));
--- a/libpurple/plugins/joinpart.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/joinpart.c	Wed Jun 13 19:30:27 2012 -0400
@@ -229,16 +229,17 @@
 
 	frame = purple_plugin_pref_frame_new();
 
-	ppref = purple_plugin_pref_new_with_label(_("Join/Part Hiding Configuration"));
+	ppref = purple_plugin_pref_new_with_label(_("Hide Joins/Parts"));
 	purple_plugin_pref_frame_add(frame, ppref);
 
 	ppref = purple_plugin_pref_new_with_name_and_label(THRESHOLD_PREF,
-	                                                 _("Minimum Room Size"));
+	                                                 /* Translators: Followed by an input request a number of people */
+	                                                 _("For rooms with more than this many people"));
 	purple_plugin_pref_set_bounds(ppref, 0, 1000);
 	purple_plugin_pref_frame_add(frame, ppref);
 
 	ppref = purple_plugin_pref_new_with_name_and_label(DELAY_PREF,
-	                                                 _("User Inactivity Timeout (in minutes)"));
+	                                                 _("If user has not spoken in this many minutes"));
 	purple_plugin_pref_set_bounds(ppref, 0, 8 * 60); /* 8 Hours */
 	purple_plugin_pref_frame_add(frame, ppref);
 
--- a/libpurple/plugins/log_reader.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/log_reader.c	Wed Jun 13 19:30:27 2012 -0400
@@ -1,7 +1,7 @@
+#include "internal.h"
+
 #include <stdio.h>
 
-#include "internal.h"
-
 #include "debug.h"
 #include "log.h"
 #include "plugin.h"
@@ -92,7 +92,7 @@
 
 	prpl_name = g_ascii_strup(prpl_info->list_icon(account, NULL), -1);
 
-	temp = g_strdup_printf("%s.%s", prpl_name, account->username);
+	temp = g_strdup_printf("%s.%s", prpl_name, purple_account_get_username(account));
 	path = g_build_filename(logdir, temp, sn, NULL);
 	g_free(temp);
 
@@ -635,7 +635,7 @@
 	g_return_val_if_fail(sn != NULL, NULL);
 	g_return_val_if_fail(account != NULL, NULL);
 
-	if (strcmp(account->protocol_id, "prpl-msn"))
+	if (strcmp(purple_account_get_protocol_id(account), "prpl-msn"))
 		return NULL;
 
 	logdir = purple_prefs_get_string("/plugins/core/log_reader/msn/log_directory");
@@ -658,7 +658,7 @@
 			return list;
 		}
 	} else {
-		username = g_strdup(purple_normalize(account, account->username));
+		username = g_strdup(purple_normalize(account, purple_account_get_username(account)));
 	}
 
 	if (buddy) {
@@ -974,7 +974,7 @@
 
 		their_name = from_name;
 		if (from_name && purple_prefs_get_bool("/plugins/core/log_reader/use_name_heuristics")) {
-			const char *friendly_name = purple_connection_get_display_name(log->account->gc);
+			const char *friendly_name = purple_connection_get_display_name(purple_account_get_connection(log->account));
 
 			if (friendly_name != NULL) {
 				int friendly_name_length = strlen(friendly_name);
@@ -987,13 +987,10 @@
 				if (buddy)
 					their_name = purple_buddy_get_alias(buddy);
 
-				if (log->account->alias)
-				{
-					alias = log->account->alias;
+				alias = purple_account_get_alias(log->account);
+				if (alias) {
 					alias_length = strlen(alias);
-				}
-				else
-				{
+				} else {
 					alias = "";
 					alias_length = 0;
 				}
@@ -1115,10 +1112,10 @@
 			text = g_string_append(text, "<b>");
 
 			if (name_guessed == NAME_GUESS_ME) {
-				if (log->account->alias)
-					text = g_string_append(text, log->account->alias);
+				if (purple_account_get_alias(log->account))
+					text = g_string_append(text, purple_account_get_alias(log->account));
 				else
-					text = g_string_append(text, log->account->username);
+					text = g_string_append(text, purple_account_get_username(log->account));
 			}
 			else if (name_guessed == NAME_GUESS_THEM)
 				text = g_string_append(text, their_name);
@@ -1454,11 +1451,15 @@
 		const char *footer = NULL;
 		GString *temp = NULL;
 
-		if ((c = strstr(c, "\n")))
-		{
-			*c = '\0';
-			c++;
-		}
+		/* There's always a trailing '\n' at the end of the file (see above), so
+		 * just quit out if we don't find another, because we're at the end.
+		 */
+		c = strchr(c, '\n');
+		if (!c)
+			break;
+
+		*c = '\0';
+		c++;
 
 		/* Convert links.
 		 *
@@ -1482,14 +1483,14 @@
 				char *end_paren;
 				char *space;
 
-				if (!(end_paren = strstr(link, ")")))
+				if (!(end_paren = strchr(link, ')')))
 				{
 					/* Something is not as we expect.  Bail out. */
 					break;
 				}
 
 				if (!temp)
-					temp = g_string_sized_new(c ? (c - 1 - line) : strlen(line));
+					temp = g_string_sized_new(strlen(line));
 
 				g_string_append_len(temp, line, (tmp - line));
 
@@ -1504,7 +1505,7 @@
 
 				/* The \r is a bit of a hack to keep there from being a \r in
 				 * the link text, which may not matter. */
-				if ((space = strstr(end_paren, " ")) || (space = strstr(end_paren, "\r")))
+				if ((space = strchr(end_paren, ' ')) || (space = strchr(end_paren, '\r')))
 				{
 					g_string_append_len(temp, end_paren + 1, space - end_paren - 1);
 
@@ -1539,7 +1540,7 @@
 		if (*line == '[') {
 			const char *timestamp;
 
-			if ((timestamp = strstr(line, "]"))) {
+			if ((timestamp = strchr(line, ']'))) {
 				line++;
 				/* TODO: Parse the timestamp and convert it to Purple's format. */
 				g_string_append(formatted, "<font size=\"2\">(");
@@ -1587,7 +1588,7 @@
 
 					if (buddy != NULL)
 						alias = purple_buddy_get_alias(buddy);
-					
+
 					if (alias != NULL)
 						g_string_append(formatted, alias);
 					else
@@ -1658,7 +1659,7 @@
 					}
 				}
 			} else {
-				const char *line2 = strstr(line, ":");
+				const char *line2 = strchr(line, ':');
 				if (line2) {
 					const char *acct_name;
 					line2++;
@@ -1777,7 +1778,7 @@
 	g_return_val_if_fail(account != NULL, NULL);
 
 	/* QIP only supports ICQ. */
-	if (strcmp(account->protocol_id, "prpl-icq"))
+	if (strcmp(purple_account_get_protocol_id(account), "prpl-icq"))
 		return NULL;
 
 	logdir = purple_prefs_get_string("/plugins/core/log_reader/qip/log_directory");
@@ -1794,7 +1795,7 @@
 	if (!prpl_info->list_icon)
 		return NULL;
 
-	username = g_strdup(purple_normalize(account, account->username));
+	username = g_strdup(purple_normalize(account, purple_account_get_username(account)));
 	filename = g_strdup_printf("%s.txt", purple_normalize(account, sn));
 	path = g_build_filename(logdir, username, "History", filename, NULL);
 	g_free(username);
@@ -1819,20 +1820,20 @@
 
 		gboolean add_new_log = FALSE;
 
-		if (*c) {
+		if (c && *c) {
 			if (purple_str_has_prefix(c, QIP_LOG_IN_MESSAGE) ||
 				purple_str_has_prefix(c, QIP_LOG_OUT_MESSAGE)) {
 
 				char *tmp;
-				
+
 				new_line = c;
 
 				/* find EOL */
-				c = strstr(c, "\n");
+				c = strchr(c, '\n');
 				c++;
 
 				/* Find the last '(' character. */
-				if ((tmp = strstr(c, "\n")) != NULL) {
+				if ((tmp = strchr(c, '\n')) != NULL) {
 					while (*tmp && *tmp != '(') --tmp;
 					c = tmp;
 				} else {
@@ -1886,7 +1887,7 @@
 			data->offset = offset;
 			offset += data->length;
 			purple_debug_info("QIP logger list",
-				"Creating log: path = (%s); length = (%d); offset = (%d)\n", 
+				"Creating log: path = (%s); length = (%d); offset = (%d)\n",
 				data->path, data->length, data->offset);
 
 			/* XXX: Look into this later... Should we pass in a struct tm? */
@@ -1902,10 +1903,10 @@
 			start_log = new_line;
 		}
 
-		if (*c) {
+		if (c && *c) {
 			/* find EOF */
-			c = strstr(c, "\n");
-			c++;
+			if ((c = strchr(c, '\n')))
+				c++;
 		}
 	}
 
@@ -1983,13 +1984,13 @@
 			is_in_message = purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC);
 
 			/* find EOL */
-			c = strstr(c, "\n");
+			c = strchr(c, '\n');
 
 			/* XXX: Do we need buddy_name when we have buddy->alias? */
 			buddy_name = ++c;
 
 			/* Find the last '(' character. */
-			if ((tmp = strstr(c, "\n")) != NULL) {
+			if ((tmp = strchr(c, '\n')) != NULL) {
 				while (*tmp && *tmp != '(') --tmp;
 				c = tmp;
 			} else {
@@ -2042,12 +2043,12 @@
 					}
 
 					/* find EOF */
-					c = strstr(c, "\n");
+					c = strchr(c, '\n');
 					line = ++c;
 				}
 			}
 		} else {
-			if ((c = strstr(c, "\n")))
+			if ((c = strchr(c, '\n')))
 				*c = '\0';
 
 			if (line[0] != '\n' && line[0] != '\r') {
@@ -2186,7 +2187,7 @@
 				                  " length = (%d)\n",
 				                  sn, data->path, data->offset, data->length);
 			}
-			c = strstr(c, "\n");
+			c = strchr(c, '\n');
 			c++;
 		}
 
@@ -2202,7 +2203,6 @@
 			log->logger = amsn_logger;
 			log->logger_data = data;
 			list = g_list_prepend(list, log);
-			found_start = FALSE;
 
 			purple_debug_info("aMSN logger",
 			                  "Found log for %s:"
@@ -2237,10 +2237,10 @@
 		return NULL;
 
 	/* aMSN only works with MSN/WLM */
-	if (strcmp(account->protocol_id, "prpl-msn"))
+	if (strcmp(purple_account_get_protocol_id(account), "prpl-msn"))
 		return NULL;
 
-	username = g_strdup(purple_normalize(account, account->username));
+	username = g_strdup(purple_normalize(account, purple_account_get_username(account)));
 	buddy_log = g_strdup_printf("%s.log", purple_normalize(account, sn));
 	log_path = g_build_filename(logdir, username, "logs", NULL);
 
@@ -2323,7 +2323,7 @@
 
 	file = g_fopen(data->path, "rb");
 	g_return_val_if_fail(file != NULL, g_strdup(""));
-	
+
 	fseek(file, data->offset, SEEK_SET);
 	data->length = fread(contents, 1, data->length, file);
 	fclose(file);
@@ -2342,7 +2342,7 @@
 		char *end;
 		char *old_tag;
 		char *tag;
-		end = strstr(start, "\n");
+		end = strchr(start, '\n');
 		if (!end)
 			break;
 		*end = '\0';
@@ -2417,7 +2417,7 @@
 	g_return_val_if_fail(log != NULL, 0);
 
 	data = log->logger_data;
-	
+
 	if (purple_prefs_get_bool("/plugins/core/log_reader/fast_sizes")) {
 		return data ? data->length : 0;
 	}
@@ -2644,7 +2644,7 @@
 			g_free(contents);
 		}
 		g_free(path);
-#endif /* !GTK_CHECK_VERSION(2,6,0) */
+#endif /* !GLIB_CHECK_VERSION(2,6,0) */
 	} /* path */
 
 	if (!found) {
--- a/libpurple/plugins/mono/loader/blist-glue.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/mono/loader/blist-glue.c	Wed Jun 13 19:30:27 2012 -0400
@@ -6,21 +6,21 @@
 MonoObject* purple_blist_get_handle_glue(void)
 {
 	void *handle = purple_blist_get_handle();
-	
+
 	return mono_value_box(ml_get_domain(), mono_get_intptr_class(), &handle);
 }
 
 MonoObject* purple_blist_build_buddy_object(void* data)
 {
 	MonoObject *obj = NULL;
-			
+
 	PurpleBuddy *buddy = (PurpleBuddy*)data;
-	
+
 	obj = ml_create_api_object("Buddy");
 	g_return_val_if_fail(obj != NULL, NULL);
-		
+
 	ml_set_prop_string(obj, "Name", (char*)purple_buddy_get_name(buddy));
 	ml_set_prop_string(obj, "Alias", (char*)purple_buddy_get_alias(buddy));
-	
+
 	return obj;
 }
--- a/libpurple/plugins/mono/loader/debug-glue.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/mono/loader/debug-glue.c	Wed Jun 13 19:30:27 2012 -0400
@@ -5,12 +5,12 @@
 {
 	char *ccat;
 	char *cstr;
-	
+
 	ccat = mono_string_to_utf8(cat);
 	cstr = mono_string_to_utf8(str);
-	
+
 	purple_debug(type, ccat, "%s", cstr);
-	
+
 	g_free(ccat);
 	g_free(cstr);
 }
--- a/libpurple/plugins/mono/loader/mono-helper.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/mono/loader/mono-helper.c	Wed Jun 13 19:30:27 2012 -0400
@@ -22,57 +22,57 @@
 gboolean ml_init()
 {
 	MonoDomain *d;
-	
+
 	g_return_val_if_fail(_runtime_active == FALSE, TRUE);
-	
+
 	d = mono_jit_init("purple");
-	
+
 	if (!d) {
 		ml_set_domain(NULL);
 		return FALSE;
 	}
-	
+
 	ml_set_domain(d);
-	
+
 	ml_init_internal_calls();
-	
+
 	_runtime_active = TRUE;
-	
+
 	return TRUE;
 }
 
 void ml_uninit()
 {
 	g_return_if_fail(_runtime_active == TRUE);
-	
+
 	mono_jit_cleanup(ml_get_domain());
-	
+
 	ml_set_domain(NULL);
-	
+
 	_runtime_active = FALSE;
 }
 
 MonoObject* ml_delegate_invoke(MonoObject *method, void **params)
 {
 	MonoObject *ret, *exception;
-	
+
 	ret = mono_runtime_delegate_invoke(method, params, &exception);
 	if (exception) {
 		purple_debug(PURPLE_DEBUG_ERROR, "mono", "caught exception: %s\n", mono_class_get_name(mono_object_get_class(exception)));
 	}
-	
+
 	return ret;
 }
 
 MonoObject* ml_invoke(MonoMethod *method, void *obj, void **params)
 {
 	MonoObject *ret, *exception;
-	
+
 	ret = mono_runtime_invoke(method, obj, params, &exception);
 	if (exception) {
 		purple_debug(PURPLE_DEBUG_ERROR, "mono", "caught exception: %s\n", mono_class_get_name(mono_object_get_class(exception)));
 	}
-	
+
 	return ret;
 }
 
@@ -84,15 +84,15 @@
 	total = mono_image_get_table_rows (image, MONO_TABLE_TYPEDEF);
 	for (i = 1; i <= total; ++i) {
 		klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | i);
-		
+
 		pklass = mono_class_get_parent(klass);
 		if (pklass) {
-		
+
 			if (strcmp("Plugin", mono_class_get_name(pklass)) == 0)
 				return klass;
 		}
 	}
-	
+
 	return NULL;
 }
 
@@ -102,15 +102,15 @@
 	MonoProperty *prop;
 	MonoString *str;
 	gpointer args[1];
-	
+
 	klass = mono_object_get_class(obj);
-	
+
 	prop = mono_class_get_property_from_name(klass, field);
-	
+
 	str = mono_string_new(ml_get_domain(), data);
-	
+
 	args[0] = str;
-	
+
 	mono_property_set_value(prop, obj, args, NULL);
 }
 
@@ -119,13 +119,13 @@
 	MonoClass *klass;
 	MonoProperty *prop;
 	MonoString *str;
-	
+
 	klass = mono_object_get_class(obj);
-	
+
 	prop = mono_class_get_property_from_name(klass, field);
-	
+
 	str = (MonoString*)mono_property_get_value(prop, obj, NULL, NULL);
-	
+
 	return mono_string_to_utf8(str);
 }
 
@@ -133,16 +133,16 @@
 {
 	MonoClass *klass;
 	MonoProperty *prop;
-	
+
 	klass = mono_class_get_parent(mono_object_get_class(obj));
-	
+
 	prop = mono_class_get_property_from_name(klass, "Info");
-	
+
 	return mono_property_get_value(prop, obj, NULL, NULL);
 }
 
 gboolean ml_is_api_dll(MonoImage *image)
-{	
+{
 	MonoClass *klass;
 	int i, total;
 
@@ -155,7 +155,7 @@
 				return TRUE;
 			}
 	}
-	
+
 	return FALSE;
 }
 
@@ -167,7 +167,7 @@
 MonoObject* ml_object_from_purple_subtype(PurpleSubType type, gpointer data)
 {
 	MonoObject *obj = NULL;
-	
+
 	switch (type) {
 		case PURPLE_SUBTYPE_BLIST_BUDDY:
 			obj = purple_blist_build_buddy_object(data);
@@ -178,7 +178,7 @@
 		default:
 		break;
 	}
-	
+
 	return obj;
 }
 
@@ -186,21 +186,21 @@
 {
 	MonoObject *obj = NULL;
 	MonoClass *klass = NULL;
-		
+
 	klass = mono_class_from_name(ml_get_api_image(), "Purple", class_name);
 	if (!klass) {
 		purple_debug(PURPLE_DEBUG_FATAL, "mono", "couldn't find the '%s' class\n", class_name);
 		return NULL;
 	}
-	
+
 	obj = mono_object_new(ml_get_domain(), klass);
 	if (!obj) {
 		purple_debug(PURPLE_DEBUG_FATAL, "mono", "couldn't create the object from class '%s'\n", class_name);
 		return NULL;
 	}
-	
+
 	mono_runtime_object_init(obj);
-	
+
 	return obj;
 }
 
@@ -241,7 +241,7 @@
 {
 	if (!plugins_hash)
 		plugins_hash = g_hash_table_new(NULL, NULL);
-		
+
 	g_hash_table_insert(plugins_hash, plugin->klass, plugin);
 }
 
--- a/libpurple/plugins/mono/loader/mono-helper.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/mono/loader/mono-helper.h	Wed Jun 13 19:30:27 2012 -0400
@@ -13,16 +13,16 @@
 
 typedef struct {
 	PurplePlugin *plugin;
-	
+
 	MonoAssembly *assm;
 	MonoClass *klass;
-	MonoObject *obj;	
-	
+	MonoObject *obj;
+
 	MonoMethod *init;
 	MonoMethod *load;
 	MonoMethod *unload;
 	MonoMethod *destroy;
-	
+
 	GList *signal_data;
 } PurpleMonoPlugin;
 
--- a/libpurple/plugins/mono/loader/signal-glue.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/mono/loader/signal-glue.c	Wed Jun 13 19:30:27 2012 -0400
@@ -22,13 +22,13 @@
 	int i;
 	gpointer meth_args[1];
 	gpointer purple_obj;
-	
+
 	va_list args;
-	
+
 	va_start(args, num_vals);
-	
+
 	array = mono_array_new(ml_get_domain(), mono_get_object_class(), num_vals);
-	
+
 	for (i = 0; i < num_vals; i++) {
 		if (purple_value_get_type(sig_data->values[i]) == PURPLE_TYPE_SUBTYPE) {
 			purple_obj = va_arg(args, gpointer);
@@ -40,22 +40,22 @@
 			mono_array_set(array, MonoObject*, i, obj);
 		}
 	}
-	
+
 	va_end(args);
-	
+
 	meth_args[0] = array;
-	
-	return ml_delegate_invoke(sig_data->func, meth_args);	
+
+	return ml_delegate_invoke(sig_data->func, meth_args);
 }
 
 static void cb_void__pointer(void *arg1, void *data)
 {
-	dispatch_callback((SignalData*)data, ((SignalData*)data)->num_vals, arg1);	
+	dispatch_callback((SignalData*)data, ((SignalData*)data)->num_vals, arg1);
 }
 
 static void cb_void__pointer_pointer_pointer(void *arg1, void *arg2, void *arg3, void *data)
 {
-	dispatch_callback((SignalData*)data, ((SignalData*)data)->num_vals, arg1, arg2, arg3);	
+	dispatch_callback((SignalData*)data, ((SignalData*)data)->num_vals, arg1, arg2, arg3);
 }
 
 
@@ -66,23 +66,23 @@
 	SignalData *sig_data;
 	PurpleMonoPlugin *mplug;
 	MonoClass *klass;
-		
+
 	sig = mono_string_to_utf8(signal);
 	purple_debug(PURPLE_DEBUG_INFO, "mono", "connecting signal: %s\n", sig);
-	
+
 	instance = (void*)mono_object_unbox(h);
-	
+
 	sig_data = g_new0(SignalData, 1);
-	
+
 	sig_data->func = func;
 	sig_data->signal = sig;
-	
+
 	purple_signal_get_values(*instance, sig, &sig_data->ret_value, &sig_data->num_vals, &sig_data->values);
-	
+
 	klass = mono_object_get_class(plugin);
-	
+
 	mplug = ml_find_plugin_by_class(klass);
-	
+
 	mplug->signal_data = g_list_append(mplug->signal_data, (gpointer)sig_data);
 
 	return purple_signal_connect(*instance, sig, (gpointer)klass, get_callback(sig_data), (gpointer)sig_data);
@@ -104,7 +104,7 @@
 	}
 }
 
-static gpointer callbacks[]= { 
+static gpointer callbacks[]= {
 										NULL,
 										cb_void__pointer,
 										NULL,
@@ -112,7 +112,7 @@
 									};
 
 static int callbacks_array_size = sizeof(callbacks) / sizeof(PurpleCallback);
-	
+
 
 static PurpleCallback get_callback(SignalData *sig_data)
 {
@@ -122,18 +122,18 @@
 		index = 0;
 	else
 		index = determine_index(purple_value_get_type(sig_data->ret_value));
-	
+
 	for (i = 0; i < sig_data->num_vals; i++) {
 		index += determine_index(purple_value_get_type(sig_data->values[i]));
 	}
-	
+
 	purple_debug(PURPLE_DEBUG_INFO, "mono", "get_callback index = %d\n", index);
-	
+
 	if (index >= callbacks_array_size || callbacks[index] == NULL) {
 		purple_debug(PURPLE_DEBUG_ERROR, "mono", "couldn't find a callback function for signal: %s\n", sig_data->signal);
 		return NULL;
 	}
-	
+
 	purple_debug(PURPLE_DEBUG_MISC, "mono", "using callback at index: %d\n", index);
 	return PURPLE_CALLBACK(callbacks[index]);
 }
--- a/libpurple/plugins/mono/loader/status-glue.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/mono/loader/status-glue.c	Wed Jun 13 19:30:27 2012 -0400
@@ -6,11 +6,11 @@
 {
 	MonoObject *obj = NULL;
 	PurpleStatus *status = (PurpleStatus*)data;
-	
+
 	obj = ml_create_api_object("Status");
 	g_return_val_if_fail(obj != NULL, NULL);
-		
+
 	ml_set_prop_string(obj, "Id", (char*)purple_status_get_id(status));
-	
+
 	return obj;
 }
--- a/libpurple/plugins/offlinemsg.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/offlinemsg.c	Wed Jun 13 19:30:27 2012 -0400
@@ -89,7 +89,7 @@
 
 	purple_pounce_action_set_enabled(pounce, "send-message", TRUE);
 	purple_pounce_action_set_attribute(pounce, "send-message", "message", offline->message);
- 
+
 	conv = offline->conv;
 	if (!purple_conversation_get_data(conv, "plugin_pack:offlinemsg"))
 		purple_conversation_write(conv, NULL, _("The rest of the messages will be saved "
@@ -156,7 +156,7 @@
 		ask = g_strdup_printf(_("\"%s\" is currently offline. Do you want to save the "
 						"rest of the messages in a pounce and automatically send them "
 						"when \"%s\" logs back in?"), who, who);
-	
+
 		purple_request_action(handle, _("Offline Message"), ask,
 					_("You can edit/delete the pounce from the `Buddy Pounces' dialog"),
 					0,
--- a/libpurple/plugins/perl/Makefile.mingw	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/Makefile.mingw	Wed Jun 13 19:30:27 2012 -0400
@@ -7,10 +7,12 @@
 PIDGIN_TREE_TOP := ../../..
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
+DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES))
+
 TARGET = perl
 
 # Perl headers with /* /* */ type comments.. Turn off warnings.
-CFLAGS += -Wno-comment
+GCCWARNINGS += -Wno-comment
 
 ##
 ## INCLUDE PATHS
--- a/libpurple/plugins/perl/common/Account.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Account.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -199,9 +199,10 @@
     Purple::Account account
 
 void
-purple_account_add_buddies(account, list)
+purple_account_add_buddies(account, list, message)
     Purple::Account account
     SV * list
+    const char *message
 PREINIT:
     GList *t_GL;
     int i, t_len;
@@ -212,13 +213,14 @@
     for (i = 0; i <= t_len; i++)
         t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(list), i, 0)));
 
-    purple_account_add_buddies(account, t_GL);
+    purple_account_add_buddies(account, t_GL, message);
     g_list_free(t_GL);
 
 void
-purple_account_add_buddy(account, buddy)
-    Purple::Account account
-    Purple::BuddyList::Buddy  buddy
+purple_account_add_buddy(account, buddy, message)
+    Purple::Account          account
+    Purple::BuddyList::Buddy buddy
+    const char *             message
 
 void
 purple_account_change_password(account, a, b)
--- a/libpurple/plugins/perl/common/BuddyList.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/BuddyList.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -2,6 +2,13 @@
 #include "module.h"
 #include "../perl-handlers.h"
 
+static void
+chat_components_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+	HV *hv = user_data;
+	hv_store(hv, key, strlen(key), newSVpv(value, 0), 0);
+}
+
 MODULE = Purple::BuddyList  PACKAGE = Purple  PREFIX = purple_
 PROTOTYPES: ENABLE
 
@@ -75,11 +82,6 @@
 purple_contact_get_priority_buddy(contact)
 	Purple::BuddyList::Contact contact
 
-void
-purple_contact_set_alias(contact, alias)
-	Purple::BuddyList::Contact contact
-	const char * alias
-
 const char *
 purple_contact_get_alias(contact)
 	Purple::BuddyList::Contact contact
@@ -193,10 +195,6 @@
 	Purple::Status old_status
 
 void
-purple_blist_update_buddy_icon(buddy)
-	Purple::BuddyList::Buddy buddy
-
-void
 purple_blist_rename_buddy(buddy, name)
 	Purple::BuddyList::Buddy buddy
 	const char * name
@@ -331,6 +329,19 @@
 purple_chat_get_name(chat)
 	Purple::BuddyList::Chat chat
 
+HV *
+purple_chat_get_components(chat)
+	Purple::BuddyList::Chat chat
+INIT:
+	HV * t_HV;
+	GHashTable * t_GHash;
+CODE:
+	t_GHash = purple_chat_get_components(chat);
+	RETVAL = t_HV = newHV();
+	g_hash_table_foreach(t_GHash, chat_components_foreach, t_HV);
+OUTPUT:
+	RETVAL
+
 Purple::BuddyList::Chat
 purple_chat_new(account, alias, components)
 	Purple::Account account
@@ -345,14 +356,14 @@
 	char *t_key, *t_value;
 CODE:
 	t_HV =  (HV *)SvRV(components);
-	t_GHash = g_hash_table_new(g_str_hash, g_str_equal);
+	t_GHash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
 
 	for (t_HE = hv_iternext(t_HV); t_HE != NULL; t_HE = hv_iternext(t_HV) ) {
 		t_key = hv_iterkey(t_HE, &len);
 		t_SV = *hv_fetch(t_HV, t_key, len, 0);
 		t_value = SvPVutf8_nolen(t_SV);
 
-		g_hash_table_insert(t_GHash, t_key, t_value);
+		g_hash_table_insert(t_GHash, g_strdup(t_key), g_strdup(t_value));
 	}
 
 	RETVAL = purple_chat_new(account, alias, t_GHash);
@@ -410,9 +421,5 @@
 	Purple::BuddyList::Buddy buddy
 
 const char *
-purple_buddy_get_local_alias(buddy)
-	Purple::BuddyList::Buddy buddy
-
-const char *
 purple_buddy_get_alias(buddy)
 	Purple::BuddyList::Buddy buddy
--- a/libpurple/plugins/perl/common/Certificate.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Certificate.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -202,7 +202,7 @@
 			l = g_list_prepend(l, purple_perl_ref_object(ST(i)));
 		}
 		l = g_list_reverse(l);
-		ret = purple_certificate_check_signature_chain(l);
+		ret = purple_certificate_check_signature_chain(l, NULL);
 		g_list_free(l);
 		if(ret) XSRETURN_YES;
 		XSRETURN_NO;
--- a/libpurple/plugins/perl/common/Connection.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Connection.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -36,15 +36,6 @@
 	const char *text
 
 void
-purple_connection_error(gc, reason)
-	Purple::Connection gc
-	const char *reason
-
-void
-purple_connection_destroy(gc)
-	Purple::Connection gc
-
-void
 purple_connection_set_state(gc, state)
 	Purple::Connection gc
 	Purple::ConnectionState state
--- a/libpurple/plugins/perl/common/Conversation.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Conversation.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -118,6 +118,12 @@
 		XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Conversation")));
 	}
 
+Purple::Conversation
+purple_find_conversation_with_account(type, name, account)
+	Purple::ConversationType type
+	const char *name
+	Purple::Account account
+
 MODULE = Purple::Conversation  PACKAGE = Purple::Conversations  PREFIX = purple_conversations_
 PROTOTYPES: ENABLE
 
@@ -140,7 +146,7 @@
 	Purple::Conversation conv
 
 Purple::Connection
-purple_conversation_get_gc(conv)
+purple_conversation_get_connection(conv)
 	Purple::Conversation conv
 
 void
@@ -297,6 +303,12 @@
 	const char *message
 
 void
+purple_conv_im_send_with_flags(im, message, flags)
+	Purple::Conversation::IM im
+	const char *message
+	Purple::MessageFlags flags
+
+void
 purple_conv_im_write(im, who, message, flags, mtime)
 	Purple::Conversation::IM im
 	const char *who
@@ -326,24 +338,6 @@
 	Purple::Conversation::Chat chat
 
 void
-purple_conv_chat_set_users(chat, users)
-	Purple::Conversation::Chat chat
-	SV * users
-PREINIT:
-	GList *l, *t_GL;
-	int i, t_len;
-PPCODE:
-	t_GL = NULL;
-	t_len = av_len((AV *)SvRV(users));
-
-	for (i = 0; i <= t_len; i++)
-		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(users), i, 0)));
-
-	for (l = purple_conv_chat_set_users(chat, t_GL); l != NULL; l = l->next) {
-		XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::ListEntry")));
-	}
-
-void
 purple_conv_chat_get_users(chat)
 	Purple::Conversation::Chat chat
 PREINIT:
@@ -410,6 +404,12 @@
 	const char * message
 
 void
+purple_conv_chat_send_with_flags(chat, message, flags)
+	Purple::Conversation::Chat chat
+	const char * message
+	Purple::MessageFlags flags
+
+void
 purple_conv_chat_write(chat, who, message, flags, mtime)
 	Purple::Conversation::Chat chat
 	const char *who
--- a/libpurple/plugins/perl/common/FT.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/FT.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -113,7 +113,7 @@
 	Purple::Xfer xfer
 
 gboolean 
-purple_xfer_is_canceled(xfer)
+purple_xfer_is_cancelled(xfer)
 	Purple::Xfer xfer
 
 gboolean 
--- a/libpurple/plugins/perl/common/Log.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Log.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -6,7 +6,7 @@
 BOOT:
 {
 	HV *type_stash = gv_stashpv("Purple::Log::Type", 1);
-	HV *flags_stash = gv_stashpv("Purple::Log:ReadFlags::", 1);
+	HV *flags_stash = gv_stashpv("Purple::Log::ReadFlags", 1);
 
 	static const constiv *civ, type_const_iv[] = {
 #define const_iv(name) {#name, (IV)PURPLE_LOG_##name}
@@ -27,6 +27,9 @@
 		newCONSTSUB(flags_stash, (char *)civ->name, newSViv(civ->iv));
 }
 
+Purple::Handle
+purple_log_get_handle()
+
 int
 purple_log_common_sizer(log)
 	Purple::Log log
--- a/libpurple/plugins/perl/common/Makefile.PL.in	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Makefile.PL.in	Wed Jun 13 19:30:27 2012 -0400
@@ -10,6 +10,7 @@
       (ABSTRACT_FROM    => '@srcdir@/Purple.pm', # finds $ABSTRACT
        AUTHOR           => 'Purple <http://pidgin.im/>') : ()),
     'DEFINE'            => '@DEBUG_CFLAGS@',
+    'dynamic_lib'       => { 'OTHERLDFLAGS' => '@LDFLAGS@' },
     'INC'               => '-I. -I@srcdir@ -I@top_srcdir@ -I@top_srcdir@/libpurple @GLIB_CFLAGS@',
     'OBJECT'            => '$(O_FILES)', # link all the C files too
 #    'OPTIMIZE'          => '-g', # For debugging
--- a/libpurple/plugins/perl/common/Makefile.mingw	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Makefile.mingw	Wed Jun 13 19:30:27 2012 -0400
@@ -5,12 +5,14 @@
 #
 
 PIDGIN_TREE_TOP := ../../../..
-GCCWARNINGS := -Wno-comment -Waggregate-return -Wcast-align -Wdeclaration-after-statement -Werror-implicit-function-declaration -Wextra -Wno-sign-compare -Wno-unused-parameter -Winit-self -Wmissing-declarations -Wmissing-prototypes -Wpointer-arith -Wundef -Wno-unused
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
+GCCWARNINGS += -Wno-comment -Wno-unused -Wno-nested-externs
+
+DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES))
+
 TARGET = Purple
 AUTOSPLIT = lib/auto/Purple/autosplit.ix
-EXTUTILS ?= C:/perl/lib/ExtUtils
 PERL_PLUGIN_TOP := ..
 
 ##
--- a/libpurple/plugins/perl/common/Network.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Network.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -23,17 +23,21 @@
 	const char *ip
 
 Purple::NetworkListenData
-purple_network_listen(port, socket_type, cb, cb_data)
+purple_network_listen(port, socket_family, socket_type, map_external, cb, cb_data)
 	unsigned short port
+	int socket_family
 	int socket_type
+	gboolean map_external
 	Purple::NetworkListenCallback cb
 	gpointer cb_data
 
 Purple::NetworkListenData
-purple_network_listen_range(start, end, socket_type, cb, cb_data)
+purple_network_listen_range(start, end, socket_family, socket_type, map_external, cb, cb_data)
 	unsigned short start
 	unsigned short end
+	int socket_family
 	int socket_type
+	gboolean map_external
 	Purple::NetworkListenCallback cb
 	gpointer cb_data
 
--- a/libpurple/plugins/perl/common/Notify.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Notify.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -135,7 +135,7 @@
 PREINIT:
 	GList *l;
 PPCODE:
-	l = purple_notify_user_info_get_entries(user_info);
+	l = purple_notify_user_info_get_entries(user_info)->head;
 	for (; l != NULL; l = l->next) {
 		XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::NotifyUserInfoEntry")));
 	}
@@ -145,12 +145,12 @@
 	Purple::NotifyUserInfo user_info
 	const char *newline
 
-void purple_notify_user_info_add_pair(user_info, label, value)
+void purple_notify_user_info_add_pair_html(user_info, label, value)
 	Purple::NotifyUserInfo user_info
 	const char *label
 	const char *value
 
-void purple_notify_user_info_prepend_pair(user_info, label, value)
+void purple_notify_user_info_prepend_pair_html(user_info, label, value)
 	Purple::NotifyUserInfo user_info
 	const char *label
 	const char *value
--- a/libpurple/plugins/perl/common/Proxy.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Proxy.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -5,7 +5,7 @@
 
 BOOT:
 {
-	HV *stash = gv_stashpv("Purple::ProxyType::", 1);
+	HV *stash = gv_stashpv("Purple::ProxyType", 1);
 
 	static const constiv *civ, const_iv[] = {
 #define const_iv(name) {#name, (IV)PURPLE_PROXY_##name}
--- a/libpurple/plugins/perl/common/Prpl.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Prpl.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -62,11 +62,15 @@
 PREINIT:
 	PurplePluginProtocolInfo *prpl_info;
 CODE:
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
-	if (prpl_info && prpl_info->send_raw != NULL) {
-		RETVAL = prpl_info->send_raw(gc, str, strlen(str));
-	} else {
+	if (!gc)
 		RETVAL = 0;
+	else {
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+		if (prpl_info && prpl_info->send_raw != NULL) {
+			RETVAL = prpl_info->send_raw(gc, str, strlen(str));
+		} else {
+			RETVAL = 0;
+		}
 	}
 OUTPUT:
 	RETVAL
--- a/libpurple/plugins/perl/common/Request.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Request.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -374,9 +374,10 @@
 	C_ARGS: id, text
 
 void
-purple_request_field_list_add(field, item, data)
+purple_request_field_list_add_icon(field, item, icon_path, data)
 	Purple::Request::Field field
 	const char *item
+	const char *icon_path
 	void * data
 
 void
--- a/libpurple/plugins/perl/common/SSLConn.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/SSLConn.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -16,13 +16,6 @@
 	Purple::Ssl::Connection gsc
 	Purple::SslInputFunction func
 
-Purple::Ssl::Connection
-purple_ssl_connect_fd(account, fd, func, error_func, data)
-	Purple::Account account
-	int fd
-	PurpleSslInputFunction func
-	PurpleSslErrorFunction error_func
-
 */
 
 MODULE = Purple::SSL  PACKAGE = Purple::SSL   PREFIX = purple_ssl_
--- a/libpurple/plugins/perl/common/Server.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Server.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -144,6 +144,7 @@
 		g_hash_table_insert(t_GHash, t_key, t_value);
 	}
 	serv_join_chat(conn, t_GHash);
+	g_hash_table_destroy(t_GHash);
 
 void 
 serv_move_buddy(buddy, group1, group2)
--- a/libpurple/plugins/perl/common/Status.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Status.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -74,28 +74,6 @@
 		newCONSTSUB(primitive_stash, (char *)civ->name, newSViv(civ->iv));
 }
 
-void
-purple_presence_add_list(presence, source_list)
-	Purple::Presence presence
-	SV *source_list
-PREINIT:
-	GList *t_GL;
-	int i, t_len;
-PPCODE:
-	t_GL = NULL;
-	t_len = av_len((AV *)SvRV(source_list));
-
-	for (i = 0; i <= t_len; i++) {
-		t_GL = g_list_append(t_GL, SvPVutf8_nolen(*av_fetch((AV *)SvRV(source_list), i, 0)));
-	}
-	purple_presence_add_list(presence, t_GL);
-	g_list_free(t_GL);
-
-void
-purple_presence_add_status(presence, status)
-	Purple::Presence presence
-	Purple::Status status
-
 gint
 purple_presence_compare(presence1, presence2)
 	Purple::Presence presence1
@@ -329,29 +307,10 @@
 	Purple::Status status
 	gboolean active
 
-void
-purple_status_set_attr_boolean(status, id, value)
-	Purple::Status status
-	const char *id
-	gboolean value
-
-void
-purple_status_set_attr_string(status, id, value)
-	Purple::Status status
-	const char *id
-	const char *value
-
 MODULE = Purple::Status  PACKAGE = Purple::StatusType  PREFIX = purple_status_type_
 PROTOTYPES: ENABLE
 
 void
-purple_status_type_add_attr(status_type, id, name, value)
-	Purple::StatusType status_type
-	const char *id
-	const char *name
-	Purple::Value value
-
-void
 purple_status_type_destroy(status_type)
 	Purple::StatusType status_type
 
@@ -397,10 +356,6 @@
 purple_status_type_get_name(status_type)
 	Purple::StatusType status_type
 
-const char *
-purple_status_type_get_primary_attr(status_type)
-	Purple::StatusType status_type
-
 Purple::StatusPrimitive
 purple_status_type_get_primitive(status_type)
 	Purple::StatusType status_type
@@ -440,8 +395,3 @@
 	gboolean saveable
 	gboolean user_settable
 	gboolean independent
-
-void
-purple_status_type_set_primary_attr(status_type, attr_id)
-	Purple::StatusType status_type
-	const char *attr_id
--- a/libpurple/plugins/perl/common/Util.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/Util.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -202,6 +202,14 @@
 purple_ip_address_is_valid(ip)
 	const char* ip
 
+gboolean
+purple_ipv4_address_is_valid(ip)
+	const char* ip
+
+gboolean
+purple_ipv6_address_is_valid(ip)
+	const char* ip
+
 const char*
 purple_normalize_nocase(account, str)
 	Purple::Account account
@@ -238,7 +246,7 @@
 	guchar *ret;
 	CODE:
 		ret = purple_base16_decode(str, &len);
-		if(len) {
+		if(ret && len > 0) {
 			RETVAL = newSVpv((gchar *)ret, len);
 		} else {
 			g_free(ret);
@@ -256,7 +264,7 @@
 	guchar *ret;
 	CODE:
 		ret = purple_base64_decode(str, &len);
-		if(len) {
+		if(ret && len > 0) {
 			RETVAL = newSVpv((gchar *)ret, len);
 		} else {
 			g_free(ret);
@@ -454,12 +462,13 @@
 
  #XXX: expand...
 void
-purple_util_fetch_url(plugin, url, full, user_agent, http11, cb)
+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;
@@ -468,7 +477,7 @@
 	SV *sv = purple_perl_sv_from_fun(plugin, cb);
 
 	if (sv != NULL) {
-		data = purple_util_fetch_url(url, full, user_agent, http11,
+		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 {
--- a/libpurple/plugins/perl/common/XMLNode.xs	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/XMLNode.xs	Wed Jun 13 19:30:27 2012 -0400
@@ -4,21 +4,24 @@
 PROTOTYPES: ENABLE
 
 Purple::XMLNode
-xmlnode_copy(class, src)
+xmlnode_copy(src)
 	Purple::XMLNode src
-    C_ARGS:
-	src
 
 void
 xmlnode_free(node)
 	Purple::XMLNode node
 
 Purple::XMLNode
-xmlnode_from_str(class, str, size)
-	const char *str
-	gssize size
-    C_ARGS:
-	str, size
+xmlnode_from_str(const char *str, gssize length(str))
+    PROTOTYPE: $
+
+const char *
+xmlnode_get_name(node)
+	Purple::XMLNode node
+	CODE:
+	RETVAL = node->name;
+	OUTPUT:
+	RETVAL
 
 const char *
 xmlnode_get_attrib(node, attr)
@@ -29,6 +32,18 @@
 xmlnode_get_child(parent, name)
 	Purple::XMLNode parent
 	const char *name
+PREINIT:
+	xmlnode *tmp;
+CODE:
+	if (!name || *name == '\0') {
+		tmp = parent->child;
+		while (tmp && tmp->type != XMLNODE_TYPE_TAG)
+			tmp = tmp->next;
+		RETVAL = tmp;
+	} else
+		RETVAL = xmlnode_get_child(parent, name);
+OUTPUT:
+	RETVAL
 
 Purple::XMLNode
 xmlnode_get_child_with_namespace(parent, name, xmlns)
@@ -41,6 +56,19 @@
 	Purple::XMLNode node
 
 Purple::XMLNode
+xmlnode_get_next(node)
+	Purple::XMLNode node
+PREINIT:
+	xmlnode *tmp;
+CODE:
+	tmp = node->next;
+	while (tmp && tmp->type != XMLNODE_TYPE_TAG)
+		tmp = tmp->next;
+	RETVAL = tmp;
+OUTPUT:
+	RETVAL
+
+Purple::XMLNode
 xmlnode_get_next_twin(node)
 	Purple::XMLNode node
 
@@ -78,11 +106,17 @@
 	const char *value
 
 gchar_own *
-xmlnode_to_formatted_str(node, len)
+xmlnode_to_formatted_str(node)
 	Purple::XMLNode node
-	int *len
+    CODE:
+	RETVAL = xmlnode_to_formatted_str(node, NULL);
+    OUTPUT:
+	RETVAL
 
 gchar_own *
-xmlnode_to_str(node, len)
+xmlnode_to_str(node)
 	Purple::XMLNode node
-	int *len
+    CODE:
+	RETVAL = xmlnode_to_str(node, NULL);
+    OUTPUT:
+	RETVAL
--- a/libpurple/plugins/perl/common/module.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/common/module.h	Wed Jun 13 19:30:27 2012 -0400
@@ -9,6 +9,7 @@
 #include <glib.h>
 #ifdef _WIN32
 #undef pipe
+#undef STRINGIFY
 #endif
 #include <EXTERN.h>
 #include <perl.h>
--- a/libpurple/plugins/perl/perl-common.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/perl-common.h	Wed Jun 13 19:30:27 2012 -0400
@@ -3,6 +3,7 @@
 
 #include <glib.h>
 #ifdef _WIN32
+#undef STRINGIFY
 #undef pipe
 #endif
 #include <EXTERN.h>
--- a/libpurple/plugins/perl/perl-handlers.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/perl-handlers.c	Wed Jun 13 19:30:27 2012 -0400
@@ -299,7 +299,7 @@
 	for (i = 0; i < value_count; i++) {
 		sv_args[i] = purple_perl_sv_from_vargs(values[i],
 #ifdef VA_COPY_AS_ARRAY
-		                                       args,
+		                                       (va_list*)args,
 #else
 		                                       (va_list*)&args,
 #endif
@@ -649,6 +649,7 @@
 static void
 destroy_cmd_handler(PurplePerlCmdHandler *handler)
 {
+	purple_cmd_unregister(handler->id);
 	cmd_handlers = g_slist_remove(cmd_handlers, handler);
 
 	if (handler->callback != NULL)
@@ -705,7 +706,6 @@
 		return;
 	}
 
-	purple_cmd_unregister(id);
 	destroy_cmd_handler(handler);
 }
 
--- a/libpurple/plugins/perl/perl.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/perl.c	Wed Jun 13 19:30:27 2012 -0400
@@ -621,6 +621,9 @@
 			g_free(gps);
 			plugin->info->extra_info = NULL;
 		}
+
+		g_free(plugin->info);
+		plugin->info = NULL;
 	}
 }
 
--- a/libpurple/plugins/perl/scripts/account.pl	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/scripts/account.pl	Wed Jun 13 19:30:27 2012 -0400
@@ -27,7 +27,7 @@
 
 	# We will create these on load then destroy them on unload
 	my $TEST_NAME	 	= "perlTestName";
-	my $PROTOCOL_ID 	= "prpl-oscar";
+	my $PROTOCOL_ID 	= "prpl-aim";
 
 
 sub plugin_init {
--- a/libpurple/plugins/perl/scripts/buddy_list.pl	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/scripts/buddy_list.pl	Wed Jun 13 19:30:27 2012 -0400
@@ -24,7 +24,7 @@
 	my $TEST_GROUP		= "UConn Buddies";
 	my $TEST_NAME	 	= "johnhkelm";
 	my $TEST_ALIAS	 	= "John Kelm";
-	my $PROTOCOL_ID 	= "prpl-oscar";
+	my $PROTOCOL_ID 	= "prpl-aim";
 
 
 sub plugin_init {
--- a/libpurple/plugins/perl/scripts/conversation.pl	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/scripts/conversation.pl	Wed Jun 13 19:30:27 2012 -0400
@@ -30,7 +30,7 @@
 	my $TEST_GROUP		= "UConn Buddies";
 	my $TEST_NAME	 	= "johnhkelm";
 	my $TEST_ALIAS	 	= "John Kelm";
-	my $PROTOCOL_ID 	= "prpl-oscar";
+	my $PROTOCOL_ID 	= "prpl-aim";
 
 
 sub plugin_init { 
--- a/libpurple/plugins/perl/scripts/plugin_pref.pl	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/scripts/plugin_pref.pl	Wed Jun 13 19:30:27 2012 -0400
@@ -28,7 +28,7 @@
 	my $TEST_GROUP		= "perlTestGroup";
 	my $TEST_NAME	 	= "perlTestName";
 	my $TEST_ALIAS	 	= "perlTestAlias";
-	my $PROTOCOL_ID 	= "prpl-oscar";
+	my $PROTOCOL_ID 	= "prpl-aim";
 
 sub foo {
 	$frame = Purple::PluginPref::Frame->new();
--- a/libpurple/plugins/perl/scripts/signals-test.pl	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/perl/scripts/signals-test.pl	Wed Jun 13 19:30:27 2012 -0400
@@ -44,6 +44,11 @@
 	Purple::Debug::misc("signals test in perl", "$data (" . $account->get_username() . ", $sender, $message, $flags)\n");
 }
 
+sub timeout_cb
+{
+	Purple::Debug::misc("signals test in perl", "timeout elapsed\n");
+}
+
 sub plugin_load
 {
 	my $plugin = shift;
@@ -71,6 +76,9 @@
 					\&conv_received_msg, "received im message");
 	Purple::Signal::connect($conv, "received-chat-msg", $plugin,
 					\&conv_received_msg, "received chat message");
+
+
+	Purple::timeout_add($plugin, 10, \&timeout_cb);
 }
 
 sub plugin_unload
--- a/libpurple/plugins/psychic.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/psychic.c	Wed Jun 13 19:30:27 2012 -0400
@@ -85,9 +85,9 @@
 
   PurplePluginPrefFrame *frame;
   PurplePluginPref *pref;
-  
+
   frame = purple_plugin_pref_frame_new();
-  
+
   pref = purple_plugin_pref_new_with_name(PREF_BUDDIES);
   purple_plugin_pref_set_label(pref, _("Only enable for users on"
 				     " the buddy list"));
@@ -118,7 +118,7 @@
 
   purple_signal_connect(convs_handle, "buddy-typing", plugin,
 		      PURPLE_CALLBACK(buddy_typing_cb), NULL);
-  
+
   return TRUE;
 }
 
@@ -127,7 +127,7 @@
   get_plugin_pref_frame,
   0,    /* page_num (Reserved) */
   NULL, /* frame (Reserved) */
-  
+
   /* padding */
   NULL,
   NULL,
@@ -145,7 +145,7 @@
   0,                      /**< flags */
   NULL,                   /**< dependencies */
   PURPLE_PRIORITY_DEFAULT,  /**< priority */
-  
+
   PLUGIN_ID,              /**< id */
   PLUGIN_NAME,            /**< name */
   DISPLAY_VERSION,        /**< version */
@@ -153,11 +153,11 @@
   PLUGIN_DESC,            /**< description */
   PLUGIN_AUTHOR,          /**< author */
   PURPLE_WEBSITE,           /**< homepage */
-  
+
   plugin_load,            /**< load */
   NULL,                   /**< unload */
   NULL,                   /**< destroy */
-  
+
   NULL,                   /**< ui_info */
   NULL,                   /**< extra_info */
   &prefs_info,            /**< prefs_info */
--- a/libpurple/plugins/signals-test.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/signals-test.c	Wed Jun 13 19:30:27 2012 -0400
@@ -20,9 +20,10 @@
  */
 #define SIGNAL_TEST_PLUGIN_ID "core-signals-test"
 
+#include "internal.h"
+
 #include <stdio.h>
 
-#include "internal.h"
 #include "cipher.h"
 #include "connection.h"
 #include "conversation.h"
@@ -546,6 +547,26 @@
 	purple_debug_misc("signals test", "quitting ()\n");
 }
 
+static void
+printhash(gpointer key, gpointer value, gpointer data)
+{
+	char *a = (char *)key;
+	char *b = (char *)value;
+	GString *str = (GString *)data;
+	g_string_append_printf(str, "   [%s] = [%s]\n", a, b ? b : "(null)");
+}
+
+static gboolean
+uri_handler(const char *proto, const char *cmd, GHashTable *params)
+{
+	GString *str = g_string_new("\n{\n");
+	g_hash_table_foreach(params, printhash, str);
+	g_string_append_c(str, '}');
+	purple_debug_misc("signals test", "uri handler (%s, %s, %s)\n", proto, cmd, str->str);
+	g_string_free(str, TRUE);
+	return FALSE;
+}
+
 /**************************************************************************
  * File transfer signal callbacks
  **************************************************************************/
@@ -571,12 +592,12 @@
 
 static void
 ft_recv_cancel_cb(PurpleXfer *xfer, gpointer data) {
-	purple_debug_misc("signals test", "file receive canceled\n");
+	purple_debug_misc("signals test", "file receive cancelled\n");
 }
 
 static void
 ft_send_cancel_cb(PurpleXfer *xfer, gpointer data) {
-	purple_debug_misc("signals test", "file send canceled\n");
+	purple_debug_misc("signals test", "file send cancelled\n");
 }
 
 static void
@@ -819,6 +840,8 @@
 	/* Core signals */
 	purple_signal_connect(core_handle, "quitting",
 						plugin, PURPLE_CALLBACK(quitting_cb), NULL);
+	purple_signal_connect(core_handle, "uri-handler",
+						plugin,	PURPLE_CALLBACK(uri_handler), NULL);
 
 	/* File transfer signals */
 	purple_signal_connect(ft_handle, "file-recv-accept",
--- a/libpurple/plugins/ssl/Makefile.am	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/ssl/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -9,30 +9,15 @@
 
 if PLUGINS
 
-# I'm sorry to report that Automake Conditionals don't support
-#   if USE_GNUTLS && USE_NSS
-# but only support testing a single variable. Hence:
-
+plugin_LTLIBRARIES = \
+	ssl.la
 if USE_GNUTLS
-if USE_NSS
-plugin_LTLIBRARIES = \
-	ssl.la           \
-	ssl-gnutls.la    \
-	ssl-nss.la
-else
-plugin_LTLIBRARIES = \
-	ssl.la           \
+plugin_LTLIBRARIES += \
 	ssl-gnutls.la
 endif
-else
 if USE_NSS
-plugin_LTLIBRARIES = \
-	ssl.la           \
+plugin_LTLIBRARIES += \
 	ssl-nss.la
-else
-plugin_LTLIBRARIES = \
-	ssl.la
-endif
 endif
 
 ssl_la_SOURCES        = ssl.c
@@ -56,3 +41,4 @@
 
 ssl_gnutls_la_CFLAGS = $(AM_CPPFLAGS) $(GNUTLS_CFLAGS)
 ssl_nss_la_CFLAGS = $(AM_CPPFLAGS) $(NSS_CFLAGS)
+
--- a/libpurple/plugins/ssl/Makefile.mingw	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/ssl/Makefile.mingw	Wed Jun 13 19:30:27 2012 -0400
@@ -15,14 +15,16 @@
 
 NEEDED_DLLS = \
 			$(NSS_TOP)/lib/freebl3.dll \
+			$(NSS_TOP)/lib/libnspr4.dll \
+			$(NSS_TOP)/lib/libplc4.dll \
+			$(NSS_TOP)/lib/libplds4.dll \
 			$(NSS_TOP)/lib/nss3.dll \
 			$(NSS_TOP)/lib/nssckbi.dll \
-			$(NSS_TOP)/lib/softokn3.dll \
+			$(NSS_TOP)/lib/nssutil3.dll \
 			$(NSS_TOP)/lib/smime3.dll \
-			$(NSS_TOP)/lib/ssl3.dll \
-			$(NSPR_TOP)/lib/nspr4.dll \
-			$(NSPR_TOP)/lib/plc4.dll \
-			$(NSPR_TOP)/lib/plds4.dll
+			$(NSS_TOP)/lib/softokn3.dll \
+                        $(NSS_TOP)/lib/sqlite3.dll \
+			$(NSS_TOP)/lib/ssl3.dll 
 
 ##
 ## INCLUDE PATHS
@@ -34,13 +36,11 @@
 			-I$(PURPLE_TOP) \
 			-I$(PURPLE_TOP)/win32 \
 			-I$(PIDGIN_TREE_TOP) \
-			-I$(NSS_TOP)/include \
-			-I$(NSPR_TOP)/include
+			-I$(NSS_TOP)/include
 
 LIB_PATHS +=		-L$(GTK_TOP)/lib \
 			-L$(PURPLE_TOP) \
-			-L$(NSS_TOP)/lib \
-			-L$(NSPR_TOP)/lib
+			-L$(NSS_TOP)/lib
 
 ##
 ##  SOURCES, OBJECTS
--- a/libpurple/plugins/ssl/ssl-gnutls.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/ssl/ssl-gnutls.c	Wed Jun 13 19:30:27 2012 -0400
@@ -36,11 +36,25 @@
 {
 	gnutls_session session;
 	guint handshake_handler;
+	guint handshake_timer;
 } PurpleSslGnutlsData;
 
 #define PURPLE_SSL_GNUTLS_DATA(gsc) ((PurpleSslGnutlsData *)gsc->private_data)
 
-static gnutls_certificate_client_credentials xcred;
+static gnutls_certificate_client_credentials xcred = NULL;
+
+#ifdef HAVE_GNUTLS_PRIORITY_FUNCS
+/* Priority strings.  The default one is, well, the default (and is always
+ * set).  The hash table is of the form hostname => priority (both
+ * char *).
+ *
+ * We only use a gnutls_priority_t for the default on the assumption that
+ * that's the more common case.  Improvement patches (like matching on
+ * subdomains) welcome.
+ */
+static gnutls_priority_t default_priority = NULL;
+static GHashTable *host_priorities = NULL;
+#endif
 
 static void
 ssl_gnutls_log(int level, const char *str)
@@ -53,6 +67,7 @@
 ssl_gnutls_init_gnutls(void)
 {
 	const char *debug_level;
+	const char *host_priorities_str;
 
 	/* Configure GnuTLS to use glib memory management */
 	/* I expect that this isn't really necessary, but it may prevent
@@ -82,6 +97,80 @@
 		gnutls_global_set_log_function(ssl_gnutls_log);
 	}
 
+	/* Expected format: host=priority;host2=priority;*=priority
+	 * where "*" is used to override the default priority string for
+	 * libpurple.
+	 */
+	host_priorities_str = g_getenv("PURPLE_GNUTLS_PRIORITIES");
+	if (host_priorities_str) {
+#ifndef HAVE_GNUTLS_PRIORITY_FUNCS
+		purple_debug_warning("gnutls", "Warning, PURPLE_GNUTLS_PRIORITIES "
+		                     "environment variable set, but we were built "
+		                     "against an older GnuTLS that doesn't support "
+		                     "this. :-(");
+#else /* HAVE_GNUTLS_PRIORITY_FUNCS */
+		char **entries = g_strsplit(host_priorities_str, ";", -1);
+		char *default_priority_str = NULL;
+		guint i;
+
+		host_priorities = g_hash_table_new_full(g_str_hash, g_str_equal,
+		                                        g_free, g_free);
+
+		for (i = 0; entries[i]; ++i) {
+			char *host = entries[i];
+			char *equals = strchr(host, '=');
+			char *prio_str;
+
+			if (equals) {
+				*equals = '\0';
+				prio_str = equals + 1;
+
+				/* Empty? */
+				if (*prio_str == '\0') {
+					purple_debug_warning("gnutls", "Ignoring empty priority "
+					                               "string for %s\n", host);
+				} else {
+					/* TODO: Validate each of these and complain */
+					if (g_str_equal(host, "*")) {
+						/* Override the default priority */
+						g_free(default_priority_str);
+						default_priority_str = g_strdup(prio_str);
+					} else
+						g_hash_table_insert(host_priorities, g_strdup(host),
+						                    g_strdup(prio_str));
+				}
+			}
+		}
+
+		if (default_priority_str) {
+			if (gnutls_priority_init(&default_priority, default_priority_str, NULL)) {
+				purple_debug_warning("gnutls", "Unable to set default priority to %s\n",
+				                     default_priority_str);
+				/* Versions of GnuTLS as of 2.8.6 (2010-03-31) don't free/NULL
+				 * this on error.
+				 */
+				gnutls_free(default_priority);
+				default_priority = NULL;
+			}
+
+			g_free(default_priority_str);
+		}
+
+		g_strfreev(entries);
+#endif /* HAVE_GNUTLS_PRIORITY_FUNCS */
+	}
+
+#ifdef HAVE_GNUTLS_PRIORITY_FUNCS
+	/* Make sure we set have a default priority! */
+	if (!default_priority) {
+		if (gnutls_priority_init(&default_priority, "NORMAL:%SSL3_RECORD_VERSION", NULL)) {
+			/* See comment above about memory leak */
+			gnutls_free(default_priority);
+			gnutls_priority_init(&default_priority, "NORMAL", NULL);
+		}
+	}
+#endif /* HAVE_GNUTLS_PRIORITY_FUNCS */
+
 	gnutls_global_init();
 
 	gnutls_certificate_allocate_credentials(&xcred);
@@ -103,6 +192,17 @@
 	gnutls_global_deinit();
 
 	gnutls_certificate_free_credentials(xcred);
+	xcred = NULL;
+
+#ifdef HAVE_GNUTLS_PRIORITY_FUNCS
+	if (host_priorities) {
+		g_hash_table_destroy(host_priorities);
+		host_priorities = NULL;
+	}
+
+	gnutls_priority_deinit(default_priority);
+	default_priority = NULL;
+#endif
 }
 
 static void
@@ -268,6 +368,19 @@
 
 }
 
+static gboolean
+start_handshake_cb(gpointer data)
+{
+	PurpleSslConnection *gsc = data;
+	PurpleSslGnutlsData *gnutls_data = PURPLE_SSL_GNUTLS_DATA(gsc);
+
+	purple_debug_info("gnutls", "Starting handshake with %s\n", gsc->host);
+
+	gnutls_data->handshake_timer = 0;
+
+	ssl_gnutls_handshake_cb(gsc, gsc->fd, PURPLE_INPUT_READ);
+	return FALSE;
+}
 
 static void
 ssl_gnutls_connect(PurpleSslConnection *gsc)
@@ -280,9 +393,22 @@
 
 	gnutls_init(&gnutls_data->session, GNUTLS_CLIENT);
 #ifdef HAVE_GNUTLS_PRIORITY_FUNCS
-	if (gnutls_priority_set_direct(gnutls_data->session,
-		                             "NORMAL:%SSL3_RECORD_VERSION", NULL))
-		gnutls_priority_set_direct(gnutls_data->session, "NORMAL", NULL);
+	{
+		const char *prio_str = NULL;
+		gboolean set = FALSE;
+
+		/* Let's see if someone has specified a specific priority */
+		if (gsc->host && host_priorities)
+			prio_str = g_hash_table_lookup(host_priorities, gsc->host);
+
+		if (prio_str)
+			set = (GNUTLS_E_SUCCESS ==
+					gnutls_priority_set_direct(gnutls_data->session, prio_str,
+				                               NULL));
+
+		if (!set)
+			gnutls_priority_set(gnutls_data->session, default_priority);
+	}
 #else
 	gnutls_set_default_priority(gnutls_data->session);
 #endif
@@ -298,10 +424,8 @@
 	gnutls_data->handshake_handler = purple_input_add(gsc->fd,
 		PURPLE_INPUT_READ, ssl_gnutls_handshake_cb, gsc);
 
-	purple_debug_info("gnutls", "Starting handshake with %s\n", gsc->host);
-
 	/* Orborde asks: Why are we configuring a callback, then
-	   immediately calling it?
+	   (almost) immediately calling it?
 
 	   Answer: gnutls_handshake (up in handshake_cb) needs to be called
 	   once in order to get the ball rolling on the SSL connection.
@@ -312,7 +436,8 @@
 	   and subsequent calls, we'll just fire the callback immediately to
 	   accomplish this.
 	*/
-	ssl_gnutls_handshake_cb(gsc, gsc->fd, PURPLE_INPUT_READ);
+	gnutls_data->handshake_timer = purple_timeout_add(0, start_handshake_cb,
+	                                                  gsc);
 }
 
 static void
@@ -325,6 +450,8 @@
 
 	if(gnutls_data->handshake_handler)
 		purple_input_remove(gnutls_data->handshake_handler);
+	if (gnutls_data->handshake_timer)
+		purple_timeout_remove(gnutls_data->handshake_timer);
 
 	gnutls_bye(gnutls_data->session, GNUTLS_SHUT_RDWR);
 
@@ -393,11 +520,18 @@
 /* Forward declarations are fun! */
 static PurpleCertificate *
 x509_import_from_datum(const gnutls_datum dt, gnutls_x509_crt_fmt mode);
+/* indeed! */
+static gboolean
+x509_certificate_signed_by(PurpleCertificate * crt,
+			   PurpleCertificate * issuer);
+static void
+x509_destroy_certificate(PurpleCertificate * crt);
 
 static GList *
 ssl_gnutls_get_peer_certificates(PurpleSslConnection * gsc)
 {
 	PurpleSslGnutlsData *gnutls_data = PURPLE_SSL_GNUTLS_DATA(gsc);
+	PurpleCertificate *prvcrt = NULL;
 
 	/* List of Certificate instances to return */
 	GList * peer_certs = NULL;
@@ -423,7 +557,17 @@
 		/* Append is somewhat inefficient on linked lists, but is easy
 		   to read. If someone complains, I'll change it.
 		   TODO: Is anyone complaining? (Maybe elb?) */
-		peer_certs = g_list_append(peer_certs, newcrt);
+		/* only append if previous cert was actually signed by this one.
+		 * Thanks Microsoft. */
+		if ((prvcrt == NULL) || x509_certificate_signed_by(prvcrt, newcrt)) {
+			peer_certs = g_list_append(peer_certs, newcrt);
+			prvcrt = newcrt;
+		} else {
+			x509_destroy_certificate(newcrt);
+			purple_debug_error("gnutls", "Dropping further peer certificates "
+			                             "because the chain is broken!\n");
+			break;
+		}
 	}
 
 	/* cert_list doesn't need free()-ing */
@@ -548,6 +692,55 @@
 	return crt;
 }
 
+/** Imports a number of PEM-formatted X.509 certificates from the specified file.
+ * @param filename Filename to import from. Format is PEM
+ *
+ * @return A newly allocated GSList of Certificate structures of the x509_gnutls scheme
+ */
+static GSList *
+x509_importcerts_from_file(const gchar * filename)
+{
+	PurpleCertificate *crt;  /* Certificate being constructed */
+	gchar *buf;        /* Used to load the raw file data */
+	gchar *begin, *end;
+	GSList *crts = NULL;
+	gsize buf_sz;      /* Size of the above */
+	gnutls_datum dt; /* Struct to pass down to GnuTLS */
+
+	purple_debug_info("gnutls",
+			  "Attempting to load X.509 certificates from %s\n",
+			  filename);
+
+	/* Next, we'll simply yank the entire contents of the file
+	   into memory */
+	/* TODO: Should I worry about very large files here? */
+	g_return_val_if_fail(
+		g_file_get_contents(filename,
+			    &buf,
+			    &buf_sz,
+			    NULL      /* No error checking for now */
+		),
+		NULL);
+
+	begin = buf;
+	while((end = strstr(begin, "-----END CERTIFICATE-----")) != NULL) {
+		end += sizeof("-----END CERTIFICATE-----")-1;
+		/* Load the datum struct */
+		dt.data = (unsigned char *) begin;
+		dt.size = (end-begin);
+
+		/* Perform the conversion; files should be in PEM format */
+		crt = x509_import_from_datum(dt, GNUTLS_X509_FMT_PEM);
+		crts = g_slist_prepend(crts, crt);
+		begin = end;
+	}
+
+	/* Cleanup */
+	g_free(buf);
+
+	return crts;
+}
+
 /**
  * Exports a PEM-formatted X.509 certificate to the specified file.
  * @param filename Filename to export to. Format will be PEM
@@ -698,9 +891,8 @@
 			crt_issuer_id =
 				purple_certificate_get_issuer_unique_id(crt);
 			purple_debug_info("gnutls/x509",
-					  "Certificate for %s claims to be "
-					  "issued by %s, but the certificate "
-					  "for %s does not match.\n",
+					  "Certificate %s is issued by "
+					  "%s, which does not match %s.\n",
 					  crt_id ? crt_id : "(null)",
 					  crt_issuer_id ? crt_issuer_id : "(null)",
 					  issuer_id ? issuer_id : "(null)");
@@ -730,6 +922,7 @@
 		return FALSE;
 	}
 
+#ifdef HAVE_GNUTLS_CERT_INSECURE_ALGORITHM
 	if (verify & GNUTLS_CERT_INSECURE_ALGORITHM) {
 		/*
 		 * A certificate in the chain is signed with an insecure
@@ -743,6 +936,7 @@
 				"Insecure hash algorithm used by %s to sign %s\n",
 				issuer_id, crt_id);
 	}
+#endif
 
 	if (verify & GNUTLS_CERT_INVALID) {
 		/* Signature didn't check out, but at least
@@ -948,6 +1142,37 @@
 	return success;
 }
 
+static GByteArray *
+x509_get_der_data(PurpleCertificate *crt)
+{
+	gnutls_x509_crt crt_dat;
+	GByteArray *data;
+	size_t len;
+	int ret;
+
+	crt_dat = X509_GET_GNUTLS_DATA(crt);
+	g_return_val_if_fail(crt_dat, NULL);
+
+	/* Obtain the output size required */
+	len = 0;
+	ret = gnutls_x509_crt_export(crt_dat, GNUTLS_X509_FMT_DER, NULL, &len);
+	g_return_val_if_fail(ret == GNUTLS_E_SHORT_MEMORY_BUFFER, NULL);
+
+	/* Now allocate a buffer and *really* export it */
+	data = g_byte_array_sized_new(len);
+	data->len = len;
+	ret = gnutls_x509_crt_export(crt_dat, GNUTLS_X509_FMT_DER, data->data, &len);
+	if (ret != 0) {
+		purple_debug_error("gnutls/x509",
+		                   "Failed to export cert to buffer with code %d\n",
+		                   ret);
+		g_byte_array_free(data, TRUE);
+		return NULL;
+	}
+
+	return data;
+}
+
 /* X.509 certificate operations provided by this plugin */
 static PurpleCertificateScheme x509_gnutls = {
 	"x509",                          /* Scheme name */
@@ -963,10 +1188,10 @@
 	x509_common_name,                /* Subject name */
 	x509_check_name,                 /* Check subject name */
 	x509_times,                      /* Activation/Expiration time */
+	x509_importcerts_from_file,      /* Multiple certificates import function */
+	x509_get_der_data,               /* Binary DER data */
 
 	NULL,
-	NULL,
-	NULL,
 	NULL
 
 };
--- a/libpurple/plugins/ssl/ssl-nss.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/ssl/ssl-nss.c	Wed Jun 13 19:30:27 2012 -0400
@@ -51,7 +51,7 @@
 	PRFileDesc *fd;
 	PRFileDesc *in;
 	guint handshake_handler;
-
+	guint handshake_timer;
 } PurpleSslNssData;
 
 #define PURPLE_SSL_NSS_DATA(gsc) ((PurpleSslNssData *)gsc->private_data)
@@ -235,6 +235,7 @@
 static void
 ssl_nss_uninit(void)
 {
+	NSS_Shutdown();
 	PR_Cleanup();
 
 	_nss_methods = NULL;
@@ -288,13 +289,13 @@
 	GList * peer_certs = NULL;
 	int count;
 	int64 now = PR_Now();
-	
+
 	curcert = SSL_PeerCertificate(socket);
 	if (curcert == NULL) {
 		purple_debug_error("nss", "could not DupCertificate\n");
 		return NULL;
 	}
-	
+
 	for (count = 0 ; count < CERT_MAX_CERT_CHAIN ; count++) {
 		purple_debug_info("nss", "subject=%s issuer=%s\n", curcert->subjectName,
 						  curcert->issuerName  ? curcert->issuerName : "(null)");
@@ -368,6 +369,18 @@
 	}
 }
 
+static gboolean
+start_handshake_cb(gpointer data)
+{
+	PurpleSslConnection *gsc = data;
+	PurpleSslNssData *nss_data = PURPLE_SSL_NSS_DATA(gsc);
+
+	nss_data->handshake_timer = 0;
+
+	ssl_nss_handshake_cb(gsc, gsc->fd, PURPLE_INPUT_READ);
+	return FALSE;
+}
+
 static void
 ssl_nss_connect(PurpleSslConnection *gsc)
 {
@@ -438,7 +451,7 @@
 	nss_data->handshake_handler = purple_input_add(gsc->fd,
 		PURPLE_INPUT_READ, ssl_nss_handshake_cb, gsc);
 
-	ssl_nss_handshake_cb(gsc, gsc->fd, PURPLE_INPUT_READ);
+	nss_data->handshake_timer = purple_timeout_add(0, start_handshake_cb, gsc);
 }
 
 static void
@@ -460,6 +473,9 @@
 	if (nss_data->handshake_handler)
 		purple_input_remove(nss_data->handshake_handler);
 
+	if (nss_data->handshake_timer)
+		purple_timeout_remove(nss_data->handshake_timer);
+
 	g_free(nss_data);
 	gsc->private_data = NULL;
 }
@@ -514,7 +530,7 @@
 		CERT_DestroyCertificate(cert);
 #endif
 
-	
+
 
 	return NULL;
 }
@@ -530,7 +546,7 @@
 /** Imports a PEM-formatted X.509 certificate from the specified file.
  * @param filename Filename to import from. Format is PEM
  *
- * @return A newly allocated Certificate structure of the x509_gnutls scheme
+ * @return A newly allocated Certificate structure of the x509_nss scheme
  */
 static PurpleCertificate *
 x509_import_from_file(const gchar *filename)
@@ -571,10 +587,64 @@
 	crt = g_new0(PurpleCertificate, 1);
 	crt->scheme = &x509_nss;
 	crt->data = crt_dat;
-	
+
 	return crt;
 }
 
+/** Imports a number of PEM-formatted X.509 certificates from the specified file.
+ * @param filename Filename to import from. Format is PEM
+ *
+ * @return A GSList of newly allocated Certificate structures of the x509_nss scheme
+ */
+static GSList *
+x509_importcerts_from_file(const gchar *filename)
+{
+	gchar *rawcert, *begin, *end;
+	gsize len = 0;
+	GSList *crts = NULL;
+	CERTCertificate *crt_dat;
+	PurpleCertificate *crt;
+
+	g_return_val_if_fail(filename != NULL, NULL);
+
+	purple_debug_info("nss/x509",
+			  "Loading certificate from %s\n",
+			  filename);
+
+	/* Load the raw data up */
+	if (!g_file_get_contents(filename,
+				 &rawcert, &len,
+				 NULL)) {
+		purple_debug_error("nss/x509", "Unable to read certificate file.\n");
+		return NULL;
+	}
+
+	if (len == 0) {
+		purple_debug_error("nss/x509",
+				"Certificate file has no contents!\n");
+		if (rawcert)
+			g_free(rawcert);
+		return NULL;
+	}
+
+	begin = rawcert;
+	while((end = strstr(begin, "-----END CERTIFICATE-----")) != NULL) {
+		end += sizeof("-----END CERTIFICATE-----")-1;
+		/* Decode the certificate */
+		crt_dat = CERT_DecodeCertFromPackage(begin, (end-begin));
+
+		g_return_val_if_fail(crt_dat != NULL, NULL);
+
+		crt = g_new0(PurpleCertificate, 1);
+		crt->scheme = &x509_nss;
+		crt->data = crt_dat;
+		crts = g_slist_prepend(crts, crt);
+		begin = end;
+	}
+	g_free(rawcert);
+
+	return crts;
+}
 /**
  * Exports a PEM-formatted X.509 certificate to the specified file.
  * @param filename Filename to export to. Format will be PEM
@@ -602,7 +672,7 @@
 
 	purple_debug_info("nss/x509",
 			  "Exporting certificate to %s\n", filename);
-	
+
 	/* First, use NSS voodoo to create a DER-formatted certificate */
 	dercrt = SEC_ASN1EncodeItem(NULL, NULL, crt_dat,
 				    SEC_ASN1_GET(SEC_SignedCertificateTemplate));
@@ -622,7 +692,7 @@
 	ret =  purple_util_write_data_to_file_absolute(filename, pemcrt, -1);
 
 	g_free(pemcrt);
-	
+
 	return ret;
 }
 
@@ -643,7 +713,7 @@
 	newcrt->scheme = &x509_nss;
 	/* NSS does refcounting automatically */
 	newcrt->data = CERT_DupCertificate(crt_dat);
-	
+
 	return newcrt;
 }
 
@@ -689,7 +759,7 @@
 	CERTCertificate *subjectCert;
 	CERTCertificate *issuerCert;
 	SECStatus st;
-	
+
 	issuerCert = X509_NSS_DATA(issuer);
 	g_return_val_if_fail(issuerCert, FALSE);
 
@@ -725,7 +795,7 @@
 	sha1sum = g_byte_array_sized_new(hashlen);
 	/* glib leaves the size as 0 by default */
 	sha1sum->len = hashlen;
-	
+
 	st = PK11_HashBuf(SEC_OID_SHA1, sha1sum->data,
 			  derCert->data, derCert->len);
 
@@ -744,7 +814,7 @@
 x509_dn (PurpleCertificate *crt)
 {
 	CERTCertificate *crt_dat;
-	
+
 	g_return_val_if_fail(crt, NULL);
 	g_return_val_if_fail(crt->scheme == &x509_nss, NULL);
 
@@ -758,7 +828,7 @@
 x509_issuer_dn (PurpleCertificate *crt)
 {
 	CERTCertificate *crt_dat;
-	
+
 	g_return_val_if_fail(crt, NULL);
 	g_return_val_if_fail(crt->scheme == &x509_nss, NULL);
 
@@ -774,7 +844,7 @@
 	CERTCertificate *crt_dat;
 	char *nss_cn;
 	gchar *ret_cn;
-	
+
 	g_return_val_if_fail(crt, NULL);
 	g_return_val_if_fail(crt->scheme == &x509_nss, NULL);
 
@@ -805,7 +875,7 @@
 {
 	CERTCertificate *crt_dat;
 	SECStatus st;
-	
+
 	g_return_val_if_fail(crt, FALSE);
 	g_return_val_if_fail(crt->scheme == &x509_nss, FALSE);
 
@@ -820,7 +890,7 @@
 	else if (st == SECFailure) {
 		return FALSE;
 	}
-	
+
 	/* If we get here...bad things! */
 	purple_debug_error("nss/x509",
 			   "x509_check_name fell through where it shouldn't "
@@ -833,7 +903,7 @@
 {
 	CERTCertificate *crt_dat;
 	PRTime nss_activ, nss_expir;
-	
+
 	g_return_val_if_fail(crt, FALSE);
 	g_return_val_if_fail(crt->scheme == &x509_nss, FALSE);
 
@@ -856,10 +926,33 @@
 	if (expiration) {
 		*expiration = nss_expir / 1000000;
 	}
-	
+
 	return TRUE;
 }
 
+static GByteArray *
+x509_get_der_data(PurpleCertificate *crt)
+{
+	CERTCertificate *crt_dat;
+	SECItem *dercrt;
+	GByteArray *data;
+
+	crt_dat = X509_NSS_DATA(crt);
+	g_return_val_if_fail(crt_dat, NULL);
+
+	dercrt = SEC_ASN1EncodeItem(NULL, NULL, crt_dat,
+	                            SEC_ASN1_GET(SEC_SignedCertificateTemplate));
+	g_return_val_if_fail(dercrt != NULL, FALSE);
+
+	data = g_byte_array_sized_new(dercrt->len);
+	memcpy(data->data, dercrt->data, dercrt->len);
+	data->len = dercrt->len;
+
+	SECITEM_FreeItem(dercrt, PR_TRUE);
+
+	return data;
+}
+
 static PurpleCertificateScheme x509_nss = {
 	"x509",                          /* Scheme name */
 	N_("X.509 Certificates"),        /* User-visible scheme name */
@@ -874,10 +967,10 @@
 	x509_common_name,                /* Subject name */
 	x509_check_name,                 /* Check subject name */
 	x509_times,                      /* Activation/Expiration time */
+	x509_importcerts_from_file,      /* Multiple certificate import function */
+	x509_get_der_data,               /* Binary DER data */
 
 	NULL,
-	NULL,
-	NULL,
 	NULL
 };
 
--- a/libpurple/plugins/statenotify.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/statenotify.c	Wed Jun 13 19:30:27 2012 -0400
@@ -30,7 +30,7 @@
 
 	if (conv == NULL)
 		return;
-	g_return_if_fail(conv->type == PURPLE_CONV_TYPE_IM);
+	g_return_if_fail(purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM);
 
 	/* Prevent duplicate notifications for buddies in multiple groups */
 	if (buddy != purple_find_buddy(account, buddy_name))
@@ -42,7 +42,7 @@
 	g_snprintf(buf, sizeof(buf), message, escaped);
 	g_free(escaped);
 
-	purple_conv_im_write(conv->u.im, NULL, buf, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_ACTIVE_ONLY | PURPLE_MESSAGE_NO_LINKIFY, time(NULL));
+	purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, buf, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_ACTIVE_ONLY | PURPLE_MESSAGE_NO_LINKIFY, time(NULL));
 }
 
 static void
--- a/libpurple/plugins/tcl/tcl_cmd.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/tcl/tcl_cmd.c	Wed Jun 13 19:30:27 2012 -0400
@@ -125,7 +125,7 @@
                                    gchar **args, gchar **errors,
                                    struct tcl_cmd_handler *handler)
 {
-	int retval, error, i;
+	int retval, i;
 	Tcl_Obj *command, *arg, *tclargs, *result;
 
 	command = Tcl_NewListObj(0, NULL);
@@ -153,8 +153,7 @@
 	}
 	Tcl_ListObjAppendElement(handler->interp, command, tclargs);
 
-	if ((error = Tcl_EvalObjEx(handler->interp, command,
-	                           TCL_EVAL_GLOBAL)) != TCL_OK) {
+	if (Tcl_EvalObjEx(handler->interp, command, TCL_EVAL_GLOBAL) != TCL_OK) {
 		gchar *errorstr;
 
 		errorstr = g_strdup_printf("error evaluating callback: %s\n",
@@ -164,8 +163,8 @@
 		retval = PURPLE_CMD_RET_FAILED;
 	} else {
 		result = Tcl_GetObjResult(handler->interp);
-		if ((error = Tcl_GetIntFromObj(handler->interp, result,
-		                               &retval)) != TCL_OK) {
+		if (Tcl_GetIntFromObj(handler->interp, result,
+		                      &retval) != TCL_OK) {
 			gchar *errorstr;
 
 			errorstr = g_strdup_printf("Error retreiving procedure result: %s\n",
--- a/libpurple/plugins/tcl/tcl_cmds.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/tcl/tcl_cmds.c	Wed Jun 13 19:30:27 2012 -0400
@@ -4,7 +4,7 @@
  * purple
  *
  * Copyright (C) 2003 Ethan Blanton <eblanton@cs.purdue.edu>
- * 
+ *
  * 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
@@ -184,7 +184,7 @@
 		if ((account = tcl_validate_account(objv[2], interp)) == NULL)
 			return TCL_ERROR;
 		if (objc == 3) {
-			Tcl_SetObjResult(interp, 
+			Tcl_SetObjResult(interp,
 					 Tcl_NewBooleanObj(
 						 purple_account_get_enabled(account,
 									    purple_core_get_ui())));
@@ -209,7 +209,7 @@
 			Tcl_WrongNumArgs(interp, 2, objv, "");
 			return TCL_ERROR;
 		}
-		Tcl_SetObjResult(interp, 
+		Tcl_SetObjResult(interp,
 				 purple_tcl_ref_new(PurpleTclRefHandle,
 						    purple_accounts_get_handle()));
 		break;
@@ -374,7 +374,7 @@
 		}
 		if ((account = tcl_validate_account(objv[2], interp)) == NULL)
 			return TCL_ERROR;
-		Tcl_SetObjResult(interp, 
+		Tcl_SetObjResult(interp,
 				 Tcl_NewStringObj((char *)purple_account_get_username(account), -1));
 		break;
 	}
@@ -865,7 +865,7 @@
 			return error;
 		from = Tcl_GetString(objv[4]);
 		what = Tcl_GetString(objv[5]);
-		
+
 		switch (style) {
 		case CMD_CONV_WRITE_SEND:
 			flags = PURPLE_MESSAGE_SEND;
@@ -1426,7 +1426,7 @@
 			Tcl_WrongNumArgs(interp, 2, objv, "");
 			return TCL_ERROR;
 		}
-		Tcl_SetObjResult(interp, 
+		Tcl_SetObjResult(interp,
 				 purple_tcl_ref_new(PurpleTclRefHandle,
 						    purple_savedstatuses_get_handle()));
 		break;
@@ -1640,13 +1640,13 @@
 int tcl_cmd_status_type(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
 {
 	const char *cmds[] = { "attr", "attrs", "available", "exclusive", "id",
-	                       "independent", "name", "primary_attr",
+	                       "independent", "name",
 	                       "primitive", "saveable", "user_settable",
 	                       NULL };
 	enum { CMD_STATUS_TYPE_ATTR, CMD_STATUS_TYPE_ATTRS,
 	       CMD_STATUS_TYPE_AVAILABLE, CMD_STATUS_TYPE_EXCLUSIVE,
 	       CMD_STATUS_TYPE_ID, CMD_STATUS_TYPE_INDEPENDENT,
-	       CMD_STATUS_TYPE_NAME, CMD_STATUS_TYPE_PRIMARY_ATTR,
+	       CMD_STATUS_TYPE_NAME,
 	       CMD_STATUS_TYPE_PRIMITIVE, CMD_STATUS_TYPE_SAVEABLE,
 	       CMD_STATUS_TYPE_USER_SETTABLE } cmd;
 	PurpleStatusType *status_type;
@@ -1751,18 +1751,6 @@
 				 Tcl_NewStringObj(purple_primitive_get_id_from_type
 						  (purple_status_type_get_primitive(status_type)), -1));
 		break;
-	case CMD_STATUS_TYPE_PRIMARY_ATTR:
-#if !(defined PURPLE_DISABLE_DEPRECATED)
-		if (objc != 3) {
-			Tcl_WrongNumArgs(interp, 2, objv, "statustype");
-			return TCL_ERROR;
-		}
-		if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL)
-			return TCL_ERROR;
-		Tcl_SetObjResult(interp,
-				 Tcl_NewStringObj(purple_status_type_get_primary_attr(status_type), -1));
-#endif
-		break;
 	case CMD_STATUS_TYPE_SAVEABLE:
 		if (objc != 3) {
 			Tcl_WrongNumArgs(interp, 2, objv, "statustype");
@@ -1809,7 +1797,7 @@
 		/* This isn't exactly OK, but heh.  What do you do? */
 		return TCL_OK;
 	}
-	/* We can't unload immediately, but we can unload at the first 
+	/* We can't unload immediately, but we can unload at the first
 	 * known safe opportunity. */
 	purple_timeout_add(0, unload_self, (gpointer)plugin);
 
--- a/libpurple/plugins/tcl/tcl_glib.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/tcl/tcl_glib.c	Wed Jun 13 19:30:27 2012 -0400
@@ -43,7 +43,7 @@
 
 /*
  * NOTES
- * 
+ *
  * This file was developed for the Purple project.  It inserts the Tcl
  * event loop into the glib2 event loop for the purposes of providing
  * Tcl bindings in a glib2 (e.g. Gtk2) program.  To use it, simply
@@ -51,9 +51,9 @@
  * function tcl_glib_init() before creating or using any Tcl
  * interpreters.  Then go ahead and use Tcl, Tk, whatever to your
  * heart's content.
- * 
+ *
  * BUGS
- * 
+ *
  * tcl_wait_for_event seems to have a bug that makes vwait not work so
  * well...  I'm not sure why, yet, but I haven't put much time into
  * it.  Hopefully I will figure it out soon.  In the meantime, this
@@ -159,7 +159,7 @@
 
 	if (g_hash_table_lookup(tcl_file_handlers, GINT_TO_POINTER(fd)))
             tcl_delete_file_handler(fd);
-	
+
 	if (mask & TCL_READABLE)
 		cond |= G_IO_IN;
 	if (mask & TCL_WRITABLE)
--- a/libpurple/plugins/tcl/tcl_glib.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/tcl/tcl_glib.h	Wed Jun 13 19:30:27 2012 -0400
@@ -2,7 +2,7 @@
  * Tcl/Glib glue
  *
  * Copyright (C) 2003 Ethan Blanton <eblanton@cs.purdue.edu>
- * 
+ *
  * 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
--- a/libpurple/plugins/tcl/tcl_purple.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/tcl/tcl_purple.h	Wed Jun 13 19:30:27 2012 -0400
@@ -4,7 +4,7 @@
  * purple
  *
  * Copyright (C) 2003 Ethan Blanton <eblanton@cs.purdue.edu>
- * 
+ *
  * 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
--- a/libpurple/plugins/tcl/tcl_ref.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/tcl/tcl_ref.c	Wed Jun 13 19:30:27 2012 -0400
@@ -4,7 +4,7 @@
  * purple
  *
  * Copyright (C) 2006 Ethan Blanton <eblanton@cs.purdue.edu>
- * 
+ *
  * 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
@@ -92,6 +92,7 @@
 
 static void purple_tcl_ref_update(Tcl_Obj *obj)
 {
+	size_t len;
 	/* This is ugly on memory, but we pretty much have to either
 	 * do this or guesstimate lengths or introduce a varargs
 	 * function in here ... ugh. */
@@ -100,8 +101,9 @@
 				      OBJ_REF_VALUE(obj));
 
 	obj->length = strlen(bytes);
-	obj->bytes = ckalloc(obj->length + 1);
-	strcpy(obj->bytes, bytes);
+	len = obj->length + 1;
+	obj->bytes = ckalloc(len);
+	g_strlcpy(obj->bytes, bytes, len);
 	g_free(bytes);
 }
 
--- a/libpurple/plugins/tcl/tcl_signals.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/plugins/tcl/tcl_signals.c	Wed Jun 13 19:30:27 2012 -0400
@@ -4,7 +4,7 @@
  * purple
  *
  * Copyright (C) 2003 Ethan Blanton <eblanton@cs.purdue.edu>
- * 
+ *
  * 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
@@ -117,7 +117,7 @@
 
 	for (cur = tcl_callbacks; cur != NULL; cur = g_list_next(cur)) {
 		handler = cur->data;
-		if (handler->interp == interp && handler->instance == instance 
+		if (handler->interp == interp && handler->instance == instance
 		    && !strcmp(signal, Tcl_GetString(handler->signal))) {
 			purple_signal_disconnect(instance, signal, handler->interp,
 					       PURPLE_CALLBACK(tcl_signal_callback));
@@ -160,7 +160,7 @@
 {
 	GString *name, *val;
 	PurpleBlistNode *node;
-	int error, i;
+	int i;
 	void *retval = NULL;
 	Tcl_Obj *cmd, *arg, *result;
 	void **vals; /* Used for inout parameters */
@@ -259,8 +259,9 @@
 					vals[i] = ckalloc(1);
 					*(char *)vals[i] = '\0';
 				} else {
-					vals[i] = ckalloc(strlen(*strs[i]) + 1);
-					strcpy(vals[i], *strs[i]);
+					size_t len = strlen(*strs[i]) + 1;
+					vals[i] = ckalloc(len);
+					g_strlcpy(vals[i], *strs[i], len);
 				}
 				Tcl_LinkVar(handler->interp, name->str,
 					    (char *)&vals[i], TCL_LINK_STRING);
@@ -335,7 +336,7 @@
 	}
 
 	/* Call the friggin' procedure already */
-	if ((error = Tcl_EvalObjEx(handler->interp, cmd, TCL_EVAL_GLOBAL)) != TCL_OK) {
+	if (Tcl_EvalObjEx(handler->interp, cmd, TCL_EVAL_GLOBAL) != TCL_OK) {
 		purple_debug(PURPLE_DEBUG_ERROR, "tcl", "error evaluating callback: %s\n",
 			   Tcl_GetString(Tcl_GetObjResult(handler->interp)));
 	} else {
@@ -345,7 +346,7 @@
 			if (purple_value_get_type(handler->returntype) == PURPLE_TYPE_STRING) {
 				retval = (void *)g_strdup(Tcl_GetString(result));
 			} else {
-				if ((error = Tcl_GetIntFromObj(handler->interp, result, (int *)&retval)) != TCL_OK) {
+				if (Tcl_GetIntFromObj(handler->interp, result, (int *)&retval) != TCL_OK) {
 					purple_debug(PURPLE_DEBUG_ERROR, "tcl", "Error retrieving procedure result: %s\n",
 						   Tcl_GetString(Tcl_GetObjResult(handler->interp)));
 					retval = NULL;
--- a/libpurple/pounce.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/pounce.c	Wed Jun 13 19:30:27 2012 -0400
@@ -32,6 +32,31 @@
 #include "pounce.h"
 #include "util.h"
 
+/**
+ * A buddy pounce structure.
+ *
+ * Buddy pounces are actions triggered by a buddy-related event. For
+ * example, a sound can be played or an IM window opened when a buddy
+ * signs on or returns from away. Such responses are handled in the
+ * UI. The events themselves are done in the core.
+ */
+struct _PurplePounce
+{
+	char *ui_type;                /**< The type of UI.            */
+
+	PurplePounceEvent events;       /**< The event(s) to pounce on. */
+	PurplePounceOption options;     /**< The pounce options         */
+	PurpleAccount *pouncer;         /**< The user who is pouncing.  */
+
+	char *pouncee;                /**< The buddy to pounce on.    */
+
+	GHashTable *actions;          /**< The registered actions.    */
+
+	gboolean save;                /**< Whether or not the pounce should
+	                                   be saved after activation. */
+	void *data;                   /**< Pounce-specific data.      */
+};
+
 typedef struct
 {
 	GString *buffer;
@@ -180,7 +205,7 @@
 	xmlnode_set_attrib(node, "ui", pounce->ui_type);
 
 	child = xmlnode_new_child(node, "account");
-	xmlnode_set_attrib(child, "protocol", pouncer->protocol_id);
+	xmlnode_set_attrib(child, "protocol", purple_account_get_protocol_id(pouncer));
 	xmlnode_insert_data(child,
 			purple_normalize(pouncer, purple_account_get_username(pouncer)), -1);
 
@@ -405,12 +430,8 @@
 	}
 
 	if (purple_strequal(element_name, "account")) {
-		char *tmp;
 		g_free(data->account_name);
 		data->account_name = g_strdup(buffer);
-		tmp = data->protocol_id;
-		data->protocol_id = g_strdup(_purple_oscar_convert(buffer, tmp));
-		g_free(tmp);
 	}
 	else if (purple_strequal(element_name, "pouncee")) {
 		g_free(data->pouncee);
@@ -695,6 +716,31 @@
 }
 
 void
+purple_pounce_destroy_all_by_buddy(PurpleBuddy *buddy)
+{
+	const char *pouncee, *bname;
+	PurpleAccount *pouncer, *bacct;
+	PurplePounce *pounce;
+	GList *l, *l_next;
+
+	g_return_if_fail(buddy != NULL);
+
+	bacct = purple_buddy_get_account(buddy);
+	bname = purple_buddy_get_name(buddy);
+
+	for (l = purple_pounces_get_all(); l != NULL; l = l_next) {
+		pounce = (PurplePounce *)l->data;
+		l_next = l->next;
+
+		pouncer = purple_pounce_get_pouncer(pounce);
+		pouncee = purple_pounce_get_pouncee(pounce);
+
+		if ( (pouncer == bacct) && (strcmp(pouncee, bname) == 0) )
+			purple_pounce_destroy(pounce);
+	}
+}
+
+void
 purple_pounce_set_events(PurplePounce *pounce, PurplePounceEvent events)
 {
 	g_return_if_fail(pounce != NULL);
@@ -1157,4 +1203,7 @@
 	}
 
 	purple_signals_disconnect_by_handle(purple_pounces_get_handle());
+
+	g_hash_table_destroy(pounce_handlers);
+	pounce_handlers = NULL;
 }
--- a/libpurple/pounce.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/pounce.h	Wed Jun 13 19:30:27 2012 -0400
@@ -59,34 +59,7 @@
 /** A pounce callback. */
 typedef void (*PurplePounceCb)(PurplePounce *, PurplePounceEvent, void *);
 
-/**
- * A buddy pounce structure.
- *
- * Buddy pounces are actions triggered by a buddy-related event. For
- * example, a sound can be played or an IM window opened when a buddy
- * signs on or returns from away. Such responses are handled in the
- * UI. The events themselves are done in the core.
- */
-struct _PurplePounce
-{
-	char *ui_type;                /**< The type of UI.            */
-
-	PurplePounceEvent events;       /**< The event(s) to pounce on. */
-	PurplePounceOption options;     /**< The pounce options         */
-	PurpleAccount *pouncer;         /**< The user who is pouncing.  */
-
-	char *pouncee;                /**< The buddy to pounce on.    */
-
-	GHashTable *actions;          /**< The registered actions.    */
-
-	gboolean save;                /**< Whether or not the pounce should
-	                                   be saved after activation. */
-	void *data;                   /**< Pounce-specific data.      */
-};
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Buddy Pounce API                                                */
@@ -123,6 +96,13 @@
 void purple_pounce_destroy_all_by_account(PurpleAccount *account);
 
 /**
+ * Destroys all buddy pounces for a buddy
+ *
+ * @param buddy The buddy whose pounces are to be removed
+ */
+void purple_pounce_destroy_all_by_buddy(PurpleBuddy *buddy);
+
+/**
  * Sets the events a pounce should watch for.
  *
  * @param pounce The buddy pounce.
@@ -350,7 +330,6 @@
  *
  * @return The list of buddy pounces. The list should be freed by
  *         the caller when it's no longer used.
- * @since  2.1.0
  */
 GList *purple_pounces_get_all_for_ui(const char *ui);
 
@@ -373,8 +352,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_POUNCE_H_ */
--- a/libpurple/prefs.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/prefs.c	Wed Jun 13 19:30:27 2012 -0400
@@ -277,6 +277,12 @@
 		}
 	}
 
+	if ((pref_type == PURPLE_PREF_BOOLEAN || pref_type == PURPLE_PREF_INT) &&
+			pref_value == NULL) {
+		/* Missing a value attribute */
+		return;
+	}
+
 	if(purple_strequal(element_name, "item")) {
 		struct purple_pref *pref;
 
@@ -506,7 +512,6 @@
 		return g_strdup("/");
 
 	name = g_string_new(pref->name);
-	parent = pref->parent;
 
 	for(parent = pref->parent; parent && parent->name; parent = parent->parent) {
 		name = g_string_prepend_c(name, '/');
@@ -1352,6 +1357,7 @@
 	purple_prefs_remove("/purple/conversations/chat/show_leave");
 	purple_prefs_remove("/purple/conversations/combine_chat_im");
 	purple_prefs_remove("/purple/conversations/use_alias_for_title");
+	purple_prefs_remove("/purple/debug/timestamps");
 	purple_prefs_remove("/purple/logging/log_signon_signoff");
 	purple_prefs_remove("/purple/logging/log_idle_state");
 	purple_prefs_remove("/purple/logging/log_away_state");
@@ -1450,6 +1456,8 @@
 		sync_prefs();
 	}
 
+	purple_prefs_disconnect_by_handle(purple_prefs_get_handle());
+
 	prefs_loaded = FALSE;
 	purple_prefs_destroy();
 	g_hash_table_destroy(prefs_hash);
--- a/libpurple/prefs.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/prefs.h	Wed Jun 13 19:30:27 2012 -0400
@@ -32,7 +32,7 @@
 /**
  * Preference data types.
  */
-typedef enum _PurplePrefType
+typedef enum
 {
 	PURPLE_PREF_NONE,        /**< No type.         */
 	PURPLE_PREF_BOOLEAN,     /**< Boolean.         */
@@ -62,9 +62,7 @@
 typedef void (*PurplePrefCallback) (const char *name, PurplePrefType type,
 		gconstpointer val, gpointer data);
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+G_BEGIN_DECLS
 
 /**************************************************************************/
 /** @name Prefs API
@@ -185,7 +183,16 @@
  *
  * @param name  The name of the pref
  * @param value The value to set
+ *
+ * @deprecated We're not really sure what purpose this function serves, so it
+ *             will be removed in 3.0.0.  Preferences values set using this
+ *             function aren't serialized to prefs.xml, which could be
+ *             misleading.  There is also no purple_prefs_get_generic, which
+ *             means that if you can't really get the value (other in a
+ *             connected callback).  If you think you have a use for this then
+ *             please let us know.
  */
+/* TODO: When this is removed, also remove struct purple_pref->value.generic */
 void purple_prefs_set_generic(const char *name, gpointer value);
 
 /**
@@ -308,8 +315,6 @@
  * @return A list of newly allocated strings denoting the names of the children.
  *         Returns @c NULL if there are no children or if pref doesn't exist.
  *         The caller must free all the strings and the list.
- *
- * @since 2.1.0
  */
 GList *purple_prefs_get_children_names(const char *name);
 
@@ -355,8 +360,6 @@
 
 /*@}*/
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_PREFS_H_ */
--- a/libpurple/privacy.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/privacy.c	Wed Jun 13 19:30:27 2012 -0400
@@ -264,9 +264,9 @@
 						gboolean restore)
 {
 	GSList *list;
-	PurplePrivacyType type = account->perm_deny;
+	PurplePrivacyType type = purple_account_get_privacy_type(account);
 
-	switch (account->perm_deny) {
+	switch (type) {
 		case PURPLE_PRIVACY_ALLOW_ALL:
 			return;
 		case PURPLE_PRIVACY_ALLOW_USERS:
@@ -287,13 +287,13 @@
 				}
 			}
 			purple_privacy_permit_add(account, who, local);
-			account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
+			purple_account_set_privacy_type(account, PURPLE_PRIVACY_ALLOW_USERS);
 			break;
 		case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
 			if (!purple_find_buddy(account, who)) {
 				add_all_buddies_to_permit_list(account, local);
 				purple_privacy_permit_add(account, who, local);
-				account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
+				purple_account_set_privacy_type(account, PURPLE_PRIVACY_ALLOW_USERS);
 			}
 			break;
 		default:
@@ -301,7 +301,7 @@
 	}
 
 	/* Notify the server if the privacy setting was changed */
-	if (type != account->perm_deny && purple_account_is_connected(account))
+	if (type != purple_account_get_privacy_type(account) && purple_account_is_connected(account))
 		serv_set_permit_deny(purple_account_get_connection(account));
 }
 
@@ -316,9 +316,9 @@
 					gboolean restore)
 {
 	GSList *list;
-	PurplePrivacyType type = account->perm_deny;
+	PurplePrivacyType type = purple_account_get_privacy_type(account);
 
-	switch (account->perm_deny) {
+	switch (type) {
 		case PURPLE_PRIVACY_ALLOW_ALL:
 			if (!restore) {
 				/* Empty the deny-list. */
@@ -331,7 +331,7 @@
 				}
 			}
 			purple_privacy_deny_add(account, who, local);
-			account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
+			purple_account_set_privacy_type(account, PURPLE_PRIVACY_DENY_USERS);
 			break;
 		case PURPLE_PRIVACY_ALLOW_USERS:
 			purple_privacy_permit_remove(account, who, local);
@@ -345,7 +345,7 @@
 			if (purple_find_buddy(account, who)) {
 				add_all_buddies_to_permit_list(account, local);
 				purple_privacy_permit_remove(account, who, local);
-				account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
+				purple_account_set_privacy_type(account, PURPLE_PRIVACY_ALLOW_USERS);
 			}
 			break;
 		default:
@@ -353,7 +353,7 @@
 	}
 
 	/* Notify the server if the privacy setting was changed */
-	if (type != account->perm_deny && purple_account_is_connected(account))
+	if (type != purple_account_get_privacy_type(account) && purple_account_is_connected(account))
 		serv_set_permit_deny(purple_account_get_connection(account));
 }
 
@@ -362,7 +362,7 @@
 {
 	GSList *list;
 
-	switch (account->perm_deny) {
+	switch (purple_account_get_privacy_type(account)) {
 		case PURPLE_PRIVACY_ALLOW_ALL:
 			return TRUE;
 
--- a/libpurple/privacy.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/privacy.h	Wed Jun 13 19:30:27 2012 -0400
@@ -29,7 +29,7 @@
 /**
  * Privacy data types.
  */
-typedef enum _PurplePrivacyType
+typedef enum
 {
 	PURPLE_PRIVACY_ALLOW_ALL = 1,
 	PURPLE_PRIVACY_DENY_ALL,
@@ -40,10 +40,6 @@
 
 #include "account.h"
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /**
  * Privacy core/UI operations.
  */
@@ -60,6 +56,8 @@
 	void (*_purple_reserved4)(void);
 } PurplePrivacyUiOps;
 
+G_BEGIN_DECLS
+
 /**
  * Adds a user to the account's permit list.
  *
@@ -187,8 +185,6 @@
  */
 void purple_privacy_init(void);
 
-#ifdef __cplusplus
-}
-#endif
+G_END_DECLS
 
 #endif /* _PURPLE_PRIVACY_H_ */
--- a/libpurple/protocols/Makefile.am	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -1,5 +1,5 @@
 EXTRA_DIST = Makefile.mingw
 
-DIST_SUBDIRS = bonjour gg irc jabber msn msnp9 myspace novell null oscar qq sametime silc silc10 simple yahoo zephyr
+DIST_SUBDIRS = bonjour gg irc jabber msn mxit myspace novell null oscar sametime silc simple yahoo zephyr
 
 SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS)
--- a/libpurple/protocols/Makefile.mingw	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/Makefile.mingw	Wed Jun 13 19:30:27 2012 -0400
@@ -8,7 +8,7 @@
 PIDGIN_TREE_TOP := ../..
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
-SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc simple yahoo bonjour myspace
+SUBDIRS = gg irc jabber msn mxit novell null oscar sametime silc simple yahoo bonjour myspace
 
 .PHONY: all install clean
 
--- a/libpurple/protocols/bonjour/Makefile.mingw	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/Makefile.mingw	Wed Jun 13 19:30:27 2012 -0400
@@ -30,7 +30,7 @@
 			-I$(GTK_TOP)/include/glib-2.0 \
 			-I$(GTK_TOP)/lib/glib-2.0/include \
 			-I$(BONJOUR_TOP)/Include \
-			-I$(LIBXML2_TOP)/include \
+			-I$(LIBXML2_TOP)/include/libxml2 \
 			-I$(PURPLE_TOP) \
 			-I$(PURPLE_TOP)/win32 \
 			-I$(PIDGIN_TREE_TOP)
--- a/libpurple/protocols/bonjour/bonjour.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/bonjour.c	Wed Jun 13 19:30:27 2012 -0400
@@ -46,7 +46,14 @@
 
 static char *default_firstname;
 static char *default_lastname;
-static char *default_hostname;
+
+const char *
+bonjour_get_jid(PurpleAccount *account)
+{
+	PurpleConnection *conn = purple_account_get_connection(account);
+	BonjourData *bd = purple_connection_get_protocol_data(conn);
+	return bd->jid;
+}
 
 static void
 bonjour_removeallfromlocal(PurpleConnection *conn, PurpleGroup *bonjour_group)
@@ -70,7 +77,6 @@
 			buddy = (PurpleBuddy *) bnode;
 			if (purple_buddy_get_account(buddy) != account)
 				continue;
-			purple_prpl_got_user_status(account, purple_buddy_get_name(buddy), "offline", NULL);
 			purple_account_remove_buddy(account, buddy, NULL);
 			purple_blist_remove_buddy(buddy);
 		}
@@ -88,7 +94,7 @@
 
 #ifdef _WIN32
 	if (!dns_sd_available()) {
-		purple_connection_error_reason(gc,
+		purple_connection_error(gc,
 				PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 				_("Unable to find Apple's \"Bonjour for Windows\" toolkit, see "
 				  "http://d.pidgin.im/BonjourWindows for more information."));
@@ -96,17 +102,20 @@
 	}
 #endif /* _WIN32 */
 
-	gc->flags |= PURPLE_CONNECTION_HTML;
-	gc->proto_data = bd = g_new0(BonjourData, 1);
+	purple_connection_set_flags(gc, PURPLE_CONNECTION_HTML);
+	bd = g_new0(BonjourData, 1);
+	purple_connection_set_protocol_data(gc, bd);
 
 	/* Start waiting for jabber connections (iChat style) */
 	bd->jabber_data = g_new0(BonjourJabber, 1);
+	bd->jabber_data->socket = -1;
+	bd->jabber_data->socket6 = -1;
 	bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
 	bd->jabber_data->account = account;
 
 	if (bonjour_jabber_start(bd->jabber_data) == -1) {
 		/* Send a message about the connection error */
-		purple_connection_error_reason (gc,
+		purple_connection_error (gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Unable to listen for incoming IM connections"));
 		return;
@@ -133,7 +142,7 @@
 	bd->dns_sd_data->account = account;
 	if (!bonjour_dns_sd_start(bd->dns_sd_data))
 	{
-		purple_connection_error_reason (gc,
+		purple_connection_error (gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Unable to establish connection with the local mDNS server.  Is it running?"));
 		return;
@@ -149,7 +158,7 @@
 bonjour_close(PurpleConnection *connection)
 {
 	PurpleGroup *bonjour_group;
-	BonjourData *bd = connection->proto_data;
+	BonjourData *bd = purple_connection_get_protocol_data(connection);
 
 	bonjour_group = purple_find_group(BONJOUR_GROUP_NAME);
 
@@ -170,7 +179,9 @@
 		g_free(bd->jabber_data);
 	}
 
-	/* Delete the bonjour group */
+	/* Delete the bonjour group
+	 * (purple_blist_remove_group will bail out if the group isn't empty)
+	 */
 	if (bonjour_group != NULL)
 		purple_blist_remove_group(bonjour_group);
 
@@ -179,8 +190,10 @@
 		purple_xfer_cancel_local(bd->xfer_lists->data);
 	}
 
+	if (bd != NULL)
+		g_free(bd->jid);
 	g_free(bd);
-	connection->proto_data = NULL;
+	purple_connection_set_protocol_data(connection, NULL);
 }
 
 static const char *
@@ -192,10 +205,12 @@
 static int
 bonjour_send_im(PurpleConnection *connection, const char *to, const char *msg, PurpleMessageFlags flags)
 {
+	BonjourData *bd = purple_connection_get_protocol_data(connection);
+
 	if(!to || !msg)
 		return 0;
 
-	return bonjour_jabber_send_message(((BonjourData*)(connection->proto_data))->jabber_data, to, msg);
+	return bonjour_jabber_send_message(bd->jabber_data, to, msg);
 }
 
 static void
@@ -203,18 +218,12 @@
 {
 	PurpleConnection *gc;
 	BonjourData *bd;
-	gboolean disconnected;
-	PurpleStatusType *type;
-	int primitive;
 	PurplePresence *presence;
 	const char *message, *bonjour_status;
 	gchar *stripped;
 
 	gc = purple_account_get_connection(account);
-	bd = gc->proto_data;
-	disconnected = purple_account_is_disconnected(account);
-	type = purple_status_get_type(status);
-	primitive = purple_status_type_get_primitive(type);
+	bd = purple_connection_get_protocol_data(gc);
 	presence = purple_account_get_presence(account);
 
 	message = purple_status_get_attr_string(status, "message");
@@ -247,7 +256,7 @@
  * if there is no add_buddy callback.
  */
 static void
-bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group) {
+bonjour_fake_add_buddy(PurpleConnection *pc, PurpleBuddy *buddy, PurpleGroup *group, const char *message) {
 	purple_debug_error("bonjour", "Buddy '%s' manually added; removing.  "
 				      "Bonjour buddies must be discovered and not manually added.\n",
 			   purple_buddy_get_name(buddy));
@@ -300,7 +309,7 @@
 static void
 bonjour_convo_closed(PurpleConnection *connection, const char *who)
 {
-	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
+	PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(connection), who);
 	BonjourBuddy *bb;
 
 	if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL)
@@ -319,7 +328,7 @@
 static
 void bonjour_set_buddy_icon(PurpleConnection *conn, PurpleStoredImage *img)
 {
-	BonjourData *bd = conn->proto_data;
+	BonjourData *bd = purple_connection_get_protocol_data(conn);
 	bonjour_dns_sd_update_buddy_icon(bd->dns_sd_data);
 }
 
@@ -365,9 +374,12 @@
 	else
 		status_description = purple_status_get_name(status);
 
-	purple_notify_user_info_add_pair(user_info, _("Status"), status_description);
-	if (message != NULL)
-		purple_notify_user_info_add_pair(user_info, _("Message"), message);
+	purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_description);
+	if (message != NULL) {
+		/* TODO: Check whether it's correct to call add_pair_html,
+		         or if we should be using add_pair_plaintext */
+		purple_notify_user_info_add_pair_html(user_info, _("Message"), message);
+	}
 
 	if (bb == NULL) {
 		purple_debug_error("bonjour", "Got tooltip request for a buddy without protocol data.\n");
@@ -376,27 +388,40 @@
 
 	/* Only show first/last name if there is a nickname set (to avoid duplication) */
 	if (bb->nick != NULL && *bb->nick != '\0') {
-		if (bb->first != NULL && *bb->first != '\0')
-			purple_notify_user_info_add_pair(user_info, _("First name"), bb->first);
-		if (bb->last != NULL && *bb->last != '\0')
-			purple_notify_user_info_add_pair(user_info, _("Last name"), bb->last);
+		if (bb->first != NULL && *bb->first != '\0') {
+			/* TODO: Check whether it's correct to call add_pair_html,
+			         or if we should be using add_pair_plaintext */
+			purple_notify_user_info_add_pair_html(user_info, _("First name"), bb->first);
+		}
+		if (bb->last != NULL && *bb->last != '\0') {
+			/* TODO: Check whether it's correct to call add_pair_html,
+			         or if we should be using add_pair_plaintext */
+			purple_notify_user_info_add_pair_html(user_info, _("Last name"), bb->last);
+		}
 	}
 
-	if (bb->email != NULL && *bb->email != '\0')
-		purple_notify_user_info_add_pair(user_info, _("Email"), bb->email);
+	if (bb->email != NULL && *bb->email != '\0') {
+		/* TODO: Check whether it's correct to call add_pair_html,
+		         or if we should be using add_pair_plaintext */
+		purple_notify_user_info_add_pair_html(user_info, _("Email"), bb->email);
+	}
 
-	if (bb->AIM != NULL && *bb->AIM != '\0')
-		purple_notify_user_info_add_pair(user_info, _("AIM Account"), bb->AIM);
+	if (bb->AIM != NULL && *bb->AIM != '\0') {
+		/* TODO: Check whether it's correct to call add_pair_html,
+		         or if we should be using add_pair_plaintext */
+		purple_notify_user_info_add_pair_html(user_info, _("AIM Account"), bb->AIM);
+	}
 
-	if (bb->jid != NULL && *bb->jid != '\0')
-		purple_notify_user_info_add_pair(user_info, _("XMPP Account"), bb->jid);
+	if (bb->jid != NULL && *bb->jid != '\0') {
+		/* TODO: Check whether it's correct to call add_pair_html,
+		         or if we should be using add_pair_plaintext */
+		purple_notify_user_info_add_pair_html(user_info, _("XMPP Account"), bb->jid);
+	}
 }
 
 static void
-bonjour_group_buddy(PurpleConnection *connection, const char *who, const char *old_group, const char *new_group)
-{
+bonjour_do_group_change(PurpleBuddy *buddy, const char *new_group) {
 	PurpleBlistNodeFlags oldflags;
-	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
 
 	if (buddy == NULL)
 		return;
@@ -404,17 +429,42 @@
 	oldflags = purple_blist_node_get_flags((PurpleBlistNode *)buddy);
 
 	/* If we're moving them out of the bonjour group, make them persistent */
-	if (strcmp(new_group, BONJOUR_GROUP_NAME) == 0)
+	if (purple_strequal(new_group, BONJOUR_GROUP_NAME))
 		purple_blist_node_set_flags((PurpleBlistNode *)buddy, oldflags | PURPLE_BLIST_NODE_FLAG_NO_SAVE);
 	else
 		purple_blist_node_set_flags((PurpleBlistNode *)buddy, oldflags ^ PURPLE_BLIST_NODE_FLAG_NO_SAVE);
 
 }
 
+static void
+bonjour_group_buddy(PurpleConnection *connection, const char *who, const char *old_group, const char *new_group)
+{
+	PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(connection), who);
+
+	bonjour_do_group_change(buddy, new_group);
+
+}
+
+static void
+bonjour_rename_group(PurpleConnection *connection, const char *old_name, PurpleGroup *group, GList *moved_buddies)
+{
+	GList *cur;
+	const char *new_group;
+	PurpleBuddy *buddy;
+
+	new_group = purple_group_get_name(group);
+
+	for (cur = moved_buddies; cur; cur = cur->next) {
+		buddy = cur->data;
+		bonjour_do_group_change(buddy, new_group);
+	}
+
+}
+
 static gboolean
 bonjour_can_receive_file(PurpleConnection *connection, const char *who)
 {
-	PurpleBuddy *buddy = purple_find_buddy(connection->account, who);
+	PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(connection), who);
 
 	return (buddy != NULL && purple_buddy_get_protocol_data(buddy) != NULL);
 }
@@ -426,7 +476,6 @@
 
 	g_free(default_firstname);
 	g_free(default_lastname);
-	g_free(default_hostname);
 
 	return TRUE;
 }
@@ -435,6 +484,7 @@
 
 static PurplePluginProtocolInfo prpl_info =
 {
+	sizeof(PurplePluginProtocolInfo),                        /* struct_size */
 	OPT_PROTO_NO_PASSWORD,
 	NULL,                                                    /* user_splits */
 	NULL,                                                    /* protocol_options */
@@ -475,10 +525,9 @@
 	NULL,                                                    /* keepalive */
 	NULL,                                                    /* register_user */
 	NULL,                                                    /* get_cb_info */
-	NULL,                                                    /* get_cb_away */
 	NULL,                                                    /* alias_buddy */
 	bonjour_group_buddy,                                     /* group_buddy */
-	NULL,                                                    /* rename_group */
+	bonjour_rename_group,                                    /* rename_group */
 	NULL,                                                    /* buddy_free */
 	bonjour_convo_closed,                                    /* convo_closed */
 	NULL,                                                    /* normalize */
@@ -500,10 +549,12 @@
 	NULL,                                                    /* unregister_user */
 	NULL,                                                    /* send_attention */
 	NULL,                                                    /* get_attention_types */
-	sizeof(PurplePluginProtocolInfo),                        /* struct_size */
 	NULL,                                                    /* get_account_text_table */
 	NULL,                                                    /* initiate_media */
-	NULL                                                     /* can_do_media */
+	NULL,                                                    /* get_media_caps */
+	NULL,                                                    /* get_moods */
+	NULL,                                                    /* set_public_alias */
+	NULL                                                     /* get_public_alias */
 };
 
 static PurplePluginInfo info =
@@ -544,7 +595,8 @@
 };
 
 #ifdef WIN32
-static gboolean _set_default_name_cb(gpointer data) {
+static gboolean
+_set_default_name_cb(gpointer data) {
 	gchar *fullname = data;
 	const char *splitpoint;
 	GList *tmp = prpl_info.protocol_options;
@@ -581,7 +633,8 @@
 	return FALSE;
 }
 
-static gpointer _win32_name_lookup_thread(gpointer data) {
+static gpointer
+_win32_name_lookup_thread(gpointer data) {
 	gchar *fullname = NULL;
 	wchar_t username[UNLEN + 1];
 	DWORD dwLenUsername = UNLEN + 1;
@@ -685,24 +738,15 @@
 	}
 
 	g_free(conv);
-
-	/* Try to figure out a good host name to use */
-	/* TODO: Avoid 'localhost,' if possible */
-	default_hostname = g_strdup(purple_get_host_name());
 }
 
 static void
 init_plugin(PurplePlugin *plugin)
 {
-	PurpleAccountUserSplit *split;
 	PurpleAccountOption *option;
 
 	initialize_default_account_values();
 
-	/* Creating the user splits */
-	split = purple_account_user_split_new(_("Hostname"), default_hostname, '@');
-	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
-
 	/* Creating the options for the protocol */
 	option = purple_account_option_int_new(_("Local Port"), "port", BONJOUR_DEFAULT_PORT);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
--- a/libpurple/protocols/bonjour/bonjour.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/bonjour.h	Wed Jun 13 19:30:27 2012 -0400
@@ -45,6 +45,12 @@
 	BonjourDnsSd *dns_sd_data;
 	BonjourJabber *jabber_data;
 	GSList *xfer_lists;
+	gchar *jid;
 } BonjourData;
 
+/**
+ *  This will always be username@machinename
+ */
+const char *bonjour_get_jid(PurpleAccount *account);
+
 #endif /* _BONJOUR_H_ */
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Wed Jun 13 19:30:27 2012 -0400
@@ -17,7 +17,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA
  */
 #include "internal.h"
 #include "util.h"
@@ -38,7 +38,7 @@
 bonjour_xfer_init(PurpleXfer *xfer);
 static void
 bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
-		     const int filesize, const char *filename, int option);
+		     const goffset filesize, const char *filename, int option);
 static void bonjour_free_xfer(PurpleXfer *xfer);
 
 /* Look for specific xfer handle */
@@ -53,11 +53,12 @@
 	g_return_if_fail(error_code != NULL);
 	g_return_if_fail(error_type != NULL);
 
-	if(!to || !id)
+	if(!to || !id) {
+		purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
 		return;
+	}
 
-	purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
-	iq = xep_iq_new(bd, XEP_IQ_ERROR, to, purple_account_get_username(bd->jabber_data->account), id);
+	iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->jabber_data->account), id);
 	if(iq == NULL)
 		return;
 
@@ -89,12 +90,12 @@
 
 static void bonjour_xfer_request_denied(PurpleXfer *xfer)
 {
-	XepXfer *xf = xfer->data;
+	XepXfer *xf = purple_xfer_get_protocol_data(xfer);
 
 	purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
 
 	if(xf)
-		xep_ft_si_reject(xf->data, xf->sid, xfer->who, "403", "cancel");
+		xep_ft_si_reject(xf->data, xf->sid, purple_xfer_get_remote_user(xfer), "403", "cancel");
 
 	bonjour_free_xfer(xfer);
 }
@@ -135,8 +136,8 @@
 	 * otherwise there is a RST resulting in an error on the client side */
 	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && purple_xfer_is_completed(xfer)) {
 		struct socket_cleanup *sc = g_new0(struct socket_cleanup, 1);
-		sc->fd = xfer->fd;
-		xfer->fd = -1;
+		sc->fd = purple_xfer_get_fd(xfer);
+		purple_xfer_set_fd(xfer, -1);
 		sc->handle = purple_input_add(sc->fd, PURPLE_INPUT_READ,
 						 _wait_for_socket_close, sc);
 	}
@@ -161,11 +162,11 @@
 		xfer = xfers->data;
 		if(xfer == NULL)
 			break;
-		xf = xfer->data;
+		xf = purple_xfer_get_protocol_data(xfer);
 		if(xf == NULL)
 			break;
-		if(xf->sid && xfer->who && !strcmp(xf->sid, sid) &&
-				!strcmp(xfer->who, from))
+		if(xf->sid && purple_xfer_get_remote_user(xfer) && !strcmp(xf->sid, sid) &&
+				!strcmp(purple_xfer_get_remote_user(xfer), from))
 			return xfer;
 	}
 
@@ -179,7 +180,7 @@
 {
 	xmlnode *si_node, *feature, *field, *file, *x;
 	XepIq *iq;
-	XepXfer *xf = xfer->data;
+	XepXfer *xf = purple_xfer_get_protocol_data(xfer);
 	BonjourData *bd = NULL;
 	char buf[32];
 
@@ -195,7 +196,7 @@
 	/* Assign stream id. */
 	g_free(xf->iq_id);
 	xf->iq_id = g_strdup_printf("%u", next_id++);
-	iq = xep_iq_new(xf->data, XEP_IQ_SET, to, purple_account_get_username(bd->jabber_data->account), xf->iq_id);
+	iq = xep_iq_new(xf->data, XEP_IQ_SET, to, bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
 	if(iq == NULL)
 		return;
 
@@ -209,8 +210,8 @@
 
 	file = xmlnode_new_child(si_node, "file");
 	xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer");
-	xmlnode_set_attrib(file, "name", xfer->filename);
-	g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
+	xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer));
+	g_snprintf(buf, sizeof(buf), "%" G_GOFFSET_FORMAT, purple_xfer_get_size(xfer));
 	xmlnode_set_attrib(file, "size", buf);
 
 	feature = xmlnode_new_child(si_node, "feature");
@@ -239,7 +240,7 @@
 }
 
 static void
-xep_ft_si_result(PurpleXfer *xfer, char *to)
+xep_ft_si_result(PurpleXfer *xfer, const char *to)
 {
 	xmlnode *si_node, *feature, *field, *value, *x;
 	XepIq *iq;
@@ -248,14 +249,14 @@
 
 	if(!to || !xfer)
 		return;
-	xf = xfer->data;
+	xf = purple_xfer_get_protocol_data(xfer);
 	if(!xf)
 		return;
 
 	bd = xf->data;
 
 	purple_debug_info("bonjour", "xep file transfer stream initialization result.\n");
-	iq = xep_iq_new(bd, XEP_IQ_RESULT, to, purple_account_get_username(bd->jabber_data->account), xf->iq_id);
+	iq = xep_iq_new(bd, XEP_IQ_RESULT, to, bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
 	if(iq == NULL)
 		return;
 
@@ -289,14 +290,14 @@
 		return;
 	}
 
-	purple_debug_info("bonjour", "bonjour-free-xfer-%p.\n", xfer);
+	purple_debug_misc("bonjour", "bonjour-free-xfer-%p.\n", xfer);
 
-	xf = (XepXfer*)xfer->data;
+	xf = purple_xfer_get_protocol_data(xfer);
 	if(xf != NULL) {
 		BonjourData *bd = (BonjourData*)xf->data;
 		if(bd != NULL) {
 			bd->xfer_lists = g_slist_remove(bd->xfer_lists, xfer);
-			purple_debug_info("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
+			purple_debug_misc("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
 		}
 		if (xf->proxy_connection != NULL)
 			purple_proxy_connect_cancel(xf->proxy_connection);
@@ -310,10 +311,10 @@
 		g_free(xf->buddy_ip);
 		g_free(xf->sid);
 		g_free(xf);
-		xfer->data = NULL;
+		purple_xfer_set_protocol_data(xfer, NULL);
 	}
 
-	purple_debug_info("bonjour", "Need close socket=%d.\n", xfer->fd);
+	purple_debug_misc("bonjour", "Need close socket.\n");
 }
 
 PurpleXfer *
@@ -327,13 +328,14 @@
 		return NULL;
 
 	purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who);
-	bd = (BonjourData*) gc->proto_data;
+	bd = purple_connection_get_protocol_data(gc);
 	if(bd == NULL)
 		return NULL;
 
 	/* Build the file transfer handle */
-	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
-	xfer->data = xep_xfer = g_new0(XepXfer, 1);
+	xfer = purple_xfer_new(purple_connection_get_account(gc), PURPLE_XFER_SEND, who);
+	xep_xfer = g_new0(XepXfer, 1);
+	purple_xfer_set_protocol_data(xfer, xep_xfer);
 	xep_xfer->data = bd;
 
 	purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data);
@@ -378,13 +380,13 @@
 	BonjourBuddy *bb;
 	XepXfer *xf;
 
-	xf = (XepXfer*)xfer->data;
+	xf = purple_xfer_get_protocol_data(xfer);
 	if(xf == NULL)
 		return;
 
 	purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
 
-	buddy = purple_find_buddy(xfer->account, xfer->who);
+	buddy = purple_find_buddy(purple_xfer_get_account(xfer), purple_xfer_get_remote_user(xfer));
 	/* this buddy is offline. */
 	if (buddy == NULL || (bb = purple_buddy_get_protocol_data(buddy)) == NULL)
 		return;
@@ -395,10 +397,10 @@
 	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
 		/* initiate file transfer, send SI offer. */
 		purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n");
-		xep_ft_si_offer(xfer, xfer->who);
+		xep_ft_si_offer(xfer, purple_xfer_get_remote_user(xfer));
 	} else {
 		/* accept file transfer request, send SI result. */
-		xep_ft_si_result(xfer, xfer->who);
+		xep_ft_si_result(xfer, purple_xfer_get_remote_user(xfer));
 		purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_RECEIVE.\n");
 	}
 }
@@ -415,7 +417,7 @@
 	g_return_if_fail(packet != NULL);
 	g_return_if_fail(pb != NULL);
 
-	bd = (BonjourData*) pc->proto_data;
+	bd = purple_connection_get_protocol_data(pc);
 	if(bd == NULL)
 		return;
 
@@ -425,81 +427,178 @@
 
 	type = xmlnode_get_attrib(packet, "type");
 	id = xmlnode_get_attrib(packet, "id");
-	if(type) {
-		if(!strcmp(type, "set")) {
-			const char *profile;
-			xmlnode *si;
-			gboolean parsed_receive = FALSE;
+	if(!type)
+		return;
 
-			si = xmlnode_get_child(packet, "si");
+	if(!strcmp(type, "set")) {
+		const char *profile;
+		xmlnode *si;
+		gboolean parsed_receive = FALSE;
+
+		si = xmlnode_get_child(packet, "si");
 
-			purple_debug_info("bonjour", "si offer Message type - SET.\n");
-			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;
-				xmlnode *file;
+		purple_debug_info("bonjour", "si offer Message type - SET.\n");
+		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;
+			xmlnode *file;
 
-				const char *sid = xmlnode_get_attrib(si, "id");
+			const char *sid = xmlnode_get_attrib(si, "id");
 
-				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);
-				}
+			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);
+			}
 
-				/* TODO: Make sure that it is advertising a bytestreams transfer */
+			/* TODO: Make sure that it is advertising a bytestreams transfer */
 
+			if (filename) {
 				bonjour_xfer_receive(pc, id, sid, name, filesize, filename, XEP_BYTESTREAMS);
 
 				parsed_receive = TRUE;
 			}
+		}
 
-			if (!parsed_receive) {
-				BonjourData *bd = purple_connection_get_protocol_data(pc);
+		if (!parsed_receive) {
+			BonjourData *bd = purple_connection_get_protocol_data(pc);
+
+			purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n");
+			xep_ft_si_reject(bd, id, name, "403", "cancel");
+			/*TODO: Send Cancel (501) */
+		}
+	} else if(!strcmp(type, "result")) {
+		purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
+
+		xfer = bonjour_si_xfer_find(bd, id, name);
+
+		if(xfer == NULL) {
+			BonjourData *bd = purple_connection_get_protocol_data(pc);
+			purple_debug_info("bonjour", "xfer find fail.\n");
+			xep_ft_si_reject(bd, id, name, "403", "cancel");
+		} else
+			bonjour_bytestreams_init(xfer);
+
+	} else if(!strcmp(type, "error")) {
+		purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
+
+		xfer = bonjour_si_xfer_find(bd, id, name);
 
-				purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n");
-				xep_ft_si_reject(bd, id, name, "403", "cancel");
-				/*TODO: Send Cancel (501) */
-			}
-		} else if(!strcmp(type, "result")) {
-			purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
+		if(xfer == NULL)
+			purple_debug_info("bonjour", "xfer find fail.\n");
+		else
+			purple_xfer_cancel_remote(xfer);
+	} else
+		purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type);
+}
 
-			xfer = bonjour_si_xfer_find(bd, id, name);
+/**
+ * Will compare a host with a buddy_ip.
+ *
+ * Additionally to a common '!strcmp(host, buddy_ip)', it will also return TRUE
+ * if 'host' is a link local IPv6 address without an appended interface
+ * identifier and 'buddy_ip' string is "host" + "%iface".
+ *
+ * Note: This may theoretically result in the attempt to connect to the wrong
+ * host, because we do not know for sure which interface the according link
+ * local IPv6 address might relate to and RFC4862 for instance only ensures the
+ * uniqueness of this address on a given link. So we could possibly have two
+ * distinct buddies with the same ipv6 link local address on two distinct
+ * interfaces. Unfortunately XEP-0065 does not seem to specify how to deal with
+ * link local ip addresses properly...
+ * However, in practice the possiblity for such a conflict is relatively low
+ * (2011 - might be different in the future though?).
+ *
+ * @param host		ipv4 or ipv6 address string
+ * @param buddy_ip	ipv4 or ipv6 address string
+ * @return		TRUE if they match, FALSE otherwise
+ */
+static gboolean
+xep_cmp_addr(const char *host, const char *buddy_ip)
+{
+#if defined(AF_INET6) && defined(HAVE_GETADDRINFO)
+	struct addrinfo hint, *res = NULL;
+	int ret;
 
-			if(xfer == NULL) {
-				BonjourData *bd = purple_connection_get_protocol_data(pc);
-				purple_debug_info("bonjour", "xfer find fail.\n");
-				xep_ft_si_reject(bd, id, name, "403", "cancel");
-			} else
-				bonjour_bytestreams_init(xfer);
+	memset(&hint, 0, sizeof(hint));
+	hint.ai_family = AF_UNSPEC;
+	hint.ai_flags = AI_NUMERICHOST;
+
+	ret = getaddrinfo(host, NULL, &hint, &res);
+	if(ret)
+		goto out;
 
-		} else if(!strcmp(type, "error")) {
-			purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
+	if(res->ai_family != AF_INET6 ||
+	   !IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)) {
+		freeaddrinfo(res);
+		goto out;
+	}
+	freeaddrinfo(res);
+
+	if(strlen(buddy_ip) <= strlen(host) ||
+	   buddy_ip[strlen(host)] != '%')
+		return FALSE;
+
+	return !strncmp(host, buddy_ip, strlen(host));
+
+out:
+#endif
+	return !strcmp(host, buddy_ip);
+}
 
-			xfer = bonjour_si_xfer_find(bd, id, name);
+static gboolean
+__xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, xmlnode *query,
+			const char *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)) {
 
-			if(xfer == NULL)
-				purple_debug_info("bonjour", "xfer find fail.\n");
-			else
-				purple_xfer_cancel_remote(xfer);
-		} else
-			purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type);
+		if(!(jid = xmlnode_get_attrib(streamhost, "jid")) ||
+		   !(host = xmlnode_get_attrib(streamhost, "host")) ||
+		   !(port = xmlnode_get_attrib(streamhost, "port")) ||
+		   !(portnum = atoi(port))) {
+			purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
+			continue;
+		}
+
+		if(!xep_cmp_addr(host, xf->buddy_ip))
+			continue;
+
+		g_free(xf->iq_id);
+		xf->iq_id = g_strdup(iq_id);
+		xf->jid = g_strdup(jid);
+		xf->proxy_host = g_strdup(xf->buddy_ip);
+		xf->proxy_port = portnum;
+		purple_debug_info("bonjour", "bytestream offer parse"
+				  "jid=%s host=%s port=%d.\n", jid, host, portnum);
+		bonjour_bytestreams_connect(xfer, pb);
+		return TRUE;
 	}
+
+	return FALSE;
 }
 
+
 void
 xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
 {
-	const char *type, *from;
+	const char *type, *from, *iq_id, *sid;
 	xmlnode *query;
 	BonjourData *bd;
+	PurpleXfer *xfer;
 
 	g_return_if_fail(pc != NULL);
 	g_return_if_fail(packet != NULL);
 	g_return_if_fail(pb != NULL);
 
-	bd = (BonjourData*) pc->proto_data;
+	bd = purple_connection_get_protocol_data(pc);
 	if(bd == NULL)
 		return;
 
@@ -508,71 +607,32 @@
 	type = xmlnode_get_attrib(packet, "type");
 	from = purple_buddy_get_name(pb);
 	query = xmlnode_get_child(packet,"query");
-	if(type) {
-		if(!strcmp(type, "set")) {
-			const char *iq_id, *sid;
-			gboolean found = FALSE;
-			PurpleXfer *xfer;
-
-			purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
-
-			iq_id = xmlnode_get_attrib(packet, "id");
-
-			sid = xmlnode_get_attrib(query, "sid");
-			xfer = bonjour_si_xfer_find(bd, sid, from);
+	if(!type)
+		return;
 
-			if(xfer) {
-				const char *jid, *host, *port;
-				xmlnode *streamhost;
-				int portnum;
-				XepXfer *xf = NULL;
+	if(strcmp(type, "set")) {
+		purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type);
+		return;
+	}
 
-				xf = (XepXfer*)xfer->data;
-				for(streamhost = xmlnode_get_child(query, "streamhost");
-						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")) &&
-					   (portnum = atoi(port))) {
+	purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
 
-						if(!strcmp(host, xf->buddy_ip)) {
-							g_free(xf->iq_id);
-							xf->iq_id = g_strdup(iq_id);
-							xf->jid = g_strdup(jid);
-							xf->proxy_host = g_strdup(host);
-							xf->proxy_port = portnum;
-							purple_debug_info("bonjour", "bytestream offer parse"
-									  "jid=%s host=%s port=%d.\n", jid, host, portnum);
-							bonjour_bytestreams_connect(xfer, pb);
-							found = TRUE;
-							break;
-						}
-					} else {
-						purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
-					}
-				}
-			} else {
+	iq_id = xmlnode_get_attrib(packet, "id");
 
-			}
-
-			if (!found) {
-				purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
+	sid = xmlnode_get_attrib(query, "sid");
+	xfer = bonjour_si_xfer_find(bd, sid, from);
+	if(xfer && __xep_bytestreams_parse(pb, xfer, query, iq_id))
+		return; /* success */
 
-				if (iq_id && xfer != NULL)
-					xep_ft_si_reject(bd, iq_id, xfer->who, "404", "cancel");
-			}
+	purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
 
-		} else {
-			purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type);
-		}
-	}
+	if (iq_id && xfer != NULL)
+		xep_ft_si_reject(bd, iq_id, xfer->who, "404", "cancel");
 }
 
 static void
 bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
-		     const int filesize, const char *filename, int option)
+		     const goffset filesize, const char *filename, int option)
 {
 	PurpleXfer *xfer;
 	XepXfer *xf;
@@ -581,15 +641,16 @@
 	if(pc == NULL || id == NULL || from == NULL)
 		return;
 
-	bd = (BonjourData*) pc->proto_data;
+	bd = purple_connection_get_protocol_data(pc);
 	if(bd == NULL)
 		return;
 
 	purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
 
 	/* Build the file transfer handle */
-	xfer = purple_xfer_new(pc->account, PURPLE_XFER_RECEIVE, from);
-	xfer->data = xf = g_new0(XepXfer, 1);
+	xfer = purple_xfer_new(purple_connection_get_account(pc), PURPLE_XFER_RECEIVE, from);
+	xf = g_new0(XepXfer, 1);
+	purple_xfer_set_protocol_data(xfer, xf);
 	xf->data = bd;
 	purple_xfer_set_filename(xfer, filename);
 	xf->iq_id = g_strdup(id);
@@ -611,7 +672,7 @@
 bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	PurpleXfer *xfer = data;
-	XepXfer *xf = xfer->data;
+	XepXfer *xf = purple_xfer_get_protocol_data(xfer);
 	int acceptfd;
 	int len = 0;
 
@@ -629,8 +690,6 @@
 			/* This should cancel the ft */
 			purple_debug_error("bonjour", "Error accepting incoming SOCKS5 connection. (%d)\n", errno);
 
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
 			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
@@ -645,29 +704,26 @@
 			fcntl(acceptfd, F_SETFD, FD_CLOEXEC);
 #endif
 
-			purple_input_remove(xfer->watcher);
+			purple_input_remove(purple_xfer_get_watcher(xfer));
 			close(source);
-			xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
-							 bonjour_sock5_request_cb, xfer);
+			purple_xfer_set_watcher(xfer, purple_input_add(acceptfd, PURPLE_INPUT_READ,
+							 bonjour_sock5_request_cb, xfer));
 			xf->sock5_req_state++;
 			xf->rxlen = 0;
 		}
 		break;
 	case 0x01:
-		xfer->fd = source;
+		purple_xfer_set_fd(xfer, source);
 		len = read(source, xf->rx_buf + xf->rxlen, 3);
 		if(len < 0 && errno == EAGAIN)
 			return;
 		else if(len <= 0){
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
-			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
 		} else {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
-							 bonjour_sock5_request_cb, xfer);
+			purple_input_remove(purple_xfer_get_watcher(xfer));
+			purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_WRITE,
+							 bonjour_sock5_request_cb, xfer));
 			xf->sock5_req_state++;
 			xf->rxlen = 0;
 			bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
@@ -680,15 +736,13 @@
 		if (len < 0 && errno == EAGAIN)
 			return;
 		else if (len < 0) {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
 			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
 		} else {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
-							 bonjour_sock5_request_cb, xfer);
+			purple_input_remove(purple_xfer_get_watcher(xfer));
+			purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_READ,
+							 bonjour_sock5_request_cb, xfer));
 			xf->sock5_req_state++;
 			xf->rxlen = 0;
 		}
@@ -697,9 +751,9 @@
 		len = read(source, xf->rx_buf + xf->rxlen, 20);
 		if(len<=0){
 		} else {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
-							 bonjour_sock5_request_cb, xfer);
+			purple_input_remove(purple_xfer_get_watcher(xfer));
+			purple_xfer_set_watcher(xfer, purple_input_add(source, PURPLE_INPUT_WRITE,
+							 bonjour_sock5_request_cb, xfer));
 			xf->sock5_req_state++;
 			xf->rxlen = 0;
 			bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
@@ -718,14 +772,12 @@
 		if (len < 0 && errno == EAGAIN) {
 			return;
 		} else if (len < 0) {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
 			close(source);
 			purple_xfer_cancel_remote(xfer);
 			return;
 		} else {
-			purple_input_remove(xfer->watcher);
-			xfer->watcher = 0;
+			purple_input_remove(purple_xfer_get_watcher(xfer));
+			purple_xfer_set_watcher(xfer, 0);
 			xf->rxlen = 0;
 			/*close(source);*/
 			purple_xfer_start(xfer, source, NULL, -1);
@@ -745,8 +797,7 @@
 	XepIq *iq;
 	xmlnode *query, *streamhost;
 	gchar *port;
-	const char *next_ip, *local_ip;
-	const char token [] = ";";
+	GSList *local_ips;
 	BonjourData *bd;
 
 	purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
@@ -755,33 +806,32 @@
 		return;
 	}
 
-	xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
-					 bonjour_sock5_request_cb, xfer);
-	xf = (XepXfer*)xfer->data;
+	purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ,
+					 bonjour_sock5_request_cb, xfer));
+	xf = purple_xfer_get_protocol_data(xfer);
 	xf->listen_data = NULL;
 
 	bd = xf->data;
 
-	iq = xep_iq_new(bd, XEP_IQ_SET, xfer->who, purple_account_get_username(bd->jabber_data->account), xf->sid);
+	iq = xep_iq_new(bd, XEP_IQ_SET, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->sid);
 
 	query = xmlnode_new_child(iq->node, "query");
 	xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
 	xmlnode_set_attrib(query, "sid", xf->sid);
 	xmlnode_set_attrib(query, "mode", "tcp");
 
-	xfer->local_port = purple_network_get_port_from_fd(sock);
+	purple_xfer_set_local_port(xfer, purple_network_get_port_from_fd(sock));
 
-	local_ip = purple_network_get_my_ip_ext2(sock);
-	/* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */
-	next_ip = strtok((char *)local_ip, token);
+	local_ips = bonjour_jabber_get_local_ips(sock);
 
-	port = g_strdup_printf("%hu", xfer->local_port);
-	while(next_ip != NULL) {
+	port = g_strdup_printf("%hu", purple_xfer_get_local_port(xfer));
+	while(local_ips) {
 		streamhost = xmlnode_new_child(query, "streamhost");
 		xmlnode_set_attrib(streamhost, "jid", xf->sid);
-		xmlnode_set_attrib(streamhost, "host", next_ip);
+		xmlnode_set_attrib(streamhost, "host", local_ips->data);
 		xmlnode_set_attrib(streamhost, "port", port);
-		next_ip = strtok(NULL, token);
+		g_free(local_ips->data);
+		local_ips = g_slist_delete_link(local_ips, local_ips);
 	}
 	g_free(port);
 
@@ -794,15 +844,15 @@
 	XepXfer *xf;
 	if(xfer == NULL)
 		return;
+
 	purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
-	xf = xfer->data;
-	purple_network_listen_map_external(FALSE);
-	xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
+	xf = purple_xfer_get_protocol_data(xfer);
+
+	xf->listen_data = purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, FALSE,
 						      bonjour_bytestreams_listen, xfer);
-	purple_network_listen_map_external(TRUE);
-	if (xf->listen_data == NULL) {
+	if (xf->listen_data == NULL)
 		purple_xfer_cancel_local(xfer);
-	}
+
 	return;
 }
 
@@ -810,7 +860,7 @@
 bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
 {
 	PurpleXfer *xfer = data;
-	XepXfer *xf = xfer->data;
+	XepXfer *xf = purple_xfer_get_protocol_data(xfer);
 	XepIq *iq;
 	xmlnode *q_node, *tmp_node;
 	BonjourData *bd;
@@ -820,7 +870,7 @@
 	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, xfer->who, "404", "cancel");
+		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;
@@ -833,7 +883,7 @@
 	/* Here, start the file transfer.*/
 
 	/* Notify Initiator of Connection */
-	iq = xep_iq_new(bd, XEP_IQ_RESULT, xfer->who, purple_account_get_username(bd->jabber_data->account), xf->iq_id);
+	iq = xep_iq_new(bd, XEP_IQ_RESULT, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
 	q_node = xmlnode_new_child(iq->node, "query");
 	xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams");
 	tmp_node = xmlnode_new_child(q_node, "streamhost-used");
@@ -859,14 +909,14 @@
 
 	purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
 
-	xf = (XepXfer*)xfer->data;
+	xf = purple_xfer_get_protocol_data(xfer);
 	if(!xf)
 		return;
 
 	name = purple_buddy_get_name(pb);
 	account = purple_buddy_get_account(pb);
 
-	p = g_strdup_printf("%s%s%s", xf->sid, name, purple_account_get_username(account));
+	p = g_strdup_printf("%s%s%s", xf->sid, name, bonjour_get_jid(account));
 	purple_cipher_digest_region("sha1", (guchar *)p, strlen(p),
 				    sizeof(hashval), hashval, NULL);
 	g_free(p);
@@ -880,14 +930,15 @@
 	purple_proxy_info_set_type(xf->proxy_info, PURPLE_PROXY_SOCKS5);
 	purple_proxy_info_set_host(xf->proxy_info, xf->proxy_host);
 	purple_proxy_info_set_port(xf->proxy_info, xf->proxy_port);
-	xf->proxy_connection = purple_proxy_connect_socks5(
+	xf->proxy_connection = purple_proxy_connect_socks5_account(
 							   purple_account_get_connection(account),
+							   account,
 							   xf->proxy_info,
 							   dstaddr, 0,
 							   bonjour_bytestreams_connect_cb, xfer);
 
 	if(xf->proxy_connection == NULL) {
-		xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel");
+		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);
 	}
--- a/libpurple/protocols/bonjour/bonjour_ft.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/bonjour_ft.h	Wed Jun 13 19:30:27 2012 -0400
@@ -17,7 +17,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA
  */
 #ifndef _BONJOUR_FT_H_
 #define _BONJOUR_FT_H_
--- a/libpurple/protocols/bonjour/dns_sd_proxy.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/dns_sd_proxy.c	Wed Jun 13 19:30:27 2012 -0400
@@ -1,181 +1,191 @@
-/*
- *
- * 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 Library 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 "win32dep.h"
-#include "dns_sd_proxy.h"
-
-#ifndef LINK_DNS_SD_DIRECTLY
-static DNSServiceErrorType (DNSSD_API* _DNSServiceAddRecord)(DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
-		uint16_t rrtype, uint16_t rdlen, const void *rdata, uint32_t ttl);
-static DNSServiceErrorType (DNSSD_API* _DNSServiceBrowse)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
-	const char *regtype, const char *domain, DNSServiceBrowseReply callBack, void *context);
-static int (DNSSD_API* _DNSServiceConstructFullName)(char *fullName, const char *service, const char *regtype, const char *domain);
-static DNSServiceErrorType (DNSSD_API* _DNSServiceProcessResult)(DNSServiceRef sdRef);
-static DNSServiceErrorType (DNSSD_API* _DNSServiceQueryRecord)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
-	const char *fullname, uint16_t rrtype, uint16_t rrclass, DNSServiceQueryRecordReply callBack, void *context);
-static void (DNSSD_API* _DNSServiceRefDeallocate)(DNSServiceRef sdRef);
-static int (DNSSD_API* _DNSServiceRefSockFD)(DNSServiceRef sdRef);
-static DNSServiceErrorType (DNSSD_API* _DNSServiceRegister)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
-	const char *name, const char *regtype, const char *domain, const char *host, uint16_t port, uint16_t txtLen,
-	const void *txtRecord, DNSServiceRegisterReply callBack, void *context);
-static DNSServiceErrorType (DNSSD_API* _DNSServiceResolve)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, const char *name,
-	const char *regtype, const char *domain, DNSServiceResolveReply callBack, void *context);
-static DNSServiceErrorType (DNSSD_API* _DNSServiceRemoveRecord)(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags);
-static DNSServiceErrorType (DNSSD_API* _DNSServiceUpdateRecord)(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
-	uint16_t rdlen, const void *rdata, uint32_t ttl);
-static void (DNSSD_API* _TXTRecordCreate)(TXTRecordRef *txtRecord, uint16_t bufferLen, void *buffer);
-static void (DNSSD_API* _TXTRecordDeallocate)(TXTRecordRef *txtRecord);
-static const void * (DNSSD_API* _TXTRecordGetBytesPtr)(const TXTRecordRef *txtRecord);
-static int16_t (DNSSD_API* _TXTRecordGetLength)(const TXTRecordRef *txtRecord);
-static const void * (DNSSD_API* _TXTRecordGetValuePtr)(uint16_t txtLen, const void *txtRecord, const char *key, uint8_t *valueLen);
-static DNSServiceErrorType (DNSSD_API* _TXTRecordSetValue)(TXTRecordRef *txtRecord, const char *key, uint8_t valueSize, const void *value);
-#endif
-
-gboolean dns_sd_available(void) {
-#ifndef LINK_DNS_SD_DIRECTLY
-	static gboolean initialized = FALSE;
-	static gboolean loaded = FALSE;
-
-	if (!initialized) {
-		initialized = TRUE;
-		if ((_DNSServiceAddRecord = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceAddRecord"))
-				&& (_DNSServiceBrowse = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceBrowse"))
-				&& (_DNSServiceConstructFullName = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceConstructFullName"))
-				&& (_DNSServiceProcessResult = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceProcessResult"))
-				&& (_DNSServiceQueryRecord = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceQueryRecord"))
-				&& (_DNSServiceRefDeallocate = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceRefDeallocate"))
-				&& (_DNSServiceRefSockFD = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceRefSockFD"))
-				&& (_DNSServiceRegister = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceRegister"))
-				&& (_DNSServiceResolve = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceResolve"))
-				&& (_DNSServiceRemoveRecord = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceRemoveRecord"))
-				&& (_DNSServiceUpdateRecord = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceUpdateRecord"))
-				&& (_TXTRecordCreate = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordCreate"))
-				&& (_TXTRecordDeallocate = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordDeallocate"))
-				&& (_TXTRecordGetBytesPtr = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordGetBytesPtr"))
-				&& (_TXTRecordGetLength = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordGetLength"))
-				&& (_TXTRecordGetValuePtr = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordGetValuePtr"))
-				&& (_TXTRecordSetValue = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordSetValue"))) {
-			loaded = TRUE;
-		}
-	}
-	return loaded;
-#else
-	return TRUE;
-#endif
-}
-
-#ifndef LINK_DNS_SD_DIRECTLY
-
-DNSServiceErrorType _wpurple_DNSServiceAddRecord(DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
-		uint16_t rrtype, uint16_t rdlen, const void *rdata, uint32_t ttl) {
-	g_return_val_if_fail(_DNSServiceAddRecord != NULL, kDNSServiceErr_Unknown);
-	return (_DNSServiceAddRecord)(sdRef, RecordRef, flags, rrtype, rdlen, rdata, ttl);
-}
-
-DNSServiceErrorType _wpurple_DNSServiceBrowse(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
-		const char *regtype, const char *domain, DNSServiceBrowseReply callBack, void *context) {
-	g_return_val_if_fail(_DNSServiceBrowse != NULL, kDNSServiceErr_Unknown);
-	return (_DNSServiceBrowse)(sdRef, flags, interfaceIndex, regtype, domain, callBack, context);
-}
-
-int _wpurple_DNSServiceConstructFullName(char *fullName, const char *service, const char *regtype, const char *domain) {
-	g_return_val_if_fail(_DNSServiceConstructFullName != NULL, 0);
-	return (_DNSServiceConstructFullName)(fullName, service, regtype, domain);
-}
-
-DNSServiceErrorType _wpurple_DNSServiceProcessResult(DNSServiceRef sdRef) {
-	g_return_val_if_fail(_DNSServiceProcessResult != NULL, kDNSServiceErr_Unknown);
-	return (_DNSServiceProcessResult)(sdRef);
-}
-
-
-DNSServiceErrorType _wpurple_DNSServiceQueryRecord(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
-		const char *fullname, uint16_t rrtype, uint16_t rrclass, DNSServiceQueryRecordReply callBack, void *context) {
-	g_return_val_if_fail(_DNSServiceQueryRecord != NULL, kDNSServiceErr_Unknown);
-	return (_DNSServiceQueryRecord)(sdRef, flags, interfaceIndex, fullname, rrtype, rrclass, callBack, context);
-}
-
-void _wpurple_DNSServiceRefDeallocate(DNSServiceRef sdRef) {
-	g_return_if_fail(_DNSServiceRefDeallocate != NULL);
-	(_DNSServiceRefDeallocate)(sdRef);
-}
-
-int _wpurple_DNSServiceRefSockFD(DNSServiceRef sdRef) {
-	g_return_val_if_fail(_DNSServiceRefSockFD != NULL, -1);
-	return (_DNSServiceRefSockFD)(sdRef);
-}
-
-DNSServiceErrorType _wpurple_DNSServiceRegister(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
-		const char *name, const char *regtype, const char *domain, const char *host, uint16_t port, uint16_t txtLen,
-		const void *txtRecord, DNSServiceRegisterReply callBack, void *context) {
-	g_return_val_if_fail(_DNSServiceRegister != NULL, kDNSServiceErr_Unknown);
-	return (_DNSServiceRegister)(sdRef, flags, interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord, callBack, context);
-}
-
-DNSServiceErrorType _wpurple_DNSServiceResolve(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, const char *name,
-		const char *regtype, const char *domain, DNSServiceResolveReply callBack, void *context) {
-	g_return_val_if_fail(_DNSServiceResolve != NULL, kDNSServiceErr_Unknown);
-	return (_DNSServiceResolve)(sdRef, flags, interfaceIndex, name, regtype, domain, callBack, context);
-}
-
-DNSServiceErrorType _wpurple_DNSServiceRemoveRecord(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags) {
-	g_return_val_if_fail(_DNSServiceRemoveRecord != NULL, kDNSServiceErr_Unknown);
-	return (_DNSServiceRemoveRecord)(sdRef, RecordRef, flags);
-}
-
-DNSServiceErrorType _wpurple_DNSServiceUpdateRecord(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
-		uint16_t rdlen, const void *rdata, uint32_t ttl) {
-	g_return_val_if_fail(_DNSServiceUpdateRecord != NULL, kDNSServiceErr_Unknown);
-	return (_DNSServiceUpdateRecord)(sdRef, RecordRef, flags, rdlen, rdata, ttl);
-}
-
-void _wpurple_TXTRecordCreate(TXTRecordRef *txtRecord, uint16_t bufferLen, void *buffer) {
-	g_return_if_fail(_TXTRecordCreate != NULL);
-	(_TXTRecordCreate)(txtRecord, bufferLen, buffer);
-}
-
-void _wpurple_TXTRecordDeallocate(TXTRecordRef *txtRecord) {
-	g_return_if_fail(_TXTRecordDeallocate != NULL);
-	(_TXTRecordDeallocate)(txtRecord);
-}
-
-const void * _wpurple_TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) {
-	g_return_val_if_fail(_TXTRecordGetBytesPtr != NULL, NULL);
-	return (_TXTRecordGetBytesPtr)(txtRecord);
-}
-
-uint16_t _wpurple_TXTRecordGetLength(const TXTRecordRef *txtRecord) {
-	g_return_val_if_fail(_TXTRecordGetLength != NULL, 0);
-	return (_TXTRecordGetLength)(txtRecord);
-}
-
-const void * _wpurple_TXTRecordGetValuePtr(uint16_t txtLen, const void *txtRecord, const char *key, uint8_t *valueLen) {
-	g_return_val_if_fail(_TXTRecordGetValuePtr != NULL, NULL);
-	return (_TXTRecordGetValuePtr)(txtLen, txtRecord, key, valueLen);
-}
-
-DNSServiceErrorType _wpurple_TXTRecordSetValue(TXTRecordRef *txtRecord, const char *key, uint8_t valueSize, const void *value) {
-	g_return_val_if_fail(_TXTRecordSetValue != NULL, kDNSServiceErr_Unknown);
-	return (_TXTRecordSetValue)(txtRecord, key, valueSize, value);
-}
-
-#endif /*LINK_DNS_SD_DIRECTLY*/
-
+/*
+ *
+ * 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 Library 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 "win32dep.h"
+#include "dns_sd_proxy.h"
+
+#ifndef LINK_DNS_SD_DIRECTLY
+static DNSServiceErrorType (DNSSD_API* _DNSServiceAddRecord)(DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
+		uint16_t rrtype, uint16_t rdlen, const void *rdata, uint32_t ttl);
+static DNSServiceErrorType (DNSSD_API* _DNSServiceBrowse)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+	const char *regtype, const char *domain, DNSServiceBrowseReply callBack, void *context);
+static int (DNSSD_API* _DNSServiceConstructFullName)(char *fullName, const char *service, const char *regtype, const char *domain);
+static DNSServiceErrorType (DNSSD_API* _DNSServiceGetAddrInfo)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+	DNSServiceProtocol protocol, const char *hostname, DNSServiceGetAddrInfoReply callBack, void *context);
+static DNSServiceErrorType (DNSSD_API* _DNSServiceProcessResult)(DNSServiceRef sdRef);
+static DNSServiceErrorType (DNSSD_API* _DNSServiceQueryRecord)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+	const char *fullname, uint16_t rrtype, uint16_t rrclass, DNSServiceQueryRecordReply callBack, void *context);
+static void (DNSSD_API* _DNSServiceRefDeallocate)(DNSServiceRef sdRef);
+static int (DNSSD_API* _DNSServiceRefSockFD)(DNSServiceRef sdRef);
+static DNSServiceErrorType (DNSSD_API* _DNSServiceRegister)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+	const char *name, const char *regtype, const char *domain, const char *host, uint16_t port, uint16_t txtLen,
+	const void *txtRecord, DNSServiceRegisterReply callBack, void *context);
+static DNSServiceErrorType (DNSSD_API* _DNSServiceResolve)(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, const char *name,
+	const char *regtype, const char *domain, DNSServiceResolveReply callBack, void *context);
+static DNSServiceErrorType (DNSSD_API* _DNSServiceRemoveRecord)(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags);
+static DNSServiceErrorType (DNSSD_API* _DNSServiceUpdateRecord)(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
+	uint16_t rdlen, const void *rdata, uint32_t ttl);
+static void (DNSSD_API* _TXTRecordCreate)(TXTRecordRef *txtRecord, uint16_t bufferLen, void *buffer);
+static void (DNSSD_API* _TXTRecordDeallocate)(TXTRecordRef *txtRecord);
+static const void * (DNSSD_API* _TXTRecordGetBytesPtr)(const TXTRecordRef *txtRecord);
+static int16_t (DNSSD_API* _TXTRecordGetLength)(const TXTRecordRef *txtRecord);
+static const void * (DNSSD_API* _TXTRecordGetValuePtr)(uint16_t txtLen, const void *txtRecord, const char *key, uint8_t *valueLen);
+static DNSServiceErrorType (DNSSD_API* _TXTRecordSetValue)(TXTRecordRef *txtRecord, const char *key, uint8_t valueSize, const void *value);
+
+#endif
+
+gboolean dns_sd_available(void) {
+#ifndef LINK_DNS_SD_DIRECTLY
+	static gboolean initialized = FALSE;
+	static gboolean loaded = FALSE;
+
+	if (!initialized) {
+		initialized = TRUE;
+		if ((_DNSServiceAddRecord = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceAddRecord"))
+				&& (_DNSServiceBrowse = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceBrowse"))
+				&& (_DNSServiceConstructFullName = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceConstructFullName"))
+				&& (_DNSServiceGetAddrInfo = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceGetAddrInfo"))
+				&& (_DNSServiceProcessResult = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceProcessResult"))
+				&& (_DNSServiceQueryRecord = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceQueryRecord"))
+				&& (_DNSServiceRefDeallocate = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceRefDeallocate"))
+				&& (_DNSServiceRefSockFD = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceRefSockFD"))
+				&& (_DNSServiceRegister = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceRegister"))
+				&& (_DNSServiceResolve = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceResolve"))
+				&& (_DNSServiceRemoveRecord = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceRemoveRecord"))
+				&& (_DNSServiceUpdateRecord = (void *) wpurple_find_and_loadproc("dnssd.dll", "DNSServiceUpdateRecord"))
+				&& (_TXTRecordCreate = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordCreate"))
+				&& (_TXTRecordDeallocate = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordDeallocate"))
+				&& (_TXTRecordGetBytesPtr = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordGetBytesPtr"))
+				&& (_TXTRecordGetLength = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordGetLength"))
+				&& (_TXTRecordGetValuePtr = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordGetValuePtr"))
+				&& (_TXTRecordSetValue = (void *) wpurple_find_and_loadproc("dnssd.dll", "TXTRecordSetValue"))) {
+			loaded = TRUE;
+		}
+	}
+	return loaded;
+#else
+	return TRUE;
+#endif
+}
+
+#ifndef LINK_DNS_SD_DIRECTLY
+
+DNSServiceErrorType _wpurple_DNSServiceAddRecord(DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
+		uint16_t rrtype, uint16_t rdlen, const void *rdata, uint32_t ttl) {
+	g_return_val_if_fail(_DNSServiceAddRecord != NULL, kDNSServiceErr_Unknown);
+	return (_DNSServiceAddRecord)(sdRef, RecordRef, flags, rrtype, rdlen, rdata, ttl);
+}
+
+DNSServiceErrorType _wpurple_DNSServiceBrowse(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+		const char *regtype, const char *domain, DNSServiceBrowseReply callBack, void *context) {
+	g_return_val_if_fail(_DNSServiceBrowse != NULL, kDNSServiceErr_Unknown);
+	return (_DNSServiceBrowse)(sdRef, flags, interfaceIndex, regtype, domain, callBack, context);
+}
+
+int _wpurple_DNSServiceConstructFullName(char *fullName, const char *service, const char *regtype, const char *domain) {
+	g_return_val_if_fail(_DNSServiceConstructFullName != NULL, 0);
+	return (_DNSServiceConstructFullName)(fullName, service, regtype, domain);
+}
+
+DNSServiceErrorType _wpurple_DNSServiceGetAddrInfo(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+		DNSServiceProtocol protocol, const char *hostname, DNSServiceGetAddrInfoReply callBack, void *context) {
+	g_return_val_if_fail(_DNSServiceGetAddrInfo != NULL, 0);
+	return (_DNSServiceGetAddrInfo)(sdRef, flags, interfaceIndex, protocol, hostname, callBack, context);
+}
+
+DNSServiceErrorType _wpurple_DNSServiceProcessResult(DNSServiceRef sdRef) {
+	g_return_val_if_fail(_DNSServiceProcessResult != NULL, kDNSServiceErr_Unknown);
+	return (_DNSServiceProcessResult)(sdRef);
+}
+
+
+DNSServiceErrorType _wpurple_DNSServiceQueryRecord(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+		const char *fullname, uint16_t rrtype, uint16_t rrclass, DNSServiceQueryRecordReply callBack, void *context) {
+	g_return_val_if_fail(_DNSServiceQueryRecord != NULL, kDNSServiceErr_Unknown);
+	return (_DNSServiceQueryRecord)(sdRef, flags, interfaceIndex, fullname, rrtype, rrclass, callBack, context);
+}
+
+void _wpurple_DNSServiceRefDeallocate(DNSServiceRef sdRef) {
+	g_return_if_fail(_DNSServiceRefDeallocate != NULL);
+	(_DNSServiceRefDeallocate)(sdRef);
+}
+
+int _wpurple_DNSServiceRefSockFD(DNSServiceRef sdRef) {
+	g_return_val_if_fail(_DNSServiceRefSockFD != NULL, -1);
+	return (_DNSServiceRefSockFD)(sdRef);
+}
+
+DNSServiceErrorType _wpurple_DNSServiceRegister(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+		const char *name, const char *regtype, const char *domain, const char *host, uint16_t port, uint16_t txtLen,
+		const void *txtRecord, DNSServiceRegisterReply callBack, void *context) {
+	g_return_val_if_fail(_DNSServiceRegister != NULL, kDNSServiceErr_Unknown);
+	return (_DNSServiceRegister)(sdRef, flags, interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord, callBack, context);
+}
+
+DNSServiceErrorType _wpurple_DNSServiceResolve(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, const char *name,
+		const char *regtype, const char *domain, DNSServiceResolveReply callBack, void *context) {
+	g_return_val_if_fail(_DNSServiceResolve != NULL, kDNSServiceErr_Unknown);
+	return (_DNSServiceResolve)(sdRef, flags, interfaceIndex, name, regtype, domain, callBack, context);
+}
+
+DNSServiceErrorType _wpurple_DNSServiceRemoveRecord(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags) {
+	g_return_val_if_fail(_DNSServiceRemoveRecord != NULL, kDNSServiceErr_Unknown);
+	return (_DNSServiceRemoveRecord)(sdRef, RecordRef, flags);
+}
+
+DNSServiceErrorType _wpurple_DNSServiceUpdateRecord(DNSServiceRef sdRef, DNSRecordRef RecordRef, DNSServiceFlags flags,
+		uint16_t rdlen, const void *rdata, uint32_t ttl) {
+	g_return_val_if_fail(_DNSServiceUpdateRecord != NULL, kDNSServiceErr_Unknown);
+	return (_DNSServiceUpdateRecord)(sdRef, RecordRef, flags, rdlen, rdata, ttl);
+}
+
+void _wpurple_TXTRecordCreate(TXTRecordRef *txtRecord, uint16_t bufferLen, void *buffer) {
+	g_return_if_fail(_TXTRecordCreate != NULL);
+	(_TXTRecordCreate)(txtRecord, bufferLen, buffer);
+}
+
+void _wpurple_TXTRecordDeallocate(TXTRecordRef *txtRecord) {
+	g_return_if_fail(_TXTRecordDeallocate != NULL);
+	(_TXTRecordDeallocate)(txtRecord);
+}
+
+const void * _wpurple_TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) {
+	g_return_val_if_fail(_TXTRecordGetBytesPtr != NULL, NULL);
+	return (_TXTRecordGetBytesPtr)(txtRecord);
+}
+
+uint16_t _wpurple_TXTRecordGetLength(const TXTRecordRef *txtRecord) {
+	g_return_val_if_fail(_TXTRecordGetLength != NULL, 0);
+	return (_TXTRecordGetLength)(txtRecord);
+}
+
+const void * _wpurple_TXTRecordGetValuePtr(uint16_t txtLen, const void *txtRecord, const char *key, uint8_t *valueLen) {
+	g_return_val_if_fail(_TXTRecordGetValuePtr != NULL, NULL);
+	return (_TXTRecordGetValuePtr)(txtLen, txtRecord, key, valueLen);
+}
+
+DNSServiceErrorType _wpurple_TXTRecordSetValue(TXTRecordRef *txtRecord, const char *key, uint8_t valueSize, const void *value) {
+	g_return_val_if_fail(_TXTRecordSetValue != NULL, kDNSServiceErr_Unknown);
+	return (_TXTRecordSetValue)(txtRecord, key, valueSize, value);
+}
+
+#endif /*LINK_DNS_SD_DIRECTLY*/
+
--- a/libpurple/protocols/bonjour/dns_sd_proxy.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/dns_sd_proxy.h	Wed Jun 13 19:30:27 2012 -0400
@@ -51,6 +51,11 @@
 #define DNSServiceConstructFullName(fullName, service, regtype, domain) \
 	_wpurple_DNSServiceConstructFullName(fullName, service, regtype, domain)
 
+DNSServiceErrorType _wpurple_DNSServiceGetAddrInfo(DNSServiceRef *sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
+	DNSServiceProtocol protocol, const char *hostname, DNSServiceGetAddrInfoReply callBack, void *context);
+#define DNSServiceGetAddrInfo(sdRef, flags, interfaceIndex, protocol, hostname, callBack, context) \
+	_wpurple_DNSServiceGetAddrInfo(sdRef, flags, interfaceIndex, protocol, hostname, callBack, context)
+
 DNSServiceErrorType _wpurple_DNSServiceProcessResult(DNSServiceRef sdRef);
 #define DNSServiceProcessResult(sdRef) \
 	_wpurple_DNSServiceProcessResult(sdRef);
--- a/libpurple/protocols/bonjour/jabber.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/jabber.c	Wed Jun 13 19:30:27 2012 -0400
@@ -44,6 +44,11 @@
 #endif
 #include <fcntl.h>
 
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
+
 #include "network.h"
 #include "eventloop.h"
 #include "connection.h"
@@ -201,7 +206,9 @@
 						g_string_append_printf(str, " face='%s'", font_face);
 					if (font_size)
 						g_string_append_printf(str, " size='%s'", font_size);
-					if (ichat_text_color)
+					if (font_color)
+						g_string_append_printf(str, " color='%s'", font_color);
+					else if (ichat_text_color)
 						g_string_append_printf(str, " color='%s'", ichat_text_color);
 					if (ichat_balloon_color)
 						g_string_append_printf(str, " back='%s'", ichat_balloon_color);
@@ -381,7 +388,7 @@
 	BonjourBuddy *bb = NULL;
 	const gchar *name = bconv->pb ? purple_buddy_get_name(bconv->pb) : "(unknown)";
 
-	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", name);
+	purple_debug_info("bonjour", "Received conversation close notification from %s.\n", name);
 
 	if(bconv->pb != NULL)
 		bb = purple_buddy_get_protocol_data(bconv->pb);
@@ -420,7 +427,7 @@
 			bonjour_jabber_close_conversation(bconv);
 			if (bconv->pb != NULL) {
 				BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
-				
+
 				if(bb != NULL)
 					bb->conversation = NULL;
 			}
@@ -529,7 +536,7 @@
 	if (bname == NULL)
 		bname = "";
 
-	stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(bconv->account), bname);
+	stream_start = g_strdup_printf(DOCTYPE, bonjour_get_jid(bconv->account), bname);
 	len = strlen(stream_start);
 
 	bconv->sent_stream_start = PARTIALLY_SENT;
@@ -577,7 +584,7 @@
 }
 
 /* This gets called when we've successfully sent our <stream:stream />
- * AND when we've recieved a <stream:stream /> */
+ * AND when we've received a <stream:stream /> */
 void bonjour_jabber_stream_started(BonjourJabberConversation *bconv) {
 
 	if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(bconv, bconv->socket)) {
@@ -623,15 +630,22 @@
 
 }
 
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
 static void
 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
 {
 	BonjourJabber *jdata = data;
-	struct sockaddr_in their_addr; /* connector's address information */
-	socklen_t sin_size = sizeof(struct sockaddr);
+	struct sockaddr_storage their_addr; /* connector's address information */
+	socklen_t sin_size = sizeof(struct sockaddr_storage);
 	int client_socket;
 	int flags;
-	char *address_text = NULL;
+#ifdef HAVE_INET_NTOP
+	char addrstr[INET6_ADDRSTRLEN];
+#endif
+	const char *address_text;
 	struct _match_buddies_by_address_t *mbba;
 	BonjourJabberConversation *bconv;
 	GSList *buddies;
@@ -640,7 +654,9 @@
 	if (condition != PURPLE_INPUT_READ)
 		return;
 
-	if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1)
+	memset(&their_addr, 0, sin_size);
+
+	if ((client_socket = accept(server_socket, (struct sockaddr*)&their_addr, &sin_size)) == -1)
 		return;
 
 	flags = fcntl(client_socket, F_GETFL);
@@ -650,7 +666,20 @@
 #endif
 
 	/* Look for the buddy that has opened the conversation and fill information */
-	address_text = inet_ntoa(their_addr.sin_addr);
+#ifdef HAVE_INET_NTOP
+	if (their_addr.ss_family == AF_INET6) {
+		address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in6 *)&their_addr)->sin6_addr,
+			addrstr, sizeof(addrstr));
+
+		append_iface_if_linklocal(addrstr,
+			((struct sockaddr_in6 *)&their_addr)->sin6_scope_id);
+	}
+	else
+		address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in *)&their_addr)->sin_addr,
+			addrstr, sizeof(addrstr));
+#else
+	address_text = inet_ntoa(((struct sockaddr_in *)&their_addr)->sin_addr);
+#endif
 	purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
 	mbba = g_new0(struct _match_buddies_by_address_t, 1);
 	mbba->address = address_text;
@@ -660,7 +689,7 @@
 	g_slist_free(buddies);
 
 	if (mbba->matched_buddies == NULL) {
-		purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n");
+		purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
 		g_free(mbba);
 		close(client_socket);
 		return;
@@ -680,58 +709,48 @@
 
 }
 
-gint
-bonjour_jabber_start(BonjourJabber *jdata)
+static int
+start_serversocket_listening(int port, int socket, struct sockaddr *addr, size_t addr_size, gboolean ip6, gboolean allow_port_fallback)
 {
-	struct sockaddr_in my_addr;
+	int ret_port = port;
 
-	/* Open a listening socket for incoming conversations */
-	jdata->socket = socket(PF_INET, SOCK_STREAM, 0);
-	if (jdata->socket < 0) {
-		gchar *buf = g_strdup_printf(_("Unable to create socket: %s"),
-				g_strerror(errno));
-		purple_connection_error_reason(jdata->account->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
-		g_free(buf);
-		return -1;
-	}
-
-	memset(&my_addr, 0, sizeof(struct sockaddr_in));
-	my_addr.sin_family = AF_INET;
+	purple_debug_info("bonjour", "Attempting to bind IPv%d socket to port %d.\n", ip6 ? 6 : 4, port);
 
 	/* Try to use the specified port - if it isn't available, use a random port */
-	my_addr.sin_port = htons(jdata->port);
-	if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
-	{
+	if (bind(socket, addr, addr_size) != 0) {
+
 		purple_debug_info("bonjour", "Unable to bind to specified "
-				"port %i: %s\n", jdata->port, g_strerror(errno));
-		my_addr.sin_port = 0;
-		if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
-		{
-			gchar *buf = g_strdup_printf(_("Unable to bind socket "
-					"to port: %s"), g_strerror(errno));
-			purple_connection_error_reason(jdata->account->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
-			g_free(buf);
+				"port %i: %s\n", port, g_strerror(errno));
+
+		if (!allow_port_fallback) {
+			purple_debug_warning("bonjour", "Not attempting random port assignment.\n");
 			return -1;
 		}
-		jdata->port = purple_network_get_port_from_fd(jdata->socket);
+#ifdef PF_INET6
+		if (ip6)
+			((struct sockaddr_in6 *) addr)->sin6_port = 0;
+		else
+#endif
+		((struct sockaddr_in *) addr)->sin_port = 0;
+
+		if (bind(socket, addr, addr_size) != 0) {
+			purple_debug_error("bonjour", "Unable to bind IPv%d socket to port: %s\n", ip6 ? 6 : 4, g_strerror(errno));
+			return -1;
+		}
+		ret_port = purple_network_get_port_from_fd(socket);
 	}
 
+	purple_debug_info("bonjour", "Bound IPv%d socket to port %d.\n", ip6 ? 6 : 4, ret_port);
+
 	/* Attempt to listen on the bound socket */
-	if (listen(jdata->socket, 10) != 0)
-	{
-		gchar *buf = g_strdup_printf(_("Unable to listen on socket: %s"),
-				g_strerror(errno));
-		purple_connection_error_reason(jdata->account->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
-		g_free(buf);
+	if (listen(socket, 10) != 0) {
+		purple_debug_error("bonjour", "Unable to listen on IPv%d socket: %s\n", ip6 ? 6 : 4, g_strerror(errno));
 		return -1;
 	}
 
 #if 0
 	/* TODO: Why isn't this being used? */
-	data->socket = purple_network_listen(jdata->port, SOCK_STREAM);
+	data->socket = purple_network_listen(jdata->port, AF_UNSPEC, SOCK_STREAM, TRUE);
 
 	if (jdata->socket == -1)
 	{
@@ -739,8 +758,70 @@
 	}
 #endif
 
-	/* Open a watcher in the socket we have just opened */
-	jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+	return ret_port;
+}
+
+gint
+bonjour_jabber_start(BonjourJabber *jdata)
+{
+	int ipv6_port = -1, ipv4_port = -1;
+
+	/* Open a listening socket for incoming conversations */
+#ifdef PF_INET6
+	jdata->socket6 = socket(PF_INET6, SOCK_STREAM, 0);
+#endif
+	jdata->socket = socket(PF_INET, SOCK_STREAM, 0);
+	if (jdata->socket == -1 && jdata->socket6 == -1) {
+		purple_debug_error("bonjour", "Unable to create socket: %s",
+				g_strerror(errno));
+		return -1;
+	}
+
+#ifdef PF_INET6
+	if (jdata->socket6 != -1) {
+		struct sockaddr_in6 addr6;
+#ifdef IPV6_V6ONLY
+		int on = 1;
+		setsockopt(jdata->socket6, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+#endif
+	        memset(&addr6, 0, sizeof(addr6));
+		addr6.sin6_family = AF_INET6;
+		addr6.sin6_port = htons(jdata->port);
+      		addr6.sin6_addr = in6addr_any;
+		ipv6_port = start_serversocket_listening(jdata->port, jdata->socket6, (struct sockaddr *) &addr6, sizeof(addr6), TRUE, TRUE);
+		/* Open a watcher in the socket we have just opened */
+		if (ipv6_port > 0) {
+			jdata->watcher_id6 = purple_input_add(jdata->socket6, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+			jdata->port = ipv6_port;
+		} else {
+			purple_debug_error("bonjour", "Failed to start listening on IPv6 socket.\n");
+			close(jdata->socket6);
+			jdata->socket6 = -1;
+		}
+	}
+#endif
+	if (jdata->socket != -1) {
+		struct sockaddr_in addr4;
+		memset(&addr4, 0, sizeof(addr4));
+		addr4.sin_family = AF_INET;
+		addr4.sin_port = htons(jdata->port);
+		ipv4_port = start_serversocket_listening(jdata->port, jdata->socket, (struct sockaddr *) &addr4, sizeof(addr4), FALSE, ipv6_port != -1);
+		/* Open a watcher in the socket we have just opened */
+		if (ipv4_port > 0) {
+			jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+			jdata->port = ipv4_port;
+		} else {
+			purple_debug_error("bonjour", "Failed to start listening on IPv4 socket.\n");
+			close(jdata->socket);
+			jdata->socket = -1;
+		}
+	}
+
+	if (!(ipv6_port > 0 || ipv4_port > 0)) {
+		purple_debug_error("bonjour", "Unable to listen on socket: %s",
+				g_strerror(errno));
+		return -1;
+	}
 
 	return jdata->port;
 }
@@ -756,12 +837,45 @@
 	if (source < 0) {
 		PurpleConversation *conv = NULL;
 		PurpleAccount *account = NULL;
+		GSList *tmp = bb->ips;
 
-		purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n",
-				   purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, error ? error : "(null)");
+		purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d (%s); Trying next IP address\n",
+				   purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, error);
+
+		/* There may be multiple entries for the same IP - one per
+		 * presence recieved (e.g. multiple interfaces).
+		 * We need to make sure that we find the previously used entry.
+		 */
+		while (tmp && bb->conversation->ip_link != tmp->data)
+			tmp = g_slist_next(tmp);
+		if (tmp)
+			tmp = g_slist_next(tmp);
 
 		account = purple_buddy_get_account(pb);
 
+		if (tmp != NULL) {
+			const gchar *ip;
+			PurpleProxyConnectData *connect_data;
+
+			bb->conversation->ip_link = ip = tmp->data;
+
+			purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n",
+					  purple_buddy_get_name(pb), ip, bb->port_p2pj);
+
+			connect_data = purple_proxy_connect(purple_account_get_connection(account),
+							    account, ip, bb->port_p2pj, _connected_to_buddy, pb);
+
+			if (connect_data != NULL) {
+				g_free(bb->conversation->ip);
+				bb->conversation->ip = g_strdup(ip);
+				bb->conversation->connect_data = connect_data;
+
+				return;
+			}
+		}
+
+		purple_debug_error("bonjour", "No more addresses for buddy %s. Aborting", purple_buddy_get_name(pb));
+
 		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, account);
 		if (conv != NULL)
 			purple_conversation_write(conv, NULL,
@@ -821,7 +935,9 @@
 		while(tmp) {
 			ip = tmp->data;
 			if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
-				BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+				PurpleConnection *pc = purple_account_get_connection(bconv->account);
+				BonjourData *bd = purple_connection_get_protocol_data(pc);
+				BonjourJabber *jdata = bd->jabber_data;
 
 				purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
 					purple_buddy_get_name(pb), bconv->ip);
@@ -854,7 +970,9 @@
 
 void
 bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) {
-	BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+	PurpleConnection *pc = purple_account_get_connection(bconv->account);
+	BonjourData *bd = purple_connection_get_protocol_data(pc);
+	BonjourJabber *jdata = bd->jabber_data;
 	struct _match_buddies_by_address_t *mbba;
 	GSList *buddies;
 
@@ -920,10 +1038,9 @@
 	{
 		PurpleProxyConnectData *connect_data;
 		PurpleProxyInfo *proxy_info;
-		/* For better or worse, use the first IP*/
-		const char *ip = bb->ips->data;
+		const char *ip = bb->ips->data; /* Start with the first IP address. */
 
-		purple_debug_info("bonjour", "Starting conversation with %s\n", to);
+		purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n", to, ip, bb->port_p2pj);
 
 		/* Make sure that the account always has a proxy of "none".
 		 * This is kind of dirty, but proxy_connect_none() isn't exposed. */
@@ -946,6 +1063,7 @@
 
 		bb->conversation = bonjour_jabber_conv_new(pb, jdata->account, ip);
 		bb->conversation->connect_data = connect_data;
+		bb->conversation->ip_link = ip;
 		/* We don't want _send_data() to register the tx_handler;
 		 * that neeeds to wait until we're actually connected. */
 		bb->conversation->tx_handler = 0;
@@ -973,7 +1091,7 @@
 
 	message_node = xmlnode_new("message");
 	xmlnode_set_attrib(message_node, "to", bb->name);
-	xmlnode_set_attrib(message_node, "from", purple_account_get_username(jdata->account));
+	xmlnode_set_attrib(message_node, "from", bonjour_get_jid(jdata->account));
 	xmlnode_set_attrib(message_node, "type", "chat");
 
 	/* Enclose the message from the UI within a "font" node */
@@ -1014,7 +1132,9 @@
 
 void
 async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) {
-	BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
+	PurpleConnection *pc = purple_account_get_connection(bconv->account);
+	BonjourData *bd = purple_connection_get_protocol_data(pc);
+	BonjourJabber *jdata = bd->jabber_data;
 
 	jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
 
@@ -1034,8 +1154,9 @@
 	if (bconv != NULL) {
 		BonjourData *bd = NULL;
 
-		if(PURPLE_CONNECTION_IS_VALID(bconv->account->gc)) {
-			bd = bconv->account->gc->proto_data;
+		PurpleConnection *pc = purple_account_get_connection(bconv->account);
+		if (PURPLE_CONNECTION_IS_VALID(pc)) {
+			bd = purple_connection_get_protocol_data(pc);
 			bd->jabber_data->pending_conversations = g_slist_remove(bd->jabber_data->pending_conversations, bconv);
 		}
 
@@ -1049,7 +1170,7 @@
 				tmp_next = xfers->next;
 				/* We only need to cancel this if it hasn't actually started transferring. */
 				/* This will change if we ever support IBB transfers. */
-				if (strcmp(xfer->who, purple_buddy_get_name(bconv->pb)) == 0
+				if (strcmp(purple_xfer_get_remote_user(xfer), purple_buddy_get_name(bconv->pb)) == 0
 						&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
 							|| purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
 					purple_xfer_cancel_remote(xfer);
@@ -1101,15 +1222,22 @@
 		close(jdata->socket);
 	if (jdata->watcher_id > 0)
 		purple_input_remove(jdata->watcher_id);
+	if (jdata->socket6 >= 0)
+		close(jdata->socket6);
+	if (jdata->watcher_id6 > 0)
+		purple_input_remove(jdata->watcher_id6);
 
 	/* Close all the conversation sockets and remove all the watchers after sending end streams */
-	if (jdata->account->gc != NULL) {
+	if (!purple_account_is_disconnected(jdata->account)) {
 		GSList *buddies, *l;
 
 		buddies = purple_find_buddies(jdata->account, NULL);
 		for (l = buddies; l; l = l->next) {
 			BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data);
-			if (bb != NULL) {
+			if (bb && bb->conversation) {
+				/* Any ongoing connection attempt is cancelled
+				 * by _purple_connection_destroy */
+				bb->conversation->connect_data = NULL;
 				bonjour_jabber_close_conversation(bb->conversation);
 				bb->conversation = NULL;
 			}
@@ -1181,7 +1309,7 @@
 
 	for(l = acc->deny; l != NULL; l = l->next) {
 		const gchar *name = purple_buddy_get_name(pb);
-		const gchar *username = purple_account_get_username(acc);
+		const gchar *username = bonjour_get_jid(acc);
 
 		if(!purple_utf8_strcasecmp(name, (char *)l->data)) {
 			purple_debug_info("bonjour", "%s has been blocked by %s.\n", name, username);
@@ -1195,7 +1323,6 @@
 static void
 xep_iq_parse(xmlnode *packet, PurpleBuddy *pb)
 {
-	xmlnode *child;
 	PurpleAccount *account;
 	PurpleConnection *gc;
 
@@ -1205,7 +1332,7 @@
 		account = purple_buddy_get_account(pb);
 		gc = purple_account_get_connection(account);
 
-	if ((child = xmlnode_get_child(packet, "si")) || (child = xmlnode_get_child(packet, "error")))
+	if (xmlnode_get_child(packet, "si") != NULL || xmlnode_get_child(packet, "error") != NULL)
 		xep_si_parse(gc, packet, pb);
 	else
 		xep_bytestreams_parse(gc, packet, pb);
@@ -1234,58 +1361,113 @@
 	return (ret >= 0) ? 0 : -1;
 }
 
-/* This returns a ';' delimited string containing all non-localhost IPs */
-const char *
-purple_network_get_my_ip_ext2(int fd)
+/* This returns a list containing all non-localhost IPs */
+GSList *
+bonjour_jabber_get_local_ips(int fd)
 {
-	char buffer[1024];
-	static char ip_ext[17 * 10];
+	GSList *ips = NULL;
+	const char *address_text;
+	int ret;
+
+#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
+	{
+	struct ifaddrs *ifap, *ifa;
+	struct sockaddr *addr;
+	char addrstr[INET6_ADDRSTRLEN];
+
+	ret = getifaddrs(&ifap);
+	if (ret != 0) {
+		const char *error = g_strerror(errno);
+		purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
+		return NULL;
+	}
+
+	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+		if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
+			continue;
+
+		addr = ifa->ifa_addr;
+		address_text = NULL;
+		switch (addr->sa_family) {
+			case AF_INET:
+				address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr,
+					addrstr, sizeof(addrstr));
+				break;
+#ifdef PF_INET6
+			case AF_INET6:
+				address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr,
+					addrstr, sizeof(addrstr));
+				break;
+#endif
+		}
+
+		if (address_text != NULL) {
+			if (addr->sa_family == AF_INET)
+				ips = g_slist_append(ips, g_strdup(address_text));
+			else
+				ips = g_slist_prepend(ips, g_strdup(address_text));
+		}
+	}
+
+	freeifaddrs(ifap);
+
+	}
+#else
+	{
 	char *tmp;
-	char *tip;
 	struct ifconf ifc;
 	struct ifreq *ifr;
+	char buffer[1024];
 	struct sockaddr_in *sinptr;
-	guint32 lhost = htonl(127 * 256 * 256 * 256 + 1);
-	long unsigned int add;
 	int source = fd;
-	int len, count = 0;
 
 	if (fd < 0)
 		source = socket(PF_INET, SOCK_STREAM, 0);
 
 	ifc.ifc_len = sizeof(buffer);
 	ifc.ifc_req = (struct ifreq *)buffer;
-	ioctl(source, SIOCGIFCONF, &ifc);
+	ret = ioctl(source, SIOCGIFCONF, &ifc);
 
 	if (fd < 0)
 		close(source);
 
-	memset(ip_ext, 0, sizeof(ip_ext));
-	memcpy(ip_ext, "0.0.0.0", 7);
+	if (ret < 0) {
+		const char *error = g_strerror(errno);
+		purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
+		return NULL;
+	}
+
 	tmp = buffer;
-	tip = ip_ext;
-	while (tmp < buffer + ifc.ifc_len && count < 10)
-	{
+	while (tmp < buffer + ifc.ifc_len) {
 		ifr = (struct ifreq *)tmp;
 		tmp += HX_SIZE_OF_IFREQ(*ifr);
 
-		if (ifr->ifr_addr.sa_family == AF_INET)
-		{
+		if (ifr->ifr_addr.sa_family == AF_INET) {
 			sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
-			if (sinptr->sin_addr.s_addr != lhost)
-			{
-				add = ntohl(sinptr->sin_addr.s_addr);
-				len = g_snprintf(tip, 17, "%lu.%lu.%lu.%lu;",
-					((add >> 24) & 255),
-					((add >> 16) & 255),
-					((add >> 8) & 255),
-					add & 255);
-				tip = &tip[len];
-				count++;
-				continue;
+			if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) {
+				address_text = inet_ntoa(sinptr->sin_addr);
+				ips = g_slist_prepend(ips, g_strdup(address_text));
 			}
-		}
+ 		}
+	}
 	}
+#endif
+
+	return ips;
+}
 
-	return ip_ext;
+void
+append_iface_if_linklocal(char *ip, guint32 interface_param) {
+	struct in6_addr in6_addr;
+	int len_remain = INET6_ADDRSTRLEN - strlen(ip);
+
+	if (len_remain <= 1)
+		return;
+
+	if (inet_pton(AF_INET6, ip, &in6_addr) != 1 ||
+	    !IN6_IS_ADDR_LINKLOCAL(&in6_addr))
+		return;
+
+	snprintf(ip + strlen(ip), len_remain, "%%%d",
+		 interface_param);
 }
--- a/libpurple/protocols/bonjour/jabber.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/jabber.h	Wed Jun 13 19:30:27 2012 -0400
@@ -37,7 +37,9 @@
 {
 	gint port;
 	gint socket;
+	gint socket6;
 	gint watcher_id;
+	gint watcher_id6;
 	PurpleAccount *account;
 	GSList *pending_conversations;
 } BonjourJabber;
@@ -61,6 +63,8 @@
 	/* The following are only needed before attaching to a PurpleBuddy */
 	gchar *buddy_name;
 	gchar *ip;
+	/* This points to a data entry in BonjourBuddy->ips */
+	const gchar *ip_link;
 } BonjourJabberConversation;
 
 /**
@@ -105,6 +109,8 @@
 
 XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id);
 int xep_iq_send_and_free(XepIq *iq);
-const char *purple_network_get_my_ip_ext2(int fd);
+GSList * bonjour_jabber_get_local_ips(int fd);
+
+void append_iface_if_linklocal(char *ip, guint32 interface_param);
 
 #endif /* _BONJOUR_JABBER_H_ */
--- a/libpurple/protocols/bonjour/mdns_avahi.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/mdns_avahi.c	Wed Jun 13 19:30:27 2012 -0400
@@ -115,7 +115,6 @@
 	AvahiStringList *l;
 	size_t size;
 	char *key, *value;
-	int ret;
 	char ip[AVAHI_ADDRESS_STR_MAX];
 	AvahiBuddyImplData *b_impl;
 	AvahiSvcResolverData *rd;
@@ -150,6 +149,10 @@
 			}
 			break;
 		case AVAHI_RESOLVER_FOUND:
+
+			purple_debug_info("bonjour", "_resolve_callback - name:%s account:%p bb:%p\n",
+				name, account, bb);
+
 			/* create a buddy record */
 			if (bb == NULL)
 				bb = bonjour_buddy_new(name, account);
@@ -173,16 +176,29 @@
 
 
 			/* Get the ip as a string */
+			ip[0] = '\0';
 			avahi_address_snprint(ip, AVAHI_ADDRESS_STR_MAX, a);
 
+			if (protocol == AVAHI_PROTO_INET6)
+				append_iface_if_linklocal(ip, interface);
+
+			purple_debug_info("bonjour", "_resolve_callback - name:%s ip:%s prev_ip:%s\n",
+				name, ip, rd->ip);
+
 			if (rd->ip == NULL || strcmp(rd->ip, ip) != 0) {
 				/* We store duplicates in bb->ips, so we always remove the one */
 				if (rd->ip != NULL) {
 					bb->ips = g_slist_remove(bb->ips, rd->ip);
 					g_free((gchar *) rd->ip);
 				}
-				bb->ips = g_slist_prepend(bb->ips, g_strdup(ip));
-				rd->ip = bb->ips->data;
+				/* IPv6 goes at the front of the list and IPv4 at the end so that we "prefer" IPv6, if present */
+				if (protocol == AVAHI_PROTO_INET6) {
+					rd->ip = g_strdup_printf("%s", ip);
+					bb->ips = g_slist_prepend(bb->ips, (gchar *) rd->ip);
+				} else {
+					rd->ip = g_strdup(ip);
+					bb->ips = g_slist_append(bb->ips, (gchar *) rd->ip);
+				}
 			}
 
 			bb->port_p2pj = port;
@@ -190,7 +206,7 @@
 			/* Obtain the parameters from the text_record */
 			clear_bonjour_buddy_values(bb);
 			for(l = txt; l != NULL; l = l->next) {
-				if ((ret = avahi_string_list_get_pair(l, &key, &value, &size)) < 0)
+				if (avahi_string_list_get_pair(l, &key, &value, &size) < 0)
 					continue;
 				set_bonjour_buddy_value(bb, key, value, size);
 				/* TODO: Since we're using the glib allocator, I think we
@@ -200,8 +216,8 @@
 			}
 
 			if (!bonjour_buddy_check(bb)) {
+				b_impl->resolvers = g_slist_remove(b_impl->resolvers, rd);
 				_cleanup_resolver_data(rd);
-				b_impl->resolvers = g_slist_remove(b_impl->resolvers, rd);
 				/* If this was the last resolver, remove the buddy */
 				if (b_impl->resolvers == NULL) {
 					if (pb != NULL)
@@ -239,9 +255,9 @@
 			/* A new peer has joined the network and uses iChat bonjour */
 			purple_debug_info("bonjour", "_browser_callback - new service\n");
 			/* Make sure it isn't us */
-			if (purple_utf8_strcasecmp(name, account->username) != 0) {
+			if (purple_utf8_strcasecmp(name, bonjour_get_jid(account)) != 0) {
 				if (!avahi_service_resolver_new(avahi_service_browser_get_client(b),
-						interface, protocol, name, type, domain, AVAHI_PROTO_INET,
+						interface, protocol, name, type, domain, protocol,
 						0, _resolver_callback, account)) {
 					purple_debug_warning("bonjour", "_browser_callback -- Error initiating resolver: %s\n",
 						avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
@@ -408,6 +424,8 @@
 
 	data->mdns_impl_data = idata;
 
+	bonjour_dns_sd_set_jid(data->account, avahi_client_get_host_name(idata->client));
+
 	return TRUE;
 }
 
@@ -440,15 +458,15 @@
 		case PUBLISH_START:
 			publish_result = avahi_entry_group_add_service_strlst(
 				idata->group, AVAHI_IF_UNSPEC,
-				AVAHI_PROTO_INET, 0,
-				purple_account_get_username(data->account),
+				AVAHI_PROTO_UNSPEC, 0,
+				bonjour_get_jid(data->account),
 				LINK_LOCAL_RECORD_NAME, NULL, NULL, data->port_p2pj, lst);
 			break;
 		case PUBLISH_UPDATE:
 			publish_result = avahi_entry_group_update_service_txt_strlst(
 				idata->group, AVAHI_IF_UNSPEC,
-				AVAHI_PROTO_INET, 0,
-				purple_account_get_username(data->account),
+				AVAHI_PROTO_UNSPEC, 0,
+				bonjour_get_jid(data->account),
 				LINK_LOCAL_RECORD_NAME, NULL, lst);
 			break;
 	}
@@ -479,7 +497,7 @@
 
 	g_return_val_if_fail(idata != NULL, FALSE);
 
-	idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account);
+	idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account);
 	if (!idata->sb) {
 
 		purple_debug_error("bonjour",
@@ -522,10 +540,10 @@
 		}
 
 		svc_name = g_strdup_printf("%s." LINK_LOCAL_RECORD_NAME "local",
-				purple_account_get_username(data->account));
+				bonjour_get_jid(data->account));
 
 		ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC,
-			AVAHI_PROTO_INET, flags, svc_name,
+			AVAHI_PROTO_UNSPEC, flags, svc_name,
 			AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len);
 
 		g_free(svc_name);
@@ -600,7 +618,7 @@
 
 void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy) {
 	PurpleConnection *conn = purple_account_get_connection(buddy->account);
-	BonjourData *bd = conn->proto_data;
+	BonjourData *bd = purple_connection_get_protocol_data(conn);
 	AvahiSessionImplData *session_idata = bd->dns_sd_data->mdns_impl_data;
 	AvahiBuddyImplData *idata = buddy->mdns_impl_data;
 	gchar *name;
@@ -614,7 +632,7 @@
 
 	name = g_strdup_printf("%s." LINK_LOCAL_RECORD_NAME "local", buddy->name);
 	idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC,
-		AVAHI_PROTO_INET, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
+		AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
 		_buddy_icon_record_cb, buddy);
 	g_free(name);
 
--- a/libpurple/protocols/bonjour/mdns_common.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/mdns_common.c	Wed Jun 13 19:30:27 2012 -0400
@@ -252,3 +252,36 @@
 void bonjour_dns_sd_stop(BonjourDnsSd *data) {
 	_mdns_stop(data);
 }
+
+void
+bonjour_dns_sd_set_jid(PurpleAccount *account, const char *hostname)
+{
+	PurpleConnection *conn = purple_account_get_connection(account);
+	BonjourData *bd = purple_connection_get_protocol_data(conn);
+	const char *tmp, *account_name = purple_account_get_username(account);
+
+	/* Previously we allowed the hostname part of the jid to be set
+	 * explicitly when it should always be the current hostname.
+	 * That is what this is intended to deal with.
+	 */
+	if ((tmp = strchr(account_name, '@'))
+	    && strstr(tmp, hostname) == (tmp + 1)
+	    && *((tmp + 1) + strlen(hostname)) == '\0')
+		bd->jid = g_strdup(account_name);
+	else {
+		const char *tmp2;
+		GString *str = g_string_new("");
+		/* Escape an '@' in the account name */
+		tmp = account_name;
+		while ((tmp2 = strchr(tmp, '@')) != NULL) {
+			g_string_append_len(str, tmp, tmp2 - tmp);
+			g_string_append(str, "\\40");
+			tmp = tmp2 + 1;
+		}
+		g_string_append(str, tmp);
+		g_string_append_c(str, '@');
+		g_string_append(str, hostname);
+
+		bd->jid = g_string_free(str, FALSE);
+	}
+}
--- a/libpurple/protocols/bonjour/mdns_common.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/mdns_common.h	Wed Jun 13 19:30:27 2012 -0400
@@ -57,4 +57,6 @@
  */
 void bonjour_dns_sd_stop(BonjourDnsSd *data);
 
+void bonjour_dns_sd_set_jid(PurpleAccount *account, const char *hostname);
+
 #endif
--- a/libpurple/protocols/bonjour/mdns_types.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/mdns_types.h	Wed Jun 13 19:30:27 2012 -0400
@@ -37,7 +37,7 @@
 	gchar *msg;
 } BonjourDnsSd;
 
-typedef enum _PublishType {
+typedef enum {
 	PUBLISH_START,
 	PUBLISH_UPDATE
 } PublishType;
--- a/libpurple/protocols/bonjour/mdns_win32.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Wed Jun 13 19:30:27 2012 -0400
@@ -25,8 +25,8 @@
 #include "buddy.h"
 #include "mdns_interface.h"
 #include "dns_sd_proxy.h"
-#include "dnsquery.h"
 #include "mdns_common.h"
+#include "bonjour.h"
 
 static GSList *pending_buddies = NULL;
 
@@ -65,7 +65,6 @@
 	BonjourBuddy *bb;
 	Win32SvcResolverData *res_data;
 	gchar *full_service_name;
-	PurpleDnsQueryData *dns_query;
 } ResolveCallbackArgs;
 
 
@@ -106,7 +105,7 @@
 		purple_debug_error("bonjour", "Error (%d) handling mDNS response.\n", errorCode);
 		/* This happens when the mDNSResponder goes down, I haven't seen it happen any other time (in my limited testing) */
 		if (errorCode == kDNSServiceErr_Unknown) {
-			purple_connection_error_reason(srh->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			purple_connection_error(srh->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Error communicating with local mDNSResponder."));
 		}
 	}
@@ -135,7 +134,7 @@
 {
 
 	if (errorCode != kDNSServiceErr_NoError) {
-		purple_debug_error("bonjour", "record query - callback error.\n");
+		purple_debug_error("bonjour", "record query - callback error (%d).\n", errorCode);
 		/* TODO: Probably should remove the buddy when this happens */
 	} else if (flags & kDNSServiceFlagsAdd) {
 		if (rrtype == kDNSServiceType_TXT) {
@@ -161,16 +160,24 @@
 	}
 }
 
-static void
-_mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message)
+static void DNSSD_API
+_mdns_resolve_host_callback(DNSServiceRef sdRef, DNSServiceFlags flags,
+	uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+	const char *hostname, const struct sockaddr *address,
+	uint32_t ttl, void *context)
 {
-	ResolveCallbackArgs *args = (ResolveCallbackArgs*) data;
+	ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
 	gboolean delete_buddy = FALSE;
 	PurpleBuddy *pb = NULL;
 
+	purple_input_remove(args->resolver_query->input_handler);
+	DNSServiceRefDeallocate(args->resolver_query->sdRef);
+	g_free(args->resolver_query);
+	args->resolver_query = NULL;
+
 	if ((pb = purple_find_buddy(args->account, args->res_data->name))) {
-		if (pb->proto_data != args->bb) {
+		if (purple_buddy_get_protocol_data(pb) != args->bb) {
 			purple_debug_error("bonjour", "Found purple buddy for %s not matching bonjour buddy record.",
 				args->res_data->name);
 			goto cleanup;
@@ -181,12 +188,10 @@
 		goto cleanup;
 	}
 
-	if (!hosts || !hosts->data) {
-		purple_debug_error("bonjour", "host resolution - callback error.\n");
+	if (errorCode != kDNSServiceErr_NoError) {
+		purple_debug_error("bonjour", "host resolution - callback error (%d).\n", errorCode);
 		delete_buddy = TRUE;
 	} else {
-		struct sockaddr_in *addr = g_slist_nth_data(hosts, 1);
-		DNSServiceErrorType errorCode;
 		DNSServiceRef txt_query_sr;
 
 		/* finally, set up the continuous txt record watcher, and add the buddy to purple */
@@ -194,7 +199,7 @@
 				kDNSServiceInterfaceIndexAny, args->full_service_name, kDNSServiceType_TXT,
 				kDNSServiceClass_IN, _mdns_record_query_callback, args->bb);
 		if (errorCode == kDNSServiceErr_NoError) {
-			const char *ip = inet_ntoa(addr->sin_addr);
+			const char *ip = inet_ntoa(((struct sockaddr_in *) address)->sin_addr);
 
 			purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", args->bb->name, ip, args->bb->port_p2pj);
 
@@ -218,13 +223,6 @@
 
 	cleanup:
 
-	/* free the hosts list*/
-	while (hosts != NULL) {
-		hosts = g_slist_remove(hosts, hosts->data);
-		g_free(hosts->data);
-		hosts = g_slist_remove(hosts, hosts->data);
-	}
-
 	if (delete_buddy) {
 		idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
 		_cleanup_resolver_data(args->res_data);
@@ -251,7 +249,7 @@
 
 static void DNSSD_API
 _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
-    const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
+    const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context)
 {
 	ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
@@ -259,17 +257,20 @@
 	/* remove the input fd and destroy the service ref */
 	purple_input_remove(args->resolver_query->input_handler);
 	DNSServiceRefDeallocate(args->resolver_query->sdRef);
-	g_free(args->resolver_query);
-	args->resolver_query = NULL;
 
 	if (errorCode != kDNSServiceErr_NoError)
-		purple_debug_error("bonjour", "service resolver - callback error.\n");
+		purple_debug_error("bonjour", "service resolver - callback error. (%d)\n", errorCode);
 	else {
+		DNSServiceRef getaddrinfo_sr;
 		/* set more arguments, and start the host resolver */
-
-		if ((args->dns_query =
-				purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args)) != NULL) {
-
+		errorCode = DNSServiceGetAddrInfo(&getaddrinfo_sr, 0, interfaceIndex,
+			kDNSServiceProtocol_IPv4, hosttarget, _mdns_resolve_host_callback, args);
+		if (errorCode != kDNSServiceErr_NoError)
+			purple_debug_error("bonjour", "service resolver - host resolution failed.\n");
+		else {
+			args->resolver_query->sdRef = getaddrinfo_sr;
+			args->resolver_query->input_handler = purple_input_add(DNSServiceRefSockFD(getaddrinfo_sr),
+				PURPLE_INPUT_READ, _mdns_handle_event, args->resolver_query);
 			args->full_service_name = g_strdup(fullname);
 
 			/* TODO: Should this be per resolver? */
@@ -277,12 +278,14 @@
 
 			/* We don't want to hit the cleanup code */
 			return;
-		} else
-			purple_debug_error("bonjour", "service resolver - host resolution failed.\n");
+		}
 	}
 
 	/* If we get this far, clean up */
 
+	g_free(args->resolver_query);
+	args->resolver_query = NULL;
+
 	idata->resolvers = g_slist_remove(idata->resolvers, args->res_data);
 	_cleanup_resolver_data(args->res_data);
 
@@ -324,7 +327,7 @@
 		purple_debug_error("bonjour", "service browser - callback error (%d)\n", errorCode);
 	else if (flags & kDNSServiceFlagsAdd) {
 		/* A presence service instance has been discovered... check it isn't us! */
-		if (purple_utf8_strcasecmp(serviceName, account->username) != 0) {
+		if (purple_utf8_strcasecmp(serviceName, bonjour_get_jid(account)) != 0) {
 			DNSServiceErrorType resErrorCode;
 			/* OK, lets go ahead and resolve it to add to the buddy list */
 			ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1);
@@ -334,7 +337,7 @@
 							  serviceName, interfaceIndex, regtype ? regtype : "",
 							  replyDomain ? replyDomain : "");
 
-			resErrorCode = DNSServiceResolve(&resolver_sr, 0, 0, serviceName, regtype,
+			resErrorCode = DNSServiceResolve(&resolver_sr, 0, interfaceIndex, serviceName, regtype,
 					replyDomain, _mdns_service_resolve_callback, args);
 			if (resErrorCode == kDNSServiceErr_NoError) {
 				GSList *tmp = pending_buddies;
@@ -345,7 +348,7 @@
 
 				/* Is there an existing buddy? */
 				if ((pb = purple_find_buddy(account, serviceName)))
-					bb = pb->proto_data;
+					bb = purple_buddy_get_protocol_data(pb);
 				/* Is there a pending buddy? */
 				else {
 					while (tmp) {
@@ -365,7 +368,7 @@
 					if (pb == NULL)
 						pending_buddies = g_slist_prepend(pending_buddies, bb);
 					else
-						pb->proto_data = bb;
+						purple_buddy_set_protocol_data(pb, bb);
 				}
 
 				rd = g_new0(Win32SvcResolverData, 1);
@@ -405,7 +408,7 @@
 			GSList *l;
 			/* There may be multiple presences, we should only get rid of this one */
 			Win32SvcResolverData *rd_search;
-			BonjourBuddy *bb = pb->proto_data;
+			BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
 			Win32BuddyImplData *idata;
 
 			g_return_if_fail(bb != NULL);
@@ -452,6 +455,9 @@
 
 gboolean _mdns_init_session(BonjourDnsSd *data) {
 	data->mdns_impl_data = g_new0(Win32SessionImplData, 1);
+
+	bonjour_dns_sd_set_jid(data->account, purple_get_host_name());
+
 	return TRUE;
 }
 
@@ -483,7 +489,8 @@
 		switch (type) {
 			case PUBLISH_START:
 				purple_debug_info("bonjour", "Registering presence on port %d\n", data->port_p2pj);
-				errorCode = DNSServiceRegister(&presence_sr, 0, 0, purple_account_get_username(data->account), LINK_LOCAL_RECORD_NAME,
+				errorCode = DNSServiceRegister(&presence_sr, kDNSServiceInterfaceIndexAny,
+					0, bonjour_get_jid(data->account), LINK_LOCAL_RECORD_NAME,
 					NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data),
 					_mdns_service_register_callback, NULL);
 				break;
@@ -522,8 +529,9 @@
 
 	g_return_val_if_fail(idata != NULL, FALSE);
 
-	errorCode = DNSServiceBrowse(&browser_sr, 0, 0, LINK_LOCAL_RECORD_NAME, NULL,
-		_mdns_service_browse_callback, data->account);
+	errorCode = DNSServiceBrowse(&browser_sr, 0, kDNSServiceInterfaceIndexAny,
+			LINK_LOCAL_RECORD_NAME, NULL,_mdns_service_browse_callback,
+			data->account);
 	if (errorCode == kDNSServiceErr_NoError) {
 		idata->browser_query = g_new(DnsSDServiceRefHandlerData, 1);
 		idata->browser_query->sdRef = browser_sr;
--- a/libpurple/protocols/bonjour/parser.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/bonjour/parser.c	Wed Jun 13 19:30:27 2012 -0400
@@ -102,7 +102,7 @@
 			attrib[attrib_len] = '\0';
 
 			txt = attrib;
-			attrib = purple_unescape_html(txt);
+			attrib = purple_unescape_text(txt);
 			g_free(txt);
 			xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
 			g_free(attrib);
--- a/libpurple/protocols/gg/Makefile.am	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/gg/Makefile.am	Wed Jun 13 19:30:27 2012 -0400
@@ -1,17 +1,35 @@
 EXTRA_DIST = \
 	Makefile.mingw \
+	win32-resolver.c \
+	win32-resolver.h \
 	lib/common.c \
 	lib/compat.h \
 	lib/COPYING \
 	lib/dcc.c \
+	lib/dcc7.c \
+	lib/debug.c \
+	lib/deflate.c \
+	lib/deflate.h \
+	lib/encoding.c \
+	lib/encoding.h \
 	lib/events.c \
+	lib/handlers.c \
 	lib/http.c \
+	lib/libgadu.h \
 	lib/libgadu.c \
 	lib/libgadu-config.h \
-	lib/libgadu.h \
+	lib/libgadu-debug.h \
+	lib/libgadu-internal.h \
+	lib/message.c \
+	lib/message.h \
 	lib/obsolete.c \
+	lib/protocol.h \
+	lib/pubdir.c \
 	lib/pubdir50.c \
-	lib/pubdir.c
+	lib/resolver.c \
+	lib/resolver.h \
+	lib/session.h \
+	lib/sha1.c
 
 pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
 
@@ -20,16 +38,36 @@
 	lib/common.c \
 	lib/compat.h \
 	lib/dcc.c \
+	lib/dcc7.c \
+	lib/debug.c \
+	lib/deflate.c \
+	lib/deflate.h \
+	lib/encoding.c \
+	lib/encoding.h \
 	lib/events.c \
+	lib/handlers.c \
 	lib/http.c \
+	lib/libgadu.h \
 	lib/libgadu.c \
 	lib/libgadu-config.h \
-	lib/libgadu.h \
+	lib/libgadu-internal.h \
+	lib/message.c \
+	lib/message.h \
 	lib/obsolete.c \
+	lib/protocol.h \
+	lib/pubdir.c \
 	lib/pubdir50.c \
-	lib/pubdir.c
+	lib/resolver.c \
+	lib/resolver.h \
+	lib/session.h \
+	lib/sha1.c
 
-INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib
+INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib -DGG_IGNORE_DEPRECATED -DUSE_INTERNAL_LIBGADU
+endif
+
+if USE_GNUTLS
+GADU_LIBS += $(GNUTLS_LIBS)
+GADU_CFLAGS += $(GNUTLS_CFLAGS)
 endif
 
 GGSOURCES = \
--- a/libpurple/protocols/gg/Makefile.mingw	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/gg/Makefile.mingw	Wed Jun 13 19:30:27 2012 -0400
@@ -8,7 +8,7 @@
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
 TARGET = libgg
-CFLAGS += -include win32dep.h
+CFLAGS += -include win32dep.h -DGG_IGNORE_DEPRECATED
 TYPE = PLUGIN
 
 # Static or Plugin...
@@ -41,17 +41,27 @@
 ##
 C_SRC =	\
 	lib/common.c \
+	lib/dcc.c \
+	lib/dcc7.c \
+	lib/debug.c \
+	lib/deflate.c \
+	lib/encoding.c \
 	lib/events.c \
+	lib/handlers.c \
 	lib/http.c \
 	lib/libgadu.c \
+	lib/message.c \
 	lib/obsolete.c \
 	lib/pubdir.c \
 	lib/pubdir50.c \
+	lib/resolver.c \
+	lib/sha1.c \
 	buddylist.c \
 	confer.c \
 	gg.c \
 	search.c \
-	gg-utils.c
+	gg-utils.c \
+	win32-resolver.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
 
--- a/libpurple/protocols/gg/buddylist.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/gg/buddylist.c	Wed Jun 13 19:30:27 2012 -0400
@@ -38,7 +38,7 @@
 /* void ggp_buddylist_send(PurpleConnection *gc) {{{ */
 void ggp_buddylist_send(PurpleConnection *gc)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	PurpleAccount *account = purple_connection_get_account(gc);
 	GSList *buddies;
 	uin_t *userlist;
@@ -90,12 +90,12 @@
 		gchar **data_tbl;
 		gchar *name, *show, *g;
 
-		if (strlen(users_tbl[i]) == 0)
+		if (!*users_tbl[i])
 			continue;
 
 		data_tbl = g_strsplit(users_tbl[i], ";", 8);
 		if (ggp_array_size(data_tbl) < 8) {
-			purple_debug_warning("gg", 
+			purple_debug_warning("gg",
 				"Something is wrong on line %d of the buddylist. Skipping.\n",
 				i + 1);
 			continue;
--- a/libpurple/protocols/gg/buddylist.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/gg/buddylist.h	Wed Jun 13 19:30:27 2012 -0400
@@ -44,7 +44,7 @@
  * Get all the buddies in the current account.
  *
  * @param account Current account.
- * 
+ *
  * @return List of buddies.
  */
 char *
--- a/libpurple/protocols/gg/confer.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/gg/confer.c	Wed Jun 13 19:30:27 2012 -0400
@@ -42,7 +42,7 @@
 							 const uin_t uin)
 {
 	PurpleConversation *conv;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPChat *chat;
 	GList *l;
 	gchar *str_uin;
@@ -73,7 +73,7 @@
 void ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name,
 				 const uin_t *recipients, int count)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GList *l;
 	gchar *str_uin;
 
@@ -111,7 +111,7 @@
 const char *ggp_confer_find_by_participants(PurpleConnection *gc,
 					    const uin_t *recipients, int count)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPChat *chat = NULL;
 	GList *l;
 	int matches;
@@ -149,7 +149,7 @@
 /* const char *ggp_confer_add_new(PurpleConnection *gc, const char *name) {{{ */
 const char *ggp_confer_add_new(PurpleConnection *gc, const char *name)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPChat *chat;
 
 	chat = g_new0(GGPChat, 1);
--- a/libpurple/protocols/gg/confer.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/gg/confer.h	Wed Jun 13 19:30:27 2012 -0400
@@ -39,7 +39,7 @@
 
 /**
  * Adds the specified UIN to the specified conversation.
- * 
+ *
  * @param gc        PurpleConnection.
  * @param chat_name Name of the conversation.
  */
@@ -61,7 +61,7 @@
 
 /**
  * Finds a conversation in which all the specified recipients participate.
- * 
+ *
  * TODO: This function should be rewritten to better handle situations when
  * somebody adds more people to the converation.
  *
@@ -81,7 +81,7 @@
  *
  * @param gc   PurpleConnection.
  * @param name Name of the conversation.
- * 
+ *
  * @return Name of the conversation.
  */
 const char*
--- a/libpurple/protocols/gg/gg-utils.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/gg/gg-utils.c	Wed Jun 13 19:30:27 2012 -0400
@@ -143,5 +143,18 @@
 				    msg ? "message" : NULL, msg, NULL);
 }
 
+guint ggp_http_input_add(struct gg_http *http_req, PurpleInputFunction func,
+	gpointer user_data)
+{
+	PurpleInputCondition cond = 0;
+	int check = http_req->check;
+
+	if (check & GG_CHECK_READ)
+		cond |= PURPLE_INPUT_READ;
+	if (check & GG_CHECK_WRITE)
+		cond |= PURPLE_INPUT_WRITE;
+
+	return purple_input_add(http_req->fd, cond, func, user_data);
+}
 
 /* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/gg/gg-utils.h	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/gg/gg-utils.h	Wed Jun 13 19:30:27 2012 -0400
@@ -101,6 +101,21 @@
 ggp_status_fake_to_self(PurpleAccount *account);
 
 
+/**
+ * Adds an input handler in purple event loop for http request.
+ *
+ * @see purple_input_add
+ *
+ * @param http_req  Http connection to watch.
+ * @param func      The callback function for data.
+ * @param user_data User-specified data.
+ *
+ * @return The resulting handle (will be greater than 0).
+ */
+guint
+ggp_http_input_add(struct gg_http *http_req, PurpleInputFunction func,
+	gpointer user_data);
+
 #endif /* _PURPLE_GG_UTILS_H */
 
 /* vim: set ts=8 sts=0 sw=8 noet: */
--- a/libpurple/protocols/gg/gg.c	Wed Jun 13 19:28:57 2012 -0400
+++ b/libpurple/protocols/gg/gg.c	Wed Jun 13 19:30:27 2012 -0400
@@ -39,16 +39,22 @@
 #include "request.h"
 #include "xmlnode.h"
 
-#include <libgadu.h>
-
 #include "gg.h"
 #include "confer.h"
 #include "search.h"
 #include "buddylist.h"
 #include "gg-utils.h"
 
+#ifdef _WIN32
+#  include "win32-resolver.h"
+#endif
+
 static PurplePlugin *my_protocol = NULL;
 
+/* Prototypes */
+static void ggp_set_status(PurpleAccount *account, PurpleStatus *status);
+static int ggp_to_gg_status(PurpleStatus *status, char **msg);
+
 /* ---------------------------------------------------------------------- */
 /* ----- EXTERNAL CALLBACKS --------------------------------------------- */
 /* ---------------------------------------------------------------------- */
@@ -93,7 +99,7 @@
 static void ggp_async_token_handler(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPToken *token = info->token;
 	GGPTokenCallback cb;
 
@@ -166,7 +172,7 @@
 	if (ggp_setup_proxy(account) == -1)
 		return;
 
-	info = gc->proto_data;
+	info = purple_connection_get_protocol_data(gc);
 
 	if ((req = gg_token(1)) == NULL) {
 		purple_notify_error(account,
@@ -195,7 +201,7 @@
 static void ggp_action_buddylist_get(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 
 	purple_debug_info("gg", "Downloading...\n");
 
@@ -210,12 +216,12 @@
 static void ggp_action_buddylist_put(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 
 	char *buddylist = ggp_buddylist_dump(purple_connection_get_account(gc));
 
 	purple_debug_info("gg", "Uploading...\n");
-	
+
 	if (buddylist == NULL)
 		return;
 
@@ -231,7 +237,7 @@
 static void ggp_action_buddylist_delete(PurplePluginAction *action)
 {
 	PurpleConnection *gc = (PurpleConnection *)action->context;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 
 	purple_debug_info("gg", "Deleting...\n");
 
@@ -330,7 +336,7 @@
 					     PurpleRequestFields *fields)
 {
 	PurpleAccount *account;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	struct gg_http *h = NULL;
 	struct gg_pubdir *s;
 	uin_t uin;
@@ -350,14 +356,14 @@
 
 	if (email == NULL || p1 == NULL || p2 == NULL || t == NULL ||
 	    *email == '\0' || *p1 == '\0' || *p2 == '\0' || *t == '\0') {
-		purple_connection_error_reason (gc,
+		purple_connection_error (gc,
 			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 			_("You must fill in all registration fields"));
 		goto exit_err;
 	}
 
 	if (g_utf8_collate(p1, p2) != 0) {
-		purple_connection_error_reason (gc,
+		purple_connection_error (gc,
 			PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
 			_("Passwords do not match"));
 		goto exit_err;
@@ -367,7 +373,7 @@
 			token->id, t);
 	h = gg_register3(email, p1, token->id, t, 0);
 	if (h == NULL || !(s = h->data) || !s->success) {
-		purple_connection_error_reason (gc,
+		purple_connection_error (gc,
 			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 			_("Unable to register new account.  An unknown error occurred."));
 		goto exit_err;
@@ -385,8 +391,8 @@
 	purple_notify_info(NULL, _("New Gadu-Gadu Account Registered"),
 			 _("Registration completed successfully!"), NULL);
 
-	if(account->registration_cb)
-		(account->registration_cb)(account, TRUE, account->registration_cb_user_data);
+	purple_account_register_completed(account, TRUE);
+
 	/* TODO: the currently open Accounts Window will not be updated withthe
 	 * new username and etc, we need to somehow have it refresh at this
 	 * point
@@ -396,8 +402,7 @@
 	purple_account_disconnect(account);
 
 exit_err:
-	if(account->registration_cb)
-		(account->registration_cb)(account, FALSE, account->registration_cb_user_data);
+	purple_account_register_completed(account, FALSE);
 
 	gg_register_free(h);
 	g_free(email);
@@ -411,10 +416,10 @@
 static void ggp_callback_register_account_cancel(PurpleConnection *gc,
 						 PurpleRequestFields *fields)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPToken *token = info->token;
 
-	purple_account_disconnect(gc->account);
+	purple_account_disconnect(purple_connection_get_account(gc));
 
 	g_free(token->id);
 	g_free(token->data);
@@ -429,7 +434,7 @@
 	PurpleRequestFieldGroup *group;
 	PurpleRequestField *field;
 
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPToken *token = info->token;
 
 
@@ -479,19 +484,20 @@
 
 static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPSearchForm *form = user_data;
 	guint32 seq;
 
-	g_free(form->offset);
-	form->offset = g_strdup(form->last_uin);
+	form->page_number++;
 
 	ggp_search_remove(info->searches, form->seq);
-	purple_debug_info("gg", "ggp_callback_show_next(): Removed seq %u", form->seq);
+	purple_debug_info("gg", "ggp_callback_show_next(): Removed seq %u\n",
+		form->seq);
 
 	seq = ggp_search_start(gc, form);
 	ggp_search_add(info->searches, seq, form);
-	purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u", seq);
+	purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u\n",
+		seq);
 }
 
 static void ggp_callback_add_buddy(PurpleConnection *gc, GList *row, gpointer user_data)
@@ -515,28 +521,23 @@
 
 static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields *fields)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPSearchForm *form;
 	guint32 seq;
 
 	form = ggp_search_form_new(GGP_SEARCH_TYPE_FULL);
 
 	form->user_data = info;
-	form->lastname  = charset_convert(
-				purple_request_fields_get_string(fields, "lastname"),
-				"UTF-8", "CP1250");
-	form->firstname = charset_convert(
-				purple_request_fields_get_string(fields, "firstname"),
-				"UTF-8", "CP1250");
-	form->nickname  = charset_convert(
-				purple_request_fields_get_string(fields, "nickname"),
-				"UTF-8", "CP1250");
-	form->city      = charset_convert(
-				purple_request_fields_get_string(fields, "city"),
-				"UTF-8", "CP1250");
-	form->birthyear = charset_convert(
-				purple_request_fields_get_string(fields, "year"),
-				"UTF-8", "CP1250");
+	form->lastname = g_strdup(
+		purple_request_fields_get_string(fields, "lastname"));
+	form->firstname = g_strdup(
+		purple_request_fields_get_string(fields, "firstname"));
+	form->nickname = g_strdup(
+		purple_request_fields_get_string(fields, "nickname"));
+	form->city = g_strdup(
+		purple_request_fields_get_string(fields, "city"));
+	form->birthyear = g_strdup(
+		purple_request_fields_get_string(fields, "year"));
 
 	switch (purple_request_fields_get_choice(fields, "gender")) {
 		case 1:
@@ -553,11 +554,10 @@
 	form->active = purple_request_fields_get_bool(fields, "active")
 				   ? g_strdup(GG_PUBDIR50_ACTIVE_TRUE) : NULL;
 
-	form->offset = g_strdup("0");
-
 	seq = ggp_search_start(gc, form);
 	ggp_search_add(info->searches, seq, form);
-	purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u", seq);
+	purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u\n",
+		seq);
 }
 
 static void ggp_find_buddies(PurplePluginAction *action)
@@ -617,75 +617,137 @@
 		gc);
 }
 
-/* ----- CHANGE PASSWORD ------------------------------------------------ */
-
-static void ggp_callback_change_passwd_ok(PurpleConnection *gc, PurpleRequestFields *fields)
+/* ----- CHANGE PASSWORD ---------------------------------------------------- */
+
+typedef struct
+{
+	guint inpa;
+	struct gg_http *http_req;
+	gchar *new_password;
+	PurpleAccount *account;
+} ggp_change_passwd_request;
+
+static void ggp_callback_change_passwd_handler(gpointer _req, gint fd,
+	PurpleInputCondition cond)
+{
+	ggp_change_passwd_request *req = _req;
+	const char *messagesTitle =
+		_("Change password for the Gadu-Gadu account");
+
+	purple_input_remove(req->inpa);
+
+	if (gg_change_passwd_watch_fd(req->http_req) == -1 ||
+		req->http_req->state == GG_STATE_ERROR)
+		goto exit_error;
+
+	if (req->http_req->state != GG_STATE_DONE)
+	{
+		req->inpa = ggp_http_input_add(req->http_req,
+			ggp_callback_change_passwd_handler, req);
+		return;
+	}
+
+	if (req->http_req->data != NULL &&
+		((struct gg_pubdir*)req->http_req->data)->success == 1)
+	{
+		purple_account_set_password(req->account, req->new_password);
+		purple_notify_info(req->account, messagesTitle,
+			_("Password was changed successfully!"), NULL);
+		goto exit_cleanup;
+	}
+
+exit_error:
+	purple_notify_error(req->account, messagesTitle,
+		_("Unable to change password. Error occurred.\n"), NULL);
+
+exit_cleanup:
+	gg_change_passwd_free(req->http_req);
+	g_free(req->new_password);
+	g_free(req);
+}
+
+static void ggp_callback_change_passwd_ok(PurpleConnection *gc,
+	PurpleRequestFields *fields)
 {
 	PurpleAccount *account;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	struct gg_http *h;
-	gchar *cur, *p1, *p2, *t;
-
-	cur = charset_convert(
-			purple_request_fields_get_string(fields, "password_cur"),
-			"UTF-8", "CP1250");
-	p1  = charset_convert(
-			purple_request_fields_get_string(fields, "password1"),
-			"UTF-8", "CP1250");
-	p2  = charset_convert(
-			purple_request_fields_get_string(fields, "password2"),
-			"UTF-8", "CP1250");
-	t   = charset_convert(
-			purple_request_fields_get_string(fields, "token"),
-			"UTF-8", "CP1250");
+	gchar *cur, *p1, *p2, *t, *mail;
+	const char *messagesTitle =
+		_("Change password for the Gadu-Gadu account");
+
+	cur = g_strdup(purple_request_fields_get_string(fields,
+		"password_cur"));
+	p1 = g_strdup(purple_request_fields_get_string(fields, "password1"));
+	p2 = g_strdup(purple_request_fields_get_string(fields, "password2"));
+	t = g_strdup(purple_request_fields_get_string(fields, "token"));
+	mail = g_strdup(purple_request_fields_get_string(fields, "email"));
 
 	account = purple_connection_get_account(gc);
 
 	if (cur == NULL || p1 == NULL || p2 == NULL || t == NULL ||
-	    *cur == '\0' || *p1 == '\0' || *p2 == '\0' || *t == '\0') {
-		purple_notify_error(account, NULL, _("Fill in the fields."), NULL);
+		mail == NULL || *cur == '\0' || *p1 == '\0' || *p2 == '\0' ||
+		*t == '\0' || *mail == '\0') {
+		purple_notify_error(account, messagesTitle,
+			_("Fill in the fields."), NULL);
 		goto exit_err;
 	}
 
 	if (g_utf8_collate(p1, p2) != 0) {
-		purple_notify_error(account, NULL,
-				  _("New passwords do not match."), NULL);
+		purple_notify_error(account, messagesTitle,
+			_("New passwords do not match."), NULL);
+		goto exit_err;
+	}
+
+	if (strlen(p1) > 15) {
+		purple_notify_error(account, messagesTitle,
+			_("New password should be at most 15 characters long."),
+			NULL);
 		goto exit_err;
 	}
 
 	if (g_utf8_collate(cur, purple_account_get_password(account)) != 0) {
-		purple_notify_error(account, NULL,
-			_("Your current password is different from the one that you specified."),
-			NULL);
+		purple_notify_error(account, messagesTitle,
+			_("Your current password is different from the one that"
+			" you specified."), NULL);
+		goto exit_err;
+	}
+
+	if (!purple_email_is_valid(mail)) {
+		purple_notify_error(account, messagesTitle,
+			_("Invalid email address"), NULL);
 		goto exit_err;
 	}
 
-	purple_debug_info("gg", "Changing password\n");
-
-	/* XXX: this email should be a pref... */
-	h = gg_change_passwd4(ggp_get_uin(account),
-			      "user@example.net", purple_account_get_password(account),
-			      p1, info->token->id, t, 0);
-
-	if (h == NULL) {
-		purple_notify_error(account, NULL,
+	purple_debug_info("gg", "Changing password with email \"%s\"...\n",
+		mail);
+
+	h = gg_change_passwd4(ggp_get_uin(account), mail,
+		purple_account_get_password(account), p1, info->token->id, t,
+		1);
+
+	if (h == NULL)
+		purple_notify_error(account, messagesTitle,
 			_("Unable to change password. Error occurred.\n"),
 			NULL);
-		goto exit_err;
+	else
+	{
+		ggp_change_passwd_request *req =
+			g_new(ggp_change_passwd_request, 1);
+		req->http_req = h;
+		req->new_password = g_strdup(p1);
+		req->account = account;
+		
+		req->inpa = ggp_http_input_add(h,
+			ggp_callback_change_passwd_handler, req);
 	}
-
-	purple_account_set_password(account, p1);
-
-	gg_change_passwd_free(h);
-
-	purple_notify_info(account, _("Change password for the Gadu-Gadu account"),
-			 _("Password was changed successfully!"), NULL);
-
+	
 exit_err:
 	g_free(cur);
 	g_free(p1);
 	g_free(p2);
 	g_free(t);
+	g_free(mail);
 	g_free(info->token->id);
 	g_free(info->token->data);
 	g_free(info->token);
@@ -697,12 +759,11 @@
 	PurpleRequestFieldGroup *group;
 	PurpleRequestField *field;
 
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPToken *token = info->token;
 
 	char *msg;
 
-
 	fields = purple_request_fields_new();
 	group = purple_request_field_group_new(NULL);
 	purple_request_fields_add_group(fields, group);
@@ -722,6 +783,11 @@
 	purple_request_field_string_set_masked(field, TRUE);
 	purple_request_field_group_add_field(group, field);
 
+	field = purple_request_field_string_new("email",
+			_("Email Address"), "", FALSE);
+	purple_request_field_string_set_masked(field, FALSE);
+	purple_request_field_group_add_field(group, field);
+
 	field = purple_request_field_string_new("token",
 			_("Enter current token"), "", FALSE);
 	purple_request_field_string_set_masked(field, FALSE);
@@ -733,8 +799,8 @@
 	purple_request_field_group_add_field(group, field);
 
 	msg = g_strdup_printf("%s %d",
-		_("Please, enter your current password and your new password for UIN: "),
-		ggp_get_uin(purple_connection_get_account(gc)));
+		_("Please, enter your current password and your new password "
+		"for UIN: "), ggp_get_uin(purple_connection_get_account(gc)));
 
 	purple_request_fields(gc,
 		_("Change Gadu-Gadu Password"),
@@ -755,11 +821,65 @@
 	ggp_token_request(gc, ggp_change_passwd_dialog);
 }
 
+/* ----- CHANGE STATUS BROADCASTING ------------------------------------------------ */
+
+static void ggp_action_change_status_broadcasting_ok(PurpleConnection *gc, PurpleRequestFields *fields)
+{
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
+	int selected_field;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	PurpleStatus *status;
+
+	selected_field = purple_request_fields_get_choice(fields, "status_broadcasting");
+	
+	if (selected_field == 0)
+		info->status_broadcasting = TRUE;
+	else
+		info->status_broadcasting = FALSE;
+	
+	status = purple_account_get_active_status(account);
+	
+	ggp_set_status(account, status);
+}
+
+static void ggp_action_change_status_broadcasting(PurplePluginAction *action)
+{
+	PurpleConnection *gc = (PurpleConnection *)action->context;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
+	
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *group;
+	PurpleRequestField *field;
+
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
+	
+	field = purple_request_field_choice_new("status_broadcasting", _("Show status to:"), 0);
+	purple_request_field_choice_add(field, _("All people"));
+	purple_request_field_choice_add(field, _("Only buddies"));
+	purple_request_field_group_add_field(group, field);
+
+	if (info->status_broadcasting)
+		purple_request_field_choice_set_default_value(field, 0);
+	else
+		purple_request_field_choice_set_default_value(field, 1);
+
+	purple_request_fields(gc,
+		_("Change status broadcasting"),
+		_("Change status broadcasting"),
+		_("Please, select who can see your status"),
+		fields,
+		_("OK"), G_CALLBACK(ggp_action_change_status_broadcasting_ok),
+		_("Cancel"), NULL,
+		purple_connection_get_account(gc), NULL, NULL,
+		gc);
+}
+
 /* ----- CONFERENCES ---------------------------------------------------- */
 
 static void ggp_callback_add_to_chat_ok(PurpleBuddy *buddy, PurpleRequestFields *fields)
 {
-	GGPInfo *info;
 	PurpleConnection *conn;
 	PurpleRequestField *field;
 	GList *sel;
@@ -768,8 +888,6 @@
 
 	g_return_if_fail(conn != NULL);
 
-	info = conn->proto_data;
-
 	field = purple_request_fields_get_field(fields, "name");
 	sel = purple_request_field_list_get_selected(field);
 
@@ -797,7 +915,7 @@
 
 	buddy = (PurpleBuddy *)node;
 	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-	info = gc->proto_data;
+	info = purple_connection_get_protocol_data(gc);
 
 	fields = purple_request_fields_new();
 	group = purple_request_field_group_new(NULL);
@@ -806,7 +924,7 @@
 	field = purple_request_field_list_new("name", "Chat name");
 	for (l = info->chats; l != NULL; l = l->next) {
 		GGPChat *chat = l->data;
-		purple_request_field_list_add(field, chat->name, chat->name);
+		purple_request_field_list_add_icon(field, chat->name, NULL, chat->name);
 	}
 	purple_request_field_group_add_field(group, field);
 
@@ -826,40 +944,32 @@
 
 /* ----- BLOCK BUDDIES -------------------------------------------------- */
 
-static void ggp_bmenu_block(PurpleBlistNode *node, gpointer ignored)
+static void ggp_add_deny(PurpleConnection *gc, const char *who)
 {
-	PurpleConnection *gc;
-	PurpleBuddy *buddy;
-	GGPInfo *info;
-	uin_t uin;
-
-	buddy = (PurpleBuddy *)node;
-	gc = purple_account_get_connection(purple_buddy_get_account(buddy));
-	info = gc->proto_data;
-
-	uin = ggp_str_to_uin(purple_buddy_get_name(buddy));
-
-	if (purple_blist_node_get_bool(node, "blocked")) {
-		purple_blist_node_set_bool(node, "blocked", FALSE);
-		gg_remove_notify_ex(info->session, uin, GG_USER_BLOCKED);
-		gg_add_notify_ex(info->session, uin, GG_USER_NORMAL);
-		purple_debug_info("gg", "send: uin=%d; mode=NORMAL\n", uin);
-	} else {
-		purple_blist_node_set_bool(node, "blocked", TRUE);
-		gg_remove_notify_ex(info->session, uin, GG_USER_NORMAL);
-		gg_add_notify_ex(info->session, uin, GG_USER_BLOCKED);
-		purple_debug_info("gg", "send: uin=%d; mode=BLOCKED\n", uin);
-	}
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
+	uin_t uin = ggp_str_to_uin(who);
+	
+	purple_debug_info("gg", "ggp_add_deny: %u\n", uin);
+	
+	gg_remove_notify_ex(info->session, uin, GG_USER_NORMAL);
+	gg_add_notify_ex(info->session, uin, GG_USER_BLOCKED);
+}
+
+static void ggp_rem_deny(PurpleConnection *gc, const char *who)
+{
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
+	uin_t uin = ggp_str_to_uin(who);
+	
+	purple_debug_info("gg", "ggp_rem_deny: %u\n", uin);
+	
+	gg_remove_notify_ex(info->session, uin, GG_USER_BLOCKED);
+	gg_add_notify_ex(info->session, uin, GG_USER_NORMAL);
 }
 
 /* ---------------------------------------------------------------------- */
 /* ----- INTERNAL CALLBACKS --------------------------------------------- */
 /* ---------------------------------------------------------------------- */
 
-/* Prototypes */
-static void ggp_set_status(PurpleAccount *account, PurpleStatus *status);
-static int ggp_to_gg_status(PurpleStatus *status, char **msg);
-
 struct gg_fetch_avatar_data
 {
 	PurpleConnection *gc;
@@ -875,6 +985,9 @@
 	PurpleBuddy *buddy;
 	gpointer buddy_icon_data;
 
+	purple_debug_info("gg", "gg_fetch_avatar_cb: got avatar image for %s\n",
+		d->uin);
+
 	/* FIXME: This shouldn't be necessary */
 	if (!PURPLE_CONNECTION_IS_VALID(d->gc)) {
 		g_free(d->uin);
@@ -893,7 +1006,8 @@
 
 	purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy),
 			buddy_icon_data, len, d->avatar_url);
-	purple_debug_info("gg", "UIN: %s should have avatar now\n", d->uin);
+	purple_debug_info("gg", "gg_fetch_avatar_cb: UIN %s should have avatar "
+		"now\n", d->uin);
 
 out:
 	g_free(d->uin);
@@ -947,7 +1061,7 @@
 		if (xmlnode_avatar == NULL)
 			goto out;
 
-		xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "bigAvatar");
+		xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "originBigAvatar");
 		if (xmlnode_bigavatar == NULL)
 			goto out;
 
@@ -975,7 +1089,10 @@
 				data->uin = g_strdup(uin);
 				data->avatar_url = g_strdup(bigavatar);
 
-				url_data = purple_util_fetch_url_request_len_with_account(account,
+				purple_debug_info("gg", "gg_get_avatar_url_cb: "
+					"requesting avatar for %s\n", uin);
+				/* FIXME: This should be cancelled somewhere if not needed. */
+				url_data = purple_util_fetch_url_request(account,
 						bigavatar, TRUE, "Mozilla/4.0 (compatible; MSIE 5.0)",
 						FALSE, NULL, FALSE, -1, gg_fetch_avatar_cb, data);
 			}
@@ -989,6 +1106,30 @@
 }
 
 /**
+ * Try to update avatar of the buddy.
+ *
+ * @param gc     PurpleConnection
+ * @param uin    UIN of the buddy.
+ */
+static void ggp_update_buddy_avatar(PurpleConnection *gc, uin_t uin)
+{
+	gchar *avatarurl;
+	PurpleUtilFetchUrlData *url_data;
+
+	purple_debug_info("gg", "ggp_update_buddy_avatar(gc, %u)\n", uin);
+
+	avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%u/0.xml", uin);
+
+	/* FIXME: This should be cancelled somewhere if not needed. */
+	url_data = purple_util_fetch_url_request(
+			purple_connection_get_account(gc), avatarurl, TRUE,
+			"Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1,
+			gg_get_avatar_url_cb, gc);
+
+	g_free(avatarurl);
+}
+
+/**
  * Handle change of the status of the buddy.
  *
  * @param gc     PurpleConnection
@@ -1001,50 +1142,68 @@
 {
 	gchar *from;
 	const char *st;
-	gchar *msg;
-	gchar *avatarurl;
-	PurpleUtilFetchUrlData *url_data;
+	char *status_msg = NULL;
+
+	ggp_update_buddy_avatar(gc, uin);
 
 	from = g_strdup_printf("%u", uin);
-	avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%s/0.xml", from);
-
-	url_data = purple_util_fetch_url_request_len_with_account(
-			purple_connection_get_account(gc), avatarurl, TRUE,
-			"Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1,
-			gg_get_avatar_url_cb, gc);
-
-	g_free(avatarurl);
 
 	switch (status) {
 		case GG_STATUS_NOT_AVAIL:
 		case GG_STATUS_NOT_AVAIL_DESCR:
-			st = "offline";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE);
+			break;
+		case GG_STATUS_FFC:
+		case GG_STATUS_FFC_DESCR:
+			st = "freeforchat";
 			break;
 		case GG_STATUS_AVAIL:
 		case GG_STATUS_AVAIL_DESCR:
-			st = "available";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
 			break;
 		case GG_STATUS_BUSY:
 		case GG_STATUS_BUSY_DESCR:
-			st = "away";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY);
+			break;
+		case GG_STATUS_INVISIBLE:
+		case GG_STATUS_INVISIBLE_DESCR:
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE);
+			break;
+		case GG_STATUS_DND:
+		case GG_STATUS_DND_DESCR:
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_UNAVAILABLE);
 			break;
 		case GG_STATUS_BLOCKED:
 			/* user is blocking us.... */
 			st = "blocked";
 			break;
 		default:
-			st = "available";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
 			purple_debug_info("gg",
 				"GG_EVENT_NOTIFY: Unknown status: %d\n", status);
 			break;
 	}
 
-	purple_debug_info("gg", "st = %s\n", st);
-	msg = charset_convert(descr, "CP1250", "UTF-8");
-	purple_prpl_got_user_status(purple_connection_get_account(gc),
-				  from, st, "message", msg, NULL);
+	if (descr != NULL) {
+		status_msg = g_strdup(descr);
+		g_strstrip(status_msg);
+		if (status_msg[0] == '\0') {
+			g_free(status_msg);
+			status_msg = NULL;
+		}
+	}
+
+	purple_debug_info("gg", "status of %u is %s [%s]\n", uin, st,
+		status_msg ? status_msg : "");
+	if (status_msg == NULL) {
+		purple_prpl_got_user_status(purple_connection_get_account(gc),
+			from, st, NULL);
+	} else {
+		purple_prpl_got_user_status(purple_connection_get_account(gc),
+			from, st, "message", status_msg, NULL);
+		g_free(status_msg);
+	}
 	g_free(from);
-	g_free(msg);
 }
 
 static void ggp_sr_close_cb(gpointer user_data)
@@ -1053,7 +1212,8 @@
 	GGPInfo *info = form->user_data;
 
 	ggp_search_remove(info->searches, form->seq);
-	purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u", form->seq);
+	purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u\n",
+		form->seq);
 	ggp_search_form_destroy(form);
 }
 
@@ -1078,6 +1238,12 @@
 		case GG_STATUS_AVAIL_DESCR:
 			st = _("Available");
 			break;
+		case GG_STATUS_FFC:
+		case GG_STATUS_FFC_DESCR:
+			return _("Chatty");
+		case GG_STATUS_DND:
+		case GG_STATUS_DND_DESCR:
+			return _("Do Not Disturb");
 		case GG_STATUS_BUSY:
 		case GG_STATUS_BUSY_DESCR:
 			st = _("Away");
@@ -1101,27 +1267,37 @@
 
 	val = ggp_search_get_result(req, 0, GG_PUBDIR50_STATUS);
 	/* XXX: Use of ggp_str_to_uin() is an ugly hack! */
-	purple_notify_user_info_add_pair(user_info, _("Status"), ggp_status_by_id(ggp_str_to_uin(val)));
+	purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), ggp_status_by_id(ggp_str_to_uin(val)));
 	g_free(val);
 
 	who = ggp_search_get_result(req, 0, GG_PUBDIR50_UIN);
-	purple_notify_user_info_add_pair(user_info, _("UIN"), who);
+	/* TODO: Check whether it's correct to call add_pair_html,
+	         or if we should be using add_pair_plaintext */
+	purple_notify_user_info_add_pair_html(user_info, _("UIN"), who);
 
 	val = ggp_search_get_result(req, 0, GG_PUBDIR50_FIRSTNAME);
-	purple_notify_user_info_add_pair(user_info, _("First Name"), val);
+	/* TODO: Check whether it's correct to call add_pair_html,
+	         or if we should be using add_pair_plaintext */
+	purple_notify_user_info_add_pair_html(user_info, _("First Name"), val);
 	g_free(val);
 
 	val = ggp_search_get_result(req, 0, GG_PUBDIR50_NICKNAME);
-	purple_notify_user_info_add_pair(user_info, _("Nickname"), val);
+	/* TODO: Check whether it's correct to call add_pair_html,
+	         or if we should be using add_pair_plaintext */
+	purple_notify_user_info_add_pair_html(user_info, _("Nickname"), val);
 	g_free(val);
 
 	val = ggp_search_get_result(req, 0, GG_PUBDIR50_CITY);
-	purple_notify_user_info_add_pair(user_info, _("City"), val);
+	/* TODO: Check whether it's correct to call add_pair_html,
+	         or if we should be using add_pair_plaintext */
+	purple_notify_user_info_add_pair_html(user_info, _("City"), val);
 	g_free(val);
 
 	val = ggp_search_get_result(req, 0, GG_PUBDIR50_BIRTHYEAR);
 	if (strncmp(val, "0", 1)) {
-		purple_notify_user_info_add_pair(user_info, _("Birth Year"), val);
+		/* TODO: Check whether it's correct to call add_pair_html,
+		         or if we should be using add_pair_plaintext */
+		purple_notify_user_info_add_pair_html(user_info, _("Birth Year"), val);
 	}
 	g_free(val);
 
@@ -1132,15 +1308,12 @@
 	if (NULL != buddy) {
 		PurpleStatus *status;
 		const char *msg;
-		char *text;
 
 		status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
 		msg = purple_status_get_attr_string(status, "message");
 
 		if (msg != NULL) {
-			text = g_markup_escape_text(msg, -1);
-			purple_notify_user_info_add_pair(user_info, _("Message"), text);
-			g_free(text);
+			purple_notify_user_info_add_pair_plaintext(user_info, _("Message"), msg);
 		}
 	}
 
@@ -1162,6 +1335,8 @@
 
 	res_count = gg_pubdir50_count(req);
 	res_count = (res_count > PUBDIR_RESULTS_MAX) ? PUBDIR_RESULTS_MAX : res_count;
+	if (form->page_size == 0)
+		form->page_size = res_count;
 
 	results = purple_notify_searchresults_new();
 
@@ -1171,7 +1346,8 @@
 		purple_notify_error(gc, NULL,
 				  _("Unable to display the search results."),
 				  NULL);
-		ggp_sr_close_cb(form);
+		if (form->window == NULL)
+			ggp_sr_close_cb(form);
 		return;
 	}
 
@@ -1213,11 +1389,6 @@
 			(birth && strncmp(birth, "0", 1)) ? birth : g_strdup("-"));
 
 		purple_notify_searchresults_row_add(results, row);
-
-		if (i == res_count - 1) {
-			g_free(form->last_uin);
-			form->last_uin = ggp_search_get_result(req, i, GG_PUBDIR50_UIN);
-		}
 	}
 
 	purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_CONTINUE,
@@ -1251,14 +1422,15 @@
 
 static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GGPSearchForm *form;
 	int res_count;
 	guint32 seq;
 
 	seq = gg_pubdir50_seq(req);
 	form = ggp_search_get(info->searches, seq);
-	purple_debug_info("gg", "ggp_pubdir_reply_handler(): seq %u --> form %p", seq, form);
+	purple_debug_info("gg",
+		"ggp_pubdir_reply_handler(): seq %u --> form %p\n", seq, form);
 	/*
 	 * this can happen when user will request more results
 	 * and close the results window before they arrive.
@@ -1271,7 +1443,8 @@
 		purple_notify_error(gc, NULL,
 			_("No matching users found"),
 			_("There are no users matching your search criteria."));
-		ggp_sr_close_cb(form);
+		if (form->window == NULL)
+			ggp_sr_close_cb(form);
 		return;
 	}
 
@@ -1291,7 +1464,7 @@
 static void ggp_recv_image_handler(PurpleConnection *gc, const struct gg_event *ev)
 {
 	gint imgid = 0;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	GList *entry = g_list_first(info->pending_richtext_messages);
 	gchar *handlerid = g_strdup_printf("IMGID_HANDLER-%i", ev->event.image_reply.crc32);
 
@@ -1311,8 +1484,8 @@
 			info->pending_richtext_messages = g_list_remove(info->pending_richtext_messages, entry->data);
 			/* We don't have any more images to download */
 			if (strstr(text, "<IMG ID=\"IMGID_HANDLER") == NULL) {
-				gchar *buf = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender);
-				serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, ev->event.msg.time);
+				gchar *buf = g_strdup_printf("%lu", (unsigned long int)ev->event.image_reply.sender);
+				serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, time(NULL));
 				g_free(buf);
 				purple_debug_info("gg", "ggp_recv_image_handler: richtext message: %s\n", text);
 				g_free(text);
@@ -1339,16 +1512,26 @@
  */
 static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	PurpleConversation *conv;
 	gchar *from;
 	gchar *msg;
 	gchar *tmp;
+	time_t mtime;
+
+	if (ev->event.msg.message == NULL)
+	{
+		purple_debug_warning("gg", "ggp_recv_message_handler: NULL as message pointer\n");
+		return;
+	}
 
 	from = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender);
 
+	/*
 	tmp = charset_convert((const char *)ev->event.msg.message,
 			      "CP1250", "UTF-8");
+	*/
+	tmp = g_strdup_printf("%s", ev->event.msg.message);
 	purple_str_strip_char(tmp, '\r');
 	msg = g_markup_escape_text(tmp, -1);
 	g_free(tmp);
@@ -1444,6 +1627,10 @@
 				increased_len += 4;
 				under = FALSE;
 			}
+
+			if (actformat->font & GG_FONT_COLOR) {
+				cformats += sizeof(struct gg_msg_richtext_color);
+			}
 		}
 
 		msg = message->str;
@@ -1459,8 +1646,13 @@
 			from, msg, ev->event.msg.msgclass,
 			ev->event.msg.recipients_count);
 
+	if (ev->event.msg.msgclass & GG_CLASS_QUEUED)
+		mtime = ev->event.msg.time;
+	else
+		mtime = time(NULL);
+
 	if (ev->event.msg.recipients_count == 0) {
-		serv_got_im(gc, from, msg, 0, ev->event.msg.time);
+		serv_got_im(gc, from, msg, 0, mtime);
 	} else {
 		const char *chat_name;
 		int chat_id;
@@ -1486,7 +1678,7 @@
 
 		buddy_name = ggp_buddy_get_name(gc, ev->event.msg.sender);
 		serv_got_chat_in(gc, chat_id, buddy_name,
-				 PURPLE_MESSAGE_RECV, msg, ev->event.msg.time);
+				 PURPLE_MESSAGE_RECV, msg, mtime);
 		g_free(buddy_name);
 	}
 	g_free(msg);
@@ -1495,11 +1687,11 @@
 
 static void ggp_send_image_handler(PurpleConnection *gc, const struct gg_event *ev)
 {
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	PurpleStoredImage *image;
-	gint imgid = GPOINTER_TO_INT(g_hash_table_lookup(info->pending_images, &ev->event.image_request.crc32));
-
-	purple_debug_info("gg", "ggp_send_image_handler: image request received, crc32: %u\n", ev->event.image_request.crc32);
+	gint imgid = GPOINTER_TO_INT(g_hash_table_lookup(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32)));
+
+	purple_debug_info("gg", "ggp_send_image_handler: image request received, crc32: %u, imgid: %d\n", ev->event.image_request.crc32, imgid);
 
 	if(imgid)
 	{
@@ -1514,26 +1706,106 @@
 		} else {
 			purple_debug_error("gg", "ggp_send_image_handler: image imgid: %i, crc: %u in hash but not found in imgstore!\n", imgid, ev->event.image_request.crc32);
 		}
-		g_hash_table_remove(info->pending_images, &ev->event.image_request.crc32);
+		g_hash_table_remove(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32));
 	}
 }
 
+static void ggp_typing_notification_handler(PurpleConnection *gc, uin_t uin, int length) {
+	gchar *from;
+
+	from = g_strdup_printf("%u", uin);
+	if (length)
+		serv_got_typing(gc, from, 0, PURPLE_TYPING);
+	else
+		serv_got_typing_stopped(gc, from);
+	g_free(from);
+}
+
+/**
+ * Handling of XML events.
+ *
+ * @param gc PurpleConnection.
+ * @param data Raw XML contents.
+ *
+ * @see http://toxygen.net/libgadu/protocol/#ch1.13
+ */
+static void ggp_xml_event_handler(PurpleConnection *gc, char *data)
+{
+	xmlnode *xml = NULL;
+	xmlnode *xmlnode_next_event;
+
+	xml = xmlnode_from_str(data, -1);
+	if (xml == NULL)
+		goto out;
+
+	xmlnode_next_event = xmlnode_get_child(xml, "event");
+	while (xmlnode_next_event != NULL)
+	{
+		xmlnode *xmlnode_current_event = xmlnode_next_event;
+		
+		xmlnode *xmlnode_type;
+		char *event_type_raw;
+		int event_type = 0;
+		
+		xmlnode *xmlnode_sender;
+		char *event_sender_raw;
+		uin_t event_sender = 0;
+
+		xmlnode_next_event = xmlnode_get_next_twin(xmlnode_next_event);
+		
+		xmlnode_type = xmlnode_get_child(xmlnode_current_event, "type");
+		if (xmlnode_type == NULL)
+			continue;
+		event_type_raw = xmlnode_get_data(xmlnode_type);
+		if (event_type_raw != NULL)
+			event_type = atoi(event_type_raw);
+		g_free(event_type_raw);
+		
+		xmlnode_sender = xmlnode_get_child(xmlnode_current_event, "sender");
+		if (xmlnode_sender != NULL)
+		{
+			event_sender_raw = xmlnode_get_data(xmlnode_sender);
+			if (event_sender_raw != NULL)
+				event_sender = ggp_str_to_uin(event_sender_raw);
+			g_free(event_sender_raw);
+		}
+		
+		switch (event_type)
+		{
+			case 28: /* avatar update */
+				purple_debug_info("gg",
+					"ggp_xml_event_handler: avatar updated (uid: %u)\n",
+					event_sender);
+				ggp_update_buddy_avatar(gc, event_sender);
+				break;
+			default:
+				purple_debug_error("gg",
+					"ggp_xml_event_handler: unsupported event type=%d from=%u\n",
+					event_type, event_sender);
+		}
+	}
+	
+	out:
+		if (xml)
+			xmlnode_free(xml);
+}
+
 static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond)
 {
 	PurpleConnection *gc = _gc;
-	GGPInfo *info = gc->proto_data;
+	GGPInfo *info = purple_connection_get_protocol_data(gc);
 	struct gg_event *ev;
 	int i;
 
 	if (!(ev = gg_watch_fd(info->session))) {
 		purple_debug_error("gg",
 			"ggp_callback_recv: gg_watch_fd failed -- CRITICAL!\n");
-		purple_connection_error_reason (gc,
+		purple_connection_error (gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Unable to read from socket"));
 		return;
 	}
-	gc->last_received = time(NULL);
+
 	switch (ev->type) {
 		case GG_EVENT_NONE:
 			/* Nothing happened. */
@@ -1562,7 +1834,7 @@
 
 				purple_debug_info("gg", "notify_pre: (%d) status: %d\n",
 						ev->event.notify->uin,
-						ev->event.notify->status);
+						GG_S(ev->event.notify->status));
 
 				n = (ev->type == GG_EVENT_NOTIFY) ? ev->event.notify
 								  : ev->event.notify_descr.notify;
@@ -1573,53 +1845,47 @@
 
 					purple_debug_info("gg",
 						"notify: (%d) status: %d; descr: %s\n",
-						n->uin, n->status, descr ? descr : "(null)");
+						n->uin, GG_S(n->status), descr ? descr : "(null)");
 
 					ggp_generic_status_handler(gc,
-						n->uin, n->status, descr);
+						n->uin, GG_S(n->status), descr);
 				}
 			}
 			break;
 		case GG_EVENT_NOTIFY60:
-			purple_debug_info("gg",
-				"notify60_pre: (%d) status=%d; version=%d; descr=%s\n",
-				ev->event.notify60->uin, ev->event.notify60->status,
-				ev->event.notify60->version,
-				ev->event.notify60->descr ? ev->event.notify60->descr : "(null)");
-
 			for (i = 0; ev->event.notify60[i].uin; i++) {
 				purple_debug_info("gg",
 					"notify60: (%d) status=%d; version=%d; descr=%s\n",
 					ev->event.notify60[i].uin,
-					ev->event.notify60[i].status,
+					GG_S(ev->event.notify60[i].status),
 					ev->event.notify60[i].version,
 					ev->event.notify60[i].descr ? ev->event.notify60[i].descr : "(null)");
 
 				ggp_generic_status_handler(gc, ev->event.notify60[i].uin,
-					ev->event.notify60[i].status,
+					GG_S(ev->event.notify60[i].status),
 					ev->event.notify60[i].descr);
 			}
 			break;
 		case GG_EVENT_STATUS:
 			purple_debug_info("gg", "status: (%d) status=%d; descr=%s\n",
-					ev->event.status.uin, ev->event.status.status,
+					ev->event.status.uin, GG_S(ev->event.status.status),
 					ev->event.status.descr ? ev->event.status.descr : "(null)");
 
 			ggp_generic_status_handler(gc, ev->event.status.uin,
-				ev->event.status.status, ev->event.status.descr);
+				GG_S(ev->event.status.status), ev->event.status.descr);
 			break;
 		case GG_EVENT_STATUS60:
 			purple_debug_info("gg",
 				"status60: (%d) status=%d; version=%d; descr=%s\n",
-				ev->event.status60.uin, ev->event.status60.status,
+				ev->event.status60.uin, GG_S(ev->event.status60.status),
 				ev->event.status60.version,
 				ev->event.status60.descr ? ev->event.status60.descr : "(null)");
 
 			ggp_generic_status_handler(gc, ev->event.status60.uin,
-				ev->event.status60.status, ev->event.status60.descr);
+				GG_S(ev->event.status60.status), ev->event.status60.descr);
 			break;
 		case GG_EVENT_USERLIST:
-	    		if (ev->event.userlist.type == GG_USERLIST_GET_REPLY) {
+			if (ev->event.userlist.type == GG_USERLIST_GET_REPLY) {
 				purple_debug_info("gg", "GG_USERLIST_GET_REPLY\n");
 				purple_notify_info(gc, NULL,
 					_("Buddy list downloaded"),
@@ -1637,6 +1903,14 @@
 		case GG_EVENT_PUBDIR50_SEARCH_REPLY:
 			ggp_pubdir_reply_handler(gc, ev->event.pubdir50);
 			break;
+		case GG_EVENT_TYPING_NOTIFICATION:
+			ggp_typing_notification_handler(gc, ev->event.typing_notification.uin,
+				ev->event.typing_notification.length);
+			break;
+		case GG_EVENT_XML_EVENT:
+			purple_debug_info("gg", "GG_EVENT_XML_EVENT\n");
+			ggp_xml_event_handler(gc, ev->event.xml_event.data);
+			break;
 		default:
 			purple_debug_error("gg",
 				"unsupported event type=%d\n", ev->type);
@@ -1654,7 +1928,7 @@
 
 	g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc));
 
-	info = gc->proto_data;
+	info = purple_connection_get_protocol_data(gc);
 
 	purple_debug_info("gg", "login_handler: session: check = %d; state = %d;\n",
 			info->session->check, info->session->state);
@@ -1663,6 +1937,9 @@
 		case GG_STATE_RESOLVING:
 			purple_debug_info("gg", "GG_STATE_RESOLVING\n");
 			break;
+		case GG_STATE_RESOLVING_GG:
+			purple_debug_info("gg", "GG_STATE_RESOLVING_GG\n");
+			break;
 		case GG_STATE_CONNECTING_HUB:
 			purple_debug_info("gg", "GG_STATE_CONNECTING_HUB\n");
 			break;
@@ -1678,6 +1955,9 @@
 		case GG_STATE_READING_REPLY:
 			purple_debug_info("gg", "GG_STATE_READING_REPLY\n");
 			break;
+		case GG_STATE_TLS_NEGOTIATION:
+			purple_debug_info("gg", "GG_STATE_TLS_NEGOTIATION\n");
+			break;
 		default:
 			purple_debug_error("gg", "unknown state = %d\n",
 					 info->session->state);
@@ -1686,7 +1966,7 @@
 
 	if (!(ev = gg_watch_fd(info->session))) {
 		purple_debug_error("gg", "login_handler: gg_watch_fd failed!\n");
-		purple_connection_error_reason (gc,
+		purple_connection_error (gc,
 			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 			_("Unable to read from socket"));
 		return;
@@ -1695,13 +1975,15 @@
 	purple_debug_info("gg", "login_handler: session: check = %d; state = %d;\n",
 			info->session->check, info->session->state);
 
-	purple_input_remove(gc->inpa);
+	purple_input_remove(info->inpa);
+	info->inpa = 0;
 
 	/** XXX I think that this shouldn't be done if ev->type is GG_EVENT_CONN_FAILED or GG_EVENT_CONN_SUCCESS -datallah */
-	gc->inpa = purple_input_add(info->session->fd,
-				  (info->session->check == 1) ? PURPLE_INPUT_WRITE
-							      : PURPLE_INPUT_READ,
-				  ggp_async_login_handler, gc);
+	if (info->session->fd >= 0)
+		info->inpa = purple_input_add(info->session->fd,
+			(info->session->check == 1) ? PURPLE_INPUT_WRITE :
+				PURPLE_INPUT_READ,
+			ggp_async_login_handler, gc);
 
 	switch (ev->type) {
 		case GG_EVENT_NONE:
@@ -1711,22 +1993,85 @@
 		case GG_EVENT_CONN_SUCCESS:
 			{
 				purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS\n");
-				purple_input_remove(gc->inpa);
-				gc->inpa = purple_input_add(info->session->fd,
+				purple_input_remove(info->inpa);
+				info->inpa = purple_input_add(info->session->fd,
 							  PURPLE_INPUT_READ,
 							  ggp_callback_recv, gc);
-				
+
 				ggp_buddylist_send(gc);
-				purple_connection_update_progress(gc, _("Connected"), 2, 2);
+				purple_connection_update_progress(gc, _("Connected"), 1, 2);
 				purple_connection_set_state(gc, PURPLE_CONNECTED);
 			}
 			break;
 		case GG_EVENT_CONN_FAILED:
-			purple_input_remove(gc->inpa);
-			gc->inpa = 0;
-			purple_connection_error_reason (gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Connection failed"));
+			purple_input_remove(info->inpa);
+			info->inpa = 0;
+			purple_debug_info("gg", "Connection failure: %d\n",
+				ev->event.failure);
+			switch (ev->event.failure) {
+				case GG_FAILURE_RESOLVING:
+					purple_connection_error(gc,
+						PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+						_("Unable to resolve "
+						"hostname"));
+					break;
+				case GG_FAILURE_PASSWORD:
+					purple_connection_error(gc,
+						PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+						_("Incorrect password"));
+					break;
+				case GG_FAILURE_TLS:
+					purple_connection_error(gc,
+						PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+						_("SSL Connection Failed"));
+					break;
+				case GG_FAILURE_INTRUDER:
+					purple_connection_error(gc,
+						PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+						_("Your account has been "
+						"disabled because too many "
+						"incorrect passwords were "
+						"entered"));
+					break;
+				case GG_FAILURE_UNAVAILABLE:
+					purple_connection_error(gc,
+						PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+						_("Service temporarily "
+						"unavailable"));
+					break;
+				case GG_FAILURE_PROXY:
+					purple_connection_error(gc,
+						PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+						_("Error connecting to proxy "
+						"server"));
+					break;
+				case GG_FAILURE_HUB:
+					purple_connection_error(gc,
+						PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+						_("Error connecting to master "
+						"server"));
+					break;
+				default:
+					purple_connection_error(gc,
+						PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+						_("Connection failed"));
+			}
+			break;
+		case GG_EVENT_MSG:
+			if (ev->event.msg.sender == 0)
+			{
+				if (ev->event.msg.message == NULL)
+					break;
+
+				/* system messages are mostly ads */
+				purple_debug_info("gg", "System message:\n%s\n",
+					ev->event.msg.message);
+			}
+			else
+				purple_debug_warning("gg", "GG_EVENT_MSG: message from user %u "
+					"unexpected while connecting:\n%s\n",
+					ev->event.msg.sender,
+					ev->event.msg.message);
 			break;
 		default:
 			purple_debug_error("gg", "strange event: %d\n", ev->type);
@@ -1745,6 +2090,19 @@
 	return "gadu-gadu";
 }
 
+static const char *ggp_normalize(const PurpleAccount *account, const char *who)
+{
+	static char normalized[21]; /* maximum unsigned long long int size */
+
+	uin_t uin = ggp_str_to_uin(who);
+	if (uin <= 0)
+		return NULL;
+
+	g_snprintf(normalized, sizeof(normalized), "%u", uin);
+
+	return normalized;
+}
+
 static char *ggp_status_text(PurpleBuddy *b)
 {
 	PurpleStatus *status;
@@ -1752,29 +2110,24 @@
 	char *text;
 	char *tmp;
 
-	status = purple_presence_get_active_status(purple_buddy_get_presence(b));
-
+	status = purple_presence_get_active_status(
+		purple_buddy_get_presence(b));
 	msg = purple_status_get_attr_string(status, "message");
 
-	if (msg != NULL) {
-		tmp = purple_markup_strip_html(msg);
-		text = g_markup_escape_text(tmp, -1);
-		g_free(tmp);
-
-		return text;
-	} else {
-		tmp = purple_utf8_salvage(purple_status_get_name(status));
-		text = g_markup_escape_text(tmp, -1);
-		g_free(tmp);
-
-		return text;
-	}
+	if (msg == NULL)
+		return NULL;
+
+	tmp = purple_markup_strip_html(msg);
+	text = g_markup_escape_text(tmp, -1);
+	g_free(tmp);
+
+	return text;
 }
 
 static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
 {
 	PurpleStatus *status;
-	char *text, *tmp;
+	char *tmp;
 	const char *msg, *name, *alias;
 
 	g_return_if_fail(b != NULL);
@@ -1784,21 +2137,19 @@
 	name = purple_status_get_name(status);
 	alias = purple_buddy_get_alias(b);
 
-	purple_notify_user_info_add_pair (user_info, _("Alias"), alias);
+	purple_notify_user_info_add_pair_plaintext(user_info, _("Alias"), alias);
 
 	if (msg != NULL) {
-		text = g_markup_escape_text(msg, -1);
 		if (PURPLE_BUDDY_IS_ONLINE(b)) {
-			tmp = g_strdup_printf("%s: %s", name, text);
-			purple_notify_user_info_add_pair(user_info, _("Status"), tmp);
+			tmp = g_strdup_printf("%s: %s", name, msg);
+			purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), tmp);
 			g_free(tmp);
 		} else {
-			purple_notify_user_info_add_pair(user_info, _("Message"), text);
+			purple_notify_user_info_add_pair_plaintext(user_info, _("Message"), msg);
 		}
-		g_free(text);
 	/* We don't want to duplicate 'Status: Offline'. */
 	} else if (PURPLE_BUDDY_IS_ONLINE(b)) {
-		purple_notify_user_info_add_pair(user_info, _("Status"), name);
+		purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), name);
 	}
 }
 
@@ -1807,40 +2158,60 @@
 	PurpleStatusType *type;
 	GList *types = NULL;
 
-	type = purple_status_type_new_with_attrs(
-			PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
-			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			NULL);
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+		NULL, NULL, TRUE, TRUE, FALSE,
+		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+		NULL);
 	types = g_list_append(types, type);
 
 	/*
-	 * Without this selecting Invisible as own status doesn't
-	 * work. It's not used and not needed to show status of buddies.
+	 * New status for GG 8.0: PoGGadaj ze mna (chatty).
+	 * NOTE: at this time, this is used only to set our own status.
 	 */
-	type = purple_status_type_new_with_attrs(
-			PURPLE_STATUS_INVISIBLE, NULL, NULL, TRUE, TRUE, FALSE,
-			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			NULL);
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+		"freeforchat", _("Chatty"), TRUE, TRUE, FALSE,
+		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+		NULL);
+	types = g_list_append(types, type);
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
+		NULL, NULL, TRUE, TRUE, FALSE,
+		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+		NULL);
 	types = g_list_append(types, type);
 
-	type = purple_status_type_new_with_attrs(
-			PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
-			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			NULL);
+	/*
+	 * New status for GG 8.0: Nie przeszkadzac (do not disturb).
+	 */
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
+		NULL, NULL, TRUE, TRUE, FALSE,
+		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+		NULL);
+	types = g_list_append(types, type);
+
+	/*
+	 * It's used on buddy list if and only if it's showing our own
+	 * (invisible) status.
+	 */
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
+		NULL, NULL, TRUE, TRUE, FALSE,
+		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+		NULL);
 	types = g_list_append(types, type);
 
 	/*
 	 * This status is necessary to display guys who are blocking *us*.
 	 */
-	type = purple_status_type_new_with_attrs(
-			PURPLE_STATUS_INVISIBLE, "blocked", _("Blocked"), TRUE, FALSE, FALSE,
-			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), NULL);
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
+		"blocked", _("Blocked"), TRUE, FALSE, FALSE,
+		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+		NULL);
 	types = g_list_append(types, type);
 
-	type = purple_status_type_new_with_attrs(
-			PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE,
-			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
-			NULL);
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE,
+		NULL, NULL, TRUE, TRUE, FALSE,
+		"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+		NULL);
 	types = g_list_append(types, type);
 
 	return types;
@@ -1851,13 +2222,15 @@
 	PurpleMenuAction *act;
 	GList *m = NULL;
 	PurpleAccount *account;
+	PurpleConnection *gc;
 	GGPInfo *info;
 
 	if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
 		return NULL;
 
 	account = purple_buddy_get_account((PurpleBuddy *) node);
-	info = purple_account_get_connection(account)->proto_data;
+	gc = purple_account_get_connection(account);
+	info = purple_connection_get_protocol_data(gc);
 	if (info->chats) {
 		act = purple_menu_action_new(_("Add to chat"),
 			PURPLE_CALLBACK(ggp_bmenu_add_to_chat),
@@ -1865,20 +2238,6 @@
 		m = g_list_append(m, act);
 	}
 
-	/* Using a blist node boolean here is also wrong.
-	 * Once the Block and Unblock actions are added to the core,
-	 * this will have to go. -- rlaager */
-	if (purple_blist_node_get_bool(node, "blocked")) {
-		act = purple_menu_action_new(_("Unblock"),
-		                           PURPLE_CALLBACK(ggp_bmenu_block),
-		                           NULL, NULL);
-	} else {
-		act = purple_menu_action_new(_("Block"),
-		                           PURPLE_CALLBACK(ggp_bmenu_block),