changeset 29955:8afc47597413

merge of 'd2f2c48292c37816f81123f7d805891c57238119' and 'd39086ef4ef94396c2b7fd5699fc3bfd04dc885d'
author Paul Aurich <paul@darkrain42.org>
date Mon, 08 Mar 2010 22:53:02 +0000
parents 41e557b8d38c (current diff) f8cedc8bc515 (diff)
children 342ed5917f56
files pidgin/gtkcelllayout.c pidgin/gtkcelllayout.h pidgin/gtkcellrendererprogress.c pidgin/gtkcellrendererprogress.h pidgin/gtkcellview.c pidgin/gtkcellview.h pidgin/gtkcellviewmenuitem.c pidgin/gtkcellviewmenuitem.h pidgin/gtkdocklet-x11.c pidgin/gtkexpander.c pidgin/gtkexpander.h pidgin/pidgincombobox.c pidgin/pidgincombobox.h pidgin/pixmaps/emblems/16/mobile.png pidgin/pixmaps/emblems/16/music.png pidgin/pixmaps/emblems/scalable/mobile.svg pidgin/pixmaps/emblems/scalable/music.svg pidgin/pixmaps/emotes/default/24/eat.png pidgin/pixmaps/emotes/default/24/in-love.png pidgin/pixmaps/emotes/default/24/love.png pidgin/pixmaps/emotes/default/24/nailbiting.png pidgin/pixmaps/emotes/default/24/shock.png pidgin/pixmaps/emotes/default/24/smile-big.png pidgin/pixmaps/emotes/default/24/smile.png pidgin/pixmaps/emotes/default/24/sweat.png pidgin/pixmaps/emotes/default/24/tremble.png pidgin/pixmaps/emotes/default/24/yawn.png pidgin/pixmaps/emotes/small/16/love.png pidgin/pixmaps/emotes/small/16/shock.png pidgin/pixmaps/emotes/small/16/smile-big.png pidgin/pixmaps/emotes/small/16/smile.png pidgin/pixmaps/emotes/small/16/yawn.png pidgin/pixmaps/tray/16/tray-away.png pidgin/pixmaps/tray/16/tray-busy.png pidgin/pixmaps/tray/16/tray-connecting.png pidgin/pixmaps/tray/16/tray-extended-away.png pidgin/pixmaps/tray/16/tray-invisible.png pidgin/pixmaps/tray/16/tray-message.png pidgin/pixmaps/tray/16/tray-new-im.png pidgin/pixmaps/tray/16/tray-offline.png pidgin/pixmaps/tray/16/tray-online.png pidgin/pixmaps/tray/22/tray-away.png pidgin/pixmaps/tray/22/tray-busy.png pidgin/pixmaps/tray/22/tray-connecting.png pidgin/pixmaps/tray/22/tray-extended-away.png pidgin/pixmaps/tray/22/tray-invisible.png pidgin/pixmaps/tray/22/tray-message.png pidgin/pixmaps/tray/22/tray-new-im.png pidgin/pixmaps/tray/22/tray-offline.png pidgin/pixmaps/tray/22/tray-online.png pidgin/pixmaps/tray/32/tray-away.png pidgin/pixmaps/tray/32/tray-busy.png pidgin/pixmaps/tray/32/tray-connecting.png pidgin/pixmaps/tray/32/tray-extended-away.png pidgin/pixmaps/tray/32/tray-invisible.png pidgin/pixmaps/tray/32/tray-message.png pidgin/pixmaps/tray/32/tray-new-im.png pidgin/pixmaps/tray/32/tray-offline.png pidgin/pixmaps/tray/32/tray-online.png pidgin/pixmaps/tray/48/tray-away.png pidgin/pixmaps/tray/48/tray-busy.png pidgin/pixmaps/tray/48/tray-connecting.png pidgin/pixmaps/tray/48/tray-extended-away.png pidgin/pixmaps/tray/48/tray-invisible.png pidgin/pixmaps/tray/48/tray-message.png pidgin/pixmaps/tray/48/tray-new-im.png pidgin/pixmaps/tray/48/tray-offline.png pidgin/pixmaps/tray/48/tray-online.png
diffstat 433 files changed, 19557 insertions(+), 21009 deletions(-) [+]
line wrap: on
line diff
--- a/.mtn-ignore	Mon Mar 08 22:51:42 2010 +0000
+++ b/.mtn-ignore	Mon Mar 08 22:53:02 2010 +0000
@@ -38,7 +38,10 @@
 pidgin.spec$
 pidgin-.*.tar.gz
 pidgin-.*.tar.bz2
+pidgin-*.*.*-dbgsym$
+pidgin-*.*.*-dbgsym.zip$
 pidgin-*.*.*-win32bin$
+pidgin-*.*.*-win32-bin.zip$
 pidgin/pidgin$
 pidgin/pixmaps/emotes/default/24/theme
 pidgin/pixmaps/emotes/none/theme
@@ -48,6 +51,9 @@
 pidgin/plugins/perl/common/Makefile.old
 pidgin/win32/pidgin_dll_rc.rc$
 pidgin/win32/pidgin_exe_rc.rc$
+pidgin/win32/nsis/gtk-runtime-*.*.*.*.zip
+pidgin/win32/nsis/gtk_runtime_stage$
+pidgin/win32/nsis/pidgin-translations.nsh$
 install-sh
 libpurple/dbus-bindings.c
 libpurple/dbus-signals.c
--- a/COPYRIGHT	Mon Mar 08 22:51:42 2010 +0000
+++ b/COPYRIGHT	Mon Mar 08 22:53:02 2010 +0000
@@ -228,6 +228,7 @@
 Instant Messaging Freedom, Inc.
 Vitaliy Ischenko
 Intel Corporation
+Andrew Ivanov
 Scott Jackson
 Hans Petter Jansson
 David Jedelsky
@@ -237,7 +238,7 @@
 Yuriy Kaminskiy
 Anders Kaseorg
 Praveen Karadakal
-Jaromír Karmazín
+Tomáš Kebert
 John Kelm
 Jochen Kemnade
 Yann Kerherve
@@ -302,6 +303,7 @@
 Peter McCurdy
 Kurt McKee
 Torrey McMahon
+Greg McNew
 Robert McQueen
 Mihály Mészáros
 Robert Mibus
@@ -541,6 +543,7 @@
 Jared Yanovich
 Timmy Yee
 Li Yuan
+Yuriy Yevgrafov
 Nickolai Zeldovich
 Tom Zickel
 Marco Ziech
--- a/ChangeLog	Mon Mar 08 22:51:42 2010 +0000
+++ b/ChangeLog	Mon Mar 08 22:53:02 2010 +0000
@@ -1,5 +1,42 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.7.0 (??/??/????):
+	General:
+	* Changed GTK+ minimum version requirement to 2.10.0.
+	* Changed GLib minimum version requirement to 2.12.0.
+
+	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)
+
+	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!)
+
+	ICQ:
+	* X-Status (Custom ICQ status icon) support (Andrew Ivanov, Tomáš Kebert,
+	  Yuriy Yevgrafov, and trac users bob007, salieff, and nops)
+
+	XMPP:
+	* Direct messages to a specific resource only upon receipt of a message
+	  with content (as opposed to a typing notification, etc).  (Thanks to
+	  rjoly for testing)
+
 version 2.6.6 (02/18/2010):
 	libpurple:
 	* Fix 'make check' on OS X. (David Fang)
@@ -44,23 +81,24 @@
 	* 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.
+	  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.
+	  (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.
+	* 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
+	* 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).
+	* 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').
@@ -99,7 +137,8 @@
 	  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).
+	  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.
 
@@ -137,11 +176,11 @@
 	* 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.)
+	* 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')
@@ -172,14 +211,14 @@
 	  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)
+	* 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.
+	  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.
@@ -203,23 +242,23 @@
 	* 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.
+	* 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.
+	  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.
+	* 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
@@ -229,10 +268,10 @@
 	* 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.
+	* 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:
@@ -272,16 +311,17 @@
 	  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)
+	* 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.
+	* 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)
@@ -323,8 +363,8 @@
 	* 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)
+	* 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
@@ -335,32 +375,34 @@
 	  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.
+	* 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.
+	  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:
@@ -379,15 +421,16 @@
 	  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.
+	* Support sending an invite message to buddies when requesting
+	  authorization from them on MSN.
 	* Timeout switchboard connections aggressively (60 seconds).
 
 	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"
@@ -404,28 +447,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
--- a/ChangeLog.API	Mon Mar 08 22:51:42 2010 +0000
+++ b/ChangeLog.API	Mon Mar 08 22:53:02 2010 +0000
@@ -1,5 +1,36 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.7.0 (??/??/????):
+	libpurple:
+		Added:
+		* purple_account_get_name_for_display
+		* purple_buddy_get_media_caps
+		* purple_buddy_set_media_caps
+		* purple_contact_get_group
+		* purple_media_candidate_copy
+		* purple_media_codec_copy
+		* purple_media_manager_get_backend_type
+		* purple_media_manager_set_backend_type
+		* 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_prpl_got_media_caps
+		* purple_unescape_text
+		* purple_uuid_random
+		* media_caps to the PurpleBuddy struct
+		* buddy-caps-changed blist signal
+		* ui-caps-changed media manager signal
+		* sent-attention conversation signal
+		* got-attention conversation signal
+
+	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
+
 version 2.6.6 (02/18/2010):
 	libpurple:
 		Changed:
@@ -115,6 +146,7 @@
 		* 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
--- a/ChangeLog.win32	Mon Mar 08 22:51:42 2010 +0000
+++ b/ChangeLog.win32	Mon Mar 08 22:53:02 2010 +0000
@@ -1,3 +1,13 @@
+version 2.7.0 (??/??/????):
+	* Minimum required GTK+ version increased to 2.14.0
+	* Private GTK+ Runtime now used (GTK+ Installer no longer supported)
+	* Win9x no longer supported.
+	* Crash Report files (pidgin.RPT) are now generated in the ~/.purple
+	  directory instead of the installation directory.
+	* NSS SSL Library upgraded to 3.12.5 (thanks to Berke Viktor)
+	* GtkSpell upgraded to 2.0.16, changing the spellchecking backend to
+	  enchant.  This means that myspell, hunspell (OpenOffice) and existing
+	  aspell dictionaries can be used.
 
 version 2.6.6 (02/18/2010):
 	* Installer translations for: Norwegian nynorsk
--- a/Makefile.mingw	Mon Mar 08 22:51:42 2010 +0000
+++ b/Makefile.mingw	Mon Mar 08 22:53:02 2010 +0000
@@ -31,31 +31,32 @@
     exit; \
 }' VERSION)
 
-GTK_INSTALL_VERSION = $(shell \
-  source ../gtk_installer/version.sh; \
-  echo $$gtk_version \
-)
+GTK_INSTALL_VERSION = 2.14.7.0
 
 STRIPPED_RELEASE_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-win32bin
+DEBUG_SYMBOLS_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-dbgsym
 
 
 # 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 \
+	libgtkspell-0.dll \
 	libmeanwhile-1.dll \
+	libnspr4.dll \
+	libplc4.dll \
+	libplds4.dll \
 	libsasl.dll \
 	libxml2.dll \
-	nspr4.dll \
 	nss3.dll \
 	nssckbi.dll \
-	plc4.dll \
-	plds4.dll \
+	nssutil3.dll \
 	saslANONYMOUS.dll \
 	saslCRAMMD5.dll \
 	saslDIGESTMD5.dll \
@@ -66,12 +67,13 @@
 	libsilcclient-1-1-2.dll \
 	smime3.dll \
 	softokn3.dll \
+	sqlite3.dll \
 	ssl3.dll
 
 #build an expression for `find` to use to ignore the above files
 EXTERNAL_DLLS_FIND_EXP = $(patsubst %,-o -name %,$(EXTERNAL_DLLS))
 
-.PHONY: all docs install installer installer_nogtk installer_debug installers clean uninstall create_release_install_dir
+.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,30 +90,64 @@
 endif
 	$(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) install
 	$(MAKE) -C share/sounds -f $(MINGW_MAKEFILE) install
+	mkdir -p $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp $(GTKSPELL_TOP)/bin/libgtkspell-0.dll $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp $(ENCHANT_TOP)/bin/libenchant.dll $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp -R $(ENCHANT_TOP)/lib $(PIDGIN_INSTALL_DIR)/spellcheck
+	cp $(WIN32_DEV_TOP)/pidgin-inst-deps-20100223/exchndl.dll $(PIDGIN_INSTALL_DIR)
+
+pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip:
+	pidgin/win32/nsis/generate_gtk_zip.sh `pwd`
+
+generate_installer_includes: create_release_install_dir pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip debug_symbols_zip
+	rm -f pidgin/win32/nsis/pidgin-translations.nsh pidgin/win32/nsis/pidgin-spellcheck.nsh pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh
+	find $(STRIPPED_RELEASE_DIR)/locale -maxdepth 1 -mindepth 1 \
+	 -exec basename {} ';' \
+	 | sed -e s/^/\!insertmacro\ LANG_SECTION\ \"/ -e s/$$/\"/ \
+	 > pidgin/win32/nsis/pidgin-translations.nsh
+	#Convert the available.lst lines to "!insertmacro SPELLCHECK_SECTION lang lang_name lang_file"
+	sed -e "/^#/d" -e "s/^[^,]\{1,\},[^,]\{1,\},/\"/" \
+	 -e "s/,/\"\ \"/" -e "s/,/\"\ \"/" -e "s/[\ \t]*$$/\"/" \
+	 -e "s/^/\!insertmacro\ SPELLCHECK_SECTION\ /" \
+         pidgin/win32/nsis/available.lst \
+         > pidgin/win32/nsis/pidgin-spellcheck.nsh
+	#Convert the lines to "!insertmacro CHECK_SPELLCHECK_SECTION lang"
+	iconv -f latin1 -t utf-8 pidgin/win32/nsis/pidgin-spellcheck.nsh | \
+	 sed -e "s/SPELLCHECK_SECTION/CHECK_SPELLCHECK_SECTION/" \
+	 -e "s/ \"[^\"]*\"\ \"[^\"]*\"[\t\ ]*$$//" | \
+         iconv -f utf-8 -t latin1 \
+        > pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh
 
 create_release_install_dir: install
 	rm -rf $(STRIPPED_RELEASE_DIR)
-	cp -R $(PIDGIN_INSTALL_DIR) $(STRIPPED_RELEASE_DIR)
+	mkdir $(STRIPPED_RELEASE_DIR)
+	tar -cf - $(PIDGIN_INSTALL_DIR) --exclude=Gtk --exclude=spellcheck/share \
+	 | tar --strip 2 -xC $(STRIPPED_RELEASE_DIR) -f -
 	find $(STRIPPED_RELEASE_DIR) \( -name '*.dll' -o -name '*.exe' \) \
-	 -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) -exec $(STRIP) --strip-unneeded {} ';'
+	 -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
@@ -125,13 +161,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)
 
-.PHONY: $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT)
--- a/configure.ac	Mon Mar 08 22:51:42 2010 +0000
+++ b/configure.ac	Mon Mar 08 22:53:02 2010 +0000
@@ -43,20 +43,20 @@
 #
 # Make sure to update finch/libgnt/configure.ac with libgnt version changes.
 #
-m4_define([purple_lt_current], [6])
+m4_define([purple_lt_current], [7])
 m4_define([purple_major_version], [2])
-m4_define([purple_minor_version], [6])
-m4_define([purple_micro_version], [6])
-m4_define([purple_version_suffix], [])
+m4_define([purple_minor_version], [7])
+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], [7])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [6])
-m4_define([gnt_micro_version], [6])
-m4_define([gnt_version_suffix], [])
+m4_define([gnt_minor_version], [7])
+m4_define([gnt_micro_version], [0])
+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]))
@@ -211,6 +211,7 @@
 	[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([
@@ -310,13 +311,13 @@
 )
 
 dnl #######################################################################
-dnl # Check for GLib 2.0 (required)
+dnl # Check for GLib 2.12 (required)
 dnl #######################################################################
-PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.4.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
+PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.12.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [
 	AC_MSG_RESULT(no)
 	AC_MSG_ERROR([
 
-You must have GLib 2.4.0 or newer development headers installed to build.
+You must have GLib 2.12.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 +353,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],
@@ -399,11 +400,11 @@
 fi
 
 if test "x$enable_gtkui" = "xyes" ; then
-	PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.4.0], , [
+	PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.10.0], , [
 		AC_MSG_RESULT(no)
 		AC_MSG_ERROR([
 
-You must have GTK+ 2.4.0 or newer development headers installed to compile
+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.
 ])])
@@ -2393,6 +2394,30 @@
         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>
+#include <stdlib.h>
+	void f (int i, ...) {
+	va_list args1, args2;
+	va_start (args1, i);
+	args2 = args1;
+	if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42)
+	  exit (1);
+	va_end (args1); va_end (args2);
+	}
+	int main() {
+	  f (0, 42);
+	  return 0;
+	}],
+	[ac_cv_va_val_copy=yes],
+	[ac_cv_va_val_copy=no],
+	[ac_cv_va_val_copy=yes])
+])
+
+if test "x$ac_cv_va_val_copy" = "xno"; then
+	AC_DEFINE(VA_COPY_AS_ARRAY, 1, ['va_lists' cannot be copied as values])
+fi
+
 dnl #######################################################################
 dnl # Check for check
 dnl #######################################################################
@@ -2491,6 +2516,8 @@
 		   pidgin/Makefile
 		   pidgin/pidgin.pc
 		   pidgin/pidgin-uninstalled.pc
+		   pidgin/pidgin-2.pc
+		   pidgin/pidgin-2-uninstalled.pc
 		   pidgin/pixmaps/Makefile
 		   pidgin/pixmaps/emotes/default/24/Makefile
 		   pidgin/pixmaps/emotes/none/Makefile
@@ -2508,6 +2535,8 @@
 		   libpurple/gconf/Makefile
 		   libpurple/purple.pc
 		   libpurple/purple-uninstalled.pc
+		   libpurple/purple-2.pc
+		   libpurple/purple-2-uninstalled.pc
 		   libpurple/plugins/Makefile
 		   libpurple/plugins/mono/Makefile
 		   libpurple/plugins/mono/api/Makefile
@@ -2578,6 +2607,7 @@
 echo Build with Cyrus SASL support. : $enable_cyrus_sasl
 echo Use kerberos 4 with zephyr.... : $kerberos
 echo Use external libzephyr........ : $zephyr
+echo Use external libgadu.......... : $gadu_libs
 echo Install pixmaps............... : $enable_pixmaps
 echo Install translations.......... : $enable_i18n
 echo Has you....................... : yes
--- a/finch/finch.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/finch/finch.c	Mon Mar 08 22:53:02 2010 +0000
@@ -431,9 +431,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/gntconv.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/finch/gntconv.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1318,7 +1318,6 @@
 	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)
 {
@@ -1359,7 +1358,6 @@
 
 	return PURPLE_CMD_RET_OK;
 }
-#endif
 
 static PurpleCmdRet
 users_command_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
@@ -1445,7 +1443,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,
@@ -1455,7 +1452,6 @@
 				                 "    &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_signal_connect(purple_conversations_get_handle(), "buddy-typing", finch_conv_get_handle(),
 					PURPLE_CALLBACK(update_buddy_typing), NULL);
--- a/finch/gntplugin.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/finch/gntplugin.c	Mon Mar 08 22:53:02 2010 +0000
@@ -498,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/gntprefs.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/finch/gntprefs.c	Mon Mar 08 22:53:02 2010 +0000
@@ -171,7 +171,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);
 		}
--- a/finch/gntsound.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/finch/gntsound.c	Mon Mar 08 22:53:02 2010 +0000
@@ -559,18 +559,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 */
 }
--- a/finch/libgnt/configure.ac	Mon Mar 08 22:51:42 2010 +0000
+++ b/finch/libgnt/configure.ac	Mon Mar 08 22:53:02 2010 +0000
@@ -24,10 +24,10 @@
 # Make sure to update ../../configure.ac with libgnt version changes.
 #
 
-m4_define([gnt_lt_current], [6])
+m4_define([gnt_lt_current], [7])
 m4_define([gnt_major_version], [2])
-m4_define([gnt_minor_version], [6])
-m4_define([gnt_micro_version], [2])
+m4_define([gnt_minor_version], [7])
+m4_define([gnt_micro_version], [0])
 m4_define([gnt_version_suffix], [devel])
 m4_define([gnt_version],
           [gnt_major_version.gnt_minor_version.gnt_micro_version])
--- a/finch/plugins/gnthistory.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/finch/plugins/gnthistory.c	Mon Mar 08 22:53:02 2010 +0000
@@ -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/libpurple/Makefile.am	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/Makefile.am	Mon Mar 08 22:53:02 2010 +0000
@@ -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 \
@@ -156,6 +161,12 @@
 	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
@@ -192,6 +203,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
@@ -226,7 +238,7 @@
 purple-client-bindings.c: dbus-analyze-functions.py $(dbus_exported)
 	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)
+purple-client-bindings.h: dbus-analyze-types.py dbus-analyze-functions.py $(purple_coreheaders) $(addprefix media/, $(purple_mediaheaders)) $(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 >> $@
 
@@ -275,6 +287,7 @@
 
 noinst_HEADERS= \
 	internal.h \
+	media/backend-fs2.h \
 	valgrind.h
 
 libpurpleincludedir=$(includedir)/libpurple
@@ -283,6 +296,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 = \
--- a/libpurple/Makefile.mingw	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/Makefile.mingw	Mon Mar 08 22:53:02 2010 +0000
@@ -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
@@ -48,8 +48,8 @@
 			idle.c \
 			imgstore.c \
 			log.c \
+			mediamanager.c \
 			media.c \
-			mediamanager.c \
 			mime.c \
 			nat-pmp.c \
 			network.c \
--- a/libpurple/account.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/account.c	Mon Mar 08 22:53:02 2010 +0000
@@ -2036,6 +2036,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)
 {
@@ -2708,11 +2744,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)) {
--- a/libpurple/account.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/account.h	Mon Mar 08 22:53:02 2010 +0000
@@ -630,6 +630,20 @@
 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.
+ *
+ * @since 2.7.0
+ */
+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.
--- a/libpurple/blist.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/blist.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1739,6 +1739,14 @@
 	g_free(contact);
 }
 
+PurpleGroup *
+purple_contact_get_group(const PurpleContact *contact)
+{
+	g_return_val_if_fail(contact, NULL);
+
+	return (PurpleGroup *)(((PurpleBlistNode *)contact)->parent);
+}
+
 void purple_contact_set_alias(PurpleContact *contact, const char *alias)
 {
 	purple_blist_alias_contact(contact,alias);
@@ -2605,6 +2613,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);
@@ -3187,6 +3207,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	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/blist.h	Mon Mar 08 22:53:02 2010 +0000
@@ -108,6 +108,7 @@
 
 #include "account.h"
 #include "buddyicon.h"
+#include "media.h"
 #include "status.h"
 
 /**************************************************************************/
@@ -143,6 +144,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. */
 };
 
 /**
@@ -657,6 +659,24 @@
 PurplePresence *purple_buddy_get_presence(const PurpleBuddy *buddy);
 
 /**
+ * Gets the media caps from a buddy.
+ *
+ * @param buddy The buddy.
+ * @return      The media caps.
+ *
+ * @since 2.7.0
+ */
+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 +735,16 @@
 void purple_contact_destroy(PurpleContact *contact);
 
 /**
+ * Gets the PurpleGroup from a PurpleContact
+ *
+ * @param contact  The contact
+ * @return         The group
+ *
+ * @since 2.7.0
+ */
+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
--- a/libpurple/certificate.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/certificate.c	Mon Mar 08 22:53:02 2010 +0000
@@ -874,7 +874,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));
--- a/libpurple/connection.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/connection.h	Mon Mar 08 22:53:02 2010 +0000
@@ -44,8 +44,9 @@
 	PURPLE_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */
 	PURPLE_CONNECTION_NO_URLDESC = 0x0040,  /**< Connection does not support descriptions with links */
 	PURPLE_CONNECTION_NO_IMAGES = 0x0080,  /**< Connection does not support sending of images */
-	PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100 /**< Connection supports sending and receiving custom smileys */
-
+	PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100, /**< Connection supports sending and receiving custom smileys */
+	PURPLE_CONNECTION_SUPPORT_MOODS = 0x0200, /**< Connection supports setting moods */
+	PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES = 0x0400 /**< Connection supports setting a message on moods */
 } PurpleConnectionFlags;
 
 typedef enum
--- a/libpurple/conversation.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/conversation.c	Mon Mar 08 22:53:02 2010 +0000
@@ -2302,7 +2302,27 @@
 						 purple_value_new(PURPLE_TYPE_SUBTYPE,
 										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,
--- a/libpurple/conversation.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/conversation.h	Mon Mar 08 22:53:02 2010 +0000
@@ -646,7 +646,6 @@
 		const char *message, PurpleMessageFlags flags,
 		time_t mtime);
 
-
 /**
 	Set the features as supported for the given conversation.
 	@param conv      The conversation
--- a/libpurple/core.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/core.c	Mon Mar 08 22:53:02 2010 +0000
@@ -496,7 +496,6 @@
 			if (purple_strequal(entry, "logs"))
 			{
 				char *link;
-#if GLIB_CHECK_VERSION(2,4,0)
 				err = NULL;
 
 				if ((link = g_file_read_link(name, &err)) == NULL)
@@ -512,27 +511,6 @@
 					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);
 
--- a/libpurple/example/nullclient.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/example/nullclient.c	Mon Mar 08 22:53:02 2010 +0000
@@ -27,7 +27,11 @@
 
 #include <signal.h>
 #include <string.h>
+#ifndef _WIN32
 #include <unistd.h>
+#else
+#include "win32/win32dep.h"
+#endif
 
 #include "defines.h"
 
@@ -80,7 +84,11 @@
 	if (condition & PURPLE_INPUT_WRITE)
 		cond |= PURPLE_GLIB_WRITE_COND;
 
+#if defined _WIN32 && !defined WINPIDGIN_USE_GLIB_IO_CHANNEL
+	channel = wpurple_g_io_channel_win32_new_socket(fd);
+#else
 	channel = g_io_channel_unix_new(fd);
+#endif
 	closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
 					      purple_glib_io_invoke, closure, purple_glib_io_destroy);
 
@@ -253,12 +261,14 @@
 	PurpleSavedStatus *status;
 	char *res;
 
+#ifndef _WIN32
 	/* libpurple's built-in DNS resolution forks processes to perform
 	 * blocking lookups without blocking the main process.  It does not
 	 * handle SIGCHLD itself, so if the UI does not you quickly get an army
 	 * of zombie subprocesses marching around.
 	 */
 	signal(SIGCHLD, SIG_IGN);
+#endif
 
 	init_libpurple();
 
--- a/libpurple/ft.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/ft.c	Mon Mar 08 22:53:02 2010 +0000
@@ -57,6 +57,7 @@
 		PURPLE_XFER_READY_UI   = 0x1,
 		PURPLE_XFER_READY_PRPL = 0x2,
 	} ready;
+	GByteArray *buffer;
 } PurpleXferPrivData;
 
 static int purple_xfer_choose_file(PurpleXfer *xfer);
@@ -66,6 +67,9 @@
 {
 	PurpleXferPrivData *priv = data;
 
+	if (priv->buffer)
+		g_byte_array_free(priv->buffer, TRUE);
+
 	g_free(priv);
 }
 
@@ -117,7 +121,7 @@
 	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;
@@ -125,6 +129,13 @@
 	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);
@@ -568,7 +579,7 @@
 	type = purple_xfer_get_type(xfer);
 	account = purple_xfer_get_account(xfer);
 
-	purple_debug_misc("xfer", "request accepted for %p\n", xfer); 
+	purple_debug_misc("xfer", "request accepted for %p\n", xfer);
 
 	if (!filename && type == PURPLE_XFER_RECEIVE) {
 		xfer->status = PURPLE_XFER_STATUS_ACCEPTED;
@@ -1074,8 +1085,10 @@
 			return;
 		}
 	} else if (xfer->type == PURPLE_XFER_SEND) {
-		size_t result;
+		size_t result = 0;
 		size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size);
+		PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
+		gboolean read = TRUE;
 
 		/* this is so the prpl can keep the connection open
 		   if it needs to for some odd reason. */
@@ -1087,65 +1100,85 @@
 			return;
 		}
 
-		if (ui_ops && ui_ops->ui_read) {
-			gssize tmp = ui_ops->ui_read(xfer, &buffer, s);
-			if (tmp == 0) {
-				PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer);
-
-				/*
-				 * 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_malloc0(s);
-			result = fread(buffer, 1, s, xfer->dest_fp);
-			if (result != s) {
-				purple_debug_error("filetransfer", "Unable to read whole buffer.\n");
-				purple_xfer_cancel_local(xfer);
-				g_free(buffer);
-				return;
+		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. */
+		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) {
+			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);
 			return;
-		} else if (r < result) {
-			if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
-				/* We have to seek back in the file now. */
-				fseek(xfer->dest_fp, r - s, SEEK_CUR);
-			}
-			else {
-				ui_ops->data_not_sent(xfer, buffer + r, result - r);
-			}
-		} 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 kee what wasn't sent for next time.
+			 */
+			buffer = NULL;
+			priv->buffer = g_byte_array_remove_range(priv->buffer, 0, r);
 		}
 	}
 
--- a/libpurple/internal.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/internal.h	Mon Mar 08 22:53:02 2010 +0000
@@ -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,38 +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. */
--- a/libpurple/log.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/log.c	Mon Mar 08 22:53:02 2010 +0000
@@ -302,7 +302,7 @@
 			}
 		}
 
-		score = (gint)score_double;
+		score = (gint) ceil(score_double);
 		g_hash_table_replace(logsize_users_decayed, lu, GINT_TO_POINTER(score));
 	}
 	return score;
--- a/libpurple/marshallers.list	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/marshallers.list	Mon Mar 08 22:53:02 2010 +0000
@@ -4,3 +4,5 @@
 VOID:STRING,STRING,DOUBLE
 VOID:ENUM,STRING,STRING
 VOID:ENUM,STRING,STRING,BOOLEAN
+VOID:FLAGS,FLAGS
+VOID:STRING,STRING,OBJECT,OBJECT
--- a/libpurple/media.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/media.c	Mon Mar 08 22:53:02 2010 +0000
@@ -23,26 +23,23 @@
  * 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 "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 */
@@ -51,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
@@ -77,10 +66,6 @@
 {
 	gchar *id;
 	PurpleMedia *media;
-	GstElement *src;
-	GstElement *tee;
-	FsSession *session;
-
 	PurpleMediaSessionType type;
 	gboolean initiator;
 };
@@ -89,11 +74,6 @@
 {
 	PurpleMediaSession *session;
 	gchar *participant;
-	FsStream *stream;
-	GstElement *src;
-	GstElement *tee;
-	GstElement *volume;
-	GstElement *level;
 
 	GList *local_candidates;
 	GList *remote_candidates;
@@ -101,13 +81,9 @@
 	gboolean initiator;
 	gboolean accepted;
 	gboolean candidates_prepared;
-	gboolean held;
-	gboolean paused;
 
 	GList *active_local_candidates;
 	GList *active_remote_candidates;
-
-	guint connected_cb_id;
 };
 #endif
 
@@ -116,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
@@ -133,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);
@@ -143,15 +115,19 @@
 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;
 
@@ -172,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()
 {
@@ -239,56 +184,6 @@
 #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_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_UNHOLD", "unhold" },
-			{ 0, NULL, NULL }
-		};
-		type = g_enum_register_static("PurpleMediaInfoType", values);
-	}
-	return type;
-}
-
 #ifdef USE_VV
 static void
 purple_media_class_init (PurpleMediaClass *klass)
@@ -308,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",
@@ -384,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);
 }
@@ -417,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;
 	}
@@ -483,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)) {
@@ -495,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;
@@ -534,23 +374,42 @@
 
 	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;
@@ -575,11 +434,15 @@
 		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);
@@ -593,1127 +456,7 @@
 	}
 
 }
-#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)
 {
@@ -1722,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)
 {
@@ -1785,58 +520,27 @@
 	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);
@@ -1846,7 +550,7 @@
 
 static void
 purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
-				     FsCandidate *candidate)
+				     PurpleMediaCandidate *candidate)
 {
 	PurpleMediaStream *stream;
 
@@ -1869,322 +573,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_set_state(session->tee, GST_STATE_PLAYING);
-	gst_element_link(session->src, session->media->priv->confbin);
-
-	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);
-		gst_bin_add(GST_BIN(session->media->priv->confbin), volume);
-		gst_bin_add(GST_BIN(session->media->priv->confbin), level);
-		gst_element_set_state(level, GST_STATE_PLAYING);
-		gst_element_set_state(volume, GST_STATE_PLAYING);
-		gst_element_link(volume, level);
-		gst_element_link(session->tee, volume);
-		srcpad = gst_element_get_static_pad(level, "src");
-		g_object_set(volume, "volume", input_volume, NULL);
-	} else {
-		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 (g_signal_has_handler_pending(media,
-					purple_media_signals[LEVEL], 0, FALSE)
-					&& gst_structure_has_name(
-					gst_message_get_structure(msg), "level")) {
-				GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
-				gchar *name;
-				gchar *participant = NULL;
-				PurpleMediaSession *session = NULL;
-				gdouble rms_db;
-				gdouble percent;
-				const GValue *list;
-				const GValue *value;
-
-				if (!PURPLE_IS_MEDIA(media) ||
-						GST_ELEMENT_PARENT(src) !=
-						media->priv->confbin)
-					break;
-
-				name = gst_element_get_name(src);
-				if (!strncmp(name, "sendlevel_", 10)) {
-					session = purple_media_get_session(
-							media, name+10);
-				} else {
-					GList *iter = media->priv->streams;
-					for (; iter; iter = g_list_next(iter)) {
-						PurpleMediaStream *stream = iter->data;
-						if (stream->level == src) {
-							session = stream->session;
-							participant = stream->participant;
-							break;
-						}
-					}
-				}
-				g_free(name);
-				if (!session)
-					break;
-
-				list = gst_structure_get_value(
-						gst_message_get_structure(msg), "rms");
-				value = gst_value_list_get_value(list, 0);
-				rms_db = g_value_get_double(value);
-				percent = pow(10, rms_db / 20) * 5;
-				if(percent > 1.0)
-					percent = 1.0;
-
-				g_signal_emit(media, purple_media_signals[LEVEL],
-						0, session->id, participant, percent);
-				break;
-			}
-			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);
-				switch (error_no) {
-					case FS_ERROR_NO_CODECS:
-						purple_media_error(media, _("No codecs found. Install some GStreamer codecs found in GStreamer plugins packages."));
-						purple_media_end(media, NULL, NULL);
-						break;
-					case FS_ERROR_NO_CODECS_LEFT:
-						purple_media_error(media, _("No codecs left. Your codec preferences in fs-codecs.conf are too strict."));
-						purple_media_end(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("media", "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(media, _("A non-recoverable Farsight2 error has occurred."));
-					purple_media_end(media, NULL, NULL);
-				}
-			} 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;
-		}
-		case GST_MESSAGE_ERROR: {
-			GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(msg));
-			GstElement *lastElement = NULL;
-			while (!GST_IS_PIPELINE(element)) {
-				if (element == media->priv->confbin) {
-					purple_media_error(media, _("Conference error"));
-					purple_media_end(media, NULL, NULL);
-					break;
-				}
-				lastElement = element;
-				element = GST_ELEMENT_PARENT(element);
-			}
-			if (GST_IS_PIPELINE(element)) {
-				GList *sessions = g_hash_table_get_values(media->priv->sessions);
-				for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
-					PurpleMediaSession *session = sessions->data;
-
-					if (session->src == lastElement) {
-						if (session->type & PURPLE_MEDIA_AUDIO)
-							purple_media_error(media, _("Error with your microphone"));
-						else
-							purple_media_error(media, _("Error with your webcam"));
-						purple_media_end(media, NULL, NULL);
-						break;
-					}
-				}
-				g_list_free(sessions);
-			}
-		}
-		default:
-			break;
-	}
-
-	return TRUE;
-}
-#endif
-
 PurpleAccount *
 purple_media_get_account(PurpleMedia *media)
 {
@@ -2245,12 +652,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
 }
@@ -2264,6 +758,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));
@@ -2271,337 +831,189 @@
 		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);
+		}
 
-			if (stream->remote_candidates != NULL &&
-					stream->initiator == FALSE) {
-				GError *err = NULL;
-				fs_stream_set_remote_candidates(stream->stream,
-						stream->remote_candidates, &err);
+		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;
 
-				if (err) {
-					purple_debug_error("media", "Error adding remote"
-							" candidates: %s\n", err->message);
-					g_error_free(err);
-				}
+			if (media->priv->sessions != NULL)
+				sessions = g_hash_table_get_values(
+					media->priv->sessions);
+
+			/* Emit for sessions */
+			for (; sessions; sessions = g_list_delete_link(
+					sessions, sessions)) {
+				PurpleMediaSession *session = sessions->data;
+
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type,
+						session->id, NULL, local);
 			}
-		}
-	} else if (local == TRUE && (type == PURPLE_MEDIA_INFO_MUTE ||
-			type == PURPLE_MEDIA_INFO_UNMUTE)) {
-		GList *sessions;
-		gboolean active = (type == PURPLE_MEDIA_INFO_MUTE);
+
+			/* Emit for participants */
+			for (; participants; participants =
+					g_list_next(participants)) {
+				gchar *participant = participants->data;
 
-		g_return_if_fail(PURPLE_IS_MEDIA(media));
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type,
+						NULL, participant, local);
+			}
 
-		if (session_id == NULL)
-			sessions = g_hash_table_get_values(
-					media->priv->sessions);
-		else
-			sessions = g_list_prepend(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));
+					media, session_id);
 
-		purple_debug_info("media", "Turning mute %s\n",
-				active ? "on" : "off");
-
-		for (; sessions; sessions = g_list_delete_link(
-				sessions, sessions)) {
-			PurpleMediaSession *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(session->media->
-						priv->confbin), name);
-				g_free(name);
-				g_object_set(volume, "mute", active, NULL);
+			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);
 			}
 		}
-	} else if (local == TRUE && (type == PURPLE_MEDIA_INFO_PAUSE ||
-			type == PURPLE_MEDIA_INFO_UNPAUSE)) {
-		gboolean active = (type == PURPLE_MEDIA_INFO_PAUSE);
-		GList *streams = purple_media_get_streams(media,
-				session_id, participant);
-		for (; streams; streams = g_list_delete_link(streams, streams)) {
-			PurpleMediaStream *stream = streams->data;
-			if (stream->session->type & PURPLE_MEDIA_SEND_VIDEO) {
-				stream->paused = active;
 
-				if (!stream->held)
-					g_object_set(stream->stream, "direction",
-							purple_media_to_fs_stream_direction(
-							stream->session->type & ((active) ?
-							~PURPLE_MEDIA_SEND_VIDEO :
-							PURPLE_MEDIA_VIDEO)), NULL);
-			}
-		}
-	} else if (local == TRUE && (type == PURPLE_MEDIA_INFO_HOLD ||
-			type == PURPLE_MEDIA_INFO_UNHOLD)) {
-		GList *streams;
-		gboolean active = (type == PURPLE_MEDIA_INFO_HOLD);
-
-		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;
-			stream->held = active;
-			if (stream->session->type & PURPLE_MEDIA_VIDEO) {
-				FsStreamDirection direction;
-
-				direction = ((active) ?
-						~PURPLE_MEDIA_VIDEO :
-						PURPLE_MEDIA_VIDEO);
-				if (!active && stream->paused)
-					direction &= ~PURPLE_MEDIA_SEND_VIDEO;
-
-				g_object_set(stream->stream, "direction",
-						purple_media_to_fs_stream_direction(
-						stream->session->type & direction), NULL);
-			} else if (stream->session->type & PURPLE_MEDIA_AUDIO) {
-				g_object_set(stream->stream, "direction",
-						purple_media_to_fs_stream_direction(
-						stream->session->type & ((active) ?
-						~PURPLE_MEDIA_AUDIO :
-						PURPLE_MEDIA_AUDIO)), NULL);
-			}
-		}
+		purple_media_end(media, session_id, participant);
+		return;
 	}
 
 	g_signal_emit(media, purple_media_signals[STREAM_INFO],
 			0, type, session_id, participant, local);
-
-	if (type == PURPLE_MEDIA_INFO_HANGUP ||
-			type == PURPLE_MEDIA_INFO_REJECT) {
-		purple_media_end(media, session_id, participant);
-	}
 #endif
 }
 
 #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) {
-			GstElement *queue = NULL;
-			double output_volume = purple_prefs_get_int(
-					"/purple/media/audio/volume/output")/10.0;
-			/*
-			 * Should this instead be:
-			 *  audioconvert ! audioresample ! liveadder !
-			 *   audioresample ! audioconvert ! realsink
-			 */
-			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(priv->manager,
-					PURPLE_MEDIA_RECV_AUDIO,
-					stream->session->media,
-					stream->session->id,
-					stream->participant);
-			gst_bin_add(GST_BIN(priv->confbin), 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(queue, GST_STATE_PLAYING);
-			gst_element_link(stream->level, sink);
-			gst_element_link(stream->volume, stream->level);
-			gst_element_link(queue, stream->volume);
-			sink = 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->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)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 */
 
@@ -2613,97 +1025,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"), 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;
@@ -2713,152 +1049,23 @@
 		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
 				0, PURPLE_MEDIA_STATE_NEW,
 				session->id, NULL);
-
-		if (type_direction & FS_DIRECTION_SEND) {
-			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();
-		guint new_num_params =
-					!stun_ip && !turn_ip ? num_params + 1 :
-					(stun_ip && is_nice) && turn_ip ?
-					num_params + 3 : num_params + 2;
-		guint next_param_index = num_params;
-		GParameter *param = g_new0(GParameter, new_num_params);
-		memcpy(param, params, sizeof(GParameter) * num_params);
-
-		/* set controlling mode according to direction */
-		param[next_param_index].name = "controlling-mode";
-		g_value_init(&param[next_param_index].value, G_TYPE_BOOLEAN);
-		g_value_set_boolean(&param[next_param_index].value, initiator);
-		next_param_index++;
-		
-		if (stun_ip || turn_ip) {
-			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, initiator == TRUE ?
-					type_direction : (type_direction &
-					FS_DIRECTION_RECV), transmitter,
-					new_num_params, param, &err);
-		g_free(param);
-
-		if (fsstream == NULL) {
-			purple_debug_error("media",
-					"Error creating stream: %s\n",
-					err && err->message ?
-					err->message : "NULL");
-			if (err)
-				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;
@@ -2893,22 +1100,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
@@ -2919,11 +1113,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
@@ -2936,7 +1129,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);
@@ -2951,18 +1143,10 @@
 	}
 
 	stream->remote_candidates = g_list_concat(stream->remote_candidates,
-			purple_media_candidate_list_to_fs(remote_candidates));
-
-	if (stream->initiator == TRUE || stream->accepted == TRUE) {
-		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
 }
 
@@ -2980,7 +1164,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;
@@ -2995,7 +1179,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_remote_candidates);
 #else
 	return NULL;
@@ -3008,29 +1192,10 @@
                                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
@@ -3067,27 +1232,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
@@ -3097,40 +1245,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;
-		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(media->priv->sessions);
-		for (; values; values = g_list_delete_link(values, values)) {
-			PurpleMediaSession *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;
+	return purple_media_backend_codecs_ready(
+			media->priv->backend, sess_id);
 #else
 	return FALSE;
 #endif
@@ -3206,31 +1324,13 @@
 		const gchar *session_id, double level)
 {
 #ifdef USE_VV
-	GList *sessions;
-
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	purple_prefs_set_int("/purple/media/audio/volume/input", level);
-
-	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) {
-			gchar *name = g_strdup_printf("volume_%s",
-					session->id);
-			GstElement *volume = gst_bin_get_by_name(
-					GST_BIN(session->media->priv->confbin),
-					name);
-			g_free(name);
-			g_object_set(volume, "volume", level/10.0, NULL);
-		}
-	}
+	purple_media_backend_fs2_set_input_volume(
+			PURPLE_MEDIA_BACKEND_FS2(
+			media->priv->backend),
+			session_id, level);
 #endif
 }
 
@@ -3239,23 +1339,13 @@
 		double level)
 {
 #ifdef USE_VV
-	GList *streams;
-
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	purple_prefs_set_int("/purple/media/audio/volume/output", level);
-
-	streams = purple_media_get_streams(media,
-			session_id, participant);
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
 
-	for (; streams; streams = g_list_delete_link(streams, streams)) {
-		PurpleMediaStream *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);
-		}
-	}
+	purple_media_backend_fs2_set_output_volume(
+			PURPLE_MEDIA_BACKEND_FS2(
+			media->priv->backend),
+			session_id, participant, level);
 #endif
 }
 
@@ -3303,16 +1393,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	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/media.h	Mon Mar 08 22:53:02 2010 +0000
@@ -27,26 +27,15 @@
 #ifndef _PURPLE_MEDIA_H_
 #define _PURPLE_MEDIA_H_
 
+#include "media/candidate.h"
+#include "media/codec.h"
+#include "media/enum-types.h"
+
 #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))
-
-#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,79 +43,8 @@
 #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())
-
 /** An opaque structure representing a media call. */
 typedef struct _PurpleMedia PurpleMedia;
-/** An opaque structure representing a network candidate (IP Address and port pair). */
-typedef struct _PurpleMediaCandidate PurpleMediaCandidate;
-/** An opaque structure representing an audio or video codec. */
-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_PAUSE,
-	PURPLE_MEDIA_INFO_UNPAUSE,
-	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"
@@ -136,33 +54,6 @@
 #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);
-
-/**
  * Gets the media class's GType
  *
  * @return The media class's GType.
@@ -172,187 +63,6 @@
 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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/backend-fs2.c	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,2038 @@
+/**
+ * @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);
+
+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;
+
+	GList *local_candidates;
+	GList *remote_candidates;
+
+	guint connected_cb_id;
+};
+
+struct _PurpleMediaBackendFs2Session
+{
+	PurpleMediaBackendFs2 *backend;
+	gchar *id;
+	FsSession *session;
+
+	GstElement *src;
+	GstElement *tee;
+
+	PurpleMediaSessionType type;
+};
+
+struct _PurpleMediaBackendFs2Private
+{
+	PurpleMedia *media;
+	GstElement *confbin;
+	FsConference *conference;
+	gchar *conference_type;
+
+	GHashTable *sessions;
+	GHashTable *participants;
+
+	GList *streams;
+};
+
+enum {
+	PROP_0,
+	PROP_CONFERENCE_TYPE,
+	PROP_MEDIA,
+};
+
+static void
+purple_media_backend_fs2_init(PurpleMediaBackendFs2 *self)
+{
+}
+
+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));
+
+		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) {
+		GList *participants =
+				g_hash_table_get_values(priv->participants);
+		for (; participants; participants = g_list_delete_link(
+				participants, participants))
+			g_object_unref(participants->data);
+		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;
+
+		/* 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);
+	}
+
+	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;
+			g_free(session->id);
+			g_free(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;
+}
+
+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 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 (g_signal_has_handler_pending(priv->media, level_id, 0, FALSE)
+			&& gst_structure_has_name(
+			gst_message_get_structure(msg), "level")) {
+		GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
+		gchar *name;
+		gchar *participant = NULL;
+		PurpleMediaBackendFs2Session *session = NULL;
+		gdouble rms_db;
+		gdouble percent;
+		const GValue *list;
+		const GValue *value;
+
+		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);
+		} else {
+			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;
+				}
+			}
+		}
+
+		g_free(name);
+
+		if (!session)
+			return;
+
+		list = gst_structure_get_value(
+				gst_message_get_structure(msg), "rms");
+		value = gst_value_list_get_value(list, 0);
+		rms_db = g_value_get_double(value);
+		percent = pow(10, rms_db / 20) * 5;
+
+		if(percent > 1.0)
+			percent = 1.0;
+
+		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;
+
+	while (!GST_IS_PIPELINE(element)) {
+		if (element == priv->confbin)
+			break;
+
+		lastElement = element;
+		element = GST_ELEMENT_PARENT(element);
+	}
+
+	if (!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
+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)
+{
+	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_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;
+	}
+
+	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;
+
+	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");
+		GstPad *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);
+
+	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);
+		gst_bin_add(GST_BIN(priv->confbin), volume);
+		gst_bin_add(GST_BIN(priv->confbin), level);
+		gst_element_set_state(level, GST_STATE_PLAYING);
+		gst_element_set_state(volume, GST_STATE_PLAYING);
+		gst_element_link(volume, level);
+		gst_element_link(session->tee, volume);
+		srcpad = gst_element_get_static_pad(level, "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_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(g_str_hash, g_str_equal);
+	}
+
+	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 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, NULL);
+	}
+
+	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) {
+			GstElement *queue = NULL;
+			double output_volume = purple_prefs_get_int(
+					"/purple/media/audio/volume/output")/10.0;
+			/*
+			 * Should this instead be:
+			 *  audioconvert ! audioresample ! liveadder !
+			 *   audioresample ! audioconvert ! realsink
+			 */
+			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), 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(queue, GST_STATE_PLAYING);
+			gst_element_link(stream->level, sink);
+			gst_element_link(stream->volume, stream->level);
+			gst_element_link(queue, stream->volume);
+			sink = 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->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 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;
+
+	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)) {
+		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 (!turn_setup) {
+			purple_debug_error("backend-fs2",
+					"Error creating relay info structure");
+			return FALSE;
+		}
+
+		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("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);
+	}
+
+	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 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;
+
+	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)
+{
+	PurpleMediaBackendFs2Private *priv;
+	PurpleMediaBackendFs2Session *session;
+	GList *fscodecs;
+	GList *codecs;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL);
+
+	priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self);
+
+	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;
+}
+#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
+	PurpleMediaBackendFs2Private *priv;
+	GList *streams;
+
+	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/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	Mon Mar 08 22:53:02 2010 +0000
@@ -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
+ *
+ * @since 2.7.0
+ */
+GType purple_media_backend_fs2_get_type(void);
+
+/*
+ * 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 */
+
+G_END_DECLS
+
+#endif /* _MEDIA_BACKEND_FS2_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/backend-iface.c	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,194 @@
+/**
+ * @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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/backend-iface.h	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,196 @@
+/**
+ * @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);
+};
+
+/**
+ * Gets the media backend's GType.
+ *
+ * @return The media backend's GType.
+ *
+ * @since 2.7.0
+ */
+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.
+ *
+ * @since 2.7.0
+ */
+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.
+ *
+ * @since 2.7.0
+ */
+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.
+ *
+ * @since 2.7.0
+ */
+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.
+ *
+ * @since 2.7.0
+ */
+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.
+ *
+ * @since 2.7.0
+ */
+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.
+ *
+ * @since 2.7.0
+ */
+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.
+ *
+ * @since 2.7.0
+ */
+gboolean purple_media_backend_set_send_codec(PurpleMediaBackend *self,
+		const gchar *sess_id, PurpleMediaCodec *codec);
+
+G_END_DECLS
+
+#endif /* _MEDIA_BACKEND_IFACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/candidate.c	Mon Mar 08 22:53:02 2010 +0000
@@ -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	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,252 @@
+/**
+ * @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
+ *
+ * @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 PurpleMediaCandidate.
+ *
+ * @param candidate The candidate to copy.
+ *
+ * @return The copy of the PurpleMediaCandidate.
+ *
+ * @since 2.7.0
+ */
+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.
+ *
+ * @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);
+
+/**
+ * Gets the foundation (identifier) from the candidate.
+ *
+ * @param candidate The candidate to get the foundation from.
+ *
+ * @return The foundation.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+gchar *purple_media_candidate_get_ip(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the port.
+ *
+ * @param candidate The candidate to get the port from.
+ *
+ * @return The port.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+PurpleMediaNetworkProtocol purple_media_candidate_get_protocol(
+		PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the priority.
+ *
+ * @param candidate The candidate to get the priority from.
+ *
+ * @return The priority.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+gchar *purple_media_candidate_get_password(PurpleMediaCandidate *candidate);
+
+/**
+ * Gets the TTL.
+ *
+ * @param The candidate to get the TTL from.
+ *
+ * @return The TTL.
+ *
+ * @since 2.6.0
+ */
+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	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,419 @@
+/**
+ * @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)) {
+		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));
+}
+
+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);
+	g_free(param);
+
+	priv->optional_params =
+			g_list_remove(priv->optional_params, 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	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,213 @@
+/**
+ * @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
+ *
+ * @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);
+
+/**
+ * Gets the codec id.
+ *
+ * @param The codec to get the id from.
+ *
+ * @return The codec id.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @since 2.6.0
+ */
+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.
+ *
+ * @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 PurpleMediaCodec object.
+ *
+ * @param codec The codec to copy.
+ *
+ * @return The copy of the codec.
+ *
+ * @since 2.7.0
+ */
+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.
+ *
+ * @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);
+
+/**
+ * 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);
+
+G_END_DECLS
+
+#endif  /* _PURPLE_MEDIA_CODEC_H_ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media/enum-types.c	Mon Mar 08 22:53:02 2010 +0000
@@ -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	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,162 @@
+/**
+ * @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.
+ *
+ * @since 2.6.0
+ */
+GType purple_media_candidate_type_get_type(void);
+
+/**
+ * Gets the type of the media caps flags
+ *
+ * @return The media caps flags' GType
+ *
+ * @since 2.7.0
+ */
+GType purple_media_caps_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 media network protocol's GType
+ *
+ * @return The media network protocol's GType.
+ *
+ * @since 2.6.0
+ */
+GType purple_media_network_protocol_get_type(void);
+
+/**
+ * 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 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);
+
+G_END_DECLS
+
+#endif  /* _PURPLE_MEDIA_ENUM_TYPES_ */
--- a/libpurple/mediamanager.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/mediamanager.c	Mon Mar 08 22:53:02 2010 +0000
@@ -37,8 +37,8 @@
 #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>
 
@@ -80,6 +80,7 @@
 	GList *elements;
 	GList *output_windows;
 	gulong next_output_window_id;
+	GType backend_type;
 
 	PurpleMediaElementInfo *video_src;
 	PurpleMediaElementInfo *video_sink;
@@ -100,6 +101,7 @@
 
 enum {
 	INIT_MEDIA,
+	UI_CAPS_CHANGED,
 	LAST_SIGNAL
 };
 static guint purple_media_manager_signals[LAST_SIGNAL] = {0};
@@ -148,6 +150,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));
 }
 
@@ -157,6 +168,9 @@
 	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");
@@ -304,34 +318,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);
 
@@ -894,8 +889,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
 }
 
@@ -911,6 +915,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
 
 /*
--- a/libpurple/mediamanager.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/mediamanager.h	Mon Mar 08 22:53:02 2010 +0000
@@ -213,6 +213,28 @@
  */
 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.
+ *
+ * @since 2.7.0
+ */
+void purple_media_manager_set_backend_type(PurpleMediaManager *manager,
+		GType backend_type);
+
+/**
+ * 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.
+ *
+ * @since 2.7.0
+ */
+GType purple_media_manager_get_backend_type(PurpleMediaManager *manager);
+
 /*}@*/
 
 #ifdef __cplusplus
--- a/libpurple/network.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/network.c	Mon Mar 08 22:53:02 2010 +0000
@@ -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
@@ -160,7 +163,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 +203,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)
 {
--- a/libpurple/network.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/network.h	Mon Mar 08 22:53:02 2010 +0000
@@ -88,6 +88,17 @@
 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, this function currently only
+ *       handles IPv4 addresses
+ * @since 2.7.0
+ *
+ * @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).
--- a/libpurple/plugin.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugin.c	Mon Mar 08 22:53:02 2010 +0000
@@ -254,11 +254,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)
 		{
@@ -287,11 +283,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)
 			{
--- a/libpurple/plugins/autoaccept.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugins/autoaccept.c	Mon Mar 08 22:53:02 2010 +0000
@@ -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>
--- a/libpurple/plugins/log_reader.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugins/log_reader.c	Mon Mar 08 22:53:02 2010 +0000
@@ -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/perl/common/Request.xs	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugins/perl/common/Request.xs	Mon Mar 08 22:53:02 2010 +0000
@@ -380,6 +380,13 @@
 	void * data
 
 void
+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
 purple_request_field_list_add_selected(field, item)
 	Purple::Request::Field field
 	const char *item
--- a/libpurple/plugins/perl/common/module.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugins/perl/common/module.h	Mon Mar 08 22:53:02 2010 +0000
@@ -9,6 +9,7 @@
 #include <glib.h>
 #ifdef _WIN32
 #undef pipe
+#undef STRINGIFY
 #endif
 #include <EXTERN.h>
 #include <perl.h>
--- a/libpurple/plugins/perl/perl-common.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugins/perl/perl-common.c	Mon Mar 08 22:53:02 2010 +0000
@@ -472,74 +472,74 @@
 }
 
 SV *
-purple_perl_sv_from_vargs(const PurpleValue *value, va_list args, void ***copy_arg)
+purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_arg)
 {
 	if (purple_value_is_outgoing(value)) {
 		switch (purple_value_get_type(value)) {
 			case PURPLE_TYPE_SUBTYPE:
-				if ((*copy_arg = va_arg(args, void **)) == NULL)
+				if ((*copy_arg = va_arg(*args, void **)) == NULL)
 					return &PL_sv_undef;
 
 				return purple_perl_sv_from_subtype(value, *(void **)*copy_arg);
 
 			case PURPLE_TYPE_BOOLEAN:
-				if ((*copy_arg = (void *)va_arg(args, gboolean *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, gboolean *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(gboolean *)*copy_arg);
 
 			case PURPLE_TYPE_INT:
-				if ((*copy_arg = (void *)va_arg(args, int *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, int *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(int *)*copy_arg);
 
 			case PURPLE_TYPE_UINT:
-				if ((*copy_arg = (void *)va_arg(args, unsigned int *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, unsigned int *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVuv(*(unsigned int *)*copy_arg);
 
 			case PURPLE_TYPE_LONG:
-				if ((*copy_arg = (void *)va_arg(args, long *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, long *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(long *)*copy_arg);
 
 			case PURPLE_TYPE_ULONG:
-				if ((*copy_arg = (void *)va_arg(args,
+				if ((*copy_arg = (void *)va_arg(*args,
 												unsigned long *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVuv(*(unsigned long *)*copy_arg);
 
 			case PURPLE_TYPE_INT64:
-				if ((*copy_arg = (void *)va_arg(args, gint64 *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, gint64 *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv(*(gint64 *)*copy_arg);
 
 			case PURPLE_TYPE_UINT64:
-				if ((*copy_arg = (void *)va_arg(args, guint64 *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, guint64 *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVuv(*(guint64 *)*copy_arg);
 
 			case PURPLE_TYPE_STRING:
-				if ((*copy_arg = (void *)va_arg(args, char **)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, char **)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVGChar(*(char **)*copy_arg);
 
 			case PURPLE_TYPE_POINTER:
-				if ((*copy_arg = va_arg(args, void **)) == NULL)
+				if ((*copy_arg = va_arg(*args, void **)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv((IV)*(void **)*copy_arg);
 
 			case PURPLE_TYPE_BOXED:
 				/* Uh.. I dunno. Try this? */
-				if ((*copy_arg = va_arg(args, void **)) == NULL)
+				if ((*copy_arg = va_arg(*args, void **)) == NULL)
 					return &PL_sv_undef;
 
 				return sv_2mortal(purple_perl_bless_object(
@@ -553,40 +553,40 @@
 	} else {
 		switch (purple_value_get_type(value)) {
 			case PURPLE_TYPE_SUBTYPE:
-				if ((*copy_arg = va_arg(args, void *)) == NULL)
+				if ((*copy_arg = va_arg(*args, void *)) == NULL)
 					return &PL_sv_undef;
 
 				return purple_perl_sv_from_subtype(value, *copy_arg);
 
 			case PURPLE_TYPE_BOOLEAN:
-				*copy_arg = GINT_TO_POINTER( va_arg(args, gboolean) );
+				*copy_arg = GINT_TO_POINTER( va_arg(*args, gboolean) );
 
 				return newSViv((gboolean)GPOINTER_TO_INT(*copy_arg));
 
 			case PURPLE_TYPE_INT:
-				*copy_arg = GINT_TO_POINTER( va_arg(args, int) );
+				*copy_arg = GINT_TO_POINTER( va_arg(*args, int) );
 
 				return newSViv(GPOINTER_TO_INT(*copy_arg));
 
 			case PURPLE_TYPE_UINT:
-				*copy_arg = GUINT_TO_POINTER(va_arg(args, unsigned int));
+				*copy_arg = GUINT_TO_POINTER(va_arg(*args, unsigned int));
 
 				return newSVuv(GPOINTER_TO_UINT(*copy_arg));
 
 			case PURPLE_TYPE_LONG:
-				*copy_arg = (void *)va_arg(args, long);
+				*copy_arg = (void *)va_arg(*args, long);
 
 				return newSViv((long)*copy_arg);
 
 			case PURPLE_TYPE_ULONG:
-				*copy_arg = (void *)va_arg(args, unsigned long);
+				*copy_arg = (void *)va_arg(*args, unsigned long);
 
 				return newSVuv((unsigned long)*copy_arg);
 
 			case PURPLE_TYPE_INT64:
 #if 0
 				/* XXX This yells and complains. */
-				*copy_arg = va_arg(args, gint64);
+				*copy_arg = va_arg(*args, gint64);
 
 				return newSViv(*copy_arg);
 #endif
@@ -595,27 +595,27 @@
 			case PURPLE_TYPE_UINT64:
 				/* XXX This also yells and complains. */
 #if 0
-				*copy_arg = (void *)va_arg(args, guint64);
+				*copy_arg = (void *)va_arg(*args, guint64);
 
 				return newSVuv(*copy_arg);
 #endif
 				break;
 
 			case PURPLE_TYPE_STRING:
-				if ((*copy_arg = (void *)va_arg(args, char *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, char *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSVGChar((char *)*copy_arg);
 
 			case PURPLE_TYPE_POINTER:
-				if ((*copy_arg = (void *)va_arg(args, void *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL)
 					return &PL_sv_undef;
 
 				return newSViv((IV)*copy_arg);
 
 			case PURPLE_TYPE_BOXED:
 				/* Uh.. I dunno. Try this? */
-				if ((*copy_arg = (void *)va_arg(args, void *)) == NULL)
+				if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL)
 					return &PL_sv_undef;
 
 				return sv_2mortal(purple_perl_bless_object(*copy_arg,
--- a/libpurple/plugins/perl/perl-common.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugins/perl/perl-common.h	Mon Mar 08 22:53:02 2010 +0000
@@ -3,6 +3,7 @@
 
 #include <glib.h>
 #ifdef _WIN32
+#undef STRINGIFY
 #undef pipe
 #endif
 #include <EXTERN.h>
@@ -65,7 +66,7 @@
 #endif
 
 void *purple_perl_data_from_sv(PurpleValue *value, SV *sv);
-SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list args,
+SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args,
                             void ***copy_arg);
 SV *purple_perl_sv_from_fun(PurplePlugin *plugin, SV *callback);
 #endif /* _PURPLE_PERL_COMMON_H_ */
--- a/libpurple/plugins/perl/perl-handlers.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugins/perl/perl-handlers.c	Mon Mar 08 22:53:02 2010 +0000
@@ -298,7 +298,11 @@
 
 	for (i = 0; i < value_count; i++) {
 		sv_args[i] = purple_perl_sv_from_vargs(values[i],
-		                                       args,
+#ifdef VA_COPY_AS_ARRAY
+		                                       (va_list*)args,
+#else
+		                                       (va_list*)&args,
+#endif
 		                                       &copy_args[i]);
 
 		XPUSHs(sv_args[i]);
--- a/libpurple/plugins/perl/scripts/signals-test.pl	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugins/perl/scripts/signals-test.pl	Mon Mar 08 22:53:02 2010 +0000
@@ -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/ssl/Makefile.mingw	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/plugins/ssl/Makefile.mingw	Mon Mar 08 22:53:02 2010 +0000
@@ -15,14 +15,16 @@
 
 NEEDED_DLLS = \
 			$(NSS_TOP)/lib/freebl3.dll \
+			$(NSS_TOP)/lib/libnspr4.dll \
+			$(NSS_TOP)/lib/libplc4.dll \
+			$(NSS_TOP)/lib/libplds4.dll \
 			$(NSS_TOP)/lib/nss3.dll \
 			$(NSS_TOP)/lib/nssckbi.dll \
-			$(NSS_TOP)/lib/softokn3.dll \
+			$(NSS_TOP)/lib/nssutil3.dll \
 			$(NSS_TOP)/lib/smime3.dll \
-			$(NSS_TOP)/lib/ssl3.dll \
-			$(NSPR_TOP)/lib/nspr4.dll \
-			$(NSPR_TOP)/lib/plc4.dll \
-			$(NSPR_TOP)/lib/plds4.dll
+			$(NSS_TOP)/lib/softokn3.dll \
+                        $(NSS_TOP)/lib/sqlite3.dll \
+			$(NSS_TOP)/lib/ssl3.dll 
 
 ##
 ## INCLUDE PATHS
@@ -34,13 +36,11 @@
 			-I$(PURPLE_TOP) \
 			-I$(PURPLE_TOP)/win32 \
 			-I$(PIDGIN_TREE_TOP) \
-			-I$(NSS_TOP)/include \
-			-I$(NSPR_TOP)/include
+			-I$(NSS_TOP)/include
 
 LIB_PATHS +=		-L$(GTK_TOP)/lib \
 			-L$(PURPLE_TOP) \
-			-L$(NSS_TOP)/lib \
-			-L$(NSPR_TOP)/lib
+			-L$(NSS_TOP)/lib
 
 ##
 ##  SOURCES, OBJECTS
--- a/libpurple/protocols/bonjour/Makefile.mingw	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/bonjour/Makefile.mingw	Mon Mar 08 22:53:02 2010 +0000
@@ -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	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Mon Mar 08 22:53:02 2010 +0000
@@ -101,6 +101,8 @@
 
 	/* Start waiting for jabber connections (iChat style) */
 	bd->jabber_data = g_new0(BonjourJabber, 1);
+	bd->jabber_data->socket = -1;
+	bd->jabber_data->socket6 = -1;
 	bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
 	bd->jabber_data->account = account;
 
@@ -525,7 +527,8 @@
 	sizeof(PurplePluginProtocolInfo),                        /* struct_size */
 	NULL,                                                    /* get_account_text_table */
 	NULL,                                                    /* initiate_media */
-	NULL                                                     /* can_do_media */
+	NULL,                                                    /* get_media_caps */
+	NULL                                                     /* get_moods */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Mon Mar 08 22:53:02 2010 +0000
@@ -747,8 +747,7 @@
 	XepIq *iq;
 	xmlnode *query, *streamhost;
 	gchar *port;
-	const char *next_ip, *local_ip;
-	const char token [] = ";";
+	GSList *local_ips;
 	BonjourData *bd;
 
 	purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
@@ -773,17 +772,16 @@
 
 	xfer->local_port = purple_network_get_port_from_fd(sock);
 
-	local_ip = purple_network_get_my_ip_ext2(sock);
-	/* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */
-	next_ip = strtok((char *)local_ip, token);
+	local_ips = bonjour_jabber_get_local_ips(sock);
 
 	port = g_strdup_printf("%hu", xfer->local_port);
-	while(next_ip != NULL) {
+	while(local_ips) {
 		streamhost = xmlnode_new_child(query, "streamhost");
 		xmlnode_set_attrib(streamhost, "jid", xf->sid);
-		xmlnode_set_attrib(streamhost, "host", next_ip);
+		xmlnode_set_attrib(streamhost, "host", local_ips->data);
 		xmlnode_set_attrib(streamhost, "port", port);
-		next_ip = strtok(NULL, token);
+		g_free(local_ips->data);
+		local_ips = g_slist_delete_link(local_ips, local_ips);
 	}
 	g_free(port);
 
@@ -796,15 +794,17 @@
 	XepXfer *xf;
 	if(xfer == NULL)
 		return;
+
 	purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
 	xf = xfer->data;
+
 	purple_network_listen_map_external(FALSE);
 	xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
 						      bonjour_bytestreams_listen, xfer);
 	purple_network_listen_map_external(TRUE);
-	if (xf->listen_data == NULL) {
+	if (xf->listen_data == NULL)
 		purple_xfer_cancel_local(xfer);
-	}
+
 	return;
 }
 
--- a/libpurple/protocols/bonjour/jabber.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Mon Mar 08 22:53:02 2010 +0000
@@ -44,6 +44,11 @@
 #endif
 #include <fcntl.h>
 
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
+
 #include "network.h"
 #include "eventloop.h"
 #include "connection.h"
@@ -623,15 +628,22 @@
 
 }
 
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
 static void
 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
 {
 	BonjourJabber *jdata = data;
-	struct sockaddr_in their_addr; /* connector's address information */
-	socklen_t sin_size = sizeof(struct sockaddr);
+	struct sockaddr_storage their_addr; /* connector's address information */
+	socklen_t sin_size = sizeof(struct sockaddr_storage);
 	int client_socket;
 	int flags;
-	char *address_text = NULL;
+#ifdef HAVE_INET_NTOP
+	char addrstr[INET6_ADDRSTRLEN];
+#endif
+	const char *address_text;
 	struct _match_buddies_by_address_t *mbba;
 	BonjourJabberConversation *bconv;
 	GSList *buddies;
@@ -640,7 +652,9 @@
 	if (condition != PURPLE_INPUT_READ)
 		return;
 
-	if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1)
+	memset(&their_addr, 0, sin_size);
+
+	if ((client_socket = accept(server_socket, (struct sockaddr*)&their_addr, &sin_size)) == -1)
 		return;
 
 	flags = fcntl(client_socket, F_GETFL);
@@ -650,7 +664,16 @@
 #endif
 
 	/* Look for the buddy that has opened the conversation and fill information */
-	address_text = inet_ntoa(their_addr.sin_addr);
+#ifdef HAVE_INET_NTOP
+	if (their_addr.ss_family == AF_INET6)
+		address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in6 *)&their_addr)->sin6_addr,
+			addrstr, sizeof(addrstr));
+	else
+		address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in *)&their_addr)->sin_addr,
+			addrstr, sizeof(addrstr));
+#else
+	address_text = inet_ntoa(((struct sockaddr_in *)&their_addr)->sin_addr);
+#endif
 	purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
 	mbba = g_new0(struct _match_buddies_by_address_t, 1);
 	mbba->address = address_text;
@@ -680,52 +703,42 @@
 
 }
 
-gint
-bonjour_jabber_start(BonjourJabber *jdata)
+static int
+start_serversocket_listening(int port, int socket, struct sockaddr *addr, size_t addr_size, gboolean ip6, gboolean allow_port_fallback)
 {
-	struct sockaddr_in my_addr;
+	int ret_port = port;
 
-	/* Open a listening socket for incoming conversations */
-	jdata->socket = socket(PF_INET, SOCK_STREAM, 0);
-	if (jdata->socket < 0) {
-		gchar *buf = g_strdup_printf(_("Unable to create socket: %s"),
-				g_strerror(errno));
-		purple_connection_error_reason(jdata->account->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
-		g_free(buf);
-		return -1;
-	}
-
-	memset(&my_addr, 0, sizeof(struct sockaddr_in));
-	my_addr.sin_family = AF_INET;
+	purple_debug_info("bonjour", "Attempting to bind IPv%d socket to port %d.\n", ip6 ? 6 : 4, port);
 
 	/* Try to use the specified port - if it isn't available, use a random port */
-	my_addr.sin_port = htons(jdata->port);
-	if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
-	{
+	if (bind(socket, addr, addr_size) != 0) {
+
 		purple_debug_info("bonjour", "Unable to bind to specified "
-				"port %i: %s\n", jdata->port, g_strerror(errno));
-		my_addr.sin_port = 0;
-		if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
-		{
-			gchar *buf = g_strdup_printf(_("Unable to bind socket "
-					"to port: %s"), g_strerror(errno));
-			purple_connection_error_reason(jdata->account->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
-			g_free(buf);
+				"port %i: %s\n", port, g_strerror(errno));
+
+		if (!allow_port_fallback) {
+			purple_debug_warning("bonjour", "Not attempting random port assignment.\n");
 			return -1;
 		}
-		jdata->port = purple_network_get_port_from_fd(jdata->socket);
+#ifdef PF_INET6
+		if (ip6)
+			((struct sockaddr_in6 *) addr)->sin6_port = 0;
+		else
+#endif
+		((struct sockaddr_in *) addr)->sin_port = 0;
+
+		if (bind(socket, addr, addr_size) != 0) {
+			purple_debug_error("bonjour", "Unable to bind IPv%d socket to port: %s\n", ip6 ? 6 : 4, g_strerror(errno));
+			return -1;
+		}
+		ret_port = purple_network_get_port_from_fd(socket);
 	}
 
+	purple_debug_info("bonjour", "Bound IPv%d socket to port %d.\n", ip6 ? 6 : 4, ret_port);
+
 	/* Attempt to listen on the bound socket */
-	if (listen(jdata->socket, 10) != 0)
-	{
-		gchar *buf = g_strdup_printf(_("Unable to listen on socket: %s"),
-				g_strerror(errno));
-		purple_connection_error_reason(jdata->account->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf);
-		g_free(buf);
+	if (listen(socket, 10) != 0) {
+		purple_debug_error("bonjour", "Unable to listen on IPv%d socket: %s\n", ip6 ? 6 : 4, g_strerror(errno));
 		return -1;
 	}
 
@@ -739,8 +752,66 @@
 	}
 #endif
 
-	/* Open a watcher in the socket we have just opened */
-	jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+	return ret_port;
+}
+
+gint
+bonjour_jabber_start(BonjourJabber *jdata)
+{
+	int ipv6_port = -1, ipv4_port = -1;
+
+	/* Open a listening socket for incoming conversations */
+#ifdef PF_INET6
+	jdata->socket6 = socket(PF_INET6, SOCK_STREAM, 0);
+#endif
+	jdata->socket = socket(PF_INET, SOCK_STREAM, 0);
+	if (jdata->socket == -1 && jdata->socket6 == -1) {
+		purple_debug_error("bonjour", "Unable to create socket: %s",
+				g_strerror(errno));
+		return -1;
+	}
+
+#ifdef PF_INET6
+	if (jdata->socket6 != -1) {
+		struct sockaddr_in6 addr6;
+	        memset(&addr6, 0, sizeof(addr6));
+		addr6.sin6_family = AF_INET6;
+		addr6.sin6_port = htons(jdata->port);
+      		addr6.sin6_addr = in6addr_any;
+		ipv6_port = start_serversocket_listening(jdata->port, jdata->socket6, (struct sockaddr *) &addr6, sizeof(addr6), TRUE, TRUE);
+		/* Open a watcher in the socket we have just opened */
+		if (ipv6_port > 0) {
+			jdata->watcher_id6 = purple_input_add(jdata->socket6, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+			jdata->port = ipv6_port;
+		} else {
+			purple_debug_error("bonjour", "Failed to start listening on IPv6 socket.\n");
+			close(jdata->socket6);
+			jdata->socket6 = -1;
+		}
+	}
+#endif
+	if (jdata->socket != -1) {
+		struct sockaddr_in addr4;
+		memset(&addr4, 0, sizeof(addr4));
+		addr4.sin_family = AF_INET;
+		addr4.sin_port = htons(jdata->port);
+		ipv4_port = start_serversocket_listening(jdata->port, jdata->socket, (struct sockaddr *) &addr4, sizeof(addr4), FALSE, ipv6_port != -1);
+		/* Open a watcher in the socket we have just opened */
+		if (ipv4_port > 0) {
+			jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
+			jdata->port = ipv4_port;
+		} else {
+			purple_debug_error("bonjour", "Failed to start listening on IPv4 socket.\n");
+			close(jdata->socket);
+			jdata->socket = -1;
+		}
+	}
+
+	if (!(ipv6_port > 0 || ipv4_port > 0)) {
+		purple_debug_error("bonjour", "Unable to listen on socket: %s",
+				g_strerror(errno));
+		return -1;
+	}
 
 	return jdata->port;
 }
@@ -1101,6 +1172,10 @@
 		close(jdata->socket);
 	if (jdata->watcher_id > 0)
 		purple_input_remove(jdata->watcher_id);
+	if (jdata->socket6 >= 0)
+		close(jdata->socket6);
+	if (jdata->watcher_id6 > 0)
+		purple_input_remove(jdata->watcher_id6);
 
 	/* Close all the conversation sockets and remove all the watchers after sending end streams */
 	if (jdata->account->gc != NULL) {
@@ -1234,58 +1309,97 @@
 	return (ret >= 0) ? 0 : -1;
 }
 
-/* This returns a ';' delimited string containing all non-localhost IPs */
-const char *
-purple_network_get_my_ip_ext2(int fd)
+/* This returns a list containing all non-localhost IPs */
+GSList *
+bonjour_jabber_get_local_ips(int fd)
 {
-	char buffer[1024];
-	static char ip_ext[17 * 10];
+	GSList *ips = NULL;
+	const char *address_text;
+	int ret;
+
+#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
+	{
+	struct ifaddrs *ifap, *ifa;
+	struct sockaddr *addr;
+	char addrstr[INET6_ADDRSTRLEN];
+
+	ret = getifaddrs(&ifap);
+	if (ret != 0) {
+		const char *error = g_strerror(errno);
+		purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
+		return NULL;
+	}
+
+	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+		if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
+			continue;
+
+		addr = ifa->ifa_addr;
+		address_text = NULL;
+		switch (addr->sa_family) {
+			case AF_INET:
+				address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr,
+					addrstr, sizeof(addrstr));
+				break;
+#ifdef PF_INET6
+			case AF_INET6:
+				address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr,
+					addrstr, sizeof(addrstr));
+				break;
+#endif
+		}
+
+		if (address_text != NULL) {
+			if (addr->sa_family == AF_INET)
+				ips = g_slist_append(ips, g_strdup(address_text));
+			else
+				ips = g_slist_prepend(ips, g_strdup(address_text));
+		}
+	}
+
+	freeifaddrs(ifap);
+
+	}
+#else
+	{
 	char *tmp;
-	char *tip;
 	struct ifconf ifc;
 	struct ifreq *ifr;
+	char buffer[1024];
 	struct sockaddr_in *sinptr;
-	guint32 lhost = htonl(127 * 256 * 256 * 256 + 1);
-	long unsigned int add;
 	int source = fd;
-	int len, count = 0;
 
 	if (fd < 0)
 		source = socket(PF_INET, SOCK_STREAM, 0);
 
 	ifc.ifc_len = sizeof(buffer);
 	ifc.ifc_req = (struct ifreq *)buffer;
-	ioctl(source, SIOCGIFCONF, &ifc);
+	ret = ioctl(source, SIOCGIFCONF, &ifc);
 
 	if (fd < 0)
 		close(source);
 
-	memset(ip_ext, 0, sizeof(ip_ext));
-	memcpy(ip_ext, "0.0.0.0", 7);
+	if (ret < 0) {
+		const char *error = g_strerror(errno);
+		purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
+		return NULL;
+	}
+
 	tmp = buffer;
-	tip = ip_ext;
-	while (tmp < buffer + ifc.ifc_len && count < 10)
-	{
+	while (tmp < buffer + ifc.ifc_len) {
 		ifr = (struct ifreq *)tmp;
 		tmp += HX_SIZE_OF_IFREQ(*ifr);
 
-		if (ifr->ifr_addr.sa_family == AF_INET)
-		{
+		if (ifr->ifr_addr.sa_family == AF_INET) {
 			sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
-			if (sinptr->sin_addr.s_addr != lhost)
-			{
-				add = ntohl(sinptr->sin_addr.s_addr);
-				len = g_snprintf(tip, 17, "%lu.%lu.%lu.%lu;",
-					((add >> 24) & 255),
-					((add >> 16) & 255),
-					((add >> 8) & 255),
-					add & 255);
-				tip = &tip[len];
-				count++;
-				continue;
+			if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) {
+				address_text = inet_ntoa(sinptr->sin_addr);
+				ips = g_slist_prepend(ips, g_strdup(address_text));
 			}
-		}
+ 		}
 	}
+	}
+#endif
 
-	return ip_ext;
+	return ips;
 }
--- a/libpurple/protocols/bonjour/jabber.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Mon Mar 08 22:53:02 2010 +0000
@@ -37,7 +37,9 @@
 {
 	gint port;
 	gint socket;
+	gint socket6;
 	gint watcher_id;
+	gint watcher_id6;
 	PurpleAccount *account;
 	GSList *pending_conversations;
 } BonjourJabber;
@@ -105,6 +107,6 @@
 
 XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id);
 int xep_iq_send_and_free(XepIq *iq);
-const char *purple_network_get_my_ip_ext2(int fd);
+GSList * bonjour_jabber_get_local_ips(int fd);
 
 #endif /* _BONJOUR_JABBER_H_ */
--- a/libpurple/protocols/bonjour/mdns_avahi.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/bonjour/mdns_avahi.c	Mon Mar 08 22:53:02 2010 +0000
@@ -189,8 +189,12 @@
 					bb->ips = g_slist_remove(bb->ips, rd->ip);
 					g_free((gchar *) rd->ip);
 				}
-				bb->ips = g_slist_prepend(bb->ips, g_strdup(ip));
-				rd->ip = bb->ips->data;
+				rd->ip = g_strdup(ip);
+				/* IPv6 goes at the front of the list and IPv4 at the end so that we "prefer" IPv6, if present */
+				if (protocol == AVAHI_PROTO_INET6)
+					bb->ips = g_slist_prepend(bb->ips, (gchar *) rd->ip);
+				else
+					bb->ips = g_slist_append(bb->ips, (gchar *) rd->ip);
 			}
 
 			bb->port_p2pj = port;
@@ -249,7 +253,7 @@
 			/* Make sure it isn't us */
 			if (purple_utf8_strcasecmp(name, account->username) != 0) {
 				if (!avahi_service_resolver_new(avahi_service_browser_get_client(b),
-						interface, protocol, name, type, domain, AVAHI_PROTO_INET,
+						interface, protocol, name, type, domain, protocol,
 						0, _resolver_callback, account)) {
 					purple_debug_warning("bonjour", "_browser_callback -- Error initiating resolver: %s\n",
 						avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
@@ -448,14 +452,14 @@
 		case PUBLISH_START:
 			publish_result = avahi_entry_group_add_service_strlst(
 				idata->group, AVAHI_IF_UNSPEC,
-				AVAHI_PROTO_INET, 0,
+				AVAHI_PROTO_UNSPEC, 0,
 				purple_account_get_username(data->account),
 				LINK_LOCAL_RECORD_NAME, NULL, NULL, data->port_p2pj, lst);
 			break;
 		case PUBLISH_UPDATE:
 			publish_result = avahi_entry_group_update_service_txt_strlst(
 				idata->group, AVAHI_IF_UNSPEC,
-				AVAHI_PROTO_INET, 0,
+				AVAHI_PROTO_UNSPEC, 0,
 				purple_account_get_username(data->account),
 				LINK_LOCAL_RECORD_NAME, NULL, lst);
 			break;
@@ -487,7 +491,7 @@
 
 	g_return_val_if_fail(idata != NULL, FALSE);
 
-	idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account);
+	idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account);
 	if (!idata->sb) {
 
 		purple_debug_error("bonjour",
@@ -533,7 +537,7 @@
 				purple_account_get_username(data->account));
 
 		ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC,
-			AVAHI_PROTO_INET, flags, svc_name,
+			AVAHI_PROTO_UNSPEC, flags, svc_name,
 			AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len);
 
 		g_free(svc_name);
@@ -622,7 +626,7 @@
 
 	name = g_strdup_printf("%s." LINK_LOCAL_RECORD_NAME "local", buddy->name);
 	idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC,
-		AVAHI_PROTO_INET, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
+		AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0,
 		_buddy_icon_record_cb, buddy);
 	g_free(name);
 
--- a/libpurple/protocols/bonjour/mdns_win32.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Mon Mar 08 22:53:02 2010 +0000
@@ -251,7 +251,7 @@
 
 static void DNSSD_API
 _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode,
-    const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context)
+    const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context)
 {
 	ResolveCallbackArgs *args = (ResolveCallbackArgs*) context;
 	Win32BuddyImplData *idata = args->bb->mdns_impl_data;
--- a/libpurple/protocols/bonjour/parser.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/bonjour/parser.c	Mon Mar 08 22:53:02 2010 +0000
@@ -49,31 +49,6 @@
 	return FALSE;
 }
 
-static char *purple_unescape_text(const char *in)
-{
-    GString *ret;
-    const char *c = in;
-
-    if (in == NULL)
-        return NULL;
-
-    ret = g_string_new("");
-    while (*c) {
-        int len;
-        const char *ent;
-
-        if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) {
-            g_string_append(ret, ent);
-            c += len;
-        } else {
-            g_string_append_c(ret, *c);
-            c++;
-        }
-    }
-
-    return g_string_free(ret, FALSE);
-}
-
 static void
 bonjour_parser_element_start_libxml(void *user_data,
 				   const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace,
--- a/libpurple/protocols/gg/Makefile.am	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/Makefile.am	Mon Mar 08 22:53:02 2010 +0000
@@ -4,14 +4,19 @@
 	lib/compat.h \
 	lib/COPYING \
 	lib/dcc.c \
+	lib/dcc7.c \
 	lib/events.c \
 	lib/http.c \
 	lib/libgadu.c \
 	lib/libgadu-config.h \
 	lib/libgadu.h \
 	lib/obsolete.c \
+	lib/protocol.h \
+	lib/pubdir.c \
 	lib/pubdir50.c \
-	lib/pubdir.c
+	lib/resolver.c \
+	lib/resolver.h \
+	lib/sha1.c
 
 pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
 
@@ -20,16 +25,21 @@
 	lib/common.c \
 	lib/compat.h \
 	lib/dcc.c \
+	lib/dcc7.c \
 	lib/events.c \
 	lib/http.c \
 	lib/libgadu.c \
 	lib/libgadu-config.h \
 	lib/libgadu.h \
 	lib/obsolete.c \
+	lib/protocol.h \
+	lib/pubdir.c \
 	lib/pubdir50.c \
-	lib/pubdir.c
+	lib/resolver.c \
+	lib/resolver.h \
+	lib/sha1.c
 
-INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib
+INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib -DGG_IGNORE_DEPRECATED
 endif
 
 GGSOURCES = \
--- a/libpurple/protocols/gg/Makefile.mingw	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/Makefile.mingw	Mon Mar 08 22:53:02 2010 +0000
@@ -8,7 +8,7 @@
 include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
 
 TARGET = libgg
-CFLAGS += -include win32dep.h
+CFLAGS += -include win32dep.h -DGG_IGNORE_DEPRECATED
 TYPE = PLUGIN
 
 # Static or Plugin...
@@ -41,12 +41,16 @@
 ##
 C_SRC =	\
 	lib/common.c \
+	lib/dcc.c \
+	lib/dcc7.c \
 	lib/events.c \
 	lib/http.c \
 	lib/libgadu.c \
 	lib/obsolete.c \
 	lib/pubdir.c \
 	lib/pubdir50.c \
+	lib/resolver.c \
+	lib/sha1.c \
 	buddylist.c \
 	confer.c \
 	gg.c \
--- a/libpurple/protocols/gg/buddylist.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/buddylist.c	Mon Mar 08 22:53:02 2010 +0000
@@ -82,7 +82,7 @@
 	gchar **users_tbl;
 	int i;
 	char *utf8buddylist = charset_convert(buddylist, "CP1250", "UTF-8");
-
+	
 	/* Don't limit the number of records in a buddylist. */
 	users_tbl = g_strsplit(utf8buddylist, "\r\n", -1);
 
--- a/libpurple/protocols/gg/gg.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/gg.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1000,8 +1000,8 @@
 				       int status, const char *descr)
 {
 	gchar *from;
+	gchar *msg;
 	const char *st;
-	gchar *msg;
 	gchar *avatarurl;
 	PurpleUtilFetchUrlData *url_data;
 
@@ -1018,29 +1018,37 @@
 	switch (status) {
 		case GG_STATUS_NOT_AVAIL:
 		case GG_STATUS_NOT_AVAIL_DESCR:
-			st = "offline";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE);
+			break;
+		case GG_STATUS_FFC:
+		case GG_STATUS_FFC_DESCR:
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
 			break;
 		case GG_STATUS_AVAIL:
 		case GG_STATUS_AVAIL_DESCR:
-			st = "available";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
 			break;
 		case GG_STATUS_BUSY:
 		case GG_STATUS_BUSY_DESCR:
-			st = "away";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY);
 			break;
+		case GG_STATUS_DND:
+		case GG_STATUS_DND_DESCR:
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_UNAVAILABLE);
 		case GG_STATUS_BLOCKED:
 			/* user is blocking us.... */
 			st = "blocked";
 			break;
 		default:
-			st = "available";
+			st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE);
 			purple_debug_info("gg",
 				"GG_EVENT_NOTIFY: Unknown status: %d\n", status);
 			break;
 	}
 
 	purple_debug_info("gg", "st = %s\n", st);
-	msg = charset_convert(descr, "CP1250", "UTF-8");
+	//msg = charset_convert(descr, "CP1250", "UTF-8");
+	msg = g_strdup_printf("%s", descr);
 	purple_prpl_got_user_status(purple_connection_get_account(gc),
 				  from, st, "message", msg, NULL);
 	g_free(from);
@@ -1078,6 +1086,12 @@
 		case GG_STATUS_AVAIL_DESCR:
 			st = _("Available");
 			break;
+		case GG_STATUS_FFC:
+		case GG_STATUS_FFC_DESCR:
+			return _("Chatty");
+		case GG_STATUS_DND:
+		case GG_STATUS_DND_DESCR:
+			return _("Do Not Disturb");
 		case GG_STATUS_BUSY:
 		case GG_STATUS_BUSY_DESCR:
 			st = _("Away");
@@ -1347,8 +1361,11 @@
 
 	from = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender);
 
+	/*
 	tmp = charset_convert((const char *)ev->event.msg.message,
 			      "CP1250", "UTF-8");
+	*/
+	tmp = g_strdup_printf("%s", ev->event.msg.message);
 	purple_str_strip_char(tmp, '\r');
 	msg = g_markup_escape_text(tmp, -1);
 	g_free(tmp);
@@ -1562,7 +1579,7 @@
 
 				purple_debug_info("gg", "notify_pre: (%d) status: %d\n",
 						ev->event.notify->uin,
-						ev->event.notify->status);
+						GG_S(ev->event.notify->status));
 
 				n = (ev->type == GG_EVENT_NOTIFY) ? ev->event.notify
 								  : ev->event.notify_descr.notify;
@@ -1573,17 +1590,17 @@
 
 					purple_debug_info("gg",
 						"notify: (%d) status: %d; descr: %s\n",
-						n->uin, n->status, descr ? descr : "(null)");
+						n->uin, GG_S(n->status), descr ? descr : "(null)");
 
 					ggp_generic_status_handler(gc,
-						n->uin, n->status, descr);
+						n->uin, GG_S(n->status), descr);
 				}
 			}
 			break;
 		case GG_EVENT_NOTIFY60:
 			purple_debug_info("gg",
 				"notify60_pre: (%d) status=%d; version=%d; descr=%s\n",
-				ev->event.notify60->uin, ev->event.notify60->status,
+				ev->event.notify60->uin, GG_S(ev->event.notify60->status),
 				ev->event.notify60->version,
 				ev->event.notify60->descr ? ev->event.notify60->descr : "(null)");
 
@@ -1602,7 +1619,7 @@
 			break;
 		case GG_EVENT_STATUS:
 			purple_debug_info("gg", "status: (%d) status=%d; descr=%s\n",
-					ev->event.status.uin, ev->event.status.status,
+					ev->event.status.uin, GG_S(ev->event.status.status),
 					ev->event.status.descr ? ev->event.status.descr : "(null)");
 
 			ggp_generic_status_handler(gc, ev->event.status.uin,
@@ -1611,12 +1628,12 @@
 		case GG_EVENT_STATUS60:
 			purple_debug_info("gg",
 				"status60: (%d) status=%d; version=%d; descr=%s\n",
-				ev->event.status60.uin, ev->event.status60.status,
+				ev->event.status60.uin, GG_S(ev->event.status60.status),
 				ev->event.status60.version,
 				ev->event.status60.descr ? ev->event.status60.descr : "(null)");
 
 			ggp_generic_status_handler(gc, ev->event.status60.uin,
-				ev->event.status60.status, ev->event.status60.descr);
+				GG_S(ev->event.status60.status), ev->event.status60.descr);
 			break;
 		case GG_EVENT_USERLIST:
 	    		if (ev->event.userlist.type == GG_USERLIST_GET_REPLY) {
@@ -1829,6 +1846,16 @@
 			NULL);
 	types = g_list_append(types, type);
 
+ 	/*
+	 * New statuses for GG 8.0 like PoGGadaj ze mna (not yet because 
+	 * libpurple can't support Chatty status) and Nie przeszkadzac
+	 */
+	type = purple_status_type_new_with_attrs(
+			PURPLE_STATUS_UNAVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
+			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	types = g_list_append(types, type);
+	
 	/*
 	 * This status is necessary to display guys who are blocking *us*.
 	 */
@@ -1930,6 +1957,9 @@
 	presence = purple_account_get_presence(account);
 	status = purple_presence_get_active_status(presence);
 
+	glp->encoding = GG_ENCODING_UTF8;
+	glp->protocol_features = (GG_FEATURE_STATUS80|GG_FEATURE_DND_FFC);
+	
 	glp->async = 1;
 	glp->status = ggp_to_gg_status(status, &glp->status_descr);
 	glp->tls = 0;
@@ -2100,8 +2130,11 @@
 		plain = purple_unescape_html(msg);
 	}
 
+	/*
 	tmp = charset_convert(plain, "UTF-8", "CP1250");
-
+	*/
+	tmp = g_strdup_printf("%s", plain);
+	
 	if (tmp && (format_length - sizeof(struct gg_msg_richtext))) {
 		if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) {
 			ret = -1;
@@ -2160,6 +2193,9 @@
 	} else if (strcmp(status_id, "away") == 0) {
 		new_status = GG_STATUS_BUSY;
 		new_status_descr = GG_STATUS_BUSY_DESCR;
+	} else if (strcmp(status_id, "unavailable") == 0) {
+		new_status = GG_STATUS_DND;
+		new_status_descr = GG_STATUS_DND_DESCR;
 	} else if (strcmp(status_id, "invisible") == 0) {
 		new_status = GG_STATUS_INVISIBLE;
 		new_status_descr = GG_STATUS_INVISIBLE_DESCR;
@@ -2177,9 +2213,12 @@
 	new_msg = purple_status_get_attr_string(status, "message");
 
 	if(new_msg) {
+		/*
 		char *tmp = purple_markup_strip_html(new_msg);
 		*msg = charset_convert(tmp, "UTF-8", "CP1250");
 		g_free(tmp);
+		*/
+		*msg = purple_markup_strip_html(new_msg);
 
 		return new_status_descr;
 	} else {
@@ -2279,7 +2318,8 @@
 	GGPInfo *info = gc->proto_data;
 	GGPChat *chat = NULL;
 	GList *l;
-	char *msg, *plain;
+	/* char *msg, *plain; */
+	gchar *msg;
 	uin_t *uins;
 	int count = 0;
 
@@ -2310,9 +2350,12 @@
 		uins[count++] = uin;
 	}
 
+	/*
 	plain = purple_unescape_html(message);
 	msg = charset_convert(plain, "UTF-8", "CP1250");
 	g_free(plain);
+	*/
+	msg = purple_unescape_html(message);
 	gg_send_message_confer(info->session, GG_CLASS_CHAT, count, uins,
 				(unsigned char *)msg);
 	g_free(msg);
@@ -2465,7 +2508,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,                           /* get_account_text_table */
 	NULL,                           /* initiate_media */
-	NULL                            /* can_do_media */
+	NULL,                            /* can_do_media */
+	NULL 				/* get_moods */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/protocols/gg/lib/common.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/common.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1,8 +1,8 @@
-/* $Id: common.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: common.c 878 2009-11-16 23:48:19Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
+ *                          Robert J. Woźny <speedy@ziew.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,109 +15,180 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/*
+ * Funkcje konwersji między UTF-8 i CP1250 są oparte o kod biblioteki iconv.
+ * Informacje o prawach autorskich oryginalnego kodu zamieszczono poniżej:
+ *
+ * Copyright (C) 1999-2001, 2004 Free Software Foundation, Inc.
+ * This file is part of the GNU LIBICONV Library.
+ *
+ * The GNU LIBICONV Library is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * The GNU LIBICONV Library is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the GNU LIBICONV Library; see the file COPYING.LIB.
+ * If not, write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * \file common.c
+ *
+ * \brief Funkcje wykorzystywane przez różne moduły biblioteki
+ */
+
 #include "libgadu.h"
+#include "libgadu-internal.h"
 
 #ifndef _WIN32
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#ifdef sun
-#  include <sys/filio.h>
-#endif
+#  include <sys/types.h>
+#  include <sys/ioctl.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  ifdef sun
+#    include <sys/filio.h>
+#  endif
 #endif
 
 #include <errno.h>
 #include <fcntl.h>
+
 #ifndef _WIN32
-#include <netdb.h>
+# include <netdb.h>
 #endif
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
+/**
+ * Plik, do którego będą przekazywane informacje odpluskwiania.
+ *
+ * Funkcja \c gg_debug() i pochodne mogą być przechwytywane przez aplikację
+ * korzystającą z biblioteki, by wyświetlić je na żądanie użytkownika lub
+ * zapisać do późniejszej analizy. Jeśli nie określono pliku, wybrane
+ * informacje będą wysyłane do standardowego wyjścia błędu (\c stderr).
+ *
+ * \ingroup debug
+ */
 FILE *gg_debug_file = NULL;
 
 #ifndef GG_DEBUG_DISABLE
 
-/*
- * gg_debug() // funkcja wewntrzna
+/**
+ * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji.
+ *
+ * Jeśli aplikacja ustawiła odpowiednią funkcję obsługi w
+ * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoływana.
+ * W przeciwnym wypadku wynik jest wysyłany do standardowego wyjścia błędu.
  *
- * wywietla komunikat o danym poziomie, o ile uytkownik sobie tego yczy.
+ * \param sess Struktura sesji (może być \c NULL)
+ * \param level Poziom informacji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
+ */
+void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
+{
+	if (gg_debug_handler_session)
+		(*gg_debug_handler_session)(sess, level, format, ap);
+	else if (gg_debug_handler)
+		(*gg_debug_handler)(level, format, ap);
+	else if (gg_debug_level & level)
+		vfprintf(gg_debug_file ? gg_debug_file : stderr, format, ap);
+}
+
+
+/**
+ * \internal Przekazuje informację odpluskawiania.
  *
- *  - level - poziom wiadomoci
- *  - format... - tre wiadomoci (kompatybilna z printf())
+ * \param level Poziom wiadomości
+ * \param format Format wiadomości (zgodny z \c printf)
+ *
+ * \ingroup debug
  */
 void gg_debug(int level, const char *format, ...)
 {
 	va_list ap;
 	int old_errno = errno;
-	
-	if (gg_debug_handler) {
-		va_start(ap, format);
-		(*gg_debug_handler)(level, format, ap);
-		va_end(ap);
+	va_start(ap, format);
+	gg_debug_common(NULL, level, format, ap);
+	va_end(ap);
+	errno = old_errno;
+}
 
-		goto cleanup;
-	}
-	
-	if ((gg_debug_level & level)) {
-		va_start(ap, format);
-		vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap);
-		va_end(ap);
-	}
-
-cleanup:
+/**
+ * \internal Przekazuje informację odpluskwiania związaną z sesją.
+ *
+ * \param sess Struktura sesji
+ * \param level Poziom wiadomości
+ * \param format Format wiadomości (zgodny z \c printf)
+ *
+ * \ingroup debug
+ */
+void gg_debug_session(struct gg_session *sess, int level, const char *format, ...)
+{
+	va_list ap;
+	int old_errno = errno;
+	va_start(ap, format);
+	gg_debug_common(sess, level, format, ap);
+	va_end(ap);
 	errno = old_errno;
 }
 
 #endif
 
-/*
- * gg_vsaprintf() // funkcja pomocnicza
+/**
+ * \internal Odpowiednik funkcji \c vsprintf alokujący miejsce na wynik.
+ *
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
  *
- * robi dokadnie to samo, co vsprintf(), tyle e alokuje sobie wczeniej
- * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj
- * vsnprintf() zgodn z C99, jak i na wczeniejszych.
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
  *
- *  - format - opis wywietlanego tekstu jak dla printf()
- *  - ap - lista argumentw dla printf()
+ * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
  *
- * zaalokowany bufor, ktry naley pniej zwolni, lub NULL
- * jeli nie udao si wykona zadania.
+ * \ingroup helper
  */
 char *gg_vsaprintf(const char *format, va_list ap)
 {
 	int size = 0;
 	const char *start;
 	char *buf = NULL;
-	
-#ifdef __GG_LIBGADU_HAVE_VA_COPY
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
 	va_list aq;
 
 	va_copy(aq, ap);
 #else
-#  ifdef __GG_LIBGADU_HAVE___VA_COPY
+#  ifdef GG_CONFIG_HAVE___VA_COPY
 	va_list aq;
 
 	__va_copy(aq, ap);
 #  endif
 #endif
 
-	start = format; 
+	start = format;
 
-#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF
+#ifndef GG_CONFIG_HAVE_C99_VSNPRINTF
 	{
 		int res;
 		char *tmp;
-		
+
 		size = 128;
 		do {
 			size *= 2;
@@ -132,9 +203,9 @@
 #else
 	{
 		char tmp[2];
-		
-		/* libce Solarisa przy buforze NULL zawsze zwracaj -1, wic
-		 * musimy poda co istniejcego jako cel printf()owania. */
+
+		/* libce Solarisa przy buforze NULL zawsze zwracają -1, więc
+		 * musimy podać coś istniejącego jako cel printf()owania. */
 		size = vsnprintf(tmp, sizeof(tmp), format, ap);
 		if (!(buf = malloc(size + 1)))
 			return NULL;
@@ -142,33 +213,33 @@
 #endif
 
 	format = start;
-	
-#ifdef __GG_LIBGADU_HAVE_VA_COPY
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
 	vsnprintf(buf, size + 1, format, aq);
 	va_end(aq);
 #else
-#  ifdef __GG_LIBGADU_HAVE___VA_COPY
+#  ifdef GG_CONFIG_HAVE___VA_COPY
 	vsnprintf(buf, size + 1, format, aq);
 	va_end(aq);
 #  else
 	vsnprintf(buf, size + 1, format, ap);
 #  endif
 #endif
-	
+
 	return buf;
 }
 
-/*
- * gg_saprintf() // funkcja pomocnicza
+/**
+ * \internal Odpowiednik funkcji \c sprintf alokujący miejsce na wynik.
+ *
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
  *
- * robi dokadnie to samo, co sprintf(), tyle e alokuje sobie wczeniej
- * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj
- * vsnprintf() zgodn z C99, jak i na wczeniejszych.
+ * \param format Format wiadomości (zgodny z \c printf)
  *
- *  - format... - tre taka sama jak w funkcji printf()
+ * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
  *
- * zaalokowany bufor, ktry naley pniej zwolni, lub NULL
- * jeli nie udao si wykona zadania.
+ * \ingroup helper
  */
 char *gg_saprintf(const char *format, ...)
 {
@@ -182,18 +253,17 @@
 	return res;
 }
 
-/*
- * gg_get_line() // funkcja pomocnicza
- * 
- * podaje kolejn lini z bufora tekstowego. niszczy go bezpowrotnie, dzielc
- * na kolejne stringi. zdarza si, nie ma potrzeby pisania funkcji dublujcej
- * bufor eby tylko mie nieruszone dane wejciowe, skoro i tak nie bd nam
- * poniej potrzebne. obcina `\r\n'.
- * 
- *  - ptr - wskanik do zmiennej, ktra przechowuje aktualn pozycj
- *    w przemiatanym buforze
- * 
- * wskanik do kolejnej linii tekstu lub NULL, jeli to ju koniec bufora.
+/**
+ * \internal Pobiera linię tekstu z bufora.
+ *
+ * Funkcja niszczy bufor źródłowy bezpowrotnie, dzieląc go na kolejne ciągi
+ * znaków i obcina znaki końca linii.
+ *
+ * \param ptr Wskaźnik do zmiennej, która przechowuje aktualne położenie
+ *            w analizowanym buforze
+ *
+ * \return Wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec
+ *         bufora.
  */
 char *gg_get_line(char **ptr)
 {
@@ -207,99 +277,30 @@
 	if (!(foo = strchr(*ptr, '\n')))
 		*ptr += strlen(*ptr);
 	else {
+		size_t len;
 		*ptr = foo + 1;
 		*foo = 0;
-		if (strlen(res) > 1 && res[strlen(res) - 1] == '\r')
-			res[strlen(res) - 1] = 0;
+
+		len = strlen(res);
+
+		if (len > 1 && res[len - 1] == '\r')
+			res[len - 1] = 0;
 	}
 
 	return res;
 }
 
-/*
- * gg_connect() // funkcja pomocnicza
+/**
+ * \internal Czyta linię tekstu z gniazda.
  *
- * czy si z serwerem. pierwszy argument jest typu (void *), eby nie
- * musie niczego inkludowa w libgadu.h i nie psu jaki gupich zalenoci
- * na dziwnych systemach.
- *
- *  - addr - adres serwera (struct in_addr *)
- *  - port - port serwera
- *  - async - asynchroniczne poczenie
+ * Funkcja czyta tekst znak po znaku, więc nie jest efektywna, ale dzięki
+ * brakowi buforowania, nie koliduje z innymi funkcjami odczytu.
  *
- * deskryptor gniazda lub -1 w przypadku bdu (kod bdu w zmiennej errno).
- */
-int gg_connect(void *addr, int port, int async)
-{
-	int sock, one = 1, errno2;
-	struct sockaddr_in sin;
-	struct in_addr *a = addr;
-	struct sockaddr_in myaddr;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
-	
-	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno));
-		return -1;
-	}
-
-	memset(&myaddr, 0, sizeof(myaddr));
-	myaddr.sin_family = AF_INET;
-
-	myaddr.sin_addr.s_addr = gg_local_ip;
-
-	if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno));
-		return -1;
-	}
-
-#ifdef ASSIGN_SOCKETS_TO_THREADS
-	gg_win32_thread_socket(0, sock);
-#endif
-
-	if (async) {
-#ifdef FIONBIO
-		if (ioctl(sock, FIONBIO, &one) == -1) {
-#else
-		if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
-#endif
-			gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno));
-			errno2 = errno;
-			close(sock);
-			errno = errno2;
-			return -1;
-		}
-	}
-
-	sin.sin_port = htons(port);
-	sin.sin_family = AF_INET;
-	sin.sin_addr.s_addr = a->s_addr;
-	
-	if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
-		if (errno && (!async || errno != EINPROGRESS)) {
-			gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno));
-			errno2 = errno;
-			close(sock);
-			errno = errno2;
-			return -1;
-		}
-		gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
-	}
-	
-	return sock;
-}
-
-/*
- * gg_read_line() // funkcja pomocnicza
+ * \param sock Deskryptor gniazda
+ * \param buf Wskaźnik do bufora
+ * \param length Długość bufora
  *
- * czyta jedn lini tekstu z gniazda.
- *
- *  - sock - deskryptor gniazda
- *  - buf - wskanik do bufora
- *  - length - dugo bufora
- *
- * jeli trafi na bd odczytu lub podano nieprawidowe parametry, zwraca NULL.
- * inaczej zwraca buf.
+ * \return Zwraca \c buf jeśli się powiodło, lub \c NULL w przypadku błędu.
  */
 char *gg_read_line(int sock, char *buf, int length)
 {
@@ -331,38 +332,116 @@
 	return buf;
 }
 
-/*
- * gg_chomp() // funkcja pomocnicza
+/**
+ * \internal Nawiązuje połączenie TCP.
+ *
+ * \param addr Wskaźnik na strukturę \c in_addr z adresem serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego połączenia
+ *
+ * \return Deskryptor gniazda lub -1 w przypadku błędu
  *
- * ucina "\r\n" lub "\n" z koca linii.
+ * \ingroup helper
+ */
+int gg_connect(void *addr, int port, int async)
+{
+	int sock, one = 1, errno2;
+	struct sockaddr_in sin;
+	struct in_addr *a = addr;
+	struct sockaddr_in myaddr;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
+
+	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno));
+		return -1;
+	}
+
+	memset(&myaddr, 0, sizeof(myaddr));
+	myaddr.sin_family = AF_INET;
+
+	myaddr.sin_addr.s_addr = gg_local_ip;
+
+	if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno));
+		errno2 = errno;
+		close(sock);
+		errno = errno2;
+		return -1;
+	}
+
+#ifdef ASSIGN_SOCKETS_TO_THREADS
+	gg_win32_thread_socket(0, sock);
+#endif
+
+	if (async) {
+#ifdef FIONBIO
+		if (ioctl(sock, FIONBIO, &one) == -1) {
+#else
+		if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+			gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno));
+			errno2 = errno;
+			close(sock);
+			errno = errno2;
+			return -1;
+		}
+	}
+
+	sin.sin_port = htons(port);
+	sin.sin_family = AF_INET;
+	sin.sin_addr.s_addr = a->s_addr;
+
+	if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
+		if (errno && (!async || errno != EINPROGRESS)) {
+			gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno));
+			errno2 = errno;
+			close(sock);
+			errno = errno2;
+			return -1;
+		}
+		gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
+	}
+
+	return sock;
+}
+
+/**
+ * \internal Usuwa znaki końca linii.
  *
- *  - line - linia do przycicia
+ * Funkcja działa bezpośrednio na buforze.
+ *
+ * \param line Bufor z tekstem
+ *
+ * \ingroup helper
  */
 void gg_chomp(char *line)
 {
 	int len;
-	
+
 	if (!line)
 		return;
 
 	len = strlen(line);
-	
+
 	if (len > 0 && line[len - 1] == '\n')
 		line[--len] = 0;
 	if (len > 0 && line[len - 1] == '\r')
 		line[--len] = 0;
 }
 
-/*
- * gg_urlencode() // funkcja wewntrzna
+/**
+ * \internal Koduje ciąg znaków do postacji adresu HTTP.
  *
- * zamienia podany tekst na cig znakw do formularza http. przydaje si
- * przy rnych usugach katalogu publicznego.
+ * Zamienia znaki niedrukowalne, spoza ASCII i mające specjalne znaczenie
+ * dla protokołu HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkową
+ * wartością znaku.
+ * 
+ * \param str Ciąg znaków do zakodowania
  *
- *  - str - cig znakw do zakodowania
+ * \return Zaalokowany bufor lub \c NULL w przypadku błędu.
  *
- * zaalokowany bufor, ktry naley pniej zwolni albo NULL
- * w przypadku bdu.
+ * \ingroup helper
  */
 char *gg_urlencode(const char *str)
 {
@@ -400,16 +479,19 @@
 	return buf;
 }
 
-/*
- * gg_http_hash() // funkcja wewntrzna
+/**
+ * \internal Wyznacza skrót dla usług HTTP.
  *
- * funkcja liczca hash dla adresu e-mail, hasa i paru innych.
+ * Funkcja jest wykorzystywana do wyznaczania skrótu adresu e-mail, hasła
+ * i innych wartości przekazywanych jako parametry usług HTTP.
  *
- *  - format... - format kolejnych parametrw ('s' jeli dany parametr jest
- *                cigiem znakw lub 'u' jeli numerem GG)
+ * W parametrze \c format należy umieścić znaki określające postać kolejnych
+ * parametrów: \c 's' jeśli parametr jest ciągiem znaków, \c 'u' jeśli jest
+ * liczbą.
  *
- * hash wykorzystywany przy rejestracji i wszelkich manipulacjach wasnego
- * wpisu w katalogu publicznym.
+ * \param format Format kolejnych parametrów (niezgodny z \c printf)
+ *
+ * \return Wartość skrótu
  */
 int gg_http_hash(const char *format, ...)
 {
@@ -428,7 +510,7 @@
 		} else {
 			if (!(arg = va_arg(ap, char*)))
 				arg = "";
-		}	
+		}
 
 		i = 0;
 		while ((c = (unsigned char) arg[i++]) != 0) {
@@ -442,95 +524,6 @@
 	return (b < 0 ? -b : b);
 }
 
-/*
- * gg_gethostbyname() // funkcja pomocnicza
- *
- * odpowiednik gethostbyname() troszczcy si o wspbieno, gdy mamy do
- * dyspozycji funkcj gethostbyname_r().
- *
- *  - hostname - nazwa serwera
- *
- * zwraca wskanik na struktur in_addr, ktr naley zwolni.
- */
-struct in_addr *gg_gethostbyname(const char *hostname)
-{
-	struct in_addr *addr = NULL;
-
-#ifdef HAVE_GETHOSTBYNAME_R
-	char *tmpbuf = NULL, *buf = NULL;
-	struct hostent *hp = NULL, *hp2 = NULL;
-	int h_errnop, ret;
-	size_t buflen = 1024;
-	int new_errno;
-	
-	new_errno = ENOMEM;
-	
-	if (!(addr = malloc(sizeof(struct in_addr))))
-		goto cleanup;
-	
-	if (!(hp = calloc(1, sizeof(*hp))))
-		goto cleanup;
-
-	if (!(buf = malloc(buflen)))
-		goto cleanup;
-
-	tmpbuf = buf;
-	
-	while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) {
-		buflen *= 2;
-		
-		if (!(tmpbuf = realloc(buf, buflen)))
-			break;
-		
-		buf = tmpbuf;
-	}
-	
-	if (ret)
-		new_errno = h_errnop;
-
-	if (ret || !hp2 || !tmpbuf)
-		goto cleanup;
-	
-	memcpy(addr, hp->h_addr, sizeof(struct in_addr));
-	
-	free(buf);
-	free(hp);
-	
-	return addr;
-	
-cleanup:
-	errno = new_errno;
-	
-	if (addr)
-		free(addr);
-	if (hp)
-		free(hp);
-	if (buf)
-		free(buf);
-	
-	return NULL;
-#else
-	struct hostent *hp;
-
-	if (!(addr = malloc(sizeof(struct in_addr)))) {
-		goto cleanup;
-	}
-
-	if (!(hp = gethostbyname(hostname)))
-		goto cleanup;
-
-	memcpy(addr, hp->h_addr, sizeof(struct in_addr));
-
-	return addr;
-	
-cleanup:
-	if (addr)
-		free(addr);
-
-	return NULL;
-#endif
-}
-
 #ifdef ASSIGN_SOCKETS_TO_THREADS
 
 typedef struct gg_win32_thread {
@@ -541,23 +534,22 @@
 
 struct gg_win32_thread *gg_win32_threads = 0;
 
-/*
- * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32
+/**
+ * \internal Zwraca deskryptor gniazda, które było ostatnio tworzone dla wątku.
  *
- * zwraca deskryptor gniazda, ktre byo ostatnio tworzone dla wtku
- * o podanym identyfikatorze.
+ * Jeśli na win32 przy połączeniach synchronicznych zapamiętamy w jakim
+ * wątku uruchomiliśmy funkcję, która się z czymkolwiek łączy, to z osobnego
+ * wątku możemy anulować połączenie poprzez \c gg_win32_thread_socket(watek,-1)
  *
- * jeli na win32 przy poczeniach synchronicznych zapamitamy w jakim
- * wtku uruchomilimy funkcj, ktra si z czymkolwiek czy, to z osobnego
- * wtku moemy anulowa poczenie poprzez gg_win32_thread_socket(watek, -1);
- * 
- * - thread_id - id wtku. jeli jest rwne 0, brany jest aktualny wtek,
- *               jeli rwne -1, usuwa wpis o podanym sockecie.
- * - socket - deskryptor gniazda. jeli rwne 0, zwraca deskryptor gniazda
- *            dla podanego wtku, jeli rwne -1, usuwa wpis, jeli co
- *            innego, ustawia dla podanego wtku dany numer deskryptora.
+ * \param thread_id Identyfikator wątku (jeśli jest równe 0, brany jest
+ *                  aktualny wątek, jeśli równe -1, usuwa wpis dotyczący
+ *                  danego gniazda sockecie)
+ * \param socket Deskryptor gniazda (jeśli równe 0, zwraca deskryptor gniazda
+ *               dla podanego wątku, jeśli równe -1, usuwa wpis, jeśli coś
+ *               innego, ustawia dla podanego wątku dany numer deskryptora)
  *
- * jeli socket jest rwne 0, zwraca deskryptor gniazda dla podanego wtku.
+ * \return Jeśli socket jest równe 0, zwraca deskryptor gniazda dla podanego
+ *         wątku.
  */
 int gg_win32_thread_socket(int thread_id, int socket)
 {
@@ -567,7 +559,7 @@
 
 	if (!thread_id)
 		thread_id = GetCurrentThreadId();
-	
+
 	while (wsk) {
 		if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) {
 			if (close) {
@@ -593,7 +585,7 @@
 		closesocket(socket);
 	if (close || !socket)
 		return 0;
-	
+
 	/* Dodaje nowy element */
 	wsk = malloc(sizeof(gg_win32_thread));
 	wsk->id = thread_id;
@@ -606,28 +598,33 @@
 
 #endif /* ASSIGN_SOCKETS_TO_THREADS */
 
+/**
+ * \internal Zestaw znaków kodowania base64.
+ */
 static char gg_base64_charset[] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-/*
- * gg_base64_encode()
+/**
+ * \internal Koduje ciąg znaków do base64.
+ *
+ * Wynik funkcji należy zwolnić za pomocą \c free.
  *
- * zapisuje cig znakw w base64.
+ * \param buf Bufor z danami do zakodowania
  *
- *  - buf - cig znakw.
+ * \return Zaalokowany bufor z zakodowanymi danymi
  *
- * zaalokowany bufor.
+ * \ingroup helper
  */
 char *gg_base64_encode(const char *buf)
 {
 	char *out, *res;
 	unsigned int i = 0, j = 0, k = 0, len = strlen(buf);
-	
+
 	res = out = malloc((len / 3 + 1) * 4 + 2);
 
 	if (!res)
 		return NULL;
-	
+
 	while (j <= len) {
 		switch (i % 4) {
 			case 0:
@@ -660,20 +657,22 @@
 	if (i % 4)
 		for (j = 0; j < 4 - (i % 4); j++, out++)
 			*out = '=';
-	
+
 	*out = 0;
-	
+
 	return res;
 }
 
-/*
- * gg_base64_decode()
+/**
+ * \internal Dekoduje ciąg znaków zapisany w base64.
+ *
+ * Wynik funkcji należy zwolnić za pomocą \c free.
  *
- * dekoduje cig znakw z base64.
+ * \param buf Bufor źródłowy z danymi do zdekodowania
  *
- *  - buf - cig znakw.
+ * \return Zaalokowany bufor ze zdekodowanymi danymi
  *
- * zaalokowany bufor.
+ * \ingroup helper
  */
 char *gg_base64_decode(const char *buf)
 {
@@ -683,7 +682,7 @@
 
 	if (!buf)
 		return NULL;
-	
+
 	save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2);
 
 	if (!save)
@@ -720,23 +719,24 @@
 		index %= 4;
 	}
 	*res = 0;
-	
+
 	return save;
 }
 
-/*
- * gg_proxy_auth() // funkcja wewntrzna
+/**
+ * \internal Tworzy nagłówek autoryzacji serwera pośredniczącego.
  *
- * tworzy nagwek autoryzacji dla proxy.
- * 
- * zaalokowany tekst lub NULL, jeli proxy nie jest wczone lub nie wymaga
- * autoryzacji.
+ * Dane pobiera ze zmiennych globalnych \c gg_proxy_username i
+ * \c gg_proxy_password.
+ *
+ * \return Zaalokowany bufor z tekstem lub NULL, jeśli serwer pośredniczący
+ *         nie jest używany lub nie wymaga autoryzacji.
  */
 char *gg_proxy_auth()
 {
 	char *tmp, *enc, *out;
 	unsigned int tmp_size;
-	
+
 	if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password)
 		return NULL;
 
@@ -749,14 +749,14 @@
 		free(tmp);
 		return NULL;
 	}
-	
+
 	free(tmp);
 
 	if (!(out = malloc(strlen(enc) + 40))) {
 		free(enc);
 		return NULL;
 	}
-	
+
 	snprintf(out, strlen(enc) + 40,  "Proxy-Authorization: Basic %s\r\n", enc);
 
 	free(enc);
@@ -764,13 +764,21 @@
 	return out;
 }
 
+/**
+ * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej.
+ */
 static uint32_t gg_crc32_table[256];
+
+/**
+ * \internal Flaga wypełnienia tablicy pomocniczej do wyznaczania sumy
+ * kontrolnej.
+ */
 static int gg_crc32_initialized = 0;
 
-/*
- * gg_crc32_make_table()  // funkcja wewntrzna
+/**
+ * \internal Tworzy tablicę pomocniczą do wyznaczania sumy kontrolnej.
  */
-static void gg_crc32_make_table()
+static void gg_crc32_make_table(void)
 {
 	uint32_t h = 1;
 	unsigned int i, j;
@@ -787,16 +795,15 @@
 	gg_crc32_initialized = 1;
 }
 
-/*
- * gg_crc32()
- *
- * wyznacza sum kontroln CRC32 danego bloku danych.
+/**
+ * Wyznacza sumę kontrolną CRC32.
  *
- *  - crc - suma kontrola poprzedniego bloku danych lub 0 jeli pierwszy
- *  - buf - bufor danych
- *  - size - ilo danych
+ * \param crc Suma kontrola poprzedniego bloku danych lub 0 jeśli liczona
+ *            jest suma kontrolna pierwszego bloku
+ * \param buf Bufor danych
+ * \param len Długość bufora danych
  *
- * suma kontrolna CRC32.
+ * \return Suma kontrolna.
  */
 uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
 {
@@ -814,6 +821,186 @@
 	return crc ^ 0xffffffffL;
 }
 
+/**
+ * \internal Tablica konwersji między CP1250 a UTF-8.
+ */
+static const uint16_t table_cp1250[] = {
+	0x20ac, '?',    0x201a,    '?', 0x201e, 0x2026, 0x2020, 0x2021, 
+	   '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, 
+	   '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 
+	   '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, 
+	0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, 
+	0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, 
+	0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 
+	0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, 
+	0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, 
+	0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, 
+	0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, 
+	0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, 
+	0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, 
+	0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, 
+	0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, 
+	0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, 
+};
+
+/**
+ * \internal Zamienia tekst kodowany CP1250 na UTF-8.
+ *
+ * \param b Tekst źródłowy w CP1250.
+ *
+ * \return Zaalokowany bufor z tekstem w UTF-8.
+ */
+char *gg_cp_to_utf8(const char *b)
+{
+	unsigned char *buf = (unsigned char *) b;
+	char *newbuf;
+	int newlen = 0;
+	int i, j;
+
+	for (i = 0; buf[i]; i++) {
+		uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
+
+		if (znak < 0x80)	newlen += 1;
+		else if (znak < 0x800)	newlen += 2;
+		else			newlen += 3;
+	}
+
+	if (!(newbuf = malloc(newlen+1))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_cp_to_utf8() not enough memory\n");
+		return NULL;
+	}
+
+	for (i = 0, j = 0; buf[i]; i++) {
+		uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80];
+		int count;
+
+		if (znak < 0x80)	count = 1;
+		else if (znak < 0x800)	count = 2;
+		else			count = 3;
+
+		switch (count) {
+			case 3: newbuf[j+2] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0x800;
+			case 2: newbuf[j+1] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0xc0;
+			case 1: newbuf[j] = znak;
+		}
+		j += count;
+	}
+	newbuf[j] = '\0';
+
+	return newbuf;
+}
+
+/**
+ * \internal Dekoduje jeden znak UTF-8.
+ *
+ * \note Funkcja nie jest kompletną implementacją UTF-8, a wersją uproszczoną
+ * do potrzeb kodowania CP1250.
+ *
+ * \param s Tekst źródłowy.
+ * \param n Długość tekstu źródłowego.
+ * \param ch Wskaźnik na wynik dekodowania.
+ *
+ * \return Długość zdekodowanej sekwencji w bajtach lub wartość mniejsza
+ * od zera w przypadku błędu.
+ */
+static int gg_utf8_helper(unsigned char *s, int n, uint16_t *ch)
+{
+	unsigned char c = s[0];
+
+	if (c < 0x80) {
+		*ch = c;
+		return 1;
+	}
+
+	if (c < 0xc2) 
+		return -1;
+
+	if (c < 0xe0) {
+		if (n < 2)
+			return -2;
+		if (!((s[1] ^ 0x80) < 0x40))
+			return -1;
+		*ch = ((uint16_t) (c & 0x1f) << 6) | (uint16_t) (s[1] ^ 0x80);
+		return 2;
+	} 
+	
+	if (c < 0xf0) {
+		if (n < 3)
+			return -2;
+		if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (c >= 0xe1 || s[1] >= 0xa0)))
+			return -1;
+		*ch = ((uint16_t) (c & 0x0f) << 12) | ((uint16_t) (s[1] ^ 0x80) << 6) | (uint16_t) (s[2] ^ 0x80);
+		return 3;
+	}
+
+	return -1;
+}
+
+/**
+ * \internal Zamienia tekst kodowany UTF-8 na CP1250.
+ *
+ * \param b Tekst źródłowy w UTF-8.
+ *
+ * \return Zaalokowany bufor z tekstem w CP1250.
+ */
+char *gg_utf8_to_cp(const char *b)
+{
+	unsigned char *buf = (unsigned char *) b;
+	char *newbuf;
+	int newlen = 0;
+	int len;
+	int i, j;
+
+	len = strlen(b);
+
+	for (i = 0; i < len; newlen++) {
+		uint16_t discard;
+		int ret;
+		
+		ret = gg_utf8_helper(&buf[i], len - i, &discard);
+
+		if (ret > 0)
+			i += ret;
+		else
+			i++;
+	}
+
+	if (!(newbuf = malloc(newlen+1))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_utf8_to_cp() not enough memory\n");
+		return NULL;
+	}
+
+	for (i = 0, j = 0; buf[i]; j++) {
+		uint16_t znak;
+		int ret, k;
+
+		ret = gg_utf8_helper(&buf[i], len - i, &znak);
+
+		if (ret > 0) {
+			i += ret;
+		} else {
+			znak = '?';
+			i++;
+		}
+
+		if (znak < 0x80) {
+			newbuf[j] = znak;
+			continue;
+		}
+
+		newbuf[j] = '?';
+
+		for (k = 0; k < (sizeof(table_cp1250)/sizeof(table_cp1250[0])); k++) {
+			if (table_cp1250[k] == znak) {
+				newbuf[j] = (0x80 | k);
+				break;
+			}
+		}
+	}
+	newbuf[j] = '\0';
+
+	return newbuf;
+}
 
 /*
  * Local variables:
--- a/libpurple/protocols/gg/lib/compat.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/compat.h	Mon Mar 08 22:53:02 2010 +0000
@@ -1,8 +1,8 @@
-/* $Id: compat.h 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: compat.h 506 2008-01-14 22:15:05Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
+ *                          Robert J. Woźny <speedy@ziew.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,10 +15,16 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file compat.h
+ *
+ * \brief Makra zapewniające kompatybilność API na różnych systemach
+ */
+
 #ifndef __COMPAT_H
 #define __COMPAT_H
 
--- a/libpurple/protocols/gg/lib/dcc.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/dcc.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1,8 +1,9 @@
-/* $Id: dcc.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: dcc.c 711 2009-04-16 00:52:47Z darkjames $ */
 
 /*
- *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Tomasz Chiliski <chilek@chilan.com>
+ *  (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,22 +16,28 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file dcc.c
+ *
+ * \brief Obsługa połączeń bezpośrednich do wersji Gadu-Gadu 6.x
+ */
 #include "libgadu.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
+
 #ifndef _WIN32
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#ifdef sun
-#  include <sys/filio.h>
-#endif
+#  include <sys/ioctl.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  ifdef sun
+#    include <sys/filio.h>
+#  endif
 #endif
 
 #include <ctype.h>
@@ -43,67 +50,71 @@
 #include <unistd.h>
 
 #include "compat.h"
+
 #ifndef GG_DEBUG_DISABLE
-/*
- * gg_dcc_debug_data() // funkcja wewntrzna
+
+/**
+ * \internal Przekazuje zawartość pakietu do odpluskwiania.
  *
- * wywietla zrzut pakietu w hexie.
- * 
- *  - prefix - prefiks zrzutu pakietu
- *  - fd - deskryptor gniazda
- *  - buf - bufor z danymi
- *  - size - rozmiar danych
+ * \param prefix Prefiks informacji
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danumi
+ * \param size Rozmiar bufora z danymi
  */
 static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size)
 {
 	unsigned int i;
-	
+
 	gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size);
-	
+
 	for (i = 0; i < size; i++)
 		gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]);
-	
+
 	gg_debug(GG_DEBUG_MISC, "\n");
 }
 #else
 #define gg_dcc_debug_data(a,b,c,d) do { } while (0)
 #endif
 
-/*
+/**
+ * Wysyła żądanie zwrotnego połączenia bezpośredniego.
+ *
+ * Funkcję wykorzystuje się, jeśli nie ma możliwości połączenia się z odbiorcą
+ * pliku lub rozmowy głosowej. Po otrzymaniu żądania druga strona spróbuje
+ * nawiązać zwrotne połączenie bezpośrednie z nadawcą.
  * gg_dcc_request()
  *
- * wysya informacj o tym, e dany klient powinien si z nami poczy.
- * wykorzystywane, kiedy druga strona, ktrej chcemy co wysa jest za
- * maskarad.
+ * \param sess Struktura sesji
+ * \param uin Numer odbiorcy
  *
- *  - sess - struktura opisujca sesj GG
- *  - uin - numerek odbiorcy
+ * \return Patrz \c gg_send_message_ctcp()
  *
- * patrz gg_send_message_ctcp().
+ * \ingroup dcc6
  */
 int gg_dcc_request(struct gg_session *sess, uin_t uin)
 {
-	return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (const unsigned char *)"\002", 1);
+	return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (unsigned char*) "\002", 1);
 }
 
-/* 
- * gg_dcc_fill_filetime()  // funkcja wewntrzna
+/**
+ * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32.
  *
- * zamienia czas w postaci unixowej na windowsowy.
+ * \note Funkcja działa jedynie gdy kompilator obsługuje typ danych
+ * \c long \c long.
  *
- *  - unix - czas w postaci unixowej
- *  - filetime - czas w postaci windowsowej
+ * \param ut Czas w postaci uniksowej
+ * \param ft Czas w postaci API WIN32
  */
 static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft)
 {
-#ifdef __GG_LIBGADU_HAVE_LONG_LONG
+#ifdef GG_CONFIG_HAVE_LONG_LONG
 	unsigned long long tmp;
 
 	tmp = ut;
 	tmp += 11644473600LL;
 	tmp *= 10000000LL;
 
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
 	ft[0] = (uint32_t) tmp;
 	ft[1] = (uint32_t) (tmp >> 32);
 #else
@@ -114,31 +125,33 @@
 #endif
 }
 
-/*
- * gg_dcc_fill_file_info()
+/**
+ * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
  *
- * wypenia pola struct gg_dcc niezbdne do wysania pliku.
+ * \note Większą funkcjonalność zapewnia funkcja \c gg_dcc_fill_file_info2().
  *
- *  - d - struktura opisujca poczenie DCC
- *  - filename - nazwa pliku
+ * \param d Struktura połączenia
+ * \param filename Nazwa pliku
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename)
 {
 	return gg_dcc_fill_file_info2(d, filename, filename);
 }
 
-/*
- * gg_dcc_fill_file_info2()
- *
- * wypenia pola struct gg_dcc niezbdne do wysania pliku.
+/**
+ * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
  *
- *  - d - struktura opisujca poczenie DCC
- *  - filename - nazwa pliku
- *  - local_filename - nazwa na lokalnym systemie plikw
+ * \param d Struktura połączenia
+ * \param filename Nazwa pliku zapisywana w strukturze
+ * \param local_filename Nazwa pliku w lokalnym systemie plików
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename)
 {
@@ -225,25 +238,23 @@
 			*q = 175;
 		}
 	}
-	
+
 	gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename);
-	strncpy((char *)d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
+	strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
 
 	return 0;
 }
 
-/*
- * gg_dcc_transfer() // funkcja wewntrzna
- * 
- * inicjuje proces wymiany pliku z danym klientem.
+/**
+ * \internal Rozpoczyna połączenie bezpośrednie z danym klientem.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wasny numer
- *  - peer_uin - numer obiorcy
- *  - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET)
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer odbiorcy
+ * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET)
  *
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
  */
 static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type)
 {
@@ -251,9 +262,9 @@
 	struct in_addr addr;
 
 	addr.s_addr = ip;
-	
+
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET");
-	
+
 	if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) {
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n");
 		errno = EINVAL;
@@ -284,18 +295,17 @@
 	return d;
 }
 
-/*
- * gg_dcc_get_file()
- * 
- * inicjuje proces odbierania pliku od danego klienta, gdy ten wysa do
- * nas danie poczenia.
+/**
+ * Rozpoczyna odbieranie pliku przez zwrotne połączenie bezpośrednie.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wasny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP nadawcy
+ * \param port Port nadawcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer nadawcy
  *
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
 {
@@ -304,17 +314,17 @@
 	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET);
 }
 
-/*
- * gg_dcc_send_file()
- * 
- * inicjuje proces wysyania pliku do danego klienta.
+/**
+ * Rozpoczyna wysyłanie pliku.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wasny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer odbiorcy
  *
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
 {
@@ -323,17 +333,17 @@
 	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND);
 }
 
-/*
- * gg_dcc_voice_chat()
- * 
- * prbuje nawiza poczenie gosowe.
+/**
+ * Rozpoczyna połączenie głosowe.
  *
- *  - ip - adres ip odbiorcy
- *  - port - port odbiorcy
- *  - my_uin - wasny numer
- *  - peer_uin - numer obiorcy
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin Własny numer
+ * \param peer_uin Numer odbiorcy
  *
- * zaalokowana struct gg_dcc lub NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
 {
@@ -342,30 +352,34 @@
 	return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE);
 }
 
-/*
- * gg_dcc_set_type()
+/**
+ * Ustawia typ przychodzącego połączenia bezpośredniego.
+ *
+ * Funkcję należy wywołać po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK.
  *
- * po zdarzeniu GG_EVENT_DCC_CALLBACK naley ustawi typ poczenia za
- * pomoc tej funkcji.
+ * \param d Struktura połączenia
+ * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub
+ *             \c GG_SESSION_DCC_VOICE)
  *
- *  - d - struktura opisujca poczenie
- *  - type - typ poczenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE)
+ * \ingroup dcc6
  */
 void gg_dcc_set_type(struct gg_dcc *d, int type)
 {
 	d->type = type;
 	d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST;
 }
-	
-/*
- * gg_dcc_callback() // funkcja wewntrzna
+
+/**
+ * \internal Funkcja zwrotna połączenia bezpośredniego.
  *
- * wywoywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza
- * rezultat w struct gg_dcc->event.
+ * Pole \c callback struktury \c gg_dcc zawiera wskaźnik do tej funkcji.
+ * Wywołuje ona \c gg_watch_fd() i zachowuje wynik w polu \c event.
  *
- *  - d - structura opisujca poczenie
+ * \note Funkcjonalność funkcjo zwrotnej nie jest już wspierana.
  *
- * 0, -1.
+ * \param d Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
 static int gg_dcc_callback(struct gg_dcc *d)
 {
@@ -376,25 +390,26 @@
 	return (e != NULL) ? 0 : -1;
 }
 
-/*
- * gg_dcc_socket_create()
+/**
+ * Tworzy gniazdo nasłuchujące dla połączeń bezpośrednich.
  *
- * tworzy gniazdo dla bezporedniej komunikacji midzy klientami.
+ * Funkcja przywiązuje gniazdo do pierwszego wolnego portu TCP.
  *
- *  - uin - wasny numer
- *  - port - preferowany port, jeli rwny 0 lub -1, prbuje domylnego
+ * \param uin Własny numer
+ * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
  *
- * zaalokowana struct gg_dcc, ktr poniej naley zwolni funkcj
- * gg_dcc_free(), albo NULL jeli wystpi bd.
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
 {
 	struct gg_dcc *c;
 	struct sockaddr_in sin;
 	int sock, bound = 0, errno2;
-	
+
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port);
-	
+
 	if (!uin) {
 		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n");
 		errno = EINVAL;
@@ -408,12 +423,12 @@
 
 	if (!port)
 		port = GG_DEFAULT_DCC_PORT;
-	
+
 	while (!bound) {
 		sin.sin_family = AF_INET;
 		sin.sin_addr.s_addr = INADDR_ANY;
 		sin.sin_port = htons(port);
-	
+
 		gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port);
 		if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin)))
 			bound = 1;
@@ -433,7 +448,7 @@
 		errno = errno2;
 		return NULL;
 	}
-	
+
 	gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port);
 
 	if (!(c = malloc(sizeof(*c)))) {
@@ -452,20 +467,20 @@
 	c->check = GG_CHECK_READ;
 	c->callback = gg_dcc_callback;
 	c->destroy = gg_dcc_free;
-	
+
 	return c;
 }
 
-/*
- * gg_dcc_voice_send()
- *
- * wysya ramk danych dla rozmowy gosowej.
+/**
+ * Wysyła ramkę danych połączenia głosowego.
  *
- *  - d - struktura opisujca poczenie dcc
- *  - buf - bufor z danymi
- *  - length - rozmiar ramki
+ * \param d Struktura połączenia
+ * \param buf Bufor z danymi
+ * \param length Długość bufora z danymi
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
  */
 int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
 {
@@ -500,7 +515,14 @@
 	return 0;
 }
 
-#define gg_read(fd, buf, size) \
+/**
+ * \internal Odbiera dane z połączenia bezpośredniego z obsługą błędów.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor na dane
+ * \param size Rozmiar bufora na dane
+ */
+#define gg_dcc_read(fd, buf, size) \
 { \
 	int tmp = read(fd, buf, size); \
 	\
@@ -517,9 +539,16 @@
 		return e; \
 	} \
 	gg_dcc_debug_data("read", fd, buf, size); \
-} 
+}
 
-#define gg_write(fd, buf, size) \
+/**
+ * \internal Wysyła dane do połączenia bezpośredniego z obsługą błędów.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danymi
+ * \param size Rozmiar bufora z danymi
+ */
+#define gg_dcc_write(fd, buf, size) \
 { \
 	int tmp; \
 	gg_dcc_debug_data("write", fd, buf, size); \
@@ -536,14 +565,18 @@
 	} \
 }
 
-/*
- * gg_dcc_watch_fd()
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
  *
- * funkcja, ktr naley wywoa, gdy co si zmieni na gg_dcc->fd.
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free.
  *
- *  - h - struktura zwrcona przez gg_create_dcc_socket()
+ * \param h Struktura połączenia
  *
- * zaalokowana struct gg_event lub NULL, jeli zabrako pamici na ni.
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup dcc6
  */
 struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
 {
@@ -551,7 +584,7 @@
 	int foo;
 
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h);
-	
+
 	if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) {
 		gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n");
 		errno = EINVAL;
@@ -568,10 +601,9 @@
 	if (h->type == GG_SESSION_DCC_SOCKET) {
 		struct sockaddr_in sin;
 		struct gg_dcc *c;
-		int fd;
-		socklen_t sin_len = sizeof(sin);
-		int one = 1;
-		
+		int fd, one = 1;
+		unsigned int sin_len = sizeof(sin);
+
 		if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno));
 			return e;
@@ -607,7 +639,7 @@
 		c->file_fd = -1;
 		c->remote_addr = sin.sin_addr.s_addr;
 		c->remote_port = ntohs(sin.sin_port);
-		
+
 		e->type = GG_EVENT_DCC_NEW;
 		e->event.dcc_new = c;
 
@@ -617,8 +649,7 @@
 		struct gg_dcc_small_packet small;
 		struct gg_dcc_big_packet big;
 		int size, tmp, res;
-		socklen_t res_size = sizeof(res);
-		unsigned int utmp;
+		unsigned int utmp, res_size = sizeof(res);
 		char buf[1024], ack[] = "UDAG";
 
 		struct gg_dcc_file_info_packet {
@@ -634,8 +665,8 @@
 				uin_t uin;
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2);
-				
-				gg_read(h->fd, &uin, sizeof(uin));
+
+				gg_dcc_read(h->fd, &uin, sizeof(uin));
 
 				if (h->state == GG_STATE_READING_UIN_1) {
 					h->state = GG_STATE_READING_UIN_2;
@@ -656,7 +687,7 @@
 			case GG_STATE_SENDING_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n");
 
-				gg_write(h->fd, ack, 4);
+				gg_dcc_write(h->fd, ack, 4);
 
 				h->state = GG_STATE_READING_TYPE;
 				h->check = GG_CHECK_READ;
@@ -666,8 +697,8 @@
 
 			case GG_STATE_READING_TYPE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n");
-				
-				gg_read(h->fd, &small, sizeof(small));
+
+				gg_dcc_read(h->fd, &small, sizeof(small));
 
 				small.type = gg_fix32(small.type);
 
@@ -680,7 +711,7 @@
 						h->timeout = GG_DEFAULT_TIMEOUT;
 
 						e->type = GG_EVENT_DCC_CALLBACK;
-			
+
 						break;
 
 					case 0x0002:	/* XXX */
@@ -703,8 +734,8 @@
 
 			case GG_STATE_READING_REQUEST:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n");
-				
-				gg_read(h->fd, &small, sizeof(small));
+
+				gg_dcc_read(h->fd, &small, sizeof(small));
 
 				small.type = gg_fix32(small.type);
 
@@ -715,7 +746,7 @@
 						h->check = GG_CHECK_READ;
 						h->timeout = GG_DEFAULT_TIMEOUT;
 						break;
-						
+
 					case 0x0003:	/* XXX */
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n");
 						h->state = GG_STATE_SENDING_VOICE_ACK;
@@ -725,22 +756,22 @@
 						e->type = GG_EVENT_DCC_NEED_VOICE_ACK;
 
 						break;
-						
+
 					default:
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin);
 						e->type = GG_EVENT_DCC_ERROR;
 						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 				}
-		 	
+
 				return e;
 
 			case GG_STATE_READING_FILE_INFO:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n");
-				
-				gg_read(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+				gg_dcc_read(h->fd, &file_info_packet, sizeof(file_info_packet));
 
 				memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info));
-		
+
 				h->file_info.mode = gg_fix32(h->file_info.mode);
 				h->file_info.size = gg_fix32(h->file_info.size);
 
@@ -749,17 +780,17 @@
 				h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
 
 				e->type = GG_EVENT_DCC_NEED_FILE_ACK;
-				
+
 				return e;
 
 			case GG_STATE_SENDING_FILE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n");
-				
+
 				big.type = gg_fix32(0x0006);	/* XXX */
 				big.dunno1 = gg_fix32(h->offset);
 				big.dunno2 = 0;
 
-				gg_write(h->fd, &big, sizeof(big));
+				gg_dcc_write(h->fd, &big, sizeof(big));
 
 				h->state = GG_STATE_READING_FILE_HEADER;
 				h->chunk_size = sizeof(big);
@@ -773,25 +804,25 @@
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				return e;
-				
+
 			case GG_STATE_SENDING_VOICE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n");
-				
+
 				tiny.type = 0x01;	/* XXX */
 
-				gg_write(h->fd, &tiny, sizeof(tiny));
+				gg_dcc_write(h->fd, &tiny, sizeof(tiny));
 
 				h->state = GG_STATE_READING_VOICE_HEADER;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				h->offset = 0;
-				
+
 				return e;
-				
+
 			case GG_STATE_READING_FILE_HEADER:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n");
-				
+
 				tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
 
 				if (tmp == -1) {
@@ -802,7 +833,7 @@
 				}
 
 				gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
-				
+
 				h->chunk_offset += tmp;
 
 				if (h->chunk_offset < h->chunk_size)
@@ -823,7 +854,7 @@
 					return e;
 				}
 
-				if (h->chunk_size == 0) { 
+				if (h->chunk_size == 0) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n");
 					e->type = GG_EVENT_DCC_DONE;
 					return e;
@@ -833,13 +864,13 @@
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 				h->established = 1;
-			 	
+
 				return e;
 
 			case GG_STATE_READING_VOICE_HEADER:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n");
-				
-				gg_read(h->fd, &tiny, sizeof(tiny));
+
+				gg_dcc_read(h->fd, &tiny, sizeof(tiny));
 
 				switch (tiny.type) {
 					case 0x03:	/* XXX */
@@ -850,19 +881,19 @@
 						break;
 					case 0x04:	/* XXX */
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n");
-						/* XXX zwraca odpowiedni event */
+						/* XXX zwracać odpowiedni event */
 					default:
 						gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type);
 						e->type = GG_EVENT_DCC_ERROR;
 						e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
 				}
-			 	
+
 				return e;
 
 			case GG_STATE_READING_VOICE_SIZE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n");
-				
-				gg_read(h->fd, &small, sizeof(small));
+
+				gg_dcc_read(h->fd, &small, sizeof(small));
 
 				small.type = gg_fix32(small.type);
 
@@ -870,7 +901,7 @@
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type);
 					e->type = GG_EVENT_DCC_ERROR;
 					e->event.dcc_error = GG_ERROR_DCC_NET;
-					
+
 					return e;
 				}
 
@@ -879,18 +910,19 @@
 
 				if (!(h->voice_buf = malloc(h->chunk_size))) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n");
+					free(e);
 					return NULL;
 				}
 
 				h->state = GG_STATE_READING_VOICE_DATA;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
-			 	
+
 				return e;
 
 			case GG_STATE_READING_VOICE_DATA:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n");
-				
+
 				tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
 				if (tmp < 1) {
 					if (tmp == -1) {
@@ -909,7 +941,7 @@
 
 				if (h->chunk_offset >= h->chunk_size) {
 					e->type = GG_EVENT_DCC_VOICE_DATA;
-					e->event.dcc_voice_data.data = h->voice_buf;
+					e->event.dcc_voice_data.data = (unsigned char*) h->voice_buf;
 					e->event.dcc_voice_data.length = h->chunk_size;
 					h->state = GG_STATE_READING_VOICE_HEADER;
 					h->voice_buf = NULL;
@@ -917,7 +949,7 @@
 
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
-				
+
 				return e;
 
 			case GG_STATE_CONNECTING:
@@ -925,7 +957,7 @@
 				uin_t uins[2];
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n");
-				
+
 				res = 0;
 				if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res));
@@ -935,23 +967,23 @@
 				}
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n");
-				
+
 				uins[0] = gg_fix32(h->uin);
 				uins[1] = gg_fix32(h->peer_uin);
 
-				gg_write(h->fd, uins, sizeof(uins));
-				
+				gg_dcc_write(h->fd, uins, sizeof(uins));
+
 				h->state = GG_STATE_READING_ACK;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
-				
+
 				return e;
 			}
 
 			case GG_STATE_READING_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n");
-				
-				gg_read(h->fd, buf, 4);
+
+				gg_dcc_read(h->fd, buf, 4);
 
 				if (strncmp(buf, ack, 4)) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n");
@@ -964,29 +996,29 @@
 				h->check = GG_CHECK_WRITE;
 				h->timeout = GG_DEFAULT_TIMEOUT;
 				h->state = GG_STATE_SENDING_REQUEST;
-				
+
 				return e;
 
 			case GG_STATE_SENDING_VOICE_REQUEST:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n");
 
 				small.type = gg_fix32(0x0003);
-				
-				gg_write(h->fd, &small, sizeof(small));
+
+				gg_dcc_write(h->fd, &small, sizeof(small));
 
 				h->state = GG_STATE_READING_VOICE_ACK;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DEFAULT_TIMEOUT;
-				
+
 				return e;
-			
+
 			case GG_STATE_SENDING_REQUEST:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n");
 
 				small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002);	/* XXX */
-				
-				gg_write(h->fd, &small, sizeof(small));
-				
+
+				gg_dcc_write(h->fd, &small, sizeof(small));
+
 				switch (h->type) {
 					case GG_SESSION_DCC_GET:
 						h->state = GG_STATE_READING_REQUEST;
@@ -1002,7 +1034,7 @@
 						if (h->file_fd == -1)
 							e->type = GG_EVENT_DCC_NEED_FILE_INFO;
 						break;
-						
+
 					case GG_SESSION_DCC_VOICE:
 						h->state = GG_STATE_SENDING_VOICE_REQUEST;
 						h->check = GG_CHECK_WRITE;
@@ -1011,7 +1043,7 @@
 				}
 
 				return e;
-			
+
 			case GG_STATE_SENDING_FILE_INFO:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n");
 
@@ -1021,8 +1053,8 @@
 				}
 
 				small.type = gg_fix32(0x0001);	/* XXX */
-				
-				gg_write(h->fd, &small, sizeof(small));
+
+				gg_dcc_write(h->fd, &small, sizeof(small));
 
 				file_info_packet.big.type = gg_fix32(0x0003);	/* XXX */
 				file_info_packet.big.dunno1 = 0;
@@ -1030,26 +1062,26 @@
 
 				memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info));
 
-				/* zostaj teraz u nas, wic odwracamy z powrotem */
+				/* zostają teraz u nas, więc odwracamy z powrotem */
 				h->file_info.size = gg_fix32(h->file_info.size);
 				h->file_info.mode = gg_fix32(h->file_info.mode);
-				
-				gg_write(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+				gg_dcc_write(h->fd, &file_info_packet, sizeof(file_info_packet));
 
 				h->state = GG_STATE_READING_FILE_ACK;
 				h->check = GG_CHECK_READ;
 				h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
 
 				return e;
-				
+
 			case GG_STATE_READING_FILE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n");
-				
-				gg_read(h->fd, &big, sizeof(big));
+
+				gg_dcc_read(h->fd, &big, sizeof(big));
 
-				/* XXX sprawdza wynik */
+				/* XXX sprawdzać wynik */
 				h->offset = gg_fix32(big.dunno1);
-				
+
 				h->state = GG_STATE_SENDING_FILE_HEADER;
 				h->check = GG_CHECK_WRITE;
 				h->timeout = GG_DEFAULT_TIMEOUT;
@@ -1060,8 +1092,8 @@
 
 			case GG_STATE_READING_VOICE_ACK:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n");
-				
-				gg_read(h->fd, &tiny, sizeof(tiny));
+
+				gg_dcc_read(h->fd, &tiny, sizeof(tiny));
 
 				if (tiny.type != 0x01) {
 					gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type);
@@ -1075,14 +1107,14 @@
 				h->timeout = GG_DEFAULT_TIMEOUT;
 
 				e->type = GG_EVENT_DCC_ACK;
-				
+
 				return e;
 
 			case GG_STATE_SENDING_FILE_HEADER:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n");
-				
+
 				h->chunk_offset = 0;
-				
+
 				if ((h->chunk_size = h->file_info.size - h->offset) > 4096) {
 					h->chunk_size = 4096;
 					big.type = gg_fix32(0x0003);  /* XXX */
@@ -1091,8 +1123,8 @@
 
 				big.dunno1 = gg_fix32(h->chunk_size);
 				big.dunno2 = 0;
-				
-				gg_write(h->fd, &big, sizeof(big));
+
+				gg_dcc_write(h->fd, &big, sizeof(big));
 
 				h->state = GG_STATE_SENDING_FILE;
 				h->check = GG_CHECK_WRITE;
@@ -1100,13 +1132,13 @@
 				h->established = 1;
 
 				return e;
-				
+
 			case GG_STATE_SENDING_FILE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n");
-				
+
 				if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
 					utmp = sizeof(buf);
-				
+
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size);
 
 				/* koniec pliku? */
@@ -1117,11 +1149,17 @@
 					return e;
 				}
 
+				if (h->offset >= h->file_info.size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
+					e->type = GG_EVENT_DCC_DONE;
+					return e;
+				}
+
 				lseek(h->file_fd, h->offset, SEEK_SET);
 
 				size = read(h->file_fd, buf, utmp);
 
-				/* bd */
+				/* błąd */
 				if (size == -1) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
 
@@ -1139,8 +1177,8 @@
 
 					return e;
 				}
-				
-				/* jeli wczytalimy wicej, utnijmy. */
+
+				/* jeśli wczytaliśmy więcej, utnijmy. */
 				if (h->offset + size > h->file_info.size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size);
 					size = h->file_info.size - h->offset;
@@ -1161,15 +1199,22 @@
 					return e;
 				}
 
-				h->offset += size;
-				
+				if (tmp == 0) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (connection reset)\n");
+					e->type = GG_EVENT_DCC_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC_NET;
+					return e;
+				}
+
+				h->offset += tmp;
+
 				if (h->offset >= h->file_info.size) {
 					e->type = GG_EVENT_DCC_DONE;
 					return e;
 				}
-				
-				h->chunk_offset += size;
-				
+
+				h->chunk_offset += tmp;
+
 				if (h->chunk_offset >= h->chunk_size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
 					h->state = GG_STATE_SENDING_FILE_HEADER;
@@ -1178,22 +1223,28 @@
 					h->state = GG_STATE_SENDING_FILE;
 					h->timeout = GG_DCC_TIMEOUT_SEND;
 				}
-				
+
 				h->check = GG_CHECK_WRITE;
 
 				return e;
 
 			case GG_STATE_GETTING_FILE:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n");
-				
+
 				if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
 					utmp = sizeof(buf);
-				
+
+				if (h->offset >= h->file_info.size) {
+					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
+					e->type = GG_EVENT_DCC_DONE;
+					return e;
+				}
+
 				size = read(h->fd, buf, utmp);
 
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size);
-				
-				/* bd */
+
+				/* błąd */
 				if (size == -1) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
 
@@ -1211,9 +1262,9 @@
 
 					return e;
 				}
-				
+
 				tmp = write(h->file_fd, buf, size);
-				
+
 				if (tmp == -1 || tmp < size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno));
 					e->type = GG_EVENT_DCC_ERROR;
@@ -1222,14 +1273,14 @@
 				}
 
 				h->offset += size;
-				
+
 				if (h->offset >= h->file_info.size) {
 					e->type = GG_EVENT_DCC_DONE;
 					return e;
 				}
 
 				h->chunk_offset += size;
-				
+
 				if (h->chunk_offset >= h->chunk_size) {
 					gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
 					h->state = GG_STATE_READING_FILE_HEADER;
@@ -1245,11 +1296,11 @@
 					h->state = GG_STATE_GETTING_FILE;
 					h->timeout = GG_DCC_TIMEOUT_GET;
 				}
-				
+
 				h->check = GG_CHECK_READ;
 
 				return e;
-				
+
 			default:
 				gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n");
 				e->type = GG_EVENT_DCC_ERROR;
@@ -1258,35 +1309,28 @@
 				return e;
 		}
 	}
-	
+
 	return e;
 }
 
-#undef gg_read
-#undef gg_write
-
-/*
- * gg_dcc_free()
+/**
+ * Zwalnia zasoby używane przez połączenie bezpośrednie.
  *
- * zwalnia pami po strukturze poczenia dcc.
+ * \param d Struktura połączenia
  *
- *  - d - zwalniana struktura
+ * \ingroup dcc6
  */
 void gg_dcc_free(struct gg_dcc *d)
 {
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d);
-	
+
 	if (!d)
 		return;
 
 	if (d->fd != -1)
 		close(d->fd);
 
-	if (d->chunk_buf) {
-		free(d->chunk_buf);
-		d->chunk_buf = NULL;
-	}
-
+	free(d->chunk_buf);
 	free(d);
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/dcc7.c	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,1217 @@
+/* $Id: dcc7.c 711 2009-04-16 00:52:47Z darkjames $ */
+
+/*
+ *  (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
+ *  
+ *  Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110,
+ *  USA.
+ */
+
+/**
+ * \file dcc7.c
+ *
+ * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x
+ */
+
+#include "libgadu.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef _WIN32
+#  include <sys/ioctl.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  ifdef sun
+#    include <sys/filio.h>
+#  endif
+#endif
+
+#include <time.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "compat.h"
+
+#define gg_debug_dcc(dcc, fmt...) \
+	gg_debug_session((dcc) ? (dcc)->sess : NULL, fmt)
+
+/**
+ * \internal Dodaje połączenie bezpośrednie do sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc);
+
+	if (!sess || !dcc || dcc->next) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	dcc->next = sess->dcc7_list;
+	sess->dcc7_list = dcc;
+
+	return 0;
+}
+
+/**
+ * \internal Usuwa połączenie bezpośrednie z sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	struct gg_dcc7 *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc);
+
+	if (!sess || !dcc) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (sess->dcc7_list == dcc) {
+		sess->dcc7_list = dcc->next;
+		dcc->next = NULL;
+		return 0;
+	}
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		if (tmp->next == dcc) {
+			tmp = dcc->next;
+			dcc->next = NULL;
+			return 0;
+		}
+	}
+
+	errno = ENOENT;
+	return -1;
+}
+
+/**
+ * \internal Zwraca strukturę połączenia o danym identyfikatorze.
+ *
+ * \param sess Struktura sesji
+ * \param id Identyfikator połączenia
+ * \param uin Numer nadawcy lub odbiorcy
+ *
+ * \return Struktura połączenia lub \c NULL jeśli nie znaleziono
+ */
+static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin)
+{
+	struct gg_dcc7 *tmp;
+	int empty;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin);
+
+	empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8);
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		if (empty) {
+			if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT)
+				return tmp;
+		} else {
+			if (!memcmp(&tmp->cid, &id, sizeof(id)))
+				return tmp;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * \internal Nawiązuje połączenie bezpośrednie
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_connect(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p, %p)\n", sess, dcc);
+
+	if (!sess || !dcc) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
+		return -1;
+	}
+
+	dcc->state = GG_STATE_CONNECTING;
+	dcc->check = GG_CHECK_WRITE;
+	dcc->timeout = GG_DCC7_TIMEOUT_CONNECT;
+	dcc->soft_timeout = 1;
+
+	return 0;
+}
+
+/**
+ * \internal Tworzy gniazdo nasłuchujące dla połączenia bezpośredniego
+ *
+ * \param dcc Struktura połączenia
+ * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port)
+{
+	struct sockaddr_in sin;
+	int fd;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
+
+	if (!dcc) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));
+		return -1;
+	}
+
+	// XXX losować porty?
+	
+	if (!port)
+		port = GG_DEFAULT_DCC_PORT;
+
+	while (1) {
+		sin.sin_family = AF_INET;
+		sin.sin_addr.s_addr = INADDR_ANY;
+		sin.sin_port = htons(port);
+
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() trying port %d\n", port);
+
+		if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin)))
+			break;
+
+		if (port++ == 65535) {
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n");
+			close(fd);
+			errno = ENOENT;
+			return -1;
+		}
+	}
+
+	if (listen(fd, 1)) {
+		int errsv = errno;
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
+		close(fd);
+		errno = errsv;
+		return -1;
+	}
+
+	dcc->fd = fd;
+	dcc->local_port = port;
+	
+	dcc->state = GG_STATE_LISTENING;
+	dcc->check = GG_CHECK_READ;
+	dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
+
+	return 0;
+}
+
+/**
+ * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
+{
+	struct gg_dcc7_info pkt;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
+
+	// XXX dać możliwość konfiguracji?
+	
+	dcc->local_addr = dcc->sess->client_addr;
+
+	if (gg_dcc7_listen(dcc, 0) == -1)
+		return -1;
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.uin = gg_fix32(dcc->peer_uin);
+	pkt.type = GG_DCC7_TYPE_P2P;
+	pkt.id = dcc->cid;
+	snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), dcc->local_port);
+
+	return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Odwraca połączenie po nieudanym connect()
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)
+{
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
+
+	if (dcc->reverse) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
+		return -1;
+	}
+
+	gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
+	close(dcc->fd);
+	dcc->fd = -1;
+	dcc->reverse = 1;
+
+	return gg_dcc7_listen_and_send_info(dcc);
+}
+
+/**
+ * \internal Wysyła do serwera żądanie nadania identyfikatora sesji
+ *
+ * \param sess Struktura sesji
+ * \param type Rodzaj połączenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type)
+{
+	struct gg_dcc7_id_request pkt;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type);
+
+	if (!sess) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n");
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type);
+		errno = EINVAL;
+		return -1;
+	}
+	
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.type = gg_fix32(type);
+
+	return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Rozpoczyna wysyłanie pliku.
+ *
+ * Funkcja jest wykorzystywana przez \c gg_dcc7_send_file() oraz
+ * \c gg_dcc_send_file_fd().
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param fd Deskryptor pliku
+ * \param size Rozmiar pliku
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skrót SHA-1 pliku
+ * \param seek Flaga mówiąca, czy można używać lseek()
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash, int seek)
+{
+	struct gg_dcc7 *dcc = NULL;
+
+	if (!sess || !rcpt || !filename1250 || !hash || fd == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() invalid parameters\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() not enough memory\n");
+		goto fail;
+	}
+
+	if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1)
+		goto fail;
+
+	memset(dcc, 0, sizeof(struct gg_dcc7));
+	dcc->type = GG_SESSION_DCC7_SEND;
+	dcc->dcc_type = GG_DCC7_TYPE_FILE;
+	dcc->state = GG_STATE_REQUESTING_ID;
+	dcc->timeout = GG_DEFAULT_TIMEOUT;
+	dcc->sess = sess;
+	dcc->fd = -1;
+	dcc->uin = sess->uin;
+	dcc->peer_uin = rcpt;
+	dcc->file_fd = fd;
+	dcc->size = size;
+	dcc->seek = seek;
+
+	strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1);
+	dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
+
+	memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN);
+
+	if (gg_dcc7_session_add(sess, dcc) == -1)
+		goto fail;
+
+	return dcc;
+
+fail:
+	free(dcc);
+	return NULL;
+}
+
+/**
+ * Rozpoczyna wysyłanie pliku o danej nazwie.
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param filename Nazwa pliku w lokalnym systemie plików
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skrót SHA-1 pliku (lub \c NULL jeśli ma być wyznaczony)
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash)
+{
+	struct gg_dcc7 *dcc = NULL;
+	const char *tmp;
+	char hash_buf[GG_DCC7_HASH_LEN];
+	struct stat st;
+	int fd = -1;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d, \"%s\", %p)\n", sess, rcpt, filename, hash);
+
+	if (!sess || !rcpt || !filename) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() invalid parameters\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if (!filename1250)
+		filename1250 = filename;
+
+	if (stat(filename, &st) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() failed (%s)\n", strerror(errno));
+		goto fail;
+	}
+
+	if ((st.st_mode & S_IFDIR)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n");
+		errno = EINVAL;
+		goto fail;
+	}
+
+	if ((fd = open(filename, O_RDONLY)) == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
+		goto fail;
+	}
+
+	if (!hash) {
+		if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1)
+			goto fail;
+
+		hash = hash_buf;
+	}
+
+	if ((tmp = strrchr(filename1250, '/')))
+		filename1250 = tmp + 1;
+
+	if (!(dcc = gg_dcc7_send_file_common(sess, rcpt, fd, st.st_size, filename1250, hash, 1)))
+		goto fail;
+
+	return dcc;
+
+fail:
+	if (fd != -1) {
+		int errsv = errno;
+		close(fd);
+		errno = errsv;
+	}
+
+	free(dcc);
+	return NULL;
+}
+
+/**
+ * \internal Rozpoczyna wysyłanie pliku o danym deskryptorze.
+ *
+ * \note Wysyłanie pliku nie będzie działać poprawnie, jeśli deskryptor
+ * źródłowy jest w trybie nieblokującym i w pewnym momencie zabraknie danych.
+ *
+ * \param sess Struktura sesji
+ * \param rcpt Numer odbiorcy
+ * \param fd Deskryptor pliku
+ * \param size Rozmiar pliku
+ * \param filename1250 Nazwa pliku w kodowaniu CP-1250
+ * \param hash Skrót SHA-1 pliku
+ *
+ * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, %d, %d, %u, \"%s\", %p)\n", sess, rcpt, fd, size, filename1250, hash);
+
+	return gg_dcc7_send_file_common(sess, rcpt, fd, size, filename1250, hash, 0);
+}
+
+
+/**
+ * Potwierdza chęć odebrania pliku.
+ *
+ * \param dcc Struktura połączenia
+ * \param offset Początkowy offset przy wznawianiu przesyłania pliku
+ *
+ * \note Biblioteka nie zmienia położenia w odbieranych plikach. Jeśli offset
+ * początkowy jest różny od zera, należy ustawić go funkcją \c lseek() lub
+ * podobną.
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
+{
+	struct gg_dcc7_accept pkt;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
+
+	if (!dcc || !dcc->sess) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.uin = gg_fix32(dcc->peer_uin);
+	pkt.id = dcc->cid;
+	pkt.offset = gg_fix32(offset);
+
+	if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1)
+		return -1;
+
+	dcc->offset = offset;
+
+	return gg_dcc7_listen_and_send_info(dcc);
+}
+
+/**
+ * Odrzuca próbę przesłania pliku.
+ *
+ * \param dcc Struktura połączenia
+ * \param reason Powód odrzucenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
+{
+	struct gg_dcc7_reject pkt;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
+
+	if (!dcc || !dcc->sess) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	memset(&pkt, 0, sizeof(pkt));
+	pkt.uin = gg_fix32(dcc->peer_uin);
+	pkt.id = dcc->cid;
+	pkt.reason = gg_fix32(reason);
+
+	return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Obsługuje pakiet identyfikatora połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_id_reply *p = payload;
+	struct gg_dcc7 *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, state %d, type %d\n", tmp, tmp->state, tmp->dcc_type);
+
+		if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != gg_fix32(p->type))
+			continue;
+		
+		tmp->cid = p->id;
+
+		switch (tmp->dcc_type) {
+			case GG_DCC7_TYPE_FILE:
+			{
+				struct gg_dcc7_new s;
+
+				memset(&s, 0, sizeof(s));
+				s.id = tmp->cid;
+				s.type = gg_fix32(GG_DCC7_TYPE_FILE);
+				s.uin_from = gg_fix32(tmp->uin);
+				s.uin_to = gg_fix32(tmp->peer_uin);
+				s.size = gg_fix32(tmp->size);
+
+				strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN);
+
+				tmp->state = GG_STATE_WAITING_FOR_ACCEPT;
+				tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
+
+				return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet akceptacji połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_accept *p = payload;
+	struct gg_dcc7 *dcc;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n");
+		// XXX wysłać reject?
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+	
+	// XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT?
+	
+	dcc->offset = gg_fix32(p->offset);
+	dcc->state = GG_STATE_WAITING_FOR_INFO;
+
+	return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_info *p = payload;
+	struct gg_dcc7 *dcc;
+	char *tmp;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n");
+		return 0;
+	}
+	
+	if (p->type != GG_DCC7_TYPE_P2P) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type);
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	// jeśli nadal czekamy na połączenie przychodzące, a druga strona nie
+	// daje rady i oferuje namiary na siebie, bierzemy co dają.
+
+	if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	if (dcc->state == GG_STATE_LISTENING) {
+		close(dcc->fd);
+		dcc->fd = -1;
+		dcc->reverse = 1;
+	}
+	
+	if (dcc->type == GG_SESSION_DCC7_SEND) {
+		e->type = GG_EVENT_DCC7_ACCEPT;
+		e->event.dcc7_accept.dcc7 = dcc;
+		e->event.dcc7_accept.type = gg_fix32(p->type);
+		e->event.dcc7_accept.remote_ip = dcc->remote_addr;
+		e->event.dcc7_accept.remote_port = dcc->remote_port;
+	} else {
+		e->type = GG_EVENT_DCC7_PENDING;
+	}
+
+	if (gg_dcc7_connect(sess, dcc) == -1) {
+		if (gg_dcc7_reverse_connect(dcc) == -1) {
+			e->type = GG_EVENT_DCC7_ERROR;
+			e->event.dcc7_error = GG_ERROR_DCC7_NET;
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet odrzucenia połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_reject *p = payload;
+	struct gg_dcc7 *dcc;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n");
+		return 0;
+	}
+	
+	if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n");
+		e->type = GG_EVENT_DCC7_ERROR;
+		e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+		return 0;
+	}
+
+	e->type = GG_EVENT_DCC7_REJECT;
+	e->event.dcc7_reject.dcc7 = dcc;
+	e->event.dcc7_reject.reason = gg_fix32(p->reason);
+
+	// XXX ustawić state na rejected?
+
+	return 0;
+}
+
+/**
+ * \internal Obsługuje pakiet nowego połączenia bezpośredniego.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param payload Treść pakietu
+ * \param len Długość pakietu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+	struct gg_dcc7_new *p = payload;
+	struct gg_dcc7 *dcc;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+	switch (gg_fix32(p->type)) {
+		case GG_DCC7_TYPE_FILE:
+			if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n");
+				return -1;
+			}
+			
+			memset(dcc, 0, sizeof(struct gg_dcc7));
+			dcc->type = GG_SESSION_DCC7_GET;
+			dcc->dcc_type = GG_DCC7_TYPE_FILE;
+			dcc->fd = -1;
+			dcc->file_fd = -1;
+			dcc->uin = sess->uin;
+			dcc->peer_uin = gg_fix32(p->uin_from);
+			dcc->cid = p->id;
+			dcc->sess = sess;
+
+			if (gg_dcc7_session_add(sess, dcc) == -1) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+				gg_dcc7_free(dcc);
+				return -1;
+			}
+
+			dcc->size = gg_fix32(p->size);
+			strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1);
+			dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
+			memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN);
+
+			e->type = GG_EVENT_DCC7_NEW;
+			e->event.dcc7_new = dcc;
+
+			break;
+
+		case GG_DCC7_TYPE_VOICE:
+			if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n");
+				return -1;
+			}
+			
+			memset(dcc, 0, sizeof(struct gg_dcc7));
+
+			dcc->type = GG_SESSION_DCC7_VOICE;
+			dcc->dcc_type = GG_DCC7_TYPE_VOICE;
+			dcc->fd = -1;
+			dcc->file_fd = -1;
+			dcc->uin = sess->uin;
+			dcc->peer_uin = gg_fix32(p->uin_from);
+			dcc->cid = p->id;
+			dcc->sess = sess;
+
+			if (gg_dcc7_session_add(sess, dcc) == -1) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n");
+				gg_dcc7_free(dcc);
+				return -1;
+			}
+
+			e->type = GG_EVENT_DCC7_NEW;
+			e->event.dcc7_new = dcc;
+
+			break;
+
+		default:
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from));
+
+			break;
+	}
+
+	return 0;
+}
+
+/**
+ * \internal Ustawia odpowiednie stany wewnętrzne w zależności od rodzaju
+ * połączenia.
+ * 
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu.
+ */
+static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)
+{
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
+
+	if (!dcc) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	switch (dcc->type) {
+		case GG_SESSION_DCC7_GET:
+			dcc->state = GG_STATE_GETTING_FILE;
+			dcc->check = GG_CHECK_READ;
+			return 0;
+
+		case GG_SESSION_DCC7_SEND:
+			dcc->state = GG_STATE_SENDING_FILE;
+			dcc->check = GG_CHECK_WRITE;
+			return 0;
+
+		case GG_SESSION_DCC7_VOICE:
+			dcc->state = GG_STATE_READING_VOICE_DATA;
+			dcc->check = GG_CHECK_READ;
+			return 0;
+	}
+
+	errno = EINVAL;
+
+	return -1;
+}
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup dcc7
+ */
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
+{
+	struct gg_event *e;
+
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);
+
+	if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
+		errno = EINVAL;
+		return NULL;
+	}
+
+	if (!(e = malloc(sizeof(struct gg_event)))) {
+		gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
+		return NULL;
+	}
+
+	memset(e, 0, sizeof(struct gg_event));
+	e->type = GG_EVENT_NONE;
+
+	switch (dcc->state) {
+		case GG_STATE_LISTENING:
+		{
+			struct sockaddr_in sin;
+			int fd, one = 1;
+			socklen_t sin_len = sizeof(sin);
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
+
+			if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
+				return e;
+			}
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+
+#ifdef FIONBIO
+			if (ioctl(fd, FIONBIO, &one) == -1) {
+#else
+			if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
+				close(fd);
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			close(dcc->fd);
+			dcc->fd = fd;
+
+			dcc->state = GG_STATE_READING_ID;
+			dcc->check = GG_CHECK_READ;
+			dcc->timeout = GG_DEFAULT_TIMEOUT;
+			dcc->incoming = 1;
+
+			dcc->remote_port = ntohs(sin.sin_port);
+			dcc->remote_addr = sin.sin_addr.s_addr;
+
+			e->type = GG_EVENT_DCC7_CONNECTED;
+			e->event.dcc7_connected.dcc7 = dcc;
+
+			return e;
+		}
+
+		case GG_STATE_CONNECTING:
+		{
+			int res = 0, error = 0;
+			unsigned int error_size = sizeof(error);
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
+
+			dcc->soft_timeout = 0;
+
+			if (dcc->timeout == 0)
+				error = ETIMEDOUT;
+
+			if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
+
+				if (gg_dcc7_reverse_connect(dcc) != -1) {
+					e->type = GG_EVENT_DCC7_PENDING;
+				} else {
+					e->type = GG_EVENT_DCC7_ERROR;
+					e->event.dcc_error = GG_ERROR_DCC7_NET;
+				}
+
+				return e;
+			}
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
+
+			dcc->state = GG_STATE_SENDING_ID;
+			dcc->check = GG_CHECK_WRITE;
+			dcc->timeout = GG_DEFAULT_TIMEOUT;
+			dcc->incoming = 0;
+
+			return e;
+		}
+
+		case GG_STATE_READING_ID:
+		{
+			gg_dcc7_id_t id;
+			int res;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
+
+			if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (memcmp(&id, &dcc->cid, sizeof(id))) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (dcc->incoming) {
+				dcc->state = GG_STATE_SENDING_ID;
+				dcc->check = GG_CHECK_WRITE;
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			} else {
+				gg_dcc7_postauth_fixup(dcc);
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			}
+
+			return e;
+		}
+
+		case GG_STATE_SENDING_ID:
+		{
+			int res;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
+
+			if ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+				return e;
+			}
+
+			if (dcc->incoming) {
+				gg_dcc7_postauth_fixup(dcc);
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			} else {
+				dcc->state = GG_STATE_READING_ID;
+				dcc->check = GG_CHECK_READ;
+				dcc->timeout = GG_DEFAULT_TIMEOUT;
+			}
+
+			return e;
+		}
+
+		case GG_STATE_SENDING_FILE:
+		{
+			char buf[1024];
+			int chunk, res;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_FILE;
+				return e;
+			}
+
+			if ((chunk = dcc->size - dcc->offset) > sizeof(buf))
+				chunk = sizeof(buf);
+
+			if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
+				return e;
+			}
+
+			if ((res = write(dcc->fd, buf, res)) == -1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_NET;
+				return e;
+			}
+
+			dcc->offset += res;
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			dcc->state = GG_STATE_SENDING_FILE;
+			dcc->check = GG_CHECK_WRITE;
+			dcc->timeout = GG_DCC7_TIMEOUT_SEND;
+
+			return e;
+		}
+
+		case GG_STATE_GETTING_FILE:
+		{
+			char buf[1024];
+			int res, wres;
+
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size);
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
+				return e;
+			}
+
+			// XXX zapisywać do skutku?
+
+			if ((wres = write(dcc->file_fd, buf, res)) < res) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno));
+				e->type = GG_EVENT_DCC7_ERROR;
+				e->event.dcc_error = GG_ERROR_DCC7_FILE;
+				return e;
+			}
+
+			dcc->offset += res;
+
+			if (dcc->offset >= dcc->size) {
+				gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+				e->type = GG_EVENT_DCC7_DONE;
+				return e;
+			}
+
+			dcc->state = GG_STATE_GETTING_FILE;
+			dcc->check = GG_CHECK_READ;
+			dcc->timeout = GG_DCC7_TIMEOUT_GET;
+
+			return e;
+		}
+
+		default:
+		{
+			gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
+			e->type = GG_EVENT_DCC7_ERROR;
+			e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
+
+			return e;
+		}
+	}
+
+	return e;
+}
+
+/**
+ * Zwalnia zasoby używane przez połączenie bezpośrednie.
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \ingroup dcc7
+ */
+void gg_dcc7_free(struct gg_dcc7 *dcc)
+{
+	gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
+
+	if (!dcc)
+		return;
+
+	if (dcc->fd != -1)
+		close(dcc->fd);
+
+	if (dcc->file_fd != -1)
+		close(dcc->file_fd);
+
+	if (dcc->sess)
+		gg_dcc7_session_remove(dcc->sess, dcc);
+
+	free(dcc);
+}
+
--- a/libpurple/protocols/gg/lib/events.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/events.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1,9 +1,10 @@
-/* $Id: events.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: events.c 855 2009-10-12 21:42:51Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
- *                          Arkadiusz Mikiewicz <arekm@pld-linux.org>
+ *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -16,71 +17,78 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file events.c
+ *
+ * \brief Obsługa zdarzeń
+ */
+
 #include "libgadu.h"
+#include "libgadu-internal.h"
 
 #include <sys/types.h>
+
 #ifndef _WIN32
-#include <sys/wait.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#  include <sys/ioctl.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
 #endif
 
-#include "libgadu-config.h"
+#include "compat.h"
+#include "protocol.h"
 
 #include <errno.h>
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-#  include <pthread.h>
-#endif
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <time.h>
 #include <unistd.h>
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#include <ctype.h>
+#ifdef GG_CONFIG_HAVE_OPENSSL
 #  include <openssl/err.h>
 #  include <openssl/x509.h>
 #endif
 
-#include "compat.h"
-
-/*
- * gg_event_free()
+/**
+ * Zwalnia pamięć zajmowaną przez informację o zdarzeniu.
  *
- * zwalnia pami zajmowan przez informacj o zdarzeniu.
+ * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci
+ * strukturę \c gg_event.
  *
- *  - e - wskanik do informacji o zdarzeniu
+ * \param e Struktura zdarzenia
+ *
+ * \ingroup events
  */
 void gg_event_free(struct gg_event *e)
 {
 	gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
-			
+
 	if (!e)
 		return;
-	
+
 	switch (e->type) {
 		case GG_EVENT_MSG:
 			free(e->event.msg.message);
 			free(e->event.msg.formats);
 			free(e->event.msg.recipients);
 			break;
-	
+
 		case GG_EVENT_NOTIFY:
 			free(e->event.notify);
 			break;
-	
+
 		case GG_EVENT_NOTIFY60:
 		{
 			int i;
 
 			for (i = 0; e->event.notify60[i].uin; i++)
 				free(e->event.notify60[i].descr);
-		
+
 			free(e->event.notify60);
 
 			break;
@@ -89,7 +97,7 @@
 		case GG_EVENT_STATUS60:
 			free(e->event.status60.descr);
 			break;
-	
+
 		case GG_EVENT_STATUS:
 			free(e->event.status.descr);
 			break;
@@ -112,26 +120,30 @@
 		case GG_EVENT_USERLIST:
 			free(e->event.userlist.reply);
 			break;
-	
+
 		case GG_EVENT_IMAGE_REPLY:
 			free(e->event.image_reply.filename);
 			free(e->event.image_reply.image);
 			break;
+
+		case GG_EVENT_XML_EVENT:
+			free(e->event.xml_event.data);
+			break;
 	}
 
 	free(e);
 }
 
-/*
- * gg_image_queue_remove()
- *
- * usuwa z kolejki dany wpis.
+/** \cond internal */
+
+/**
+ * \internal Usuwa obrazek z kolejki do wysłania.
  *
- *  - s - sesja
- *  - q - kolejka
- *  - freeq - czy zwolni kolejk
+ * \param s Struktura sesji
+ * \param q Struktura obrazka
+ * \param freeq Flaga zwolnienia elementu kolejki
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
  */
 int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
 {
@@ -162,13 +174,14 @@
 	return 0;
 }
 
-/*
- * gg_image_queue_parse() // funkcja wewntrzna
+/**
+ * \internal Analizuje przychodzący pakiet z obrazkiem.
  *
- * parsuje przychodzcy pakiet z obrazkiem.
- *
- *  - e - opis zdarzenia
- *  - 
+ * \param e Struktura zdarzenia
+ * \param p Bufor z danymi
+ * \param len Długość bufora
+ * \param sess Struktura sesji
+ * \param sender Numer nadawcy
  */
 static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender)
 {
@@ -180,8 +193,8 @@
 		return;
 	}
 
-	/* znajd dany obrazek w kolejce danej sesji */
-	
+	/* znajdź dany obrazek w kolejce danej sesji */
+
 	for (qq = sess->images, q = NULL; qq; qq = qq->next) {
 		if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) {
 			q = qq;
@@ -190,34 +203,23 @@
 	}
 
 	if (!q) {
-		gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
 		return;
 	}
 
 	if (p[0] == 0x05) {
-		unsigned int i, ok = 0;
-		
 		q->done = 0;
 
 		len -= sizeof(struct gg_msg_image_reply);
 		p += sizeof(struct gg_msg_image_reply);
 
-		/* sprawd, czy mamy tekst zakoczony \0 */
-
-		for (i = 0; i < len; i++) {
-			if (!p[i]) {
-				ok = 1;
-				break;
-			}
-		}
-
-		if (!ok) {
-			gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
+		if (memchr(p, 0, len) == NULL) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
 			return;
 		}
 
 		if (!(q->filename = strdup(p))) {
-			gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
 			return;
 		}
 
@@ -230,11 +232,11 @@
 
 	if (q->done + len > q->size)
 		len = q->size - q->done;
-		
+
 	memcpy(q->image + q->done, p, len);
 	q->done += len;
 
-	/* jeli skoczono odbiera obrazek, wygeneruj zdarzenie */
+	/* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
 
 	if (q->done >= q->size) {
 		e->type = GG_EVENT_IMAGE_REPLY;
@@ -250,78 +252,55 @@
 	}
 }
 
-/*
- * gg_handle_recv_msg() // funkcja wewntrzna
- *
- * obsuguje pakiet z przychodzc wiadomoci, rozbijajc go na dodatkowe
- * struktury (konferencje, kolorki) w razie potrzeby.
+/**
+ * \internal Analizuje informacje rozszerzone wiadomości.
+ * 
+ * \param sess Struktura sesji.
+ * \param e Struktura zdarzenia.
+ * \param sender Numer nadawcy.
+ * \param p Wskaźnik na dane rozszerzone.
+ * \param packet_end Wskaźnik na koniec pakietu.
  *
- *  - h - nagwek pakietu
- *  - e - opis zdarzenia
- *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 jeśli wiadomość obsłużono i wynik ma
+ * zostać przekazany aplikacji, -2 jeśli wystąpił błąd ogólny, -3 jeśli
+ * wiadomość jest niepoprawna.
  */
-static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end)
 {
-	struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header));
-	char *p, *packet_end = (char*) r + h->length;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
-
-	if (!r->seq && !r->msgclass) {
-		gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
-		e->type = GG_EVENT_NONE;
-		return 0;
-	}
-	
-	for (p = (char*) r + sizeof(*r); *p; p++) {
-		if (*p == 0x02 && p == packet_end - 1) {
-			gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
-			break;
-		}
-		if (p >= packet_end) {
-			gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
-			goto malformed;
-		}
-	}
-	
-	p++;
-
-	/* przeanalizuj dodatkowe opcje */
 	while (p < packet_end) {
 		switch (*p) {
 			case 0x01:		/* konferencja */
 			{
 				struct gg_msg_recipients *m = (void*) p;
 				uint32_t i, count;
-			
+
 				p += sizeof(*m);
-			
+
 				if (p > packet_end) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n");
 					goto malformed;
 				}
 
 				count = gg_fix32(m->count);
 
 				if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n");
 					goto malformed;
 				}
-			
+
 				if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n");
 					goto fail;
 				}
-			
+
 				for (i = 0; i < count; i++, p += sizeof(uint32_t)) {
 					uint32_t u;
 					memcpy(&u, p, sizeof(uint32_t));
 					e->event.msg.recipients[i] = gg_fix32(u);
 				}
-				
+
 				e->event.msg.recipients_count = count;
-				
+
 				break;
 			}
 
@@ -329,9 +308,9 @@
 			{
 				uint16_t len;
 				char *buf;
-			
+
 				if (p + 3 > packet_end) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
 					goto malformed;
 				}
 
@@ -339,18 +318,18 @@
 				len = gg_fix16(len);
 
 				if (!(buf = malloc(len))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
 					goto fail;
 				}
 
 				p += 3;
 
 				if (p + len > packet_end) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
 					free(buf);
 					goto malformed;
 				}
-				
+
 				memcpy(buf, p, len);
 
 				e->event.msg.formats = buf;
@@ -366,17 +345,17 @@
 				struct gg_msg_image_request *i = (void*) p;
 
 				if (p + sizeof(*i) > packet_end) {
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
 					goto malformed;
 				}
 
-				e->event.image_request.sender = gg_fix32(r->sender);
+				e->event.image_request.sender = sender;
 				e->event.image_request.size = gg_fix32(i->size);
 				e->event.image_request.crc32 = gg_fix32(i->crc32);
 
 				e->type = GG_EVENT_IMAGE_REQUEST;
 
-				return 0;
+				goto handled;
 			}
 
 			case 0x05:		/* image_reply */
@@ -386,75 +365,347 @@
 
 				if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
 
-					/* pusta odpowied - klient po drugiej stronie nie ma danego obrazka */
+					/* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */
 
 					e->type = GG_EVENT_IMAGE_REPLY;
-					e->event.image_reply.sender = gg_fix32(r->sender);
+					e->event.image_reply.sender = sender;
 					e->event.image_reply.size = 0;
 					e->event.image_reply.crc32 = gg_fix32(rep->crc32);
 					e->event.image_reply.filename = NULL;
 					e->event.image_reply.image = NULL;
-					return 0;
+					goto handled;
 
 				} else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
 
-					gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n");
 					goto malformed;
 				}
 
 				rep->size = gg_fix32(rep->size);
 				rep->crc32 = gg_fix32(rep->crc32);
-				gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender));
-
-				return 0;
+				gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender);
+
+				goto handled;
 			}
 
 			default:
 			{
-				gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
 				p = packet_end;
 			}
 		}
 	}
 
+	return 0;
+
+handled:
+	return -1;
+
+fail:
+	return -2;
+
+malformed:
+	return -3;
+}
+
+/**
+ * \internal Analizuje przychodzący pakiet z wiadomością.
+ *
+ * Rozbija pakiet na poszczególne składniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wskaźnik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+{
+	struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header));
+	char *p, *packet_end = (char*) r + h->length;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
+
+	if (!r->seq && !r->msgclass) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
+		e->type = GG_EVENT_NONE;
+		return 0;
+	}
+
+	/* znajdź \0 */
+	for (p = (char*) r + sizeof(*r); ; p++) {
+		if (p >= packet_end) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
+			goto malformed;
+		}
+
+		if (*p == 0x02 && p == packet_end - 1) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
+			break;
+		}
+
+		if (!*p)
+			break;
+	}
+
+	p++;
+
+	switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), p, packet_end)) {
+		case -1:	// handled
+			return 0;
+
+		case -2:	// failed
+			goto fail;
+
+		case -3:	// malformed
+			goto malformed;
+	}
+
 	e->type = GG_EVENT_MSG;
 	e->event.msg.msgclass = gg_fix32(r->msgclass);
 	e->event.msg.sender = gg_fix32(r->sender);
 	e->event.msg.time = gg_fix32(r->time);
-	e->event.msg.message = (unsigned char *)strdup((char*) r + sizeof(*r));
+	e->event.msg.seq = gg_fix32(r->seq);
+	e->event.msg.message = (unsigned char*) strdup((char*) r + sizeof(*r));
 
 	return 0;
 
 malformed:
 	e->type = GG_EVENT_NONE;
-
+	free(e->event.msg.message);
 	free(e->event.msg.recipients);
 	free(e->event.msg.formats);
 
 	return 0;
 
 fail:
+	free(e->event.msg.message);
 	free(e->event.msg.recipients);
 	free(e->event.msg.formats);
 	return -1;
 }
 
-/*
- * gg_watch_fd_connected() // funkcja wewntrzna
+/**
+ * \internal Zamienia tekst w formacie HTML na czysty tekst.
+ *
+ * \param dst Bufor wynikowy (może być \c NULL)
+ * \param html Tekst źródłowy
+ *
+ * \note Dokleja \c \\0 na końcu bufora wynikowego.
  *
- * patrzy na gniazdo, odbiera pakiet i wypenia struktur zdarzenia.
+ * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
+ */
+static int gg_convert_from_html(char *dst, const char *html)
+{
+	const char *src, *entity, *tag;
+	int len, in_tag, in_entity;
+
+	len = 0;
+	in_tag = 0;
+	tag = NULL;
+	in_entity = 0;
+	entity = NULL;
+
+	for (src = html; *src != 0; src++) {
+		if (*src == '<') {
+			tag = src;
+			in_tag = 1;
+			continue;
+		}
+
+		if (in_tag && (*src == '>')) {
+			if (strncmp(tag, "<br", 3) == 0) {
+				if (dst != NULL)
+					dst[len] = '\n';
+				len++;
+			}
+			in_tag = 0;
+			continue;
+		}
+
+		if (in_tag)
+			continue;
+
+		if (*src == '&') {
+			in_entity = 1;
+			entity = src;
+			continue;
+		}
+
+		if (in_entity && *src == ';') {
+			in_entity = 0;
+			if (dst != NULL) {
+				if (strncmp(entity, "&lt;", 4) == 0)
+					dst[len] = '<';
+				else if (strncmp(entity, "&gt;", 4) == 0)
+					dst[len] = '>';
+				else if (strncmp(entity, "&quot;", 6) == 0)
+					dst[len] = '"';
+				else if (strncmp(entity, "&apos;", 6) == 0)
+					dst[len] = '\'';
+				else if (strncmp(entity, "&amp;", 5) == 0)
+					dst[len] = '&';
+				else
+					dst[len] = '?';
+			}
+			len++;
+			continue;
+		}
+
+		if (in_entity && !(isalnum(*src) || *src == '#'))
+			in_entity = 0;
+
+		if (in_entity)
+			continue;
+
+		if (dst != NULL)
+			dst[len] = *src;
+
+		len++;
+	}
+
+	if (dst != NULL)
+		dst[len] = 0;
+	
+	return len;
+}
+
+/**
+ * \internal Analizuje przychodzący pakiet z wiadomością protokołu Gadu-Gadu 8.0.
+ *
+ * Rozbija pakiet na poszczególne składniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h Wskaźnik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
  *
- *  - sess - struktura opisujca sesj
- *  - e - opis zdarzenia
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+{
+	char *packet = (char*) h + sizeof(struct gg_header);
+	struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet;
+	uint32_t offset_plain;
+	uint32_t offset_attr;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %p);\n", h, e);
+
+	if (!r->seq && !r->msgclass) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n");
+		goto malformed;
+	}
+
+	offset_plain = gg_fix32(r->offset_plain);
+	offset_attr  = gg_fix32(r->offset_attr);
+
+	if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= h->length) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n");
+		goto malformed;
+	}
+
+	if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > h->length) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n");
+		offset_attr = 0;	/* nie parsuj attr. */
+		/* goto ignore; */
+	}
+
+	/* Normalna sytuacja, więc nie podpada pod powyższy warunek. */
+	if (offset_attr == h->length)
+		offset_attr = 0;
+
+	if (memchr(packet + offset_plain, 0, h->length - offset_plain) == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n");
+		goto malformed;
+	}
+
+	if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n");
+		goto malformed;
+	}
+
+	e->type = GG_EVENT_MSG;
+	e->event.msg.msgclass = gg_fix32(r->msgclass);
+	e->event.msg.sender = gg_fix32(r->sender);
+	e->event.msg.time = gg_fix32(r->time);
+	e->event.msg.seq = gg_fix32(r->seq);
+
+	if (sess->encoding == GG_ENCODING_CP1250) {
+		e->event.msg.message = (unsigned char*) strdup(packet + offset_plain);
+	} else {
+		if (offset_plain > sizeof(struct gg_recv_msg80)) {
+			int len;
+
+			len = gg_convert_from_html(NULL, packet + sizeof(struct gg_recv_msg80));
+
+			e->event.msg.message = malloc(len + 1);
+
+			if (e->event.msg.message == NULL)
+				goto fail;
+
+			gg_convert_from_html((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80));
+		} else {
+			e->event.msg.message = (unsigned char*) gg_cp_to_utf8(packet + offset_plain);
+		}
+	}
+
+	if (offset_plain > sizeof(struct gg_recv_msg80)) {
+		if (sess->encoding == GG_ENCODING_UTF8)
+			e->event.msg.xhtml_message = strdup(packet + sizeof(struct gg_recv_msg80));
+		else
+			e->event.msg.xhtml_message = gg_utf8_to_cp(packet + sizeof(struct gg_recv_msg80));
+	} else {
+		e->event.msg.xhtml_message = NULL;
+	}
+
+	if (offset_attr != 0) {
+		switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) {
+			case -1:	// handled
+				return 0;
+
+			case -2:	// failed
+				goto fail;
+
+			case -3:	// malformed
+				goto malformed;
+		}
+	}
+
+	return 0;
+
+fail:
+	free(e->event.msg.message);
+	free(e->event.msg.xhtml_message);
+	free(e->event.msg.recipients);
+	free(e->event.msg.formats);
+	return -1;
+
+malformed:
+	e->type = GG_EVENT_NONE;
+	free(e->event.msg.message);
+	free(e->event.msg.xhtml_message);
+	free(e->event.msg.recipients);
+	free(e->event.msg.formats);
+	return 0;
+}
+
+/**
+ * \internal Odbiera pakiet od serwera.
  *
- * 0, -1.
+ * Analizuje pakiet i wypełnia strukturę zdarzenia.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ *
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
  */
 static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
 {
 	struct gg_header *h = NULL;
 	char *p;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
 
 	if (!sess) {
 		errno = EFAULT;
@@ -462,76 +713,86 @@
 	}
 
 	if (!(h = gg_recv_packet(sess))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
 		goto fail;
 	}
 
 	p = (char*) h + sizeof(struct gg_header);
-	
+
 	switch (h->type) {
 		case GG_RECV_MSG:
 		{
 			if (h->length >= sizeof(struct gg_recv_msg))
 				if (gg_handle_recv_msg(h, e, sess))
 					goto fail;
-			
+
 			break;
 		}
 
+		case GG_RECV_MSG80:
+		{
+			if (h->length >= sizeof(struct gg_recv_msg80))
+				if (gg_handle_recv_msg80(h, e, sess))
+					goto fail;
+
+			break;
+		}
+
+
 		case GG_NOTIFY_REPLY:
 		{
 			struct gg_notify_reply *n = (void*) p;
 			unsigned int count, i;
 			char *tmp;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
 
 			if (h->length < sizeof(*n)) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n");
 				errno = EINVAL;
 				goto fail;
 			}
 
 			if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) {
 				e->type = GG_EVENT_NOTIFY_DESCR;
-				
+
 				if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 					goto fail;
 				}
 				e->event.notify_descr.notify[1].uin = 0;
 				memcpy(e->event.notify_descr.notify, p, sizeof(*n));
 				e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin);
 				e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status);
-				e->event.notify_descr.notify[0].remote_ip = e->event.notify_descr.notify[0].remote_ip;
 				e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port);
+				e->event.notify_descr.notify[0].version = gg_fix32(e->event.notify_descr.notify[0].version);
 
 				count = h->length - sizeof(*n);
 				if (!(tmp = malloc(count + 1))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 					goto fail;
 				}
 				memcpy(tmp, p + sizeof(*n), count);
 				tmp[count] = 0;
 				e->event.notify_descr.descr = tmp;
-				
+
 			} else {
 				e->type = GG_EVENT_NOTIFY;
-				
+
 				if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 					goto fail;
 				}
-				
+
 				memcpy(e->event.notify, p, h->length);
 				count = h->length / sizeof(*n);
 				e->event.notify[count].uin = 0;
-				
+
 				for (i = 0; i < count; i++) {
 					e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin);
 					e->event.notify[i].status = gg_fix32(e->event.notify[i].status);
-					e->event.notify[i].remote_ip = e->event.notify[i].remote_ip;
 					e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port);
+					e->event.notify[i].version = gg_fix32(e->event.notify[i].version);
 				}
 			}
 
@@ -542,7 +803,7 @@
 		{
 			struct gg_status *s = (void*) p;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
 
 			if (h->length >= sizeof(*s)) {
 				e->type = GG_EVENT_STATUS;
@@ -564,23 +825,176 @@
 			break;
 		}
 
-		case GG_NOTIFY_REPLY60:
+		case GG_NOTIFY_REPLY77:
+		case GG_NOTIFY_REPLY80BETA:
 		{
-			struct gg_notify_reply60 *n = (void*) p;
+			struct gg_notify_reply77 *n = (void*) p;
 			unsigned int length = h->length, i = 0;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
 
 			e->type = GG_EVENT_NOTIFY60;
 			e->event.notify60 = malloc(sizeof(*e->event.notify60));
 
 			if (!e->event.notify60) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 				goto fail;
 			}
 
 			e->event.notify60[0].uin = 0;
-			
+
+			while (length >= sizeof(struct gg_notify_reply77)) {
+				uin_t uin = gg_fix32(n->uin);
+				char *tmp;
+
+				e->event.notify60[i].uin = uin & 0x00ffffff;
+				e->event.notify60[i].status = n->status;
+				e->event.notify60[i].remote_ip = n->remote_ip;
+				e->event.notify60[i].remote_port = gg_fix16(n->remote_port);
+				e->event.notify60[i].version = n->version;
+				e->event.notify60[i].image_size = n->image_size;
+				e->event.notify60[i].descr = NULL;
+				e->event.notify60[i].time = 0;
+
+				if (uin & 0x40000000)
+					e->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
+				if (uin & 0x20000000)
+					e->event.notify60[i].version |= GG_HAS_AUDIO7_MASK;
+				if (uin & 0x08000000)
+					e->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
+
+				if (GG_S_D(n->status)) {
+					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply77));
+
+					if (sizeof(struct gg_notify_reply77) + descr_len <= length) {
+						char *descr;
+
+						if (!(descr = malloc(descr_len + 1))) {
+							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+							goto fail;
+						}
+
+						memcpy(descr, (char*) n + sizeof(struct gg_notify_reply77) + 1, descr_len);
+						descr[descr_len] = 0;
+
+						if (h->type == GG_NOTIFY_REPLY80BETA && sess->encoding != GG_ENCODING_UTF8) {
+							char *cp_descr = gg_utf8_to_cp(descr);
+
+							if (!cp_descr) {
+								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+								free(descr);
+								goto fail;
+							}
+
+							free(descr);
+							descr = cp_descr;
+						}
+
+						e->event.notify60[i].descr = descr;
+
+						/* XXX czas */
+							
+						length -= sizeof(struct gg_notify_reply77) + descr_len + 1;
+						n = (void*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1);
+					} else {
+						length = 0;
+					}
+
+				} else {
+					length -= sizeof(struct gg_notify_reply77);
+					n = (void*) ((char*) n + sizeof(struct gg_notify_reply77));
+				}
+
+				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					free(e->event.notify60);
+					goto fail;
+				}
+
+				e->event.notify60 = (void*) tmp;
+				e->event.notify60[++i].uin = 0;
+			}
+
+			break;
+		}
+
+		case GG_STATUS77:
+		case GG_STATUS80BETA:
+		{
+			struct gg_status77 *s = (void*) p;
+			uint32_t uin;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+			if (h->length < sizeof(*s))
+				break;
+
+			uin = gg_fix32(s->uin);
+
+			e->type = GG_EVENT_STATUS60;
+			e->event.status60.uin = uin & 0x00ffffff;
+			e->event.status60.status = s->status;
+			e->event.status60.remote_ip = s->remote_ip;
+			e->event.status60.remote_port = gg_fix16(s->remote_port);
+			e->event.status60.version = s->version;
+			e->event.status60.image_size = s->image_size;
+			e->event.status60.descr = NULL;
+			e->event.status60.time = 0;
+
+			if (uin & 0x40000000)
+				e->event.status60.version |= GG_HAS_AUDIO_MASK;
+			if (uin & 0x20000000)
+				e->event.status60.version |= GG_HAS_AUDIO7_MASK;
+			if (uin & 0x08000000)
+				e->event.status60.version |= GG_ERA_OMNIX_MASK;
+
+			if (h->length > sizeof(*s)) {
+				int len = h->length - sizeof(*s);
+				char *buf = malloc(len + 1);
+
+				/* XXX, jesli malloc() sie nie uda to robic tak samo jak przy GG_NOTIFY_REPLY* ?
+				 * 	- goto fail; (?)
+				 */
+				if (buf) {
+					memcpy(buf, (char*) p + sizeof(*s), len);
+					buf[len] = 0;
+
+					if (h->type == GG_STATUS80BETA && sess->encoding != GG_ENCODING_UTF8) {
+						char *cp_buf = gg_utf8_to_cp(buf);
+						free(buf);
+						buf = cp_buf;
+					}
+				}
+
+				e->event.status60.descr = buf;
+
+				if (len > 4 && p[h->length - 5] == 0) {
+					uint32_t t;
+					memcpy(&t, p + h->length - 4, sizeof(uint32_t));
+					e->event.status60.time = gg_fix32(t);
+				}
+			}
+
+			break;
+		}
+
+		case GG_NOTIFY_REPLY60:
+		{
+			struct gg_notify_reply60 *n = (void*) p;
+			unsigned int length = h->length, i = 0;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+			e->type = GG_EVENT_NOTIFY60;
+			e->event.notify60 = malloc(sizeof(*e->event.notify60));
+
+			if (!e->event.notify60) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+				goto fail;
+			}
+
+			e->event.notify60[0].uin = 0;
+
 			while (length >= sizeof(struct gg_notify_reply60)) {
 				uin_t uin = gg_fix32(n->uin);
 				char *tmp;
@@ -602,9 +1016,9 @@
 				if (GG_S_D(n->status)) {
 					unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60));
 
-					if (descr_len < length) {
+					if (sizeof(struct gg_notify_reply60) + descr_len <= length) {
 						if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) {
-							gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 							goto fail;
 						}
 
@@ -612,17 +1026,20 @@
 						e->event.notify60[i].descr[descr_len] = 0;
 
 						/* XXX czas */
+							
+						length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
+						n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
+					} else {
+						length = 0;
 					}
-					
-					length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
-					n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
+
 				} else {
 					length -= sizeof(struct gg_notify_reply60);
 					n = (void*) ((char*) n + sizeof(struct gg_notify_reply60));
 				}
 
 				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
 					free(e->event.notify60);
 					goto fail;
 				}
@@ -633,13 +1050,13 @@
 
 			break;
 		}
-		
+
 		case GG_STATUS60:
 		{
 			struct gg_status60 *s = (void*) p;
 			uint32_t uin;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
 
 			if (h->length < sizeof(*s))
 				break;
@@ -682,11 +1099,132 @@
 			break;
 		}
 
+		case GG_STATUS80:
+		{
+			struct gg_notify_reply80 *s = (void*) p;
+			uint32_t descr_len;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+			if (h->length < sizeof(*s))
+				break;
+
+			e->type = GG_EVENT_STATUS60;
+			e->event.status60.uin		= gg_fix32(s->uin);
+			e->event.status60.status	= gg_fix32(s->status);
+			e->event.status60.remote_ip	= s->remote_ip;
+			e->event.status60.remote_port	= gg_fix16(s->remote_port);
+			e->event.status60.image_size	= s->image_size;
+			e->event.status60.descr		= NULL;
+			e->event.status60.version	= 0x00;	/* not-supported */
+			e->event.status60.time		= 0;	/* not-supported */
+
+			descr_len = gg_fix32(s->descr_len);
+
+			if (descr_len > 0 && h->length-sizeof(*s) >= descr_len) {
+				char *buf = malloc(descr_len + 1);
+
+				if (buf) {
+					memcpy(buf, (char*) p + sizeof(*s), descr_len);
+					buf[descr_len] = 0;
+
+					if (sess->encoding != GG_ENCODING_UTF8) {
+						char *cp_buf = gg_utf8_to_cp(buf);
+						free(buf);
+						buf = cp_buf;
+					}
+				}
+
+				e->event.status60.descr = buf;
+			}
+			break;
+		}
+
+		case GG_NOTIFY_REPLY80:
+		{
+			struct gg_notify_reply80 *n = (void*) p;
+			int length = h->length, i = 0;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+			e->type = GG_EVENT_NOTIFY60;
+			e->event.notify60 = malloc(sizeof(*e->event.notify60));
+			if (!e->event.notify60) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+				goto fail;
+			}
+
+			e->event.notify60[0].uin = 0;
+
+			while (length >= sizeof(struct gg_notify_reply80)) {
+				uint32_t descr_len;
+				char *tmp;
+				
+				e->event.notify60[i].uin	= gg_fix32(n->uin);
+				e->event.notify60[i].status	= gg_fix32(n->status);
+				e->event.notify60[i].remote_ip	= n->remote_ip;
+				e->event.notify60[i].remote_port= gg_fix16(n->remote_port);
+				e->event.notify60[i].image_size	= n->image_size;
+				e->event.notify60[i].descr	= NULL;
+				e->event.notify60[i].version	= 0x00;	/* not-supported */
+				e->event.notify60[i].time	= 0;	/* not-supported */
+
+				
+				descr_len = gg_fix32(n->descr_len);
+
+				length -= sizeof(struct gg_notify_reply80);
+				n = (void*) ((char*) n + sizeof(struct gg_notify_reply80));
+
+				if (descr_len) {
+					if (length >= descr_len) {
+						/* XXX, GG_S_D(n->status) */
+						char *descr;
+
+						if (!(descr = malloc(descr_len + 1))) {
+							gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+							goto fail;
+						}
+
+						memcpy(descr, n, descr_len);
+						descr[descr_len] = 0;
+
+						if (sess->encoding != GG_ENCODING_UTF8) {
+							char *cp_descr = gg_utf8_to_cp(descr);
+
+							if (!cp_descr) {
+								gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+								free(descr);
+								goto fail;
+							}
+
+							free(descr);
+							descr = cp_descr;
+						}
+						e->event.notify60[i].descr = descr;
+
+						length -= descr_len;
+						n = (void*) ((char*) n + descr_len);
+					} else
+						length = 0;
+				}
+
+				if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+					free(e->event.notify60);
+					goto fail;
+				}
+
+				e->event.notify60 = (void*) tmp;
+				e->event.notify60[++i].uin = 0;
+			}
+			break;
+		}
+
 		case GG_SEND_MSG_ACK:
 		{
 			struct gg_send_msg_ack *s = (void*) p;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
 
 			if (h->length < sizeof(*s))
 				break;
@@ -699,9 +1237,9 @@
 			break;
 		}
 
-		case GG_PONG: 
+		case GG_PONG:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
 
 			e->type = GG_EVENT_PONG;
 			sess->last_pong = time(NULL);
@@ -711,27 +1249,47 @@
 
 		case GG_DISCONNECTING:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
 			e->type = GG_EVENT_DISCONNECT;
 			break;
 		}
 
+		case GG_DISCONNECT_ACK:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection acknowledge\n");
+			e->type = GG_EVENT_DISCONNECT_ACK;
+			break;
+		}
+
+		case GG_XML_EVENT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n");
+			e->type = GG_EVENT_XML_EVENT;
+			if (!(e->event.xml_event.data = (char *) malloc(h->length + 1))) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML event data\n");
+				goto fail;
+			}
+			memcpy(e->event.xml_event.data, p, h->length);
+			e->event.xml_event.data[h->length] = 0;
+			break;
+		}
+
 		case GG_PUBDIR50_REPLY:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
-			if (gg_pubdir50_handle_reply(e, p, h->length) == -1)
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
+			if (gg_pubdir50_handle_reply_sess(sess, e, p, h->length) == -1)
 				goto fail;
 			break;
 		}
 
 		case GG_USERLIST_REPLY:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
 
 			if (h->length < 1)
 				break;
 
-			/* jeli odpowied na eksport, wywoaj zdarzenie tylko
+			/* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
 			 * gdy otrzymano wszystkie odpowiedzi */
 			if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) {
 				if (--sess->userlist_blocks)
@@ -743,11 +1301,11 @@
 			if (h->length > 1) {
 				char *tmp;
 				unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0;
-				
-				gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
-				
+
+				gg_debug_session(sess, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
+
 				if (!(tmp = realloc(sess->userlist_reply, len + h->length))) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
 					free(sess->userlist_reply);
 					sess->userlist_reply = NULL;
 					goto fail;
@@ -769,10 +1327,75 @@
 			break;
 		}
 
+		case GG_DCC7_ID_REPLY:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n");
+
+			if (h->length < sizeof(struct gg_dcc7_id_reply))
+				break;
+
+			if (gg_dcc7_handle_id(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_ACCEPT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n");
+
+			if (h->length < sizeof(struct gg_dcc7_accept))
+				break;
+
+			if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_NEW:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n");
+
+			if (h->length < sizeof(struct gg_dcc7_new))
+				break;
+
+			if (gg_dcc7_handle_new(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_REJECT:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n");
+
+			if (h->length < sizeof(struct gg_dcc7_reject))
+				break;
+
+			if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
+		case GG_DCC7_INFO:
+		{
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n");
+
+			if (h->length < sizeof(struct gg_dcc7_info))
+				break;
+
+			if (gg_dcc7_handle_info(sess, e, p, h->length) == -1)
+				goto fail;
+
+			break;
+		}
+
 		default:
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
 	}
-	
+
 	free(h);
 	return 0;
 
@@ -781,19 +1404,20 @@
 	return -1;
 }
 
-/*
- * gg_watch_fd()
- *
- * funkcja, ktr naley wywoa, gdy co si stanie z obserwowanym
- * deskryptorem. zwraca klientowi informacj o tym, co si dzieje.
+/** \endcond */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
  *
- *  - sess - opis sesji
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
  *
- * wskanik do struktury gg_event, ktr trzeba zwolni pniej
- * za pomoc gg_event_free(). jesli rodzaj zdarzenia jest rwny
- * GG_EVENT_NONE, naley je zignorowa. jeli zwrcio NULL,
- * stao si co niedobrego -- albo zabrako pamici albo zerwao
- * poczenie.
+ * \param sess Struktura sesji
+ *
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup events
  */
 struct gg_event *gg_watch_fd(struct gg_session *sess)
 {
@@ -802,68 +1426,79 @@
 	int port = 0;
 	int errno2 = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
-	
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
+
 	if (!sess) {
 		errno = EFAULT;
 		return NULL;
 	}
 
 	if (!(e = (void*) calloc(1, sizeof(*e)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
 		return NULL;
 	}
 
 	e->type = GG_EVENT_NONE;
 
+	if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);
+
+		res = write(sess->fd, sess->send_buf, sess->send_left);
+
+		if (res == -1 && errno != EAGAIN) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno));
+
+			if (sess->state == GG_STATE_READING_REPLY)
+				goto fail_connecting;
+			else
+				goto done;
+		}
+
+		if (res == sess->send_left) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
+			free(sess->send_buf);
+			sess->send_buf = NULL;
+			sess->send_left = 0;
+		} else if (res > 0) {
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res);
+
+			memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
+			sess->send_left -= res;
+		}
+	}
+
 	switch (sess->state) {
 		case GG_STATE_RESOLVING:
 		{
 			struct in_addr addr;
 			int failed = 0;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
 
 			if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
 				failed = 1;
 				errno2 = errno;
 			}
-			
+
 			close(sess->fd);
 			sess->fd = -1;
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-			if (sess->resolver) {
-				pthread_cancel(*((pthread_t*) sess->resolver));
-				free(sess->resolver);
-				sess->resolver = NULL;
-			}
-#elif defined _WIN32
-			if (sess->resolver) {
-				HANDLE h = sess->resolver;
-				TerminateThread(h, 0);
-				CloseHandle(h);
-				sess->resolver = NULL;
-			}
-#else
-			waitpid(sess->pid, NULL, 0);
-			sess->pid = -1;
-#endif
+			sess->resolver_cleanup(&sess->resolver, 0);
 
 			if (failed) {
 				errno = errno2;
 				goto fail_resolving;
 			}
 
-			/* jeli jestemy w resolverze i mamy ustawiony port
-			 * proxy, znaczy, e resolvowalimy proxy. zatem
+			/* jeśli jesteśmy w resolverze i mamy ustawiony port
+			 * proxy, znaczy, że resolvowaliśmy proxy. zatem
 			 * wpiszmy jego adres. */
 			if (sess->proxy_port)
 				sess->proxy_addr = addr.s_addr;
 
 			/* zapiszmy sobie adres huba i adres serwera (do
-			 * bezporedniego poczenia, jeli hub ley)
+			 * bezpośredniego połączenia, jeśli hub leży)
 			 * z resolvera. */
 			if (sess->proxy_addr && sess->proxy_port)
 				port = sess->proxy_port;
@@ -872,21 +1507,27 @@
 				port = GG_APPMSG_PORT;
 			}
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
-			
-			/* czymy si albo z hubem, albo z proxy, zalenie
-			 * od tego, co resolvowalimy. */
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
+
+			/* łączymy się albo z hubem, albo z proxy, zależnie
+			 * od tego, co resolvowaliśmy. */
 			if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) {
-				/* jeli w trybie asynchronicznym gg_connect()
-				 * zwrci bd, nie ma sensu prbowa dalej. */
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+				/* jeśli w trybie asynchronicznym gg_connect()
+				 * zwróci błąd, nie ma sensu próbować dalej. */
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
 				goto fail_connecting;
 			}
 
-			/* jeli podano serwer i czmy si przez proxy,
-			 * jest to bezporednie poczenie, inaczej jest
+			/* jeśli podano serwer i łączmy się przez proxy,
+			 * jest to bezpośrednie połączenie, inaczej jest
 			 * do huba. */
-			sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB;
+
+			if (sess->proxy_addr && sess->proxy_port && sess->server_addr) {
+				sess->state = GG_STATE_CONNECTING_GG;
+				sess->soft_timeout = 1;
+			} else
+				sess->state = GG_STATE_CONNECTING_HUB;
+
 			sess->check = GG_CHECK_WRITE;
 			sess->timeout = GG_DEFAULT_TIMEOUT;
 
@@ -898,42 +1539,25 @@
 			char buf[1024], *client, *auth;
 			int res = 0;
 			socklen_t res_size = sizeof(res);
-			const char *host, *appmsg;
-
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
-
-			/* jeli asynchroniczne, sprawdzamy, czy nie wystpi
-			 * przypadkiem jaki bd. */
+			const char *host;
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
+
+			/* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił
+			 * przypadkiem jakiś błąd. */
 			if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-				/* no tak, nie udao si poczy z proxy. nawet
-				 * nie prbujemy dalej. */
-				if (sess->proxy_addr && sess->proxy_port) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
-					goto fail_connecting;
-				}
-					
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res));
-				close(sess->fd);
-
-				if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) {
-					/* przy asynchronicznych, gg_connect()
-					 * zwraca -1 przy bdach socket(),
-					 * ioctl(), braku routingu itd. dlatego
-					 * nawet nie prbujemy dalej. */
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
-					goto fail_connecting;
-				}
-
-				sess->state = GG_STATE_CONNECTING_GG;
-				sess->check = GG_CHECK_WRITE;
-				sess->timeout = GG_DEFAULT_TIMEOUT;
-				break;
+				if (sess->proxy_addr && sess->proxy_port)
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+				else
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s)\n", res, strerror(res));
+
+				goto fail_connecting;
 			}
-			
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
 
 			if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
 				goto fail_connecting;
 			}
 
@@ -942,41 +1566,43 @@
 			else
 				host = "";
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-			if (sess->ssl)
-				appmsg = "appmsg3.asp";
-			else
+			auth = gg_proxy_auth();
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+			if (sess->ssl) {
+				snprintf(buf, sizeof(buf) - 1,
+					"GET %s/appsvc/appmsg3.asp?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n"
+					"Host: " GG_APPMSG_HOST "\r\n"
+					"User-Agent: " GG_HTTP_USERAGENT "\r\n"
+					"Pragma: no-cache\r\n"
+					"%s"
+					"\r\n", host, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
+			} else
 #endif
-				appmsg = "appmsg2.asp";
-
-			auth = gg_proxy_auth();
-
-			snprintf(buf, sizeof(buf) - 1,
-				"GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n"
-				"Host: " GG_APPMSG_HOST "\r\n"
-				"User-Agent: " GG_HTTP_USERAGENT "\r\n"
-				"Pragma: no-cache\r\n"
-				"%s" 
-				"\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
-
-			if (auth)
-				free(auth);
-			
+			{
+				snprintf(buf, sizeof(buf) - 1,
+					"GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
+					"Host: " GG_APPMSG_HOST "\r\n"
+					"%s"
+					"\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
+			}
+
+			free(auth);
 			free(client);
 
-			/* zwolnij pami po wersji klienta. */
+			/* zwolnij pamięć po wersji klienta. */
 			if (sess->client_version) {
 				free(sess->client_version);
 				sess->client_version = NULL;
 			}
 
-			gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
-	 
-			/* zapytanie jest krtkie, wic zawsze zmieci si
-			 * do bufora gniazda. jeli write() zwrci mniej,
-			 * stao si co zego. */
+			gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
+
+			/* zapytanie jest krótkie, więc zawsze zmieści się
+			 * do bufora gniazda. jeśli write() zwróci mniej,
+			 * stało się coś złego. */
 			if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
 
 				e->type = GG_EVENT_CONN_FAILED;
 				e->event.failure = GG_FAILURE_WRITING;
@@ -999,72 +1625,36 @@
 			int port = GG_DEFAULT_PORT;
 			struct in_addr addr;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
-
-			/* czytamy lini z gniazda i obcinamy \r\n. */
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
+
+			/* czytamy linię z gniazda i obcinamy \r\n. */
 			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 			gg_chomp(buf);
-			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
-	
-			/* sprawdzamy, czy wszystko w porzdku. */
+			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
+
+			/* sprawdzamy, czy wszystko w porządku. */
 			if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n");
-
-				close(sess->fd);
-
-				/* jeli otrzymalimy jakie dziwne informacje,
-				 * prbujemy si czy z pominiciem huba. */
-				if (sess->proxy_addr && sess->proxy_port) {
-					if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
-						/* trudno. nie wyszo. */
-						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
-						goto fail_connecting;
-					}
-
-					sess->state = GG_STATE_CONNECTING_GG;
-					sess->check = GG_CHECK_WRITE;
-					sess->timeout = GG_DEFAULT_TIMEOUT;
-					break;
-				}
-				
-				sess->port = GG_DEFAULT_PORT;
-
-				/* czymy si na port 8074 huba. */
-				if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
-
-					sess->port = GG_HTTPS_PORT;
-					
-					/* czymy si na port 443. */
-					if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
-						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
-						goto fail_connecting;
-					}
-				}
-				
-				sess->state = GG_STATE_CONNECTING_GG;
-				sess->check = GG_CHECK_WRITE;
-				sess->timeout = GG_DEFAULT_TIMEOUT;
-				break;
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
+				goto fail_connecting;
 			}
-	
-			/* ignorujemy reszt nagwka. */
+
+			/* ignorujemy resztę nagłówka. */
 			while (strcmp(buf, "\r\n") && strcmp(buf, ""))
 				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 
-			/* czytamy pierwsz lini danych. */
+			/* czytamy pierwszą linię danych. */
 			gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 			gg_chomp(buf);
-			
-			/* jeli pierwsza liczba w linii nie jest rwna zeru,
-			 * oznacza to, e mamy wiadomo systemow. */
+
+			/* jeśli pierwsza liczba w linii nie jest równa zeru,
+			 * oznacza to, że mamy wiadomość systemową. */
 			if (atoi(buf)) {
 				char tmp[1024], *foo, *sysmsg_buf = NULL;
 				int len = 0;
-				
+
 				while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) {
 					if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) {
-						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
+						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
 						break;
 					}
 
@@ -1074,23 +1664,32 @@
 						strcpy(sysmsg_buf, tmp);
 					else
 						strcat(sysmsg_buf, tmp);
-					
+
 					len += strlen(tmp);
 				}
-				
+
 				e->type = GG_EVENT_MSG;
 				e->event.msg.msgclass = atoi(buf);
 				e->event.msg.sender = 0;
-				e->event.msg.message = (unsigned char *)sysmsg_buf;
+				e->event.msg.message = (unsigned char*) sysmsg_buf;
 			}
-	
+
 			close(sess->fd);
-	
-			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
+
+			gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
 
 			/* analizujemy otrzymane dane. */
 			tmp = buf;
-			
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+			if (!sess->ssl)
+#endif
+			{
+				while (*tmp && *tmp != ' ')
+					tmp++;
+				while (*tmp && *tmp == ' ')
+					tmp++;
+			}
 			while (*tmp && *tmp != ' ')
 				tmp++;
 			while (*tmp && *tmp == ' ')
@@ -1105,36 +1704,43 @@
 				port = atoi(tmp + 1);
 			}
 
+			if (!strcmp(host, "notoperating")) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno));
+				sess->fd = -1;
+				goto fail_unavailable;
+			}
+
 			addr.s_addr = inet_addr(host);
 			sess->server_addr = addr.s_addr;
 
 			if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) {
-				/* jeli mamy proxy, czymy si z nim. */
+				/* jeśli mamy proxy, łączymy się z nim. */
 				if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
-					/* nie wyszo? trudno. */
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
+					/* nie wyszło? trudno. */
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
 					goto fail_connecting;
 				}
-				
+
 				sess->state = GG_STATE_CONNECTING_GG;
 				sess->check = GG_CHECK_WRITE;
 				sess->timeout = GG_DEFAULT_TIMEOUT;
+				sess->soft_timeout = 1;
 				break;
 			}
 
 			sess->port = port;
 
-			/* czymy si z waciwym serwerem. */
+			/* łączymy się z właściwym serwerem. */
 			if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
 
 				sess->port = GG_HTTPS_PORT;
 
-				/* nie wyszo? prbujemy portu 443. */
+				/* nie wyszło? próbujemy portu 443. */
 				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
-					/* ostatnia deska ratunku zawioda?
+					/* ostatnia deska ratunku zawiodła?
 					 * w takim razie zwijamy manatki. */
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 					goto fail_connecting;
 				}
 			}
@@ -1142,7 +1748,8 @@
 			sess->state = GG_STATE_CONNECTING_GG;
 			sess->check = GG_CHECK_WRITE;
 			sess->timeout = GG_DEFAULT_TIMEOUT;
-		
+			sess->soft_timeout = 1;
+
 			break;
 		}
 
@@ -1151,14 +1758,16 @@
 			int res = 0;
 			socklen_t res_size = sizeof(res);
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
-
-			/* jeli wystpi bd podczas czenia si... */
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
+
+			sess->soft_timeout = 0;
+
+			/* jeśli wystąpił błąd podczas łączenia się... */
 			if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
-				/* jeli nie udao si poczenie z proxy,
-				 * nie mamy czego prbowa wicej. */
+				/* jeśli nie udało się połączenie z proxy,
+				 * nie mamy czego próbować więcej. */
 				if (sess->proxy_addr && sess->proxy_port) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
 					goto fail_connecting;
 				}
 
@@ -1170,36 +1779,46 @@
 					errno = ETIMEDOUT;
 #endif
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-				/* jeli logujemy si po TLS, nie prbujemy
-				 * si czy ju z niczym innym w przypadku
-				 * bdu. nie do, e nie ma sensu, to i
-				 * trzeba by si bawi w tworzenie na nowo
+#ifdef GG_CONFIG_HAVE_OPENSSL
+				/* jeśli logujemy się po TLS, nie próbujemy
+				 * się łączyć już z niczym innym w przypadku
+				 * błędu. nie dość, że nie ma sensu, to i
+				 * trzeba by się bawić w tworzenie na nowo
 				 * SSL i SSL_CTX. */
 
 				if (sess->ssl) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
 					goto fail_connecting;
 				}
 #endif
 
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
+
+				if (sess->port == GG_HTTPS_PORT)
+					goto fail_connecting;
 
 				sess->port = GG_HTTPS_PORT;
 
-				/* prbujemy na port 443. */
+				/* próbujemy na port 443. */
 				if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 					goto fail_connecting;
 				}
+
+				sess->state = GG_STATE_CONNECTING_GG;
+				sess->check = GG_CHECK_WRITE;
+				sess->timeout = GG_DEFAULT_TIMEOUT;
+				sess->soft_timeout = 1;
+
+				break;
 			}
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
-			
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
+
 			if (gg_proxy_http_only)
 				sess->proxy_port = 0;
 
-			/* jeli mamy proxy, wylijmy zapytanie. */
+			/* jeśli mamy proxy, wyślijmy zapytanie. */
 			if (sess->proxy_addr && sess->proxy_port) {
 				char buf[100], *auth = gg_proxy_auth();
 				struct in_addr addr;
@@ -1211,20 +1830,22 @@
 
 				snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
 
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n//   %s", buf);
-				
-				/* wysyamy zapytanie. jest ono na tyle krtkie,
-				 * e musi si zmieci w buforze gniazda. jeli
-				 * write() zawiedzie, stao si co zego. */
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n//   %s", buf);
+
+				/* wysyłamy zapytanie. jest ono na tyle krótkie,
+				 * że musi się zmieścić w buforze gniazda. jeśli
+				 * write() zawiedzie, stało się coś złego. */
 				if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					free(auth);
 					goto fail_connecting;
 				}
 
 				if (auth) {
-					gg_debug(GG_DEBUG_MISC, "//   %s", auth);
+					gg_debug_session(sess, GG_DEBUG_MISC, "//   %s", auth);
 					if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) {
-						gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+						free(auth);
 						goto fail_connecting;
 					}
 
@@ -1232,12 +1853,12 @@
 				}
 
 				if (write(sess->fd, "\r\n", 2) < 2) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
 					goto fail_connecting;
 				}
 			}
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 			if (sess->ssl) {
 				SSL_set_fd(sess->ssl, sess->fd);
 
@@ -1256,19 +1877,19 @@
 			break;
 		}
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 		case GG_STATE_TLS_NEGOTIATION:
 		{
 			int res;
 			X509 *peer;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
 
 			if ((res = SSL_connect(sess->ssl)) <= 0) {
 				int err = SSL_get_error(sess->ssl, res);
 
 				if (res == 0) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
 
 					e->type = GG_EVENT_CONN_FAILED;
 					e->event.failure = GG_FAILURE_TLS;
@@ -1277,9 +1898,9 @@
 					sess->fd = -1;
 					break;
 				}
-				
+
 				if (err == SSL_ERROR_WANT_READ) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
 
 					sess->state = GG_STATE_TLS_NEGOTIATION;
 					sess->check = GG_CHECK_READ;
@@ -1287,7 +1908,7 @@
 
 					break;
 				} else if (err == SSL_ERROR_WANT_WRITE) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
 
 					sess->state = GG_STATE_TLS_NEGOTIATION;
 					sess->check = GG_CHECK_WRITE;
@@ -1299,8 +1920,8 @@
 
 					ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
 
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
- 
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
+
 					e->type = GG_EVENT_CONN_FAILED;
 					e->event.failure = GG_FAILURE_TLS;
 					sess->state = GG_STATE_IDLE;
@@ -1310,20 +1931,20 @@
 				}
 			}
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n//   cipher: %s\n", SSL_get_cipher_name(sess->ssl));
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n//   cipher: %s\n", SSL_get_cipher_name(sess->ssl));
 
 			peer = SSL_get_peer_certificate(sess->ssl);
 
 			if (!peer)
-				gg_debug(GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! unable to get peer certificate!\n");
 			else {
 				char buf[1024];
 
 				X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
-				gg_debug(GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
+				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert subject: %s\n", buf);
 
 				X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
-				gg_debug(GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
+				gg_debug_session(sess, GG_DEBUG_MISC, "//   cert issuer: %s\n", buf);
 			}
 
 			sess->state = GG_STATE_READING_KEY;
@@ -1336,45 +1957,48 @@
 
 		case GG_STATE_READING_KEY:
 		{
-			struct gg_header *h;			
+			struct gg_header *h;
 			struct gg_welcome *w;
-			struct gg_login60 l;
-			unsigned int hash;
-			char *password = sess->password;
+			unsigned char *password = (unsigned char*) sess->password;
 			int ret;
-			
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
-
-			memset(&l, 0, sizeof(l));
-			l.dunno2 = 0xbe;
-
-			/* XXX bardzo, bardzo, bardzo gupi pomys na pozbycie
-			 * si tekstu wrzucanego przez proxy. */
+			uint8_t login_hash[64];
+
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
+
+			memset(login_hash, 0, sizeof(login_hash));
+
+			/* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie
+			 * się tekstu wrzucanego przez proxy. */
 			if (sess->proxy_addr && sess->proxy_port) {
 				char buf[100];
 
 				strcpy(buf, "");
 				gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 				gg_chomp(buf);
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n//   %s\n", buf);
-				
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n//   %s\n", buf);
+
 				while (strcmp(buf, "")) {
 					gg_read_line(sess->fd, buf, sizeof(buf) - 1);
 					gg_chomp(buf);
 					if (strcmp(buf, ""))
-						gg_debug(GG_DEBUG_MISC, "//   %s\n", buf);
+						gg_debug_session(sess, GG_DEBUG_MISC, "//   %s\n", buf);
 				}
 
 				/* XXX niech czeka jeszcze raz w tej samej
-				 * fazie. gupio, ale dziaa. */
+				 * fazie. głupio, ale działa. */
 				sess->proxy_port = 0;
-				
+
 				break;
 			}
 
 			/* czytaj pierwszy pakiet. */
 			if (!(h = gg_recv_packet(sess))) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+				if (errno == EAGAIN) {
+					sess->check = GG_CHECK_READ;
+					break;
+				}
+
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
 
 				e->type = GG_EVENT_CONN_FAILED;
 				e->event.failure = GG_FAILURE_READING;
@@ -1387,7 +2011,7 @@
 			}
 
 			if (h->type != GG_WELCOME) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
 				free(h);
 				close(sess->fd);
 				sess->fd = -1;
@@ -1397,61 +2021,127 @@
 				sess->state = GG_STATE_IDLE;
 				break;
 			}
-	
+
 			w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header));
 			w->key = gg_fix32(w->key);
 
-			hash = gg_login_hash((unsigned char *)password, w->key);
-	
-			gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash);
-	
+			switch (sess->hash_type) {
+				case GG_LOGIN_HASH_GG32:
+				{
+					unsigned int hash;
+
+					hash = gg_fix32(gg_login_hash(password, w->key));
+					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash);
+					memcpy(login_hash, &hash, sizeof(hash));
+
+					break;
+				}
+
+				case GG_LOGIN_HASH_SHA1:
+				{
+					char tmp[41];
+					int i;
+
+					gg_login_hash_sha1((char*) password, w->key, login_hash);
+					for (i = 0; i < 40; i += 2)
+						snprintf(tmp + i, sizeof(tmp) - i, "%02x", login_hash[i / 2]);
+
+					gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp);
+
+					break;
+				}
+			}
+
 			free(h);
-
 			free(sess->password);
 			sess->password = NULL;
 
-			{
-				struct in_addr dcc_ip;
-				dcc_ip.s_addr = gg_dcc_ip;
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip));
-			}
-			
 			if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
 				struct sockaddr_in sin;
 				socklen_t sin_len = sizeof(sin);
 
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
 
 				if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
-					l.local_ip = sin.sin_addr.s_addr;
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
+					sess->client_addr = sin.sin_addr.s_addr;
 				} else {
-					gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
-					l.local_ip = 0;
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
+					sess->client_addr = 0;
 				}
-			} else 
-				l.local_ip = gg_dcc_ip;
-		
-			l.uin = gg_fix32(sess->uin);
-			l.hash = gg_fix32(hash);
-			l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
-			l.version = gg_fix32(sess->protocol_version);
-			l.local_port = gg_fix16(gg_dcc_port);
-			l.image_size = sess->image_size;
-			
-			if (sess->external_addr && sess->external_port > 1023) {
-				l.external_ip = sess->external_addr;
-				l.external_port = gg_fix16(sess->external_port);
+			} else
+				sess->client_addr = gg_dcc_ip;
+
+			if (sess->protocol_version >= 0x2e) {
+				struct gg_login80 l;
+
+				uint32_t tmp_version_len	= gg_fix32(strlen(GG8_VERSION));
+				uint32_t tmp_descr_len		= gg_fix32((sess->initial_descr) ? strlen(sess->initial_descr) : 0);
+				
+				memset(&l, 0, sizeof(l));
+				l.uin           = gg_fix32(sess->uin);
+				memcpy(l.language, GG8_LANG, sizeof(l.language));
+				l.hash_type     = sess->hash_type;
+				memcpy(l.hash, login_hash, sizeof(login_hash));
+				l.status        = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+				l.flags		= gg_fix32(0x00800001);
+				l.features	= gg_fix32(sess->protocol_features);
+				l.image_size    = sess->image_size;
+				l.dunno2        = 0x64;
+
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80 packet\n");
+				ret = gg_send_packet(sess, GG_LOGIN80, 
+						&l, sizeof(l), 
+						&tmp_version_len, sizeof(uint32_t), GG8_VERSION, strlen(GG8_VERSION),
+						&tmp_descr_len, sizeof(uint32_t), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+						NULL);
+
+			} else if (sess->protocol_version == 0x2d) {
+				struct gg_login70 l;
+
+				memset(&l, 0, sizeof(l));
+				l.uin		= gg_fix32(sess->uin);
+				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr;
+				l.local_port	= gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
+				l.hash_type	= sess->hash_type;
+				memcpy(l.hash, login_hash, sizeof(login_hash));
+				l.image_size	= sess->image_size;
+				l.dunno2 	= 0x64;
+				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags);
+
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80BETA packet\n");
+				ret = gg_send_packet(sess, GG_LOGIN80BETA,
+						&l, sizeof(l),
+						sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+						(sess->initial_descr) ? "\0" : NULL, (sess->initial_descr) ? 1 : 0,
+						NULL);
+			} else {
+				struct gg_login70 l;
+
+				memset(&l, 0, sizeof(l));
+				l.local_ip	= (sess->external_addr) ? sess->external_addr : sess->client_addr;
+				l.uin		= gg_fix32(sess->uin);
+				l.local_port	= gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port);
+				l.hash_type	= sess->hash_type;
+				memcpy(l.hash, login_hash, sizeof(login_hash));
+				l.image_size	= sess->image_size;
+				l.dunno2	= 0xbe;
+				l.status	= gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+				l.version	= gg_fix32(sess->protocol_version | sess->protocol_flags);
+
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN70 packet\n");
+				ret = gg_send_packet(sess, GG_LOGIN70,
+						&l, sizeof(l),
+						sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0,
+						NULL);
 			}
 
-			gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n");
-			ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL);
-
 			free(sess->initial_descr);
 			sess->initial_descr = NULL;
 
 			if (ret == -1) {
-				gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
+				gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
 				errno2 = errno;
 				close(sess->fd);
 				errno = errno2;
@@ -1461,8 +2151,9 @@
 				sess->state = GG_STATE_IDLE;
 				break;
 			}
-	
+
 			sess->state = GG_STATE_READING_REPLY;
+			sess->check = GG_CHECK_READ;
 
 			break;
 		}
@@ -1471,10 +2162,15 @@
 		{
 			struct gg_header *h;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
 
 			if (!(h = gg_recv_packet(sess))) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+				if (errno == EAGAIN) {
+					sess->check = GG_CHECK_READ;
+					break;
+				}
+
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
 				e->type = GG_EVENT_CONN_FAILED;
 				e->event.failure = GG_FAILURE_READING;
 				sess->state = GG_STATE_IDLE;
@@ -1484,11 +2180,12 @@
 				sess->fd = -1;
 				break;
 			}
-	
-			if (h->type == GG_LOGIN_OK) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
+
+			if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL || h->type == GG_LOGIN80_OK) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
 				e->type = GG_EVENT_CONN_SUCCESS;
 				sess->state = GG_STATE_CONNECTED;
+				sess->check = GG_CHECK_READ;
 				sess->timeout = -1;
 				sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL;
 				free(h);
@@ -1496,15 +2193,15 @@
 			}
 
 			if (h->type == GG_LOGIN_FAILED) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
 				e->event.failure = GG_FAILURE_PASSWORD;
 				errno = EACCES;
-			} else if (h->type == GG_NEED_EMAIL) {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() email change needed\n");
-				e->event.failure = GG_FAILURE_NEED_EMAIL;
+			} else if (h->type == GG_DISCONNECTING) {
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n");
+				e->event.failure = GG_FAILURE_INTRUDER;
 				errno = EACCES;
 			} else {
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
 				e->event.failure = GG_FAILURE_INVALID;
 				errno = EINVAL;
 			}
@@ -1522,13 +2219,13 @@
 
 		case GG_STATE_CONNECTED:
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
 
 			sess->last_event = time(NULL);
-			
+
 			if ((res = gg_watch_fd_connected(sess, e)) == -1) {
 
-				gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno));
 
  				if (errno == EAGAIN) {
 					e->type = GG_EVENT_NONE;
@@ -1536,6 +2233,9 @@
 				} else
 					res = -1;
 			}
+
+			sess->check = GG_CHECK_READ;
+
 			break;
 		}
 	}
@@ -1544,10 +2244,13 @@
 	if (res == -1) {
 		free(e);
 		e = NULL;
+	} else {
+		if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED))
+			sess->check |= GG_CHECK_WRITE;
 	}
 
 	return e;
-	
+
 fail_connecting:
 	if (sess->fd != -1) {
 		errno2 = errno;
@@ -1565,6 +2268,12 @@
 	e->event.failure = GG_FAILURE_RESOLVING;
 	sess->state = GG_STATE_IDLE;
 	goto done;
+
+fail_unavailable:
+	e->type = GG_EVENT_CONN_FAILED;
+	e->event.failure = GG_FAILURE_UNAVAILABLE;
+	sess->state = GG_STATE_IDLE;
+	goto done;
 }
 
 /*
--- a/libpurple/protocols/gg/lib/http.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/http.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1,4 +1,4 @@
-/* $Id: http.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: http.c 833 2009-10-01 20:48:01Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -14,52 +14,68 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file http.c
+ *
+ * \brief Obsługa połączeń HTTP
+ */
+
 #include "libgadu.h"
 
 #include <sys/types.h>
+
 #ifndef _WIN32
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
 #endif
 
-#include "libgadu-config.h"
+#include "compat.h"
+#include "resolver.h"
 
 #include <ctype.h>
 #include <errno.h>
+
 #ifndef _WIN32
-#include <netdb.h>
+#  include <netdb.h>
 #endif
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-#  include <pthread.h>
-#endif
+
+#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include "compat.h"
-
-/*
- * gg_http_connect() // funkcja pomocnicza
+/**
+ * Rozpoczyna połączenie HTTP.
  *
- * rozpoczyna poczenie po http.
+ * Funkcja przeprowadza połączenie HTTP przy połączeniu synchronicznym,
+ * zwracając wynik w polach struktury \c gg_http, lub błąd, gdy sesja się
+ * nie powiedzie.
+ *
+ * Przy połączeniu asynchronicznym, funkcja rozpoczyna połączenie, a dalsze
+ * etapy będą przeprowadzane po wykryciu zmian (\c watch) na obserwowanym
+ * deskryptorze (\c fd) i wywołaniu funkcji \c gg_http_watch_fd().
  *
- *  - hostname - adres serwera
- *  - port - port serwera
- *  - async - asynchroniczne poczenie
- *  - method - metoda http (GET, POST, cokolwiek)
- *  - path - cieka do zasobu (musi by poprzedzona ,,/'')
- *  - header - nagwek zapytania plus ewentualne dane dla POST
+ * Po zakończeniu, należy zwolnić strukturę za pomocą funkcji
+ * \c gg_http_free(). Połączenie asynchroniczne można zatrzymać w każdej
+ * chwili za pomocą \c gg_http_stop().
  *
- * zaalokowana struct gg_http, ktr poniej naley
- * zwolni funkcj gg_http_free(), albo NULL jeli wystpi bd.
+ * \param hostname Adres serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego połączenia
+ * \param method Metoda HTTP
+ * \param path Ścieżka do zasobu (musi być poprzedzona znakiem '/')
+ * \param header Nagłówek zapytania plus ewentualne dane dla POST
+ *
+ * \return Zaalokowana struktura \c gg_http lub NULL, jeśli wystąpił błąd.
+ *
+ * \ingroup http
  */
 struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header)
 {
@@ -70,7 +86,7 @@
 		errno = EFAULT;
 		return NULL;
 	}
-	
+
 	if (!(h = malloc(sizeof(*h))))
 		return NULL;
 	memset(h, 0, sizeof(*h));
@@ -80,6 +96,8 @@
 	h->fd = -1;
 	h->type = GG_SESSION_HTTP;
 
+	gg_http_set_resolver(h, GG_RESOLVER_DEFAULT);
+
 	if (gg_proxy_enabled) {
 		char *auth = gg_proxy_auth();
 
@@ -88,9 +106,8 @@
 				"", header);
 		hostname = gg_proxy_host;
 		h->port = port = gg_proxy_port;
+		free(auth);
 
-		if (auth)
-			free(auth);
 	} else {
 		h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
 				method, path, header);
@@ -102,17 +119,11 @@
 		errno = ENOMEM;
 		return NULL;
 	}
-	
+
 	gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
 
 	if (async) {
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-		if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) {
-#elif defined _WIN32
-		if (gg_resolve_win32thread(&h->fd, &h->resolver, hostname)) {
-#else
-		if (gg_resolve(&h->fd, &h->pid, hostname)) {
-#endif
+		if (h->resolver_start(&h->fd, &h->resolver, hostname) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
 			gg_http_free(h);
 			errno = ENOENT;
@@ -125,19 +136,16 @@
 		h->check = GG_CHECK_READ;
 		h->timeout = GG_DEFAULT_TIMEOUT;
 	} else {
-		struct in_addr *hn, a;
+		struct in_addr addr;
 
-		if (!(hn = gg_gethostbyname(hostname))) {
+		if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
 			gg_http_free(h);
 			errno = ENOENT;
 			return NULL;
-		} else {
-			a.s_addr = hn->s_addr;
-			free(hn);
 		}
 
-		if (!(h->fd = gg_connect(&a, port, 0)) == -1) {
+		if ((h->fd = gg_connect(&addr, port, 0)) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno));
 			gg_http_free(h);
 			return NULL;
@@ -159,10 +167,12 @@
 
 	h->callback = gg_http_watch_fd;
 	h->destroy = gg_http_free;
-	
+
 	return h;
 }
 
+#ifndef DOXYGEN
+
 #define gg_http_error(x) \
 	close(h->fd); \
 	h->fd = -1; \
@@ -170,17 +180,22 @@
 	h->error = x; \
 	return 0;
 
-/*
- * gg_http_watch_fd()
- *
- * przy asynchronicznej obsudze HTTP funkcj t naley wywoa, jeli
- * zmienio si co na obserwowanym deskryptorze.
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
  *
- *  - h - struktura opisujca poczenie
+ * Operacja będzie zakończona, gdy pole \c state będzie równe
+ * \c GG_STATE_PARSING. W tym miejscu działanie przejmuje zwykle funkcja
+ * korzystająca z \c gg_http_watch_fd(). W przypadku błędu połączenia,
+ * pole \c state będzie równe \c GG_STATE_ERROR, a kod błędu znajdzie się
+ * w polu \c error.
  *
- * jeli wszystko poszo dobrze to 0, inaczej -1. poczenie bdzie
- * zakoczone, jeli h->state == GG_STATE_PARSING. jeli wystpi jaki
- * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error.
+ * \param h Struktura połączenia
+ *
+ * \return \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup http
  */
 int gg_http_watch_fd(struct gg_http *h)
 {
@@ -205,22 +220,7 @@
 		close(h->fd);
 		h->fd = -1;
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-		if (h->resolver) {
-			pthread_cancel(*((pthread_t *) h->resolver));
-			free(h->resolver);
-			h->resolver = NULL;
-		}
-#elif defined _WIN32
-		if (h->resolver) {
-			HANDLE hnd = h->resolver;
-			TerminateThread(hnd, 0);
-			CloseHandle(hnd);
-			h->resolver = NULL;
-		}
-#else
-		waitpid(h->pid, NULL, 0);
-#endif
+		h->resolver_cleanup(&h->resolver, 0);
 
 		gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port);
 
@@ -264,7 +264,7 @@
 			gg_http_error(GG_ERROR_WRITING);
 		}
 
-		if (res < 0 || (size_t)res < strlen(h->query)) {
+		if (res < strlen(h->query)) {
 			gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res);
 
 			memmove(h->query, h->query + res, strlen(h->query) - res + 1);
@@ -346,7 +346,7 @@
 			h->body_size = 0;
 			line = h->header;
 			*tmp = 0;
-                        
+
 			gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
 
 			while (line) {
@@ -449,7 +449,7 @@
 
 		return 0;
 	}
-	
+
 	if (h->fd != -1)
 		close(h->fd);
 
@@ -460,14 +460,14 @@
 	return -1;
 }
 
-#undef gg_http_error
-
-/*
- * gg_http_stop()
+/**
+ * Kończy asynchroniczne połączenie HTTP.
  *
- * jeli poczenie jest w trakcie, przerywa je. nie zwalnia h->data.
- * 
- *  - h - struktura opisujca poczenie
+ * Po zatrzymaniu należy zwolnić zasoby funkcją \c gg_http_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup http
  */
 void gg_http_stop(struct gg_http *h)
 {
@@ -477,15 +477,20 @@
 	if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
 		return;
 
-	if (h->fd != -1)
+	if (h->fd != -1) {
 		close(h->fd);
-	h->fd = -1;
+		h->fd = -1;
+	}
+
+	h->resolver_cleanup(&h->resolver, 1);
 }
 
-/*
- * gg_http_free_fields() // funkcja wewntrzna
+/**
+ * \internal Zwalnia pola struktury \c gg_http.
  *
- * zwalnia pola struct gg_http, ale nie zwalnia samej struktury.
+ * Funkcja zwalnia same pola, nie zwalnia struktury.
+ *
+ * \param h Struktura połączenia
  */
 void gg_http_free_fields(struct gg_http *h)
 {
@@ -501,19 +506,21 @@
 		free(h->query);
 		h->query = NULL;
 	}
-	
+
 	if (h->header) {
 		free(h->header);
 		h->header = NULL;
 	}
 }
 
-/*
- * gg_http_free()
+/**
+ * Zwalnia zasoby po połączeniu HTTP.
  *
- * prbuje zamkn poczenie i zwalnia pami po nim.
+ * Jeśli połączenie nie zostało jeszcze zakończone, jest przerywane.
  *
- *  - h - struktura, ktr naley zlikwidowa
+ * \param h Struktura połączenia
+ *
+ * \ingroup http
  */
 void gg_http_free(struct gg_http *h)
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/libgadu-internal.h	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,30 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#ifndef LIBGADU_INTERNAL_H
+#define LIBGADU_INTERNAL_H
+
+#include "libgadu.h"
+
+char *gg_cp_to_utf8(const char *b);
+char *gg_utf8_to_cp(const char *b);
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length);
+
+#endif /* LIBGADU_INTERNAL_H */
--- a/libpurple/protocols/gg/lib/libgadu.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/libgadu.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1,10 +1,11 @@
-/* $Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
- *                          Arkadiusz Mikiewicz <arekm@pld-linux.org>
- *                          Tomasz Chiliski <chilek@chilan.com>
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -17,227 +18,200 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file libgadu.c
+ *
+ * \brief Główny moduł biblioteki
+ */
+
 #include "libgadu.h"
+#include "libgadu-config.h"
+#include "libgadu-internal.h"
 
 #include <sys/types.h>
-#ifndef _WIN32
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#ifdef sun
-#  include <sys/filio.h>
-#endif
+
+#ifdef _WIN32
+#  include <io.h>
+#  include <fcntl.h>
+#  include <errno.h>
+#  define SHUT_RDWR SD_BOTH
 #else
-#include <io.h>
-#include <fcntl.h>
-#include <errno.h>
-#define SHUT_RDWR SD_BOTH
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  ifdef sun
+#    include <sys/filio.h>
+#  endif
 #endif
 
-#include "libgadu-config.h"
+#include "compat.h"
+#include "protocol.h"
+#include "resolver.h"
 
-#include <errno.h>
 #ifndef _WIN32
-#include <netdb.h>
+#  include <errno.h> /* on Win32 this is included above */
+#  include <netdb.h>
 #endif
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-#  include <pthread.h>
-#endif
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <signal.h>
+#include <time.h>
 #include <unistd.h>
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 #  include <openssl/err.h>
 #  include <openssl/rand.h>
 #endif
 
-#include "compat.h"
+#define GG_LIBGADU_VERSION "1.9.0-rc2"
+
+/**
+ * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową
+ * składającą się ze stałych \c GG_DEBUG_...
+ *
+ * \ingroup debug
+ */
+int gg_debug_level = 0;
 
-int gg_debug_level = 0;
+/**
+ * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe
+ * \c NULL, informacje są wysyłane do standardowego wyjścia błędu (\c stderr).
+ *
+ * \param level Poziom rejestracji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
+ *
+ * \note Funkcja jest przesłaniana przez \c gg_debug_handler_session.
+ *
+ * \ingroup debug
+ */
 void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL;
 
+/**
+ * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe
+ * \c NULL, informacje są wysyłane do standardowego wyjścia błędu.
+ *
+ * \param sess Sesja której dotyczy informacja lub \c NULL
+ * \param level Poziom rejestracji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentów (zgodna z \c printf)
+ *
+ * \note Funkcja przesłania przez \c gg_debug_handler_session.
+ *
+ * \ingroup debug
+ */
+void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL;
+
+/**
+ * Port gniazda nasłuchującego dla połączeń bezpośrednich.
+ * 
+ * \ingroup ip
+ */
 int gg_dcc_port = 0;
+
+/**
+ * Adres IP gniazda nasłuchującego dla połączeń bezpośrednich.
+ *
+ * \ingroup ip
+ */
 unsigned long gg_dcc_ip = 0;
 
+/**
+ * Adres lokalnego interfejsu IP, z którego wywoływane są wszystkie połączenia.
+ *
+ * \ingroup ip
+ */
 unsigned long gg_local_ip = 0;
-/*
- * zmienne opisujce parametry proxy http.
+
+/**
+ * Flaga włączenia połączeń przez serwer pośredniczący.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_enabled = 0;
+
+/**
+ * Adres serwera pośredniczącego.
+ *
+ * \ingroup proxy
  */
 char *gg_proxy_host = NULL;
+
+/**
+ * Port serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
 int gg_proxy_port = 0;
-int gg_proxy_enabled = 0;
+
+/**
+ * Flaga używania serwera pośredniczącego jedynie dla usług HTTP.
+ *
+ * \ingroup proxy
+ */
 int gg_proxy_http_only = 0;
+
+/**
+ * Nazwa użytkownika do autoryzacji serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
 char *gg_proxy_username = NULL;
+
+/**
+ * Hasło użytkownika do autoryzacji serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
 char *gg_proxy_password = NULL;
 
-#ifndef lint 
+#ifndef DOXYGEN
+
+#ifndef lint
 static char rcsid[]
 #ifdef __GNUC__
 __attribute__ ((unused))
 #endif
-= "$Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $";
-#endif 
-
-#ifdef _WIN32
-/**
- *  Deal with the fact that you can't select() on a win32 file fd.
- *  This makes it practically impossible to tie into purple's event loop.
- *
- *  -This is thanks to Tor Lillqvist.
- *  XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu.
- */
-static int
-socket_pipe (int *fds)
-{
-	SOCKET temp, socket1 = -1, socket2 = -1;
-	struct sockaddr_in saddr;
-	int len;
-	u_long arg;
-	fd_set read_set, write_set;
-	struct timeval tv;
-
-	temp = socket(AF_INET, SOCK_STREAM, 0);
-
-	if (temp == INVALID_SOCKET) {
-		goto out0;
-	}
-
-	arg = 1;
-	if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out0;
-	}
-
-	memset(&saddr, 0, sizeof(saddr));
-	saddr.sin_family = AF_INET;
-	saddr.sin_port = 0;
-	saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-
-	if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) {
-		goto out0;
-	}
-
-	if (listen(temp, 1) == SOCKET_ERROR) {
-		goto out0;
-	}
-
-	len = sizeof(saddr);
-	if (getsockname(temp, (struct sockaddr *)&saddr, &len)) {
-		goto out0;
-	}
-
-	socket1 = socket(AF_INET, SOCK_STREAM, 0);
-
-	if (socket1 == INVALID_SOCKET) {
-		goto out0;
-	}
-
-	arg = 1;
-	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out1;
-	}
-
-	if (connect(socket1, (struct sockaddr  *)&saddr, len) != SOCKET_ERROR ||
-			WSAGetLastError() != WSAEWOULDBLOCK) {
-		goto out1;
-	}
-
-	FD_ZERO(&read_set);
-	FD_SET(temp, &read_set);
-
-	tv.tv_sec = 0;
-	tv.tv_usec = 0;
-
-	if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) {
-		goto out1;
-	}
-
-	if (!FD_ISSET(temp, &read_set)) {
-		goto out1;
-	}
-
-	socket2 = accept(temp, (struct sockaddr *) &saddr, &len);
-	if (socket2 == INVALID_SOCKET) {
-		goto out1;
-	}
-
-	FD_ZERO(&write_set);
-	FD_SET(socket1, &write_set);
-
-	tv.tv_sec = 0;
-	tv.tv_usec = 0;
-
-	if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) {
-		goto out2;
-	}
-
-	if (!FD_ISSET(socket1, &write_set)) {
-		goto out2;
-	}
-
-	arg = 0;
-	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out2;
-	}
-
-	arg = 0;
-	if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) {
-		goto out2;
-	}
-
-	fds[0] = socket1;
-	fds[1] = socket2;
-
-	closesocket (temp);
-
-	return 0;
-
-out2:
-	closesocket (socket2);
-out1:
-	closesocket (socket1);
-out0:
-	closesocket (temp);
-	errno = EIO;            /* XXX */
-
-	return -1;
-}
+= "$Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $";
 #endif
 
-/*
- * gg_libgadu_version()
+#endif /* DOXYGEN */
+
+/**
+ * Zwraca wersję biblioteki.
  *
- * zwraca wersj libgadu.
+ * \return Wskaźnik na statyczny bufor z wersją biblioteki.
  *
- *  - brak
- *
- * wersja libgadu.
+ * \ingroup version
  */
 const char *gg_libgadu_version()
 {
 	return GG_LIBGADU_VERSION;
 }
 
-/*
- * gg_fix32()
+/**
+ * \internal Zamienia kolejność bajtów w 32-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych odwraca kolejność bajtów w słowie.
  *
- * zamienia kolejno bajtw w liczbie 32-bitowej tak, by odpowiadaa
- * kolejnoci bajtw w protokole GG. ze wzgldu na LE-owo serwera,
- * zamienia tylko na maszynach BE-wych.
+ * \param x Liczba do zamiany
  *
- *  - x - liczba do zamiany
+ * \return Liczba z odpowiednią kolejnością bajtów
  *
- * liczba z odpowiedni kolejnoci bajtw.
+ * \ingroup helper
  */
 uint32_t gg_fix32(uint32_t x)
 {
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
 	return x;
 #else
 	return (uint32_t)
@@ -248,20 +222,21 @@
 #endif
 }
 
-/*
- * gg_fix16()
+/**
+ * \internal Zamienia kolejność bajtów w 16-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych zamienia kolejność bajtów w słowie.
  *
- * zamienia kolejno bajtw w liczbie 16-bitowej tak, by odpowiadaa
- * kolejnoci bajtw w protokole GG. ze wzgldu na LE-owo serwera,
- * zamienia tylko na maszynach BE-wych.
+ * \param x Liczba do zamiany
  *
- *  - x - liczba do zamiany
+ * \return Liczba z odpowiednią kolejnością bajtów
  *
- * liczba z odpowiedni kolejnoci bajtw.
+ * \ingroup helper
  */
 uint16_t gg_fix16(uint16_t x)
 {
-#ifndef __GG_LIBGADU_BIGENDIAN
+#ifndef GG_CONFIG_BIGENDIAN
 	return x;
 #else
 	return (uint16_t)
@@ -270,15 +245,13 @@
 #endif
 }
 
-/* 
- * gg_login_hash() // funkcja wewntrzna
- * 
- * liczy hash z hasa i danego seeda.
- * 
- *  - password - haso do hashowania
- *  - seed - warto podana przez serwer
+/**
+ * \internal Liczy skrót z hasła i ziarna.
  *
- * hash.
+ * \param password Hasło
+ * \param seed Ziarno podane przez serwer
+ *
+ * \return Wartość skrótu
  */
 unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
 {
@@ -304,313 +277,22 @@
 	return y;
 }
 
-#ifndef _WIN32
-
-/*
- * gg_resolve() // funkcja wewntrzna
- *
- * tworzy potok, forkuje si i w drugim procesie zaczyna resolvowa 
- * podanego hosta. zapisuje w sesji deskryptor potoku. jeli co tam
- * bdzie gotowego, znaczy, e mona wczyta struct in_addr. jeli
- * nie znajdzie, zwraca INADDR_NONE.
- *
- *  - fd - wskanik gdzie wrzuci deskryptor
- *  - pid - gdzie wrzuci pid procesu potomnego
- *  - hostname - nazwa hosta do zresolvowania
+/**
+ * \internal Odbiera od serwera dane binarne.
  *
- * 0, -1.
- */
-int gg_resolve(int *fd, int *pid, const char *hostname)
-{
-	int pipes[2], res;
-	struct in_addr a;
-	int errno2;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname);
-	
-	if (!fd || !pid) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (pipe(pipes) == -1)
-		return -1;
-
-	if ((res = fork()) == -1) {
-		errno2 = errno;
-		close(pipes[0]);
-		close(pipes[1]);
-		errno = errno2;
-		return -1;
-	}
-
-	if (!res) {
-		if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
-			struct in_addr *hn;
-		
-			if (!(hn = gg_gethostbyname(hostname)))
-				a.s_addr = INADDR_NONE;
-			else {
-				a.s_addr = hn->s_addr;
-				free(hn);
-			}
-		}
-
-		write(pipes[1], &a, sizeof(a));
-
-		_exit(0);
-	}
-
-	close(pipes[1]);
-
-	*fd = pipes[0];
-	*pid = res;
-
-	return 0;
-}
-#endif
-
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-
-struct gg_resolve_pthread_data {
-	char *hostname;
-	int fd;
-};
-
-static void *gg_resolve_pthread_thread(void *arg)
-{
-	struct gg_resolve_pthread_data *d = arg;
-	struct in_addr a;
-
-	pthread_detach(pthread_self());
-
-	if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
-		struct in_addr *hn;
-		
-		if (!(hn = gg_gethostbyname(d->hostname)))
-			a.s_addr = INADDR_NONE;
-		else {
-			a.s_addr = hn->s_addr;
-			free(hn);
-		}
-	}
-
-	write(d->fd, &a, sizeof(a));
-	close(d->fd);
-
-	free(d->hostname);
-	d->hostname = NULL;
-
-	free(d);
-
-	pthread_exit(NULL);
-
-	return NULL;	/* eby kompilator nie marudzi */
-}
-
-/*
- * gg_resolve_pthread() // funkcja wewntrzna
- *
- * tworzy potok, nowy wtek i w nim zaczyna resolvowa podanego hosta.
- * zapisuje w sesji deskryptor potoku. jeli co tam bdzie gotowego,
- * znaczy, e mona wczyta struct in_addr. jeli nie znajdzie, zwraca
- * INADDR_NONE.
- *
- *  - fd - wskanik do zmiennej przechowujcej desktyptor resolvera
- *  - resolver - wskanik do wskanika resolvera
- *  - hostname - nazwa hosta do zresolvowania
+ * Funkcja odbiera dane od serwera zajmując się TLS w razie konieczności.
  *
- * 0, -1.
- */
-int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
-{
-	struct gg_resolve_pthread_data *d = NULL;
-	pthread_t *tmp;
-	int pipes[2], new_errno;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname);
-	
-	if (!resolver || !fd || !hostname) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n");
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (!(tmp = malloc(sizeof(pthread_t)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n");
-		return -1;
-	}
-	
-	if (pipe(pipes) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
-		free(tmp);
-		return -1;
-	}
-
-	if (!(d = malloc(sizeof(*d)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n");
-		new_errno = errno;
-		goto cleanup;
-	}
-	
-	d->hostname = NULL;
-
-	if (!(d->hostname = strdup(hostname))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n");
-		new_errno = errno;
-		goto cleanup;
-	}
-
-	d->fd = pipes[1];
-
-	if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n");
-		new_errno = errno;
-		goto cleanup;
-	}
-
-	gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp);
-
-	*resolver = tmp;
-
-	*fd = pipes[0];
-
-	return 0;
-
-cleanup:
-	if (d) {
-		free(d->hostname);
-		free(d);
-	}
-
-	close(pipes[0]);
-	close(pipes[1]);
-
-	free(tmp);
-
-	errno = new_errno;
-
-	return -1;
-}
-
-#elif defined _WIN32
-
-struct gg_resolve_win32thread_data {
-	char *hostname;
-	int fd;
-};
-
-static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg)
-{
-	struct gg_resolve_win32thread_data *d = arg;
-	struct in_addr a;
-
-	if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
-		struct in_addr *hn;
-		
-		if (!(hn = gg_gethostbyname(d->hostname)))
-			a.s_addr = INADDR_NONE;
-		else {
-			a.s_addr = hn->s_addr;
-			free(hn);
-		}
-	}
-
-	write(d->fd, &a, sizeof(a));
-	close(d->fd);
-
-	free(d->hostname);
-	d->hostname = NULL;
-
-	free(d);
-
-	return 0;
-}
-
-
-int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname)
-{
-	struct gg_resolve_win32thread_data *d = NULL;
-	HANDLE h;
-	DWORD dwTId;
-	int pipes[2], new_errno;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname);
-	
-	if (!resolver || !fd || !hostname) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n");
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (socket_pipe(pipes) == -1) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
-		return -1;
-	}
-
-	if (!(d = malloc(sizeof(*d)))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
-		new_errno = GetLastError();
-		goto cleanup;
-	}
-
-	d->hostname = NULL;
-
-	if (!(d->hostname = strdup(hostname))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
-		new_errno = GetLastError();
-		goto cleanup;
-	}
-
-	d->fd = pipes[1];
-
-	h = CreateThread(NULL, 0, gg_resolve_win32thread_thread,
-		d, 0, &dwTId);
-
-	if (h == NULL) {
-		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n");
-		new_errno = GetLastError();
-		goto cleanup;
-	}
-
-	*resolver = h;
-	*fd = pipes[0];
-
-	return 0;
-
-cleanup:
-	if (d) {
-		free(d->hostname);
-		free(d);
-	}
-
-	close(pipes[0]);
-	close(pipes[1]);
-
-	errno = new_errno;
-
-	return -1;
-
-}
-#endif
-
-/*
- * gg_read() // funkcja pomocnicza
+ * \param sess Struktura sesji
+ * \param buf Bufor na danymi
+ * \param length Długość bufora
  *
- * czyta z gniazda okrelon ilo bajtw. bierze pod uwag, czy mamy
- * poczenie zwyke czy TLS.
- *
- *  - sess - sesja,
- *  - buf - bufor,
- *  - length - ilo bajtw,
- *
- * takie same wartoci jak read().
+ * \return To samo co funkcja systemowa \c read
  */
 int gg_read(struct gg_session *sess, char *buf, int length)
 {
 	int res;
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 	if (sess->ssl) {
 		int err;
 
@@ -631,23 +313,22 @@
 	return res;
 }
 
-/*
- * gg_write() // funkcja pomocnicza
+/**
+ * \internal Wysyła do serwera dane binarne.
  *
- * zapisuje do gniazda okrelon ilo bajtw. bierze pod uwag, czy mamy
- * poczenie zwyke czy TLS.
+ * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności.
  *
- *  - sess - sesja,
- *  - buf - bufor,
- *  - length - ilo bajtw,
+ * \param sess Struktura sesji
+ * \param buf Bufor z danymi
+ * \param length Długość bufora
  *
- * takie same wartoci jak write().
+ * \return To samo co funkcja systemowa \c write
  */
 int gg_write(struct gg_session *sess, const char *buf, int length)
 {
 	int res = 0;
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 	if (sess->ssl) {
 		int err;
 
@@ -664,19 +345,48 @@
 	} else
 #endif
 	{
-		int written = 0;
-		
-		while (written < length) {
-			res = write(sess->fd, buf + written, length - written);
+		if (!sess->async) {
+			int written = 0;
+
+			while (written < length) {
+				res = write(sess->fd, buf + written, length - written);
+
+				if (res == -1) {
+					if (errno != EINTR)
+						break;
+
+					continue;
+				}
+
+				written += res;
+				res = written;
+			}
+		} else {
+			if (!sess->send_buf)
+				res = write(sess->fd, buf, length);
+			else
+				res = 0;
 
 			if (res == -1) {
-				if (errno == EAGAIN)
-					continue;
-				else
-					break;
-			} else {
-				written += res;
-				res = written;
+				if (errno != EAGAIN)
+					return res;
+
+				res = 0;
+			}
+
+			if (res < length) {
+				char *tmp;
+
+				if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) {
+					errno = ENOMEM;
+					return -1;
+				}
+
+				sess->send_buf = tmp;
+
+				memcpy(sess->send_buf + sess->send_left, buf + res, length - res);
+
+				sess->send_left += length - res;
 			}
 		}
 	}
@@ -684,17 +394,19 @@
 	return res;
 }
 
-/*
- * gg_recv_packet() // funkcja wewntrzna
+/**
+ * \internal Odbiera pakiet od serwera.
  *
- * odbiera jeden pakiet i zwraca wskanik do niego. pami po nim
- * naley zwolni za pomoc free().
+ * Funkcja odczytuje nagłówek pakietu, a następnie jego zawartość i zwraca
+ * w zaalokowanym buforze.
  *
- *  - sess - opis sesji
+ * Przy połączeniach asynchronicznych, funkcja może nie być w stanie
+ * skompletować całego pakietu -- w takim przypadku zwróci -1, a kodem błędu
+ * będzie \c EAGAIN.
  *
- * w przypadku bdu NULL, kod bdu w errno. naley zwrci uwag, e gdy
- * poczenie jest nieblokujce, a kod bdu wynosi EAGAIN, nie udao si
- * odczyta caego pakietu i nie naley tego traktowa jako bd.
+ * \param sess Struktura sesji
+ *
+ * \return Wskaźnik do zaalokowanego bufora
  */
 void *gg_recv_packet(struct gg_session *sess)
 {
@@ -702,8 +414,8 @@
 	char *buf = NULL;
 	int ret = 0, offset, size = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
-	
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
+
 	if (!sess) {
 		errno = EFAULT;
 		return NULL;
@@ -712,7 +424,7 @@
 	if (sess->recv_left < 1) {
 		if (sess->header_buf) {
 			memcpy(&h, sess->header_buf, sess->header_done);
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done);
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done);
 			free(sess->header_buf);
 			sess->header_buf = NULL;
 		} else
@@ -721,34 +433,36 @@
 		while (sess->header_done < sizeof(h)) {
 			ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done);
 
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret);
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret);
 
 			if (!ret) {
 				errno = ECONNRESET;
-				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n");
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n");
 				return NULL;
 			}
 
 			if (ret == -1) {
 				if (errno == EINTR) {
-					gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n");
 					continue;
 				}
 
 				if (errno == EAGAIN) {
-					gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n");
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n");
 
 					if (!(sess->header_buf = malloc(sess->header_done))) {
-						gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n");
+						gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n");
 						return NULL;
 					}
 
 					memcpy(sess->header_buf, &h, sess->header_done);
 
+					errno = EAGAIN;
+
 					return NULL;
 				}
 
-				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
 
 				return NULL;
 			}
@@ -761,22 +475,22 @@
 		h.length = gg_fix32(h.length);
 	} else
 		memcpy(&h, sess->recv_buf, sizeof(h));
-	
-	/* jakie sensowne limity na rozmiar pakietu */
+
+	/* jakieś sensowne limity na rozmiar pakietu */
 	if (h.length > 65535) {
-		gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length);
 		errno = ERANGE;
 		return NULL;
 	}
 
 	if (sess->recv_left > 0) {
-		gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n");
 		size = sess->recv_left;
 		offset = sess->recv_done;
 		buf = sess->recv_buf;
 	} else {
 		if (!(buf = malloc(sizeof(h) + h.length + 1))) {
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
 			return NULL;
 		}
 
@@ -788,24 +502,23 @@
 
 	while (size > 0) {
 		ret = gg_read(sess, buf + sizeof(h) + offset, size);
-		gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret);
 		if (!ret) {
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n");
-			free(buf);
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n");
 			errno = ECONNRESET;
 			return NULL;
 		}
 		if (ret > -1 && ret <= size) {
 			offset += ret;
 			size -= ret;
-		} else if (ret == -1) {	
+		} else if (ret == -1) {
 			int errno2 = errno;
 
-			gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno));
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno));
 			errno = errno2;
 
 			if (errno == EAGAIN) {
-				gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size);
+				gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size);
 				sess->recv_buf = buf;
 				sess->recv_left = size;
 				sess->recv_done = offset;
@@ -823,49 +536,45 @@
 	if ((gg_debug_level & GG_DEBUG_DUMP)) {
 		unsigned int i;
 
-		gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type);
-		for (i = 0; i < sizeof(h) + h.length; i++) 
-			gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]);
-		gg_debug(GG_DEBUG_DUMP, "\n");
+		gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type);
+		for (i = 0; i < sizeof(h) + h.length; i++)
+			gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]);
+		gg_debug_session(sess, GG_DEBUG_DUMP, "\n");
 	}
 
 	return buf;
 }
 
-/*
- * gg_send_packet() // funkcja wewntrzna
+/**
+ * \internal Wysyła pakiet do serwera.
  *
- * konstruuje pakiet i wysya go do serwera.
+ * Funkcja konstruuje pakiet do wysłania z dowolnej liczby fragmentów. Jeśli
+ * rozmiar pakietu jest za duży, by móc go wysłać za jednym razem, pozostała
+ * część zostanie zakolejkowana i wysłana, gdy będzie to możliwe.
  *
- *  - sock - deskryptor gniazda
- *  - type - typ pakietu
- *  - payload_1 - pierwsza cz pakietu
- *  - payload_length_1 - dugo pierwszej czci
- *  - payload_2 - druga cz pakietu
- *  - payload_length_2 - dugo drugiej czci
- *  - ... - kolejne czci pakietu i ich dugoci
- *  - NULL - kocowym parametr (konieczny!)
+ * \param sess Struktura sesji
+ * \param type Rodzaj pakietu
+ * \param ... Lista kolejnych części pakietu (wskaźnik na bufor i długość
+ *            typu \c int) zakończona \c NULL
  *
- * jeli si powiodo, zwraca 0, w przypadku bdu -1. jeli errno == ENOMEM,
- * zabrako pamici. inaczej by bd przy wysyaniu pakietu. dla errno == 0
- * nie wysano caego pakietu.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
 int gg_send_packet(struct gg_session *sess, int type, ...)
 {
 	struct gg_header *h;
 	char *tmp;
-	int tmp_length;
+	unsigned int tmp_length;
 	void *payload;
 	unsigned int payload_length;
 	va_list ap;
 	int res;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type);
 
 	tmp_length = sizeof(struct gg_header);
 
 	if (!(tmp = malloc(tmp_length))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n");
 		return -1;
 	}
 
@@ -879,14 +588,14 @@
 		payload_length = va_arg(ap, unsigned int);
 
 		if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
-			gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n");
 			free(tmp);
 			va_end(ap);
 			return -1;
 		}
 
 		tmp = tmp2;
-		
+
 		memcpy(tmp + tmp_length, payload, payload_length);
 		tmp_length += payload_length;
 
@@ -902,52 +611,81 @@
 	if ((gg_debug_level & GG_DEBUG_DUMP)) {
 		int i;
 
-		gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
+		gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
 		for (i = 0; i < tmp_length; ++i)
-			gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]);
-		gg_debug(GG_DEBUG_DUMP, "\n");
+			gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]);
+		gg_debug_session(sess, GG_DEBUG_DUMP, "\n");
 	}
-	
-	if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) {
-		gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
-		free(tmp);
+
+	res = gg_write(sess, tmp, tmp_length);
+
+	free(tmp);
+
+	if (res == -1) {
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
 		return -1;
 	}
-	
-	free(tmp);	
+
+	if (sess->async)
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() partial write(), %d sent, %d left, %d total left\n", res, tmp_length - res, sess->send_left);
+
+	if (sess->send_buf)
+		sess->check |= GG_CHECK_WRITE;
+
 	return 0;
 }
 
-/*
- * gg_session_callback() // funkcja wewntrzna
+/**
+ * \internal Funkcja zwrotna sesji.
+ *
+ * Pole \c callback struktury \c gg_session zawiera wskaźnik do tej funkcji.
+ * Wywołuje ona \c gg_watch_fd i zachowuje wynik w polu \c event.
+ *
+ * \note Korzystanie z tej funkcjonalności nie jest już zalecane.
  *
- * wywoywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje
- * do gg_session->event jego wynik.
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
-static int gg_session_callback(struct gg_session *s)
+static int gg_session_callback(struct gg_session *sess)
 {
-	if (!s) {
+	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
 
-	return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1;
+	return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1;
 }
 
-/*
- * gg_login()
+/**
+ * Łączy się z serwerem Gadu-Gadu.
  *
- * rozpoczyna procedur czenia si z serwerem. reszt obsuguje si przez
- * gg_watch_fd().
+ * Przy połączeniu synchronicznym funkcja zakończy działanie po nawiązaniu
+ * połączenia lub gdy wystąpi błąd. Po udanym połączeniu należy wywoływać
+ * funkcję \c gg_watch_fd(), która odbiera informacje od serwera i zwraca
+ * informacje o zdarzeniach.
  *
- * UWAGA! program musi obsuy SIGCHLD, jeli czy si asynchronicznie,
- * eby poprawnie zamkn proces resolvera.
+ * Przy połączeniu asynchronicznym funkcja rozpocznie procedurę połączenia
+ * i zwróci zaalokowaną strukturę. Pole \c fd struktury \c gg_session zawiera
+ * deskryptor, który należy obserwować funkcją \c select, \c poll lub za
+ * pomocą mechanizmów użytej pętli zdarzeń (Glib, Qt itp.). Pole \c check
+ * jest maską bitową mówiącą, czy biblioteka chce być informowana o możliwości
+ * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE).
+ * Po zaobserwowaniu zmian na deskryptorze należy wywołać funkcję
+ * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie
+ * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę
+ * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD.
  *
- *  - p - struktura opisujca pocztkowy stan. wymagane pola: uin, 
- *    password
+ * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów
+ * za pomocą funkcji \c gg_notify() lub \c gg_notify_ex().
  *
- * w przypadku bdu NULL, jeli idzie dobrze (async) albo poszo
- * dobrze (sync), zwrci wskanik do zaalokowanej struct gg_session.
+ * \param p Struktura opisująca parametry połączenia. Wymagane pola: uin,
+ *          password, async.
+ *
+ * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL
+ *         w przypadku błędu.
+ *
+ * \ingroup login
  */
 struct gg_session *gg_login(const struct gg_login_params *p)
 {
@@ -981,8 +719,9 @@
 		goto fail;
 	}
 
-	if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
+	if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type);
+		errno = EFAULT;
 		goto fail;
 	}
 
@@ -999,18 +738,59 @@
 	sess->server_addr = p->server_addr;
 	sess->external_port = p->external_port;
 	sess->external_addr = p->external_addr;
+
+	sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77));
+
+	if (!(p->protocol_features & GG_FEATURE_STATUS77))
+		sess->protocol_features |= GG_FEATURE_STATUS80;
+
+	if (!(p->protocol_features & GG_FEATURE_MSG77))
+		sess->protocol_features |= GG_FEATURE_MSG80;
+
 	sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION;
+
 	if (p->era_omnix)
-		sess->protocol_version |= GG_ERA_OMNIX_MASK;
+		sess->protocol_flags |= GG_ERA_OMNIX_MASK;
 	if (p->has_audio)
-		sess->protocol_version |= GG_HAS_AUDIO_MASK;
+		sess->protocol_flags |= GG_HAS_AUDIO_MASK;
 	sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL;
 	sess->last_sysmsg = p->last_sysmsg;
 	sess->image_size = p->image_size;
 	sess->pid = -1;
+	sess->encoding = p->encoding;
+
+	if (gg_session_set_resolver(sess, p->resolver) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unsupported resolver type (%d)\n", p->resolver);
+		errno = EFAULT;
+		goto fail;
+	}
+
+	if (p->status_descr) {
+		int max_length;
+
+		if (sess->protocol_version >= 0x2d)
+			max_length = GG_STATUS_DESCR_MAXSIZE;
+		else
+			max_length = GG_STATUS_DESCR_MAXSIZE_PRE_8_0;
+
+		if (sess->protocol_version >= 0x2d && p->encoding != GG_ENCODING_UTF8)
+			sess->initial_descr = gg_cp_to_utf8(p->status_descr);
+		else
+			sess->initial_descr = strdup(p->status_descr);
+
+		if (!sess->initial_descr) {
+			gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
+			goto fail;
+		}
+		
+		// XXX pamiętać, żeby nie ciąć w środku znaku utf-8
+		
+		if (strlen(sess->initial_descr) > max_length)
+			sess->initial_descr[max_length] = 0;
+	}
 
 	if (p->tls == 1) {
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#ifdef GG_CONFIG_HAVE_OPENSSL
 		char buf[1024];
 
 		OpenSSL_add_ssl_algorithms();
@@ -1023,7 +803,7 @@
 			} rstruct;
 
 			time(&rstruct.time);
-			rstruct.ptr = (void *) &rstruct;			
+			rstruct.ptr = (void *) &rstruct;
 
 			RAND_seed((void *) rdata, sizeof(rdata));
 			RAND_seed((void *) &rstruct, sizeof(rstruct));
@@ -1050,7 +830,7 @@
 		gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");
 #endif
 	}
-	
+
 	if (gg_proxy_enabled) {
 		hostname = gg_proxy_host;
 		sess->proxy_port = port = gg_proxy_port;
@@ -1059,37 +839,50 @@
 		port = GG_APPMSG_PORT;
 	}
 
-	if (!p->async) {
-		struct in_addr a;
+	if (p->hash_type)
+		sess->hash_type = p->hash_type;
+	else
+		sess->hash_type = GG_LOGIN_HASH_SHA1;
 
-		if (!p->server_addr || !p->server_port) {
-			if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
-				struct in_addr *hn;
-	
-				if (!(hn = gg_gethostbyname(hostname))) {
+	if (!p->async) {
+		struct in_addr addr;
+
+		if (!sess->server_addr) {
+			if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+				if (gg_gethostbyname_real(hostname, &addr, 0) == -1) {
 					gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname);
 					goto fail;
-				} else {
-					a.s_addr = hn->s_addr;
-					free(hn);
 				}
 			}
 		} else {
-			a.s_addr = p->server_addr;
-			port = p->server_port;
+			addr.s_addr = sess->server_addr;
+			port = sess->port;
 		}
 
-		sess->hub_addr = a.s_addr;
+		sess->hub_addr = addr.s_addr;
 
 		if (gg_proxy_enabled)
-			sess->proxy_addr = a.s_addr;
+			sess->proxy_addr = addr.s_addr;
+
+		if ((sess->fd = gg_connect(&addr, port, 0)) == -1) {
+			gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+
+			/* nie wyszło? próbujemy portu 443. */
+			if (sess->server_addr) {
+				sess->port = GG_HTTPS_PORT;
 
-		if ((sess->fd = gg_connect(&a, port, 0)) == -1) {
-			gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
-			goto fail;
+				if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, 0)) == -1) {
+					/* ostatnia deska ratunku zawiodła?
+					 * w takim razie zwijamy manatki. */
+					gg_debug_session(sess, GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+					goto fail;
+				}
+			} else {
+				goto fail;
+			}
 		}
 
-		if (p->server_addr && p->server_port)
+		if (sess->server_addr)
 			sess->state = GG_STATE_CONNECTING_GG;
 		else
 			sess->state = GG_STATE_CONNECTING_HUB;
@@ -1114,15 +907,9 @@
 
 		return sess;
 	}
-	
+
 	if (!sess->server_addr || gg_proxy_enabled) {
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-		if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) {
-#elif defined _WIN32
-		if (gg_resolve_win32thread(&sess->fd, &sess->resolver, hostname)) {
-#else
-		if (gg_resolve(&sess->fd, &sess->pid, hostname)) {
-#endif
+		if (sess->resolver_start(&sess->fd, &sess->resolver, hostname) == -1) {
 			gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
 			goto fail;
 		}
@@ -1133,49 +920,121 @@
 		}
 		sess->state = GG_STATE_CONNECTING_GG;
 		sess->check = GG_CHECK_WRITE;
+		sess->soft_timeout = 1;
 	}
 
 	return sess;
 
 fail:
 	if (sess) {
-		if (sess->password)
-			free(sess->password);
-		if (sess->initial_descr)
-			free(sess->initial_descr);
+		free(sess->password);
+		free(sess->initial_descr);
 		free(sess);
 	}
-	
+
 	return NULL;
 }
 
-/* 
- * gg_free_session()
+/**
+ * Wysyła do serwera pakiet utrzymania połączenia.
+ *
+ * Klient powinien regularnie co minutę wysyłać pakiet utrzymania połączenia,
+ * inaczej serwer uzna, że klient stracił łączność z siecią i zerwie
+ * połączenie.
+ *
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  *
- * prbuje zamkn poczenia i zwalnia pami zajmowan przez sesj.
+ * \ingroup login
+ */
+int gg_ping(struct gg_session *sess)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	return gg_send_packet(sess, GG_PING, NULL);
+}
+
+/**
+ * Kończy połączenie z serwerem.
  *
- *  - sess - opis sesji
+ * Funkcja nie zwalnia zasobów, więc po jej wywołaniu należy użyć
+ * \c gg_free_session(). Jeśli chce się ustawić opis niedostępności, należy
+ * wcześniej wywołać funkcję \c gg_change_status_descr() lub
+ * \c gg_change_status_descr_time().
+ *
+ * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze
+ * dane (np. z powodu strat pakietów na łączu), prawdopodobnie zostaną one
+ * utracone przy zrywaniu połączenia.
+ *
+ * \param sess Struktura sesji
+ *
+ * \ingroup login
  */
-void gg_free_session(struct gg_session *sess)
+void gg_logoff(struct gg_session *sess)
 {
 	if (!sess)
 		return;
 
-	/* XXX dopisa zwalnianie i zamykanie wszystkiego, co mogo zosta */
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
+
+	if (GG_S_NA(sess->status))
+		gg_change_status(sess, GG_STATUS_NOT_AVAIL);
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+	if (sess->ssl)
+		SSL_shutdown(sess->ssl);
+#endif
+
+	sess->resolver_cleanup(&sess->resolver, 1);
 
-	if (sess->password)
-		free(sess->password);
-	
-	if (sess->initial_descr)
-		free(sess->initial_descr);
+	if (sess->fd != -1) {
+		shutdown(sess->fd, SHUT_RDWR);
+		close(sess->fd);
+		sess->fd = -1;
+	}
+
+	if (sess->send_buf) {
+		free(sess->send_buf);
+		sess->send_buf = NULL;
+		sess->send_left = 0;
+	}
+}
 
-	if (sess->client_version)
-		free(sess->client_version);
+/**
+ * Zwalnia zasoby używane przez połączenie z serwerem. Funkcję należy wywołać
+ * po zamknięciu połączenia z serwerem, by nie doprowadzić do wycieku zasobów
+ * systemowych.
+ *
+ * \param sess Struktura sesji
+ *
+ * \ingroup login
+ */
+void gg_free_session(struct gg_session *sess)
+{
+	struct gg_dcc7 *dcc;
 
-	if (sess->header_buf)
-		free(sess->header_buf);
+	if (!sess)
+		return;
+
+	/* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+	free(sess->password);
+	free(sess->initial_descr);
+	free(sess->client_version);
+	free(sess->header_buf);
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
 	if (sess->ssl)
 		SSL_free(sess->ssl);
 
@@ -1183,23 +1042,7 @@
 		SSL_CTX_free(sess->ssl_ctx);
 #endif
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-	if (sess->resolver) {
-		pthread_cancel(*((pthread_t*) sess->resolver));
-		free(sess->resolver);
-		sess->resolver = NULL;
-	}
-#elif defined _WIN32
-	if (sess->resolver) {
-		HANDLE h = sess->resolver;
-		TerminateThread(h, 0);
-		CloseHandle(h);
-		sess->resolver = NULL;
-	}
-#else
-	if (sess->pid != -1)
-		waitpid(sess->pid, NULL, WNOHANG);
-#endif
+	sess->resolver_cleanup(&sess->resolver, 1);
 
 	if (sess->fd != -1)
 		close(sess->fd);
@@ -1207,24 +1050,37 @@
 	while (sess->images)
 		gg_image_queue_remove(sess, sess->images, 1);
 
+	free(sess->send_buf);
+
+	for (dcc = sess->dcc7_list; dcc; dcc = dcc->next)
+		dcc->sess = NULL;
+
 	free(sess);
 }
 
-/*
- * gg_change_status()
+#ifndef DOXYGEN
+
+/**
+ * \internal Funkcja wysyłająca pakiet zmiany statusu użytkownika.
  *
- * zmienia status uytkownika. przydatne do /away i /busy oraz /quit.
+ * \param sess Struktura sesji
+ * \param status Nowy status użytkownika
+ * \param descr Opis statusu użytkownika (lub \c NULL)
+ * \param time Czas powrotu w postaci uniksowego znacznika czasu (lub 0)
  *
- *  - sess - opis sesji
- *  - status - nowy status uytkownika
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  *
- * 0, -1.
+ * \ingroup status
  */
-int gg_change_status(struct gg_session *sess, int status)
+static int gg_change_status_common(struct gg_session *sess, int status, const char *descr, int time)
 {
-	struct gg_new_status p;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
+	char *new_descr = NULL;
+	uint32_t new_time;
+	int descr_len = 0;
+	int descr_len_max;
+	int packet_type;
+	int append_null = 0;
+	int res;
 
 	if (!sess) {
 		errno = EFAULT;
@@ -1236,67 +1092,422 @@
 		return -1;
 	}
 
-	p.status = gg_fix32(status);
+	/* XXX, obcinać stany których stary protokół niezna (czyt. dnd->aw; ffc->av) */
+
+	/* dodaj flagę obsługi połączeń głosowych zgodną z GG 7.x */
+	if ((sess->protocol_version >= 0x2a) && (sess->protocol_version < 0x2d /* ? */ ) && (sess->protocol_flags & GG_HAS_AUDIO_MASK) && !GG_S_I(status))
+		status |= GG_STATUS_VOICE_MASK;
 
 	sess->status = status;
 
-	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL);
+	if (sess->protocol_version >= 0x2d) {
+		if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
+			new_descr = gg_cp_to_utf8(descr);
+
+			if (!new_descr)
+				return -1;
+		}
+
+		if (sess->protocol_version >= 0x2e)
+			packet_type = GG_NEW_STATUS80;
+		else /* sess->protocol_version == 0x2d */
+			packet_type = GG_NEW_STATUS80BETA;
+		descr_len_max = GG_STATUS_DESCR_MAXSIZE;
+		append_null = 1;
+
+	} else {
+		packet_type = GG_NEW_STATUS;
+		descr_len_max = GG_STATUS_DESCR_MAXSIZE_PRE_8_0;
+
+		if (time != 0)
+			append_null = 1;
+	}
+
+	if (descr) {
+		descr_len = strlen((new_descr) ? new_descr : descr);
+
+		if (descr_len > descr_len_max)
+			descr_len = descr_len_max;
+
+		// XXX pamiętać o tym, żeby nie ucinać w środku znaku utf-8
+	}
+
+	if (time)
+		new_time = gg_fix32(time);
+
+	if (packet_type == GG_NEW_STATUS80) {
+		struct gg_new_status80 p;
+
+		p.status		= gg_fix32(status);
+		p.flags			= gg_fix32(0x00800001);
+		p.description_size	= gg_fix32(descr_len);
+		res = gg_send_packet(sess,
+				packet_type,
+				&p,
+				sizeof(p),
+				(new_descr) ? new_descr : descr,
+				descr_len,
+				NULL);
+
+	} else {
+		struct gg_new_status p;
+
+		p.status = gg_fix32(status);
+		res = gg_send_packet(sess,
+				packet_type,
+				&p,
+				sizeof(p),
+				(new_descr) ? new_descr : descr,
+				descr_len,
+				(append_null) ? "\0" : NULL,
+				(append_null) ? 1 : 0,
+				(time) ? &new_time : NULL,
+				(time) ? sizeof(new_time) : 0,
+				NULL);
+	}
+
+	free(new_descr);
+	return res;
 }
 
-/*
- * gg_change_status_descr()
+#endif /* DOXYGEN */
+
+/**
+ * Zmienia status użytkownika.
  *
- * zmienia status uytkownika na opisowy.
+ * \param sess Struktura sesji
+ * \param status Nowy status użytkownika
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  *
- *  - sess - opis sesji
- *  - status - nowy status uytkownika
- *  - descr - opis statusu
+ * \ingroup status
+ */
+int gg_change_status(struct gg_session *sess, int status)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
+
+	return gg_change_status_common(sess, status, NULL, 0);
+}
+
+/**
+ * Zmienia status użytkownika na status opisowy.
  *
- * 0, -1.
+ * \param sess Struktura sesji
+ * \param status Nowy status użytkownika
+ * \param descr Opis statusu użytkownika
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup status
  */
 int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
 {
-	struct gg_new_status p;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
-
-	if (!sess || !descr) {
-		errno = EFAULT;
-		return -1;
-	}
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
 
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	p.status = gg_fix32(status);
-
-	sess->status = status;
-
-	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL);
+	return gg_change_status_common(sess, status, descr, 0);
 }
 
-/*
- * gg_change_status_descr_time()
- *
- * zmienia status uytkownika na opisowy z godzin powrotu.
+/**
+ * Zmienia status użytkownika na status opisowy z podanym czasem powrotu.
  *
- *  - sess - opis sesji
- *  - status - nowy status uytkownika
- *  - descr - opis statusu
- *  - time - czas w formacie uniksowym
+ * \param sess Struktura sesji
+ * \param status Nowy status użytkownika
+ * \param descr Opis statusu użytkownika
+ * \param time Czas powrotu w postaci uniksowego znacznika czasu
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup status
  */
 int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time)
 {
-	struct gg_new_status p;
-	uint32_t newtime;
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
+
+	return gg_change_status_common(sess, status, descr, time);
+}
+
+/**
+ * Wysyła wiadomość do użytkownika.
+ *
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipient Numer adresata
+ * \param message Treść wiadomości
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
+ */
+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message);
+
+	return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, NULL, 0);
+}
+
+/**
+ * Wysyła wiadomość formatowaną.
+ *
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipient Numer adresata
+ * \param message Treść wiadomości
+ * \param format Informacje o formatowaniu
+ * \param formatlen Długość informacji o formatowaniu
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
+ */
+int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen);
+
+	return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, format, formatlen);
+}
+
+/**
+ * Wysyła wiadomość w ramach konferencji.
+ *
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipients_count Liczba adresatów
+ * \param recipients Wskaźnik do tablicy z numerami adresatów
+ * \param message Treść wiadomości
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
+ */
+int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message)
+{
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message);
+
+	return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0);
+}
+
+/**
+ * \internal Dodaje tekst na koniec bufora.
+ * 
+ * \param dst Wskaźnik na bufor roboczy
+ * \param pos Wskaźnik na aktualne położenie w buforze roboczym
+ * \param src Dodawany tekst
+ * \param len Długość dodawanego tekstu
+ */
+static void gg_append(char *dst, int *pos, const void *src, int len)
+{
+	if (dst != NULL)
+		memcpy(&dst[*pos], src, len);
+
+	*pos += len;
+}
+
+/**
+ * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML.
+ *
+ * \param dst Bufor wynikowy (może być \c NULL)
+ * \param utf_msg Tekst źródłowy
+ * \param format Atrybuty tekstu źródłowego
+ * \param format_len Długość bloku atrybutów tekstu źródłowego
+ *
+ * \note Dokleja \c \\0 na końcu bufora wynikowego.
+ *
+ * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
+ */
+static int gg_convert_to_html(char *dst, const char *utf_msg, const unsigned char *format, int format_len)
+{
+	const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">";
+	const int span_len = 75;
+	const char img_fmt[] = "<img src=\"%02x%02x%02x%02x%02x%02x%02x%02x\">";
+	const int img_len = 28;
+	int char_pos = 0;
+	int format_idx = 3;
+	unsigned char old_attr = 0;
+	const unsigned char *color = (const unsigned char*) "\x00\x00\x00";
+	int len, i;
+
+	len = 0;
+
+	for (i = 0; utf_msg[i] != 0; i++) {
+		unsigned char attr;
+		int attr_pos;
+
+		if (format_idx + 3 <= format_len) {
+			attr_pos = format[format_idx] | (format[format_idx + 1] << 8);
+			attr = format[format_idx + 2];
+		} else {
+			attr_pos = -1;
+			attr = 0;
+		}
+
+		if (attr_pos == char_pos) {
+			format_idx += 3;
+
+			if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0) {
+				if (char_pos != 0) {
+					if ((old_attr & GG_FONT_UNDERLINE) != 0)
+						gg_append(dst, &len, "</u>", 4);
+
+					if ((old_attr & GG_FONT_ITALIC) != 0)
+						gg_append(dst, &len, "</i>", 4);
+
+					if ((old_attr & GG_FONT_BOLD) != 0)
+						gg_append(dst, &len, "</b>", 4);
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
+					gg_append(dst, &len, "</span>", 7);
+				}
+
+				if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) {
+					color = &format[format_idx];
+					format_idx += 3;
+				} else {
+					color = (const unsigned char*) "\x00\x00\x00";
+				}
+
+				if (dst != NULL)
+					sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
+				len += span_len;
+			} else if (char_pos == 0) {
+				if (dst != NULL)
+					sprintf(&dst[len], span_fmt, 0, 0, 0);
+				len += span_len;
+			}
+
+			if ((attr & GG_FONT_BOLD) != 0)
+				gg_append(dst, &len, "<b>", 3);
+
+			if ((attr & GG_FONT_ITALIC) != 0)
+				gg_append(dst, &len, "<i>", 3);
+
+			if ((attr & GG_FONT_UNDERLINE) != 0)
+				gg_append(dst, &len, "<u>", 3);
+
+			if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) {
+				if (dst != NULL) {
+					sprintf(&dst[len], img_fmt,
+						format[format_idx + 9],
+						format[format_idx + 8], 
+						format[format_idx + 7],
+						format[format_idx + 6], 
+						format[format_idx + 5],
+						format[format_idx + 4],
+						format[format_idx + 3],
+						format[format_idx + 2]);
+				}
+
+				len += img_len;
+				format_idx += 10;
+			}
+
+			old_attr = attr;
+		} else if (i == 0) {
+			if (dst != NULL)
+				sprintf(&dst[len], span_fmt, 0, 0, 0);
+
+			len += span_len;
+		}
 
-	if (!sess || !descr || !time) {
+		switch (utf_msg[i]) {
+			case '&':
+				gg_append(dst, &len, "&amp;", 5);
+				break;
+			case '<':
+				gg_append(dst, &len, "&lt;", 4);
+				break;
+			case '>':
+				gg_append(dst, &len, "&gt;", 4);
+				break;
+			case '\'':
+				gg_append(dst, &len, "&apos;", 6);
+				break;
+			case '\"':
+				gg_append(dst, &len, "&quot;", 6);
+				break;
+			case '\n':
+				gg_append(dst, &len, "<br>", 4);
+				break;
+			case '\r':
+				break;
+			default:
+				if (dst != NULL)
+					dst[len] = utf_msg[i];
+				len++;
+		}
+
+		/* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */
+
+		if ((utf_msg[i] & 0xc0) != 0xc0)
+			char_pos++;
+	}
+
+	if ((old_attr & GG_FONT_UNDERLINE) != 0)
+		gg_append(dst, &len, "</u>", 4);
+
+	if ((old_attr & GG_FONT_ITALIC) != 0)
+		gg_append(dst, &len, "</i>", 4);
+
+	if ((old_attr & GG_FONT_BOLD) != 0)
+		gg_append(dst, &len, "</b>", 4);
+
+	/* Dla pustych tekstów dodaj pusty <span>. */
+
+	if (i == 0) {
+		if (dst != NULL)
+			sprintf(&dst[len], span_fmt, 0, 0, 0);
+
+		len += span_len;
+	}
+
+	gg_append(dst, &len, "</span>", 7);
+
+	if (dst != NULL)
+		dst[len] = 0;
+
+	return len;
+}
+
+/**
+ * Wysyła wiadomość formatowaną w ramach konferencji.
+ *
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipients_count Liczba adresatów
+ * \param recipients Wskaźnik do tablicy z numerami adresatów
+ * \param message Treść wiadomości
+ * \param format Informacje o formatowaniu
+ * \param formatlen Długość informacji o formatowaniu
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ * 
+ * \ingroup messages
+ */
+int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+	struct gg_send_msg s;
+	struct gg_send_msg80 s80;
+	struct gg_msg_recipients r;
+	char *cp_msg = NULL;
+	char *utf_msg = NULL;
+	char *html_msg = NULL;
+	int seq_no;
+	int i, j, k;
+	uin_t *recps;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen);
+
+	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
@@ -1306,75 +1517,184 @@
 		return -1;
 	}
 
-	p.status = gg_fix32(status);
+	if (message == NULL || recipients_count <= 0 || recipients_count > 0xffff || (recipients_count != 1 && recipients == NULL)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (sess->encoding == GG_ENCODING_UTF8) {
+		if (!(cp_msg = gg_utf8_to_cp((const char *) message)))
+			return -1;
+
+		utf_msg = (char*) message;
+	} else {
+		if (sess->protocol_version >= 0x2d) {
+			if (!(utf_msg = gg_cp_to_utf8((const char *) message)))
+				return -1;
+		}
+
+		cp_msg = (char*) message;
+	}
+
+	if (sess->protocol_version < 0x2d) {
+		if (!sess->seq)
+			sess->seq = 0x01740000 | (rand() & 0xffff);
+		seq_no = sess->seq;
+		sess->seq += (rand() % 0x300) + 0x300;
 
-	sess->status = status;
+		s.msgclass = gg_fix32(msgclass);
+		s.seq = gg_fix32(seq_no);
+	} else {
+		int len;
+		
+		// Drobne odchylenie od protokołu. Jeśli wysyłamy kilka
+		// wiadomości w ciągu jednej sekundy, zwiększamy poprzednią
+		// wartość, żeby każda wiadomość miała unikalny numer.
+
+		seq_no = time(NULL);
+
+		if (seq_no <= sess->seq)
+			seq_no = sess->seq + 1;
+
+		sess->seq = seq_no;
+
+		if (format == NULL || formatlen < 3) {
+			format = (unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00";
+			formatlen = 9;
+		}
+
+		len = gg_convert_to_html(NULL, utf_msg, format, formatlen);
+
+		html_msg = malloc(len + 1);
+
+		if (html_msg == NULL) {
+			seq_no = -1;
+			goto cleanup;
+		}
+
+		gg_convert_to_html(html_msg, utf_msg, format, formatlen);
 
-	newtime = gg_fix32(time);
+		s80.seq = gg_fix32(seq_no);
+		s80.msgclass = gg_fix32(msgclass);
+		s80.offset_plain = gg_fix32(sizeof(s80) + strlen(html_msg) + 1);
+		s80.offset_attr = gg_fix32(sizeof(s80) + strlen(html_msg) + 1 + strlen(cp_msg) + 1);
+	}
+
+	if (recipients_count > 1) {
+		r.flag = 0x01;
+		r.count = gg_fix32(recipients_count - 1);
+
+		recps = malloc(sizeof(uin_t) * recipients_count);
+
+		if (!recps) {
+			seq_no = -1;
+			goto cleanup;
+		}
+
+		for (i = 0; i < recipients_count; i++) {
+			for (j = 0, k = 0; j < recipients_count; j++) {
+				if (recipients[j] != recipients[i]) {
+					recps[k] = gg_fix32(recipients[j]);
+					k++;
+				}
+			}
+
+			if (sess->protocol_version < 0x2d) {
+				s.recipient = gg_fix32(recipients[i]);
 
-	return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL);
+				if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1)
+					seq_no = -1;
+			} else {
+				s80.recipient = gg_fix32(recipients[i]);
+
+				if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1)
+					seq_no = -1;
+			}
+		}
+
+		free(recps);
+	} else {
+		if (sess->protocol_version < 0x2d) {
+			s.recipient = gg_fix32(recipients[0]);
+
+			if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
+				seq_no = -1;
+		} else {
+			s80.recipient = gg_fix32(recipients[0]);
+
+			if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
+				seq_no = -1;
+		}
+	}
+
+cleanup:
+	if (cp_msg != (char*) message)
+		free(cp_msg);
+
+	if (utf_msg != (char*) message)
+		free(utf_msg);
+
+	free(html_msg);
+
+	return seq_no;
 }
 
-/*
- * gg_logoff()
+/**
+ * Wysyła wiadomość binarną przeznaczoną dla klienta.
+ *
+ * Wiadomości między klientami przesyła się np. w celu wywołania zwrotnego
+ * połączenia bezpośredniego. Funkcja zwraca losowy numer sekwencyjny,
+ * który można zignorować albo wykorzystać do potwierdzenia.
  *
- * wylogowuje uytkownika i zamyka poczenie, ale nie zwalnia pamici.
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipient Numer adresata
+ * \param message Treść wiadomości
+ * \param message_len Długość wiadomości
  *
- *  - sess - opis sesji
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
  */
-void gg_logoff(struct gg_session *sess)
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
 {
-	if (!sess)
-		return;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
-
-	if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK))
-		gg_change_status(sess, GG_STATUS_NOT_AVAIL);
-
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-	if (sess->ssl)
-		SSL_shutdown(sess->ssl);
-#endif
+	struct gg_send_msg s;
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-	if (sess->resolver) {
-		pthread_cancel(*((pthread_t*) sess->resolver));
-		free(sess->resolver);
-		sess->resolver = NULL;
-	}
-#elif defined _WIN32
-	if (sess->resolver) {
-		HANDLE h = sess->resolver;
-		TerminateThread(h, 0);
-		CloseHandle(h);
-		sess->resolver = NULL;
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
 	}
-#else
-	if (sess->pid != -1) {
-		waitpid(sess->pid, NULL, WNOHANG);
-		sess->pid = -1;
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
 	}
-#endif
-	
-	if (sess->fd != -1) {
-		shutdown(sess->fd, SHUT_RDWR);
-		close(sess->fd);
-		sess->fd = -1;
-	}
+
+	s.recipient = gg_fix32(recipient);
+	s.seq = gg_fix32(0);
+	s.msgclass = gg_fix32(msgclass);
+
+	return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
 }
 
-/*
- * gg_image_request()
+/**
+ * Wysyła żądanie obrazka o podanych parametrach.
  *
- * wysya danie wysania obrazka o podanych parametrach.
+ * Wiadomości obrazkowe nie zawierają samych obrazków, a tylko ich rozmiary
+ * i sumy kontrolne. Odbiorca najpierw szuka obrazków w swojej pamięci
+ * podręcznej i dopiero gdy ich nie znajdzie, wysyła żądanie do nadawcy.
+ * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY.
  *
- *  - sess - opis sesji
- *  - recipient - numer adresata
- *  - size - rozmiar obrazka
- *  - crc32 - suma kontrolna obrazka
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param size Rozmiar obrazka w bajtach
+ * \param crc32 Suma kontrola obrazka
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup messages
  */
 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
 {
@@ -1383,13 +1703,13 @@
 	char dummy = 0;
 	int res;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32);
 
 	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
-	
+
 	if (sess->state != GG_STATE_CONNECTED) {
 		errno = ENOTCONN;
 		return -1;
@@ -1407,7 +1727,7 @@
 	r.flag = 0x04;
 	r.size = gg_fix32(size);
 	r.crc32 = gg_fix32(crc32);
-	
+
 	res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL);
 
 	if (!res) {
@@ -1415,14 +1735,14 @@
 		char *buf;
 
 		if (!q) {
-			gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n");
 			return -1;
 		}
 
 		buf = malloc(size);
 		if (size && !buf)
 		{
-			gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
+			gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
 			free(q);
 			return -1;
 		}
@@ -1449,20 +1769,20 @@
 	return res;
 }
 
-/*
- * gg_image_reply()
- *
- * wysya dany obrazek.
+/**
+ * Wysyła żądany obrazek.
  *
- *  - sess - opis sesji
- *  - recipient - numer adresata
- *  - filename - nazwa pliku
- *  - image - bufor z obrazkiem
- *  - size - rozmiar obrazka
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param filename Nazwa pliku
+ * \param image Bufor z obrazkiem
+ * \param size Rozmiar obrazka
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup messages
  */
-int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size)
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
 {
 	struct gg_msg_image_reply *r;
 	struct gg_send_msg s;
@@ -1470,7 +1790,7 @@
 	char buf[1910];
 	int res = -1;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size);
 
 	if (!sess || !filename || !image) {
 		errno = EFAULT;
@@ -1487,7 +1807,7 @@
 		return -1;
 	}
 
-	/* wytnij cieki, zostaw tylko nazw pliku */
+	/* wytnij ścieżki, zostaw tylko nazwę pliku */
 	while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
 		filename = tmp + 1;
 
@@ -1495,7 +1815,7 @@
 		errno = EINVAL;
 		return -1;
 	}
-	
+
 	s.recipient = gg_fix32(recipient);
 	s.seq = gg_fix32(0);
 	s.msgclass = gg_fix32(GG_CLASS_MSG);
@@ -1505,26 +1825,26 @@
 
 	r->flag = 0x05;
 	r->size = gg_fix32(size);
-	r->crc32 = gg_fix32(gg_crc32(0, image, size));
+	r->crc32 = gg_fix32(gg_crc32(0, (unsigned char*) image, size));
 
 	while (size > 0) {
-		size_t buflen, chunklen;
-		
+		int buflen, chunklen;
+
 		/* \0 + struct gg_msg_image_reply */
 		buflen = sizeof(struct gg_msg_image_reply) + 1;
 
-		/* w pierwszym kawaku jest nazwa pliku */
+		/* w pierwszym kawałku jest nazwa pliku */
 		if (r->flag == 0x05) {
 			strcpy(buf + buflen, filename);
 			buflen += strlen(filename) + 1;
 		}
 
-		chunklen = ((size_t)size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t)size;
+		chunklen = (size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : size;
 
 		memcpy(buf + buflen, image, chunklen);
 		size -= chunklen;
 		image += chunklen;
-		
+
 		res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL);
 
 		if (res == -1)
@@ -1536,83 +1856,34 @@
 	return res;
 }
 
-/*
- * gg_send_message_ctcp()
+/**
+ * Wysyła do serwera listę kontaktów.
  *
- * wysya wiadomo do innego uytkownika. zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
+ * Funkcja informuje serwer o liście kontaktów, których statusy będą
+ * obserwowane lub kontaktów, które bedą blokowane. Dla każdego z \c count
+ * kontaktów tablica \c userlist zawiera numer, a tablica \c types rodzaj
+ * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED).
+ *
+ * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli
+ * jest pusta.
  *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipient - numer adresata
- *  - message - tre wiadomoci
- *  - message_len - dugo
+ * \param sess Struktura sesji
+ * \param userlist Wskaźnik do tablicy numerów kontaktów
+ * \param types Wskaźnik do tablicy rodzajów kontaktów
+ * \param count Liczba kontaktów
  *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
  */
-int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
+int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
 {
-	struct gg_send_msg s;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-	
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	s.recipient = gg_fix32(recipient);
-	s.seq = gg_fix32(0);
-	s.msgclass = gg_fix32(msgclass);
-	
-	return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
-}
+	struct gg_notify *n;
+	uin_t *u;
+	char *t;
+	int i, res = 0;
 
-/*
- * gg_send_message()
- *
- * wysya wiadomo do innego uytkownika. zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipient - numer adresata
- *  - message - tre wiadomoci
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
-{
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message);
-
-	return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0);
-}
-
-/*
- * gg_send_message_richtext()
- *
- * wysya kolorow wiadomo do innego uytkownika. zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipient - numer adresata
- *  - message - tre wiadomoci
- *  - format - informacje o formatowaniu
- *  - formatlen - dugo informacji o formatowaniu
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen)
-{
-	struct gg_send_msg s;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
 
 	if (!sess) {
 		errno = EFAULT;
@@ -1624,186 +1895,12 @@
 		return -1;
 	}
 
-	if (!message) {
-		errno = EFAULT;
-		return -1;
-	}
-	
-	s.recipient = gg_fix32(recipient);
-	if (!sess->seq)
-		sess->seq = 0x01740000 | (rand() & 0xffff);
-	s.seq = gg_fix32(sess->seq);
-	s.msgclass = gg_fix32(msgclass);
-	sess->seq += (rand() % 0x300) + 0x300;
-	
-	if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, format, formatlen, NULL) == -1)
-		return -1;
-
-	return gg_fix32(s.seq);
-}
-
-/*
- * gg_send_message_confer()
- *
- * wysya wiadomo do kilku uytkownikow (konferencja). zwraca losowy numer
- * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipients_count - ilo adresatw
- *  - recipients - numerki adresatw
- *  - message - tre wiadomoci
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message)
-{
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message);
-
-	return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0);
-}
-
-/*
- * gg_send_message_confer_richtext()
- *
- * wysya kolorow wiadomo do kilku uytkownikow (konferencja). zwraca
- * losowy numer sekwencyjny, ktry mona zignorowa albo wykorzysta do
- * potwierdzenia.
- *
- *  - sess - opis sesji
- *  - msgclass - rodzaj wiadomoci
- *  - recipients_count - ilo adresatw
- *  - recipients - numerki adresatw
- *  - message - tre wiadomoci
- *  - format - informacje o formatowaniu
- *  - formatlen - dugo informacji o formatowaniu
- *
- * numer sekwencyjny wiadomoci lub -1 w przypadku bdu.
- */
-int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen)
-{
-	struct gg_send_msg s;
-	struct gg_msg_recipients r;
-	int i, j, k;
-	uin_t *recps;
-		
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) {
-		errno = EINVAL;
-		return -1;
-	}
-	
-	r.flag = 0x01;
-	r.count = gg_fix32(recipients_count - 1);
-	
-	if (!sess->seq)
-		sess->seq = 0x01740000 | (rand() & 0xffff);
-	s.seq = gg_fix32(sess->seq);
-	s.msgclass = gg_fix32(msgclass);
-
-	recps = malloc(sizeof(uin_t) * recipients_count);
-	if (!recps)
-		return -1;
-
-	for (i = 0; i < recipients_count; i++) {
-	 
-		s.recipient = gg_fix32(recipients[i]);
-		
-		for (j = 0, k = 0; j < recipients_count; j++)
-			if (recipients[j] != recipients[i]) {
-				recps[k] = gg_fix32(recipients[j]);
-				k++;
-			}
-				
-		if (!i)
-			sess->seq += (rand() % 0x300) + 0x300;
-		
-		if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) {
-			free(recps);
-			return -1;
-		}
-	}
-
-	free(recps);
-	
-	return gg_fix32(s.seq);
-}
-
-/*
- * gg_ping()
- *
- * wysya do serwera pakiet ping.
- *
- *  - sess - opis sesji
- *
- * 0, -1.
- */
-int gg_ping(struct gg_session *sess)
-{
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
-
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
-	return gg_send_packet(sess, GG_PING, NULL);
-}
-
-/*
- * gg_notify_ex()
- *
- * wysya serwerowi list kontaktw (wraz z odpowiadajcymi im typami userw),
- * dziki czemu wie, czyj stan nas interesuje.
- *
- *  - sess - opis sesji
- *  - userlist - wskanik do tablicy numerw
- *  - types - wskanik do tablicy typw uytkownikw
- *  - count - ilo numerkw
- *
- * 0, -1.
- */
-int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
-{
-	struct gg_notify *n;
-	uin_t *u;
-	char *t;
-	int i, res = 0;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
-	
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-	
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-
 	if (!userlist || !count)
 		return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
-	
+
 	while (count > 0) {
 		int part_count, packet_type;
-		
+
 		if (count > 400) {
 			part_count = 400;
 			packet_type = GG_NOTIFY_FIRST;
@@ -1814,12 +1911,12 @@
 
 		if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
 			return -1;
-	
-		for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { 
+
+		for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) {
 			n[i].uin = gg_fix32(*u);
 			n[i].dunno1 = *t;
 		}
-	
+
 		if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
 			free(n);
 			res = -1;
@@ -1836,17 +1933,19 @@
 	return res;
 }
 
-/*
- * gg_notify()
+/**
+ * Wysyła do serwera listę kontaktów.
  *
- * wysya serwerowi list kontaktw, dziki czemu wie, czyj stan nas
- * interesuje.
+ * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty
+ * są rodzaju \c GG_USER_NORMAL.
  *
- *  - sess - opis sesji
- *  - userlist - wskanik do tablicy numerw
- *  - count - ilo numerkw
+ * \param sess Struktura sesji
+ * \param userlist Wskaźnik do tablicy numerów kontaktów
+ * \param count Liczba kontaktów
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
  */
 int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
 {
@@ -1854,13 +1953,13 @@
 	uin_t *u;
 	int i, res = 0;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count);
-	
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count);
+
 	if (!sess) {
 		errno = EFAULT;
 		return -1;
 	}
-	
+
 	if (sess->state != GG_STATE_CONNECTED) {
 		errno = ENOTCONN;
 		return -1;
@@ -1868,10 +1967,10 @@
 
 	if (!userlist || !count)
 		return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
-	
+
 	while (count > 0) {
 		int part_count, packet_type;
-		
+
 		if (count > 400) {
 			part_count = 400;
 			packet_type = GG_NOTIFY_FIRST;
@@ -1879,15 +1978,15 @@
 			part_count = count;
 			packet_type = GG_NOTIFY_LAST;
 		}
-			
+
 		if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
 			return -1;
-	
-		for (u = userlist, i = 0; i < part_count; u++, i++) { 
+
+		for (u = userlist, i = 0; i < part_count; u++, i++) {
 			n[i].uin = gg_fix32(*u);
 			n[i].dunno1 = GG_USER_NORMAL;
 		}
-	
+
 		if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
 			res = -1;
 			free(n);
@@ -1903,73 +2002,27 @@
 	return res;
 }
 
-/*
- * gg_add_notify_ex()
+/**
+ * Dodaje kontakt.
  *
- * dodaje do listy kontaktw dany numer w trakcie poczenia.
- * dodawanemu uytkownikowi okrelamy jego typ (patrz protocol.html)
+ * Dodaje do listy kontaktów dany numer w trakcie połączenia. Aby zmienić
+ * rodzaj kontaktu (np. z normalnego na zablokowany), należy najpierw usunąć
+ * poprzedni rodzaj, ponieważ serwer operuje na maskach bitowych.
  *
- *  - sess - opis sesji
- *  - uin - numer
- *  - type - typ
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ * \param type Rodzaj kontaktu
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
  */
 int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
 {
 	struct gg_add_remove a;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
-	
-	if (!sess) {
-		errno = EFAULT;
-		return -1;
-	}
-
-	if (sess->state != GG_STATE_CONNECTED) {
-		errno = ENOTCONN;
-		return -1;
-	}
-	
-	a.uin = gg_fix32(uin);
-	a.dunno1 = type;
-	
-	return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
-}
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
 
-/*
- * gg_add_notify()
- *
- * dodaje do listy kontaktw dany numer w trakcie poczenia.
- *
- *  - sess - opis sesji
- *  - uin - numer
- *
- * 0, -1.
- */
-int gg_add_notify(struct gg_session *sess, uin_t uin)
-{
-	return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
-}
-
-/*
- * gg_remove_notify_ex()
- *
- * usuwa z listy kontaktw w trakcie poczenia.
- * usuwanemu uytkownikowi okrelamy jego typ (patrz protocol.html)
- *
- *  - sess - opis sesji
- *  - uin - numer
- *  - type - typ
- *
- * 0, -1.
- */
-int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
-{
-	struct gg_add_remove a;
-
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
-	
 	if (!sess) {
 		errno = EFAULT;
 		return -1;
@@ -1982,35 +2035,101 @@
 
 	a.uin = gg_fix32(uin);
 	a.dunno1 = type;
-	
+
+	return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
+}
+
+/**
+ * Dodaje kontakt.
+ *
+ * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
+ * kontaktów to \c GG_USER_NORMAL.
+ *
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
+ */
+int gg_add_notify(struct gg_session *sess, uin_t uin)
+{
+	return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
+}
+
+/**
+ * Usuwa kontakt.
+ *
+ * Usuwa z listy kontaktów dany numer w trakcie połączenia.
+ *
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ * \param type Rodzaj kontaktu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
+ */
+int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
+{
+	struct gg_add_remove a;
+
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
+
+	if (!sess) {
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (sess->state != GG_STATE_CONNECTED) {
+		errno = ENOTCONN;
+		return -1;
+	}
+
+	a.uin = gg_fix32(uin);
+	a.dunno1 = type;
+
 	return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
 }
 
-/*
- * gg_remove_notify()
+/**
+ * Usuwa kontakt.
  *
- * usuwa z listy kontaktw w trakcie poczenia.
+ * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
+ * kontaktów to \c GG_USER_NORMAL.
  *
- *  - sess - opis sesji
- *  - uin - numer
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
  *
- * 0, -1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
  */
 int gg_remove_notify(struct gg_session *sess, uin_t uin)
 {
 	return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL);
 }
 
-/*
- * gg_userlist_request()
+/**
+ * Wysyła do serwera zapytanie dotyczące listy kontaktów.
  *
- * wysya danie/zapytanie listy kontaktw na serwerze.
+ * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
+ * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
+ * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
+ * listy kontaktów jest ignorowany przez serwer, ale ze względu na
+ * kompatybilność z innymi klientami, należy przechowywać dane w tym samym
+ * formacie co oryginalny klient Gadu-Gadu.
  *
- *  - sess - opis sesji
- *  - type - rodzaj zapytania/dania
- *  - request - tre zapytania/dania (moe by NULL)
+ * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą
+ * z protokołu -- wysyła i odbiera kompletną listę.
  *
- * 0, -1
+ * \param sess Struktura sesji
+ * \param type Rodzaj zapytania
+ * \param request Treść zapytania (może być równe NULL)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup importexport
  */
 int gg_userlist_request(struct gg_session *sess, char type, const char *request)
 {
@@ -2020,7 +2139,7 @@
 		errno = EFAULT;
 		return -1;
 	}
-	
+
 	if (sess->state != GG_STATE_CONNECTED) {
 		errno = ENOTCONN;
 		return -1;
@@ -2030,7 +2149,7 @@
 		sess->userlist_blocks = 1;
 		return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
 	}
-	
+
 	len = strlen(request);
 
 	sess->userlist_blocks = 0;
@@ -2053,6 +2172,8 @@
 	return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
 }
 
+/* @} */
+
 /*
  * Local variables:
  * c-indentation-style: k&r
--- a/libpurple/protocols/gg/lib/libgadu.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/libgadu.h	Mon Mar 08 22:53:02 2010 +0000
@@ -1,12 +1,13 @@
-/* $Id: libgadu.h 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: libgadu.h.in 878 2009-11-16 23:48:19Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
- *                          Robert J. Wony <speedy@ziew.org>
- *                          Arkadiusz Mikiewicz <arekm@pld-linux.org>
- *                          Tomasz Chiliski <chilek@chilan.com>
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliński <chilek@chilan.com>
  *                          Piotr Wysocki <wysek@linux.bydg.org>
  *                          Dawid Jarosz <dawjar@poczta.onet.pl>
+ *                          Jakub Zawadzki <darkjames@darkjames.ath.cx>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -19,338 +20,568 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file libgadu.h
+ *
+ * \brief Główny plik nagłówkowy biblioteki
+ */
+
 #ifndef __GG_LIBGADU_H
 #define __GG_LIBGADU_H
 
 #ifdef __cplusplus
-#ifdef _MSC_VER
+#ifdef _WIN32
 #pragma pack(push, 1)
 #endif
 extern "C" {
 #endif
 
-#include <libgadu-config.h>
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdarg.h>
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
+/** \cond ignore */
+
+/* Defined if libgadu was compiled for bigendian machine. */
+#undef GG_CONFIG_BIGENDIAN
+
+/* Defined if this machine has gethostbyname_r(). */
+#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+
+/* Defined if libgadu was compiled and linked with pthread support. */
+#undef GG_CONFIG_HAVE_PTHREAD
+
+/* Defined if pthread resolver is the default one. */
+#undef GG_CONFIG_PTHREAD_DEFAULT 
+
+/* Defined if this machine has C99-compiliant vsnprintf(). */
+#undef GG_CONFIG_HAVE_C99_VSNPRINTF
+
+/* Defined if this machine has va_copy(). */
+#undef GG_CONFIG_HAVE_VA_COPY
+
+/* Defined if this machine has __va_copy(). */
+#undef GG_CONFIG_HAVE___VA_COPY
+
+/* Defined if this machine supports long long. */
+#undef GG_CONFIG_HAVE_LONG_LONG
+
+/* Defined if libgadu was compiled and linked with TLS support. */
+#undef GG_CONFIG_HAVE_OPENSSL
+
+/* Defined if uintX_t types are defined in <stdint.h>. */
+#undef GG_CONFIG_HAVE_STDINT_H
+
+/* Defined if uintX_t types are defined in <inttypes.h>. */
+#undef GG_CONFIG_HAVE_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/inttypes.h>. */
+#undef GG_CONFIG_HAVE_SYS_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/int_types.h>. */
+#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+
+/* Defined if uintX_t types are defined in <sys/types.h>. */
+#undef GG_CONFIG_HAVE_SYS_TYPES_H
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
 #include <openssl/ssl.h>
 #endif
 
-/*
- * typedef uin_t
- *
- * typ reprezentujcy numer osoby.
+#ifdef GG_CONFIG_HAVE_STDINT_H
+#include <stdint.h>
+#else
+#  ifdef GG_CONFIG_HAVE_INTTYPES_H
+#  include <inttypes.h>
+#  else
+#    ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H
+#    include <sys/inttypes.h>
+#    else
+#      ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+#      include <sys/int_types.h>
+#      else
+#        ifdef GG_CONFIG_HAVE_SYS_TYPES_H
+#        include <sys/types.h>
+#        else
+
+#ifndef __AC_STDINT_H
+#define __AC_STDINT_H
+
+/* ISO C 9X: 7.18 Integer types <stdint.h> */
+
+typedef unsigned char   uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int   uint32_t;
+
+#ifndef __CYGWIN__
+#define __int8_t_defined
+typedef   signed char    int8_t;
+typedef   signed short  int16_t;
+typedef   signed int    int32_t;
+#endif
+
+#endif /* __AC_STDINT_H */
+
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/** \endcond */
+
+/**
+ * Numer Gadu-Gadu.
  */
 typedef uint32_t uin_t;
 
-/*
- * oglna struktura opisujca rne sesje. przydatna w klientach.
+/**
+ * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x.
+ */
+typedef struct {
+	uint8_t id[8];
+} gg_dcc7_id_t;
+
+/**
+ * Makro deklarujące pola wspólne dla struktur sesji.
  */
 #define gg_common_head(x) \
-	int fd;			/* podgldany deskryptor */ \
-	int check;		/* sprawdzamy zapis czy odczyt */ \
-	int state;		/* aktualny stan maszynki */ \
-	int error;		/* kod bdu dla GG_STATE_ERROR */ \
-	int type;		/* rodzaj sesji */ \
-	int id;			/* identyfikator */ \
-	int timeout;		/* sugerowany timeout w sekundach */ \
-	int (*callback)(x*); 	/* callback przy zmianach */ \
-	void (*destroy)(x*); 	/* funkcja niszczenia */
+	int fd;			/**< Obserwowany deskryptor */ \
+	int check;		/**< Informacja o żądaniu odczytu/zapisu (patrz \ref gg_check_t) */ \
+	int state;		/**< Aktualny stan połączenia (patrz \ref gg_state_t) */ \
+	int error;		/**< Kod błędu dla \c GG_STATE_ERROR (patrz \ref gg_error_t) */ \
+	int type;		/**< Rodzaj sesji (patrz \ref gg_session_t) */ \
+	int id;			/**< Identyfikator sesji */ \
+	int timeout;		/**< Czas pozostały do zakończenia stanu */ \
+	int (*callback)(x*); 	/**< Funkcja zwrotna */ \
+	void (*destroy)(x*); 	/**< Funkcja zwalniania zasobów */
 
+/**
+ * Struktura wspólna dla wszystkich sesji i połączeń. Pozwala na proste
+ * rzutowanie niezależne od rodzaju połączenia.
+ */
 struct gg_common {
 	gg_common_head(struct gg_common)
 };
 
 struct gg_image_queue;
 
-/*
- * struct gg_session
+struct gg_dcc7;
+
+/**
+ * Sposób rozwiązywania nazw serwerów.
+ */
+typedef enum {
+	GG_RESOLVER_DEFAULT = 0,	/**< Domyślny sposób rozwiązywania nazw (jeden z poniższych) */
+	GG_RESOLVER_FORK,		/**< Rozwiązywanie nazw bazujące na procesach */
+	GG_RESOLVER_PTHREAD,		/**< Rozwiązywanie nazw bazujące na wątkach */
+	GG_RESOLVER_WIN32,
+	GG_RESOLVER_CUSTOM,		/**< Funkcje rozwiązywania nazw dostarczone przed aplikację */
+	GG_RESOLVER_INVALID = -1	/**< Nieprawidłowy sposób rozwiązywania nazw (wynik \c gg_session_get_resolver) */
+} gg_resolver_t;
+
+/**
+ * Rodzaj kodowania znaków.
+ */
+typedef enum {
+	GG_ENCODING_CP1250 = 0,		/**< Kodowanie CP1250 */
+	GG_ENCODING_UTF8,		/**< Kodowanie UTF-8 */
+	GG_ENCODING_INVALID = -1	/**< Nieprawidłowe kodowanie */
+} gg_encoding_t;
+
+/**
+ * Sesja Gadu-Gadu.
  *
- * struktura opisujca dan sesj. tworzona przez gg_login(), zwalniana
- * przez gg_free_session().
+ * Tworzona przez funkcję \c gg_login(), zwalniana przez \c gg_free_session().
+ *
+ * \ingroup login
  */
 struct gg_session {
 	gg_common_head(struct gg_session)
 
-	int async;      	/* czy poczenie jest asynchroniczne */
-	int pid;        	/* pid procesu resolvera */
-	int port;       	/* port, z ktrym si czymy */
-	int seq;        	/* numer sekwencyjny ostatniej wiadomoci */
-	int last_pong;  	/* czas otrzymania ostatniego ping/pong */
-	int last_event;		/* czas otrzymania ostatniego pakietu */
+	int async;      	/**< Flaga połączenia asynchronicznego */
+	int pid;        	/**< Numer procesu rozwiązującego nazwę serwera */
+	int port;       	/**< Port serwera */
+	int seq;        	/**< Numer sekwencyjny ostatniej wiadomości */
+	int last_pong;  	/**< Czas otrzymania ostatniej ramki utrzymaniowej */
+	int last_event;		/**< Czas otrzymania ostatniego pakietu */
 
-	struct gg_event *event;	/* zdarzenie po ->callback() */
+	struct gg_event *event;	/**< Zdarzenie po wywołaniu \c callback */
 
-	uint32_t proxy_addr;	/* adres proxy, keszowany */
-	uint16_t proxy_port;	/* port proxy */
+	uint32_t proxy_addr;	/**< Adres serwera pośredniczącego */
+	uint16_t proxy_port;	/**< Port serwera pośredniczącego */
 
-	uint32_t hub_addr;	/* adres huba po resolvniciu */
-	uint32_t server_addr;	/* adres serwera, od huba */
+	uint32_t hub_addr;	/**< Adres huba po rozwiązaniu nazwy */
+	uint32_t server_addr;	/**< Adres serwera otrzymany od huba */
 
-	uint32_t client_addr;	/* adres klienta */
-	uint16_t client_port;	/* port, na ktrym klient sucha */
+	uint32_t client_addr;	/**< Adres gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
+	uint16_t client_port;	/**< Port gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
 
-	uint32_t external_addr;	/* adres zewnetrzny klienta */
-	uint16_t external_port;	/* port zewnetrzny klienta */
+	uint32_t external_addr;	/**< Publiczny adres dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
+	uint16_t external_port;	/**< Publiczny port dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */
 
-	uin_t uin;		/* numerek klienta */
-	char *password;		/* i jego haso. zwalniane automagicznie */
+	uin_t uin;		/**< Własny numer Gadu-Gadu */
+	char *password;		/**< Hasło (zwalniane po użyciu) */
 
-	int initial_status;	/* pocztkowy stan klienta */
-	int status;		/* aktualny stan klienta */
+	int initial_status;	/**< Początkowy status */
+	int status;		/**< Aktualny status */
 
-	char *recv_buf;		/* bufor na otrzymywane pakiety */
-	int recv_done;		/* ile ju wczytano do bufora */
-	int recv_left;		/* i ile jeszcze trzeba wczyta */
+	char *recv_buf;		/**< Bufor na odbierany pakiety */
+	int recv_done;		/**< Liczba wczytanych bajtów pakietu */
+	int recv_left;		/**< Liczba pozostałych do wczytania bajtów pakietu */
 
-	int protocol_version;	/* wersja uywanego protokou */
-	char *client_version;	/* wersja uywanego klienta */
-	int last_sysmsg;	/* ostatnia wiadomo systemowa */
+	int protocol_version;	/**< Wersja protokołu (bez flag) */
+	char *client_version;	/**< Wersja klienta */
+	int last_sysmsg;	/**< Numer ostatniej wiadomości systemowej */
 
-	char *initial_descr;	/* pocztkowy opis stanu klienta */
+	char *initial_descr;	/**< Początkowy opis statusu */
 
-	void *resolver;		/* wskanik na informacje resolvera */
+	void *resolver;		/**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
 
-	char *header_buf;	/* bufor na pocztek nagwka */
-	unsigned int header_done;/* ile ju mamy */
+	char *header_buf;	/**< Bufor na początek nagłówka pakietu */
+	unsigned int header_done;	/**< Liczba wczytanych bajtów nagłówka pakietu */
 
-#ifdef __GG_LIBGADU_HAVE_OPENSSL
-	SSL *ssl;		/* sesja TLS */
-	SSL_CTX *ssl_ctx;	/* kontekst sesji? */
+#ifdef GG_CONFIG_HAVE_OPENSSL
+	SSL *ssl;		/**< Struktura TLS */
+	SSL_CTX *ssl_ctx;	/**< Kontekst sesji TLS */
 #else
-	void *ssl;		/* zachowujemy ABI */
-	void *ssl_ctx;
+	void *ssl;		/**< Struktura TLS */
+	void *ssl_ctx;		/**< Kontekst sesji TLS */
 #endif
 
-	int image_size;		/* maksymalny rozmiar obrazkw w KiB */
+	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+
+	char *userlist_reply;	/**< Bufor z odbieraną listą kontaktów */
+
+	int userlist_blocks;	/**< Liczba części listy kontaktów */
 
-	char *userlist_reply;	/* fragment odpowiedzi listy kontaktw */
+	struct gg_image_queue *images;	/**< Lista wczytywanych obrazków */
+
+	int hash_type;		/**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
+
+	char *send_buf;		/**< Bufor z danymi do wysłania */
+	int send_left;		/**< Liczba bajtów do wysłania */
 
-	int userlist_blocks;	/* na ile kawakw podzielono list kontaktw */
+	struct gg_dcc7 *dcc7_list;	/**< Lista połączeń bezpośrednich skojarzonych z sesją */
+	
+	int soft_timeout;	/**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_watch_fd() */
+
+	int protocol_flags;	/**< Flagi protokołu */
 
-	struct gg_image_queue *images;	/* aktualnie wczytywane obrazki */
+	gg_encoding_t encoding;		/**< Rodzaj kodowania znaków */
+
+	gg_resolver_t resolver_type;	/**< Sposób rozwiązywania nazw serwerów */
+	int (*resolver_start)(int *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynająca rozwiązywanie nazwy */
+	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
+
+	int protocol_features;	/**< Opcje protokołu */
 };
 
-/*
- * struct gg_http
+/**
+ * Połączenie HTTP.
  *
- * oglna struktura opisujca stan wszystkich operacji HTTP. tworzona
- * przez gg_http_connect(), zwalniana przez gg_http_free().
+ * Tworzone przez \c gg_http_connect(), zwalniane przez \c gg_http_free().
+ *
+ * \ingroup http
  */
 struct gg_http {
 	gg_common_head(struct gg_http)
 
-	int async;              /* czy poczenie asynchroniczne */
-	int pid;                /* pid procesu resolvera */
-	int port;               /* port, z ktrym si czymy */
+	int async;              /**< Flaga połączenia asynchronicznego */
+	int pid;                /**< Identyfikator procesu rozwiązującego nazwę serwera */
+	int port;               /**< Port */
+
+	char *query;            /**< Zapytanie HTTP */
+	char *header;           /**< Odebrany nagłówek */
+	int header_size;        /**< Rozmiar wczytanego nagłówka */
+	char *body;             /**< Odebrana strona */
+	unsigned int body_size; /**< Rozmiar strony */
+
+	void *data;             /**< Dane prywatne usługi HTTP */
 
-	char *query;            /* bufor zapytania http */
-	char *header;           /* bufor nagwka */
-	int header_size;        /* rozmiar wczytanego nagwka */
-	char *body;             /* bufor otrzymanych informacji */
-	unsigned int body_size; /* oczekiwana ilo informacji */
+	char *user_data;	/**< Dane prywatne użytkownika (nie są zwalniane) */
+
+	void *resolver;		/**< Dane prywatne procesu lub wątku rozwiązującego nazwę */
+
+	unsigned int body_done;	/**< Liczba odebranych bajtów strony */
 
-	void *data;             /* dane danej operacji http */
-
-	char *user_data;	/* dane uytkownika, nie s zwalniane przez gg_http_free() */
+	gg_resolver_t resolver_type;	/**< Sposób rozwiązywania nazw serwerów */
+	int (*resolver_start)(int *fd, void **private_data, const char *hostname);	/**< Funkcja rozpoczynająca rozwiązywanie nazwy */
+	void (*resolver_cleanup)(void **private_data, int force);	/**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
+};
 
-	void *resolver;		/* wskanik na informacje resolvera */
-
-	unsigned int body_done;	/* ile ju treci odebrano? */
-};
+/** \cond ignore */
 
 #ifdef __GNUC__
 #define GG_PACKED __attribute__ ((packed))
+#ifndef GG_IGNORE_DEPRECATED
+#define GG_DEPRECATED __attribute__ ((deprecated))
+#else
+#define GG_DEPRECATED
+#endif
 #else
 #define GG_PACKED
+#define GG_DEPRECATED
 #endif
 
-#define GG_MAX_PATH 276
+/** \endcond */
+
+#define GG_MAX_PATH 276		/**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */
 
-/*
- * struct gg_file_info
+/**
+ * Odpowiednik struktury WIN32_FIND_DATA z API WIN32.
  *
- * odpowiednik windowsowej struktury WIN32_FIND_DATA niezbdnej przy
- * wysyaniu plikw.
+ * Wykorzystywana przy połączeniach bezpośrednich do wersji Gadu-Gadu 6.x.
  */
 struct gg_file_info {
-	uint32_t mode;			/* dwFileAttributes */
-	uint32_t ctime[2];		/* ftCreationTime */
-	uint32_t atime[2];		/* ftLastAccessTime */
-	uint32_t mtime[2];		/* ftLastWriteTime */
-	uint32_t size_hi;		/* nFileSizeHigh */
-	uint32_t size;			/* nFileSizeLow */
-	uint32_t reserved0;		/* dwReserved0 */
-	uint32_t reserved1;		/* dwReserved1 */
-	unsigned char filename[GG_MAX_PATH - 14];	/* cFileName */
-	unsigned char short_filename[14];		/* cAlternateFileName */
-} GG_PACKED;
+	uint32_t mode;			/**< dwFileAttributes */
+	uint32_t ctime[2];		/**< ftCreationTime */
+	uint32_t atime[2];		/**< ftLastAccessTime */
+	uint32_t mtime[2];		/**< ftLastWriteTime */
+	uint32_t size_hi;		/**< nFileSizeHigh */
+	uint32_t size;			/**< nFileSizeLow */
+	uint32_t reserved0;		/**< dwReserved0 */
+	uint32_t reserved1;		/**< dwReserved1 */
+	unsigned char filename[GG_MAX_PATH - 14];	/**< cFileName */
+	unsigned char short_filename[14];		/**< cAlternateFileName */
+} /** \cond ignore */ GG_PACKED /** \endcond */;
 
-/*
- * struct gg_dcc
+/**
+ * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x.
  *
- * struktura opisujca nasuchujce gniazdo pocze midzy klientami.
- * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free().
+ * Tworzone przez \c gg_dcc_socket_create(), \c gg_dcc_get_file(),
+ * \c gg_dcc_send_file() lub \c gg_dcc_voice_chat(), zwalniane przez
+ * \c gg_dcc_free().
+ *
+ * \ingroup dcc6
  */
 struct gg_dcc {
 	gg_common_head(struct gg_dcc)
 
-	struct gg_event *event;	/* opis zdarzenia */
+	struct gg_event *event;	/**< Zdarzenie po wywołaniu \c callback */
 
-	int active;		/* czy to my si czymy? */
-	int port;		/* port, na ktrym siedzi */
-	uin_t uin;		/* uin klienta */
-	uin_t peer_uin;		/* uin drugiej strony */
-	int file_fd;		/* deskryptor pliku */
-	unsigned int offset;	/* offset w pliku */
-	unsigned int chunk_size;/* rozmiar kawaka */
-	unsigned int chunk_offset;/* offset w aktualnym kawaku */
+	int active;		/**< Flaga połączenia aktywnego (nieużywana) */
+	int port;		/**< Port gniazda nasłuchującego */
+	uin_t uin;		/**< Własny numer Gadu-Gadu */
+	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony połączenia */
+	int file_fd;		/**< deskryptor pliku */
+	unsigned int offset;	/**< Położenie w pliku */
+	unsigned int chunk_size;
+				/**< Rozmiar kawałka pliku */
+	unsigned int chunk_offset;
+				/**< Położenie w aktualnym kawałku pliku */
 	struct gg_file_info file_info;
-				/* informacje o pliku */
-	int established;	/* poczenie ustanowione */
-	uint8_t *voice_buf;	/* bufor na pakiet poczenia gosowego */
-	int incoming;		/* poczenie przychodzce */
-	char *chunk_buf;	/* bufor na kawaek danych */
-	uint32_t remote_addr;	/* adres drugiej strony */
-	uint16_t remote_port;	/* port drugiej strony */
+				/**< Informacje o pliku */
+	int established;	/**< Flaga ustanowienia połączenia */
+	char *voice_buf;	/**< Bufor na pakiet połączenia głosowego */
+	int incoming;		/**< Flaga połączenia przychodzącego */
+	char *chunk_buf;	/**< Bufor na fragment danych */
+	uint32_t remote_addr;	/**< Adres drugiej strony */
+	uint16_t remote_port;	/**< Port drugiej strony */
 };
 
-/*
- * enum gg_session_t
+#define GG_DCC7_HASH_LEN	20	/**< Maksymalny rozmiar skrótu pliku w połączeniach bezpośrenich */
+#define GG_DCC7_FILENAME_LEN	255	/**< Maksymalny rozmiar nazwy pliku w połączeniach bezpośrednich */
+#define GG_DCC7_INFO_LEN	64	/**< Maksymalny rozmiar informacji o połączeniach bezpośrednich */
+
+/**
+ * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x.
  *
- * rodzaje sesji.
+ * \ingroup dcc7
+ */
+struct gg_dcc7 {
+	gg_common_head(struct gg_dcc7)
+
+	gg_dcc7_id_t cid;	/**< Identyfikator połączenia */
+
+	struct gg_event *event;	/**< Struktura zdarzenia */
+
+	uin_t uin;		/**< Własny numer Gadu-Gadu */
+	uin_t peer_uin;		/**< Numer Gadu-Gadu drugiej strony połączenia */
+
+	int file_fd;		/**< Deskryptor przesyłanego pliku */
+	unsigned int offset;	/**< Aktualne położenie w przesyłanym pliku */
+	unsigned int size;	/**< Rozmiar przesyłanego pliku */
+	unsigned char filename[GG_DCC7_FILENAME_LEN + 1];
+				/**< Nazwa przesyłanego pliku */
+	unsigned char hash[GG_DCC7_HASH_LEN];
+				/**< Skrót SHA1 przesyłanego pliku */
+
+	int dcc_type;		/**< Rodzaj połączenia bezpośredniego */
+	int established;	/**< Flaga ustanowienia połączenia */
+	int incoming;		/**< Flaga połączenia przychodzącego */
+	int reverse;		/**< Flaga połączenia zwrotnego */
+
+	uint32_t local_addr;	/**< Adres lokalny */
+	uint16_t local_port;	/**< Port lokalny */
+
+	uint32_t remote_addr;	/**< Adres drugiej strony */
+	uint16_t remote_port;	/**< Port drugiej strony */
+
+	struct gg_session *sess;
+				/**< Sesja do której przypisano połączenie */
+	struct gg_dcc7 *next;	/**< Następne połączenie w liście */
+
+	int soft_timeout;	/**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_dcc7_watch_fd() */
+	int seek;		/**< Flaga mówiąca, że można zmieniać położenie w wysyłanym pliku */
+};
+
+/**
+ * Rodzaj sesji.
  */
 enum gg_session_t {
-	GG_SESSION_GG = 1,	/* poczenie z serwerem gg */
-	GG_SESSION_HTTP,	/* oglna sesja http */
-	GG_SESSION_SEARCH,	/* szukanie */
-	GG_SESSION_REGISTER,	/* rejestrowanie */
-	GG_SESSION_REMIND,	/* przypominanie hasa */
-	GG_SESSION_PASSWD,	/* zmiana hasa */
-	GG_SESSION_CHANGE,	/* zmiana informacji o sobie */
-	GG_SESSION_DCC,		/* oglne poczenie DCC */
-	GG_SESSION_DCC_SOCKET,	/* nasuchujcy socket */
-	GG_SESSION_DCC_SEND,	/* wysyanie pliku */
-	GG_SESSION_DCC_GET,	/* odbieranie pliku */
-	GG_SESSION_DCC_VOICE,	/* rozmowa gosowa */
-	GG_SESSION_USERLIST_GET,	/* pobieranie userlisty */
-	GG_SESSION_USERLIST_PUT,	/* wysyanie userlisty */
-	GG_SESSION_UNREGISTER,	/* usuwanie konta */
-	GG_SESSION_USERLIST_REMOVE,	/* usuwanie userlisty */
-	GG_SESSION_TOKEN,	/* pobieranie tokenu */
+	GG_SESSION_GG = 1,	/**< Połączenie z serwerem Gadu-Gadu */
+	GG_SESSION_HTTP,	/**< Połączenie HTTP */
+	GG_SESSION_SEARCH,	/**< Wyszukiwanie w katalogu publicznym (nieaktualne) */
+	GG_SESSION_REGISTER,	/**< Rejestracja nowego konta */
+	GG_SESSION_REMIND,	/**< Przypominanie hasła */
+	GG_SESSION_PASSWD,	/**< Zmiana hasła */
+	GG_SESSION_CHANGE,	/**< Zmiana informacji w katalogu publicznym (nieaktualne) */
+	GG_SESSION_DCC,		/**< Połączenie bezpośrednie (do wersji 6.x) */
+	GG_SESSION_DCC_SOCKET,	/**< Gniazdo nasłuchujące (do wersji 6.x) */
+	GG_SESSION_DCC_SEND,	/**< Wysyłanie pliku (do wersji 6.x) */
+	GG_SESSION_DCC_GET,	/**< Odbieranie pliku (do wersji 6.x) */
+	GG_SESSION_DCC_VOICE,	/**< Rozmowa głosowa (do wersji 6.x) */
+	GG_SESSION_USERLIST_GET,	/**< Import listy kontaktów z serwera (nieaktualne) */
+	GG_SESSION_USERLIST_PUT,	/**< Eksport listy kontaktów do serwera (nieaktualne) */
+	GG_SESSION_UNREGISTER,	/**< Usuwanie konta */
+	GG_SESSION_USERLIST_REMOVE,	/**< Usuwanie listy kontaktów z serwera (nieaktualne) */
+	GG_SESSION_TOKEN,	/**< Pobieranie tokenu */
+	GG_SESSION_DCC7_SOCKET,	/**< Gniazdo nasłuchujące (od wersji 7.x) */
+	GG_SESSION_DCC7_SEND,	/**< Wysyłanie pliku (od wersji 7.x) */
+	GG_SESSION_DCC7_GET,	/**< Odbieranie pliku (od wersji 7.x) */
+	GG_SESSION_DCC7_VOICE,	/**< Rozmowa głosowa (od wersji 7.x) */
 
-	GG_SESSION_USER0 = 256,	/* zdefiniowana dla uytkownika */
-	GG_SESSION_USER1,	/* j.w. */
-	GG_SESSION_USER2,	/* j.w. */
-	GG_SESSION_USER3,	/* j.w. */
-	GG_SESSION_USER4,	/* j.w. */
-	GG_SESSION_USER5,	/* j.w. */
-	GG_SESSION_USER6,	/* j.w. */
-	GG_SESSION_USER7	/* j.w. */
+	GG_SESSION_USER0 = 256,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER1,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER2,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER3,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER4,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER5,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER6,	/**< Rodzaj zadeklarowany dla użytkownika */
+	GG_SESSION_USER7	/**< Rodzaj zadeklarowany dla użytkownika */
 };
 
-/*
- * enum gg_state_t
- *
- * opisuje stan asynchronicznej maszyny.
+/**
+ * Aktualny stan sesji.
  */
 enum gg_state_t {
-		/* wsplne */
-	GG_STATE_IDLE = 0,		/* nie powinno wystpi. */
-	GG_STATE_RESOLVING,             /* wywoa gethostbyname() */
-	GG_STATE_CONNECTING,            /* wywoa connect() */
-	GG_STATE_READING_DATA,		/* czeka na dane http */
-	GG_STATE_ERROR,			/* wystpi bd. kod w x->error */
+	/* wspólne */
+	GG_STATE_IDLE = 0,		/**< Nie dzieje się nic */
+	GG_STATE_RESOLVING,             /**< Oczekiwanie na rozwiązanie nazwy serwera */
+	GG_STATE_CONNECTING,            /**< Oczekiwanie na połączenie */
+	GG_STATE_READING_DATA,		/**< Oczekiwanie na dane */
+	GG_STATE_ERROR,			/**< Kod błędu w polu \c error */
 
-		/* gg_session */
-	GG_STATE_CONNECTING_HUB,	/* wywoa connect() na huba */
-	GG_STATE_CONNECTING_GG,         /* wywoa connect() na serwer */
-	GG_STATE_READING_KEY,           /* czeka na klucz */
-	GG_STATE_READING_REPLY,         /* czeka na odpowied */
-	GG_STATE_CONNECTED,             /* poczy si */
+	/* gg_session */
+	GG_STATE_CONNECTING_HUB,	/**< Oczekiwanie na połączenie z hubem */
+	GG_STATE_CONNECTING_GG,         /**< Oczekiwanie na połączenie z serwerem */
+	GG_STATE_READING_KEY,           /**< Oczekiwanie na klucz */
+	GG_STATE_READING_REPLY,         /**< Oczekiwanie na odpowiedź serwera */
+	GG_STATE_CONNECTED,             /**< Połączono z serwerem */
 
-		/* gg_http */
-	GG_STATE_SENDING_QUERY,		/* wysya zapytanie http */
-	GG_STATE_READING_HEADER,	/* czeka na nagwek http */
-	GG_STATE_PARSING,               /* przetwarza dane */
-	GG_STATE_DONE,                  /* skoczy */
+	/* gg_http */
+	GG_STATE_SENDING_QUERY,		/**< Wysłano zapytanie HTTP */
+	GG_STATE_READING_HEADER,	/**< Oczekiwanie na nagłówek HTTP */
+	GG_STATE_PARSING,               /**< Przetwarzanie danych */
+	GG_STATE_DONE,                  /**< Połączenie zakończone */
 
-		/* gg_dcc */
-	GG_STATE_LISTENING,		/* czeka na poczenia */
+	/* gg_dcc */
+	GG_STATE_LISTENING,		/* czeka na połączenia */
 	GG_STATE_READING_UIN_1,		/* czeka na uin peera */
-	GG_STATE_READING_UIN_2,		/* czeka na swj uin */
-	GG_STATE_SENDING_ACK,		/* wysya potwierdzenie dcc */
+	GG_STATE_READING_UIN_2,		/* czeka na swój uin */
+	GG_STATE_SENDING_ACK,		/* wysyła potwierdzenie dcc */
 	GG_STATE_READING_ACK,		/* czeka na potwierdzenie dcc */
-	GG_STATE_READING_REQUEST,	/* czeka na komend */
-	GG_STATE_SENDING_REQUEST,	/* wysya komend */
-	GG_STATE_SENDING_FILE_INFO,	/* wysya informacje o pliku */
+	GG_STATE_READING_REQUEST,	/* czeka na komendę */
+	GG_STATE_SENDING_REQUEST,	/* wysyła komendę */
+	GG_STATE_SENDING_FILE_INFO,	/* wysyła informacje o pliku */
 	GG_STATE_READING_PRE_FILE_INFO,	/* czeka na pakiet przed file_info */
 	GG_STATE_READING_FILE_INFO,	/* czeka na informacje o pliku */
-	GG_STATE_SENDING_FILE_ACK,	/* wysya potwierdzenie pliku */
+	GG_STATE_SENDING_FILE_ACK,	/* wysyła potwierdzenie pliku */
 	GG_STATE_READING_FILE_ACK,	/* czeka na potwierdzenie pliku */
-	GG_STATE_SENDING_FILE_HEADER,	/* wysya nagwek pliku */
-	GG_STATE_READING_FILE_HEADER,	/* czeka na nagwek */
+	GG_STATE_SENDING_FILE_HEADER,	/* wysyła nagłówek pliku */
+	GG_STATE_READING_FILE_HEADER,	/* czeka na nagłówek */
 	GG_STATE_GETTING_FILE,		/* odbiera plik */
-	GG_STATE_SENDING_FILE,		/* wysya plik */
+	GG_STATE_SENDING_FILE,		/* wysyła plik */
 	GG_STATE_READING_VOICE_ACK,	/* czeka na potwierdzenie voip */
 	GG_STATE_READING_VOICE_HEADER,	/* czeka na rodzaj bloku voip */
 	GG_STATE_READING_VOICE_SIZE,	/* czeka na rozmiar bloku voip */
 	GG_STATE_READING_VOICE_DATA,	/* czeka na dane voip */
-	GG_STATE_SENDING_VOICE_ACK,	/* wysya potwierdzenie voip */
-	GG_STATE_SENDING_VOICE_REQUEST,	/* wysya danie voip */
-	GG_STATE_READING_TYPE,		/* czeka na typ poczenia */
+	GG_STATE_SENDING_VOICE_ACK,	/* wysyła potwierdzenie voip */
+	GG_STATE_SENDING_VOICE_REQUEST,	/* wysyła żądanie voip */
+	GG_STATE_READING_TYPE,		/* czeka na typ połączenia */
 
 	/* nowe. bez sensu jest to API. */
-	GG_STATE_TLS_NEGOTIATION	/* negocjuje poczenie TLS */
+	GG_STATE_TLS_NEGOTIATION,	/**< Negocjacja połączenia szyfrowanego */
+
+	GG_STATE_REQUESTING_ID,		/**< Oczekiwanie na nadanie identyfikatora połączenia bezpośredniego */
+	GG_STATE_WAITING_FOR_ACCEPT,	/**< Oczekiwanie na potwierdzenie lub odrzucenie połączenia bezpośredniego */
+	GG_STATE_WAITING_FOR_INFO,	/**< Oczekiwanie na informacje o połączeniu bezpośrednim */
+
+	GG_STATE_READING_ID,		/**< Odebranie identyfikatora połączenia bezpośredniego */
+	GG_STATE_SENDING_ID		/**< Wysłano identyfikatora połączenia bezpośredniego */
 };
 
-/*
- * enum gg_check_t
+/**
+ * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać
+ * z deskryptora. Maska bitowa.
  *
- * informuje, co proces klienta powinien sprawdzi na deskryptorze danego
- * poczenia.
+ * \ingroup events
  */
 enum gg_check_t {
-	GG_CHECK_NONE = 0,		/* nic. nie powinno wystpi */
-	GG_CHECK_WRITE = 1,		/* sprawdzamy moliwo zapisu */
-	GG_CHECK_READ = 2		/* sprawdzamy moliwo odczytu */
+	GG_CHECK_NONE = 0,		/**< Nie sprawdzaj niczego */
+	GG_CHECK_WRITE = 1,		/**< Sprawdź możliwość zapisu */
+	GG_CHECK_READ = 2		/**< Sprawdź możliwość odczytu */
 };
 
-/*
- * struct gg_login_params
+/**
+ * Parametry połączenia z serwerem Gadu-Gadu. Parametry zostały przeniesione
+ * do struktury, by uniknąć zmian API po rozszerzeniu protokołu i dodaniu
+ * kolejnych opcji połączenia. Część parametrów, które nie są już aktualne
+ * lub nie mają znaczenia, została usunięta z dokumentacji.
  *
- * parametry gg_login(). przeniesiono do struktury, eby unikn problemw
- * z cigymi zmianami API, gdy dodano co nowego do protokou.
+ * \ingroup login
  */
 struct gg_login_params {
-	uin_t uin;			/* numerek */
-	char *password;			/* haso */
-	int async;			/* asynchroniczne sockety? */
-	int status;			/* pocztkowy status klienta */
-	char *status_descr;		/* opis statusu */
-	uint32_t server_addr;		/* adres serwera gg */
-	uint16_t server_port;		/* port serwera gg */
-	uint32_t client_addr;		/* adres dcc klienta */
-	uint16_t client_port;		/* port dcc klienta */
-	int protocol_version;		/* wersja protokou */
-	char *client_version;		/* wersja klienta */
-	int has_audio;			/* czy ma dwik? */
-	int last_sysmsg;		/* ostatnia wiadomo systemowa */
-	uint32_t external_addr;		/* adres widziany na zewnatrz */
-	uint16_t external_port;		/* port widziany na zewnatrz */
-	int tls;			/* czy czymy po TLS? */
-	int image_size;			/* maksymalny rozmiar obrazka w KiB */
-	int era_omnix;			/* czy udawa klienta era omnix? */
+	uin_t uin;			/**< Numer Gadu-Gadu */
+	char *password;			/**< Hasło */
+	int async;			/**< Flaga asynchronicznego połączenia (domyślnie nie) */
+	int status;			/**< Początkowy status użytkownika (domyślnie \c GG_STATUS_AVAIL) */
+	char *status_descr;		/**< Początkowy opis użytkownika (domyślnie brak) */
+	uint32_t server_addr;		/**< Adres serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
+	uint16_t server_port;		/**< Port serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
+#ifndef DOXYGEN
+	uint32_t client_addr;		/**< Adres połączeń bezpośrednich (nieaktualne) */
+	uint16_t client_port;		/**< Port połączeń bezpośrednich (nieaktualne) */
+#endif
+	int protocol_version;		/**< Wersja protokołu wysyłana do serwera (domyślnie najnowsza obsługiwana) */
+	char *client_version;		/**< Wersja klienta wysyłana do serwera (domyślnie najnowsza znana) */
+	int has_audio;			/**< Flaga obsługi połączeń głosowych */
+	int last_sysmsg;		/**< Numer ostatnio odebranej wiadomości systemowej */
+	uint32_t external_addr;		/**< Adres publiczny dla połączeń bezpośrednich (6.x) */
+	uint16_t external_port;		/**< Port publiczny dla połączeń bezpośrednich (6.x) */
+#ifndef DOXYGEN
+	int tls;			/**< Flaga połączenia szyfrowanego (nieaktualna) */
+#endif
+	int image_size;			/**< Maksymalny rozmiar obsługiwanych obrazków w kilobajtach */
+#ifndef DOXYGEN
+	int era_omnix;			/**< Flaga udawania klienta Era Omnix (nieaktualna) */
+#endif
+	int hash_type;			/**< Rodzaj skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domyślnie SHA1) */
+	gg_encoding_t encoding;		/**< Rodzaj kodowania używanego w sesji (domyślnie CP1250) */
+	gg_resolver_t resolver;		/**< Sposób rozwiązywania nazw (patrz \ref build-resolver) */
+	int protocol_features;		/**< Opcje protokołu (flagi GG_FEATURE_*). */
 
-	char dummy[6 * sizeof(int)];	/* miejsce na kolejnych 6 zmiennych,
-					 * eby z dodaniem parametru nie
-					 * zmienia si rozmiar struktury */
+#ifndef DOXYGEN
+	char dummy[2 * sizeof(int)];	/**< \internal Miejsce na kilka kolejnych
+					  parametrów, żeby wraz z dodawaniem kolejnych
+					  parametrów nie zmieniał się rozmiar struktury */
+#endif
+
 };
 
 struct gg_session *gg_login(const struct gg_login_params *p);
@@ -367,232 +598,337 @@
 int gg_ping(struct gg_session *sess);
 int gg_userlist_request(struct gg_session *sess, char type, const char *request);
 int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32);
-int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size);
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size);
 
 uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
 
-struct gg_image_queue {
-	uin_t sender;			/* nadawca obrazka */
-	uint32_t size;			/* rozmiar */
-	uint32_t crc32;			/* suma kontrolna */
-	char *filename;			/* nazwa pliku */
-	char *image;			/* bufor z obrazem */
-	uint32_t done;			/* ile ju wczytano */
+int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type);
+gg_resolver_t gg_session_get_resolver(struct gg_session *gs);
+int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
+
+int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type);
+gg_resolver_t gg_http_get_resolver(struct gg_http *gh);
+int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
 
-	struct gg_image_queue *next;	/* nastpny na licie */
-};
+int gg_global_set_resolver(gg_resolver_t type);
+gg_resolver_t gg_global_get_resolver(void);
+int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
 
-/*
- * enum gg_event_t
+/**
+ * Rodzaj zdarzenia.
  *
- * rodzaje zdarze.
+ * \ingroup events
  */
 enum gg_event_t {
-	GG_EVENT_NONE = 0,		/* nic si nie wydarzyo */
-	GG_EVENT_MSG,			/* otrzymano wiadomo */
-	GG_EVENT_NOTIFY,		/* kto si pojawi */
-	GG_EVENT_NOTIFY_DESCR,		/* kto si pojawi z opisem */
-	GG_EVENT_STATUS,		/* kto zmieni stan */
-	GG_EVENT_ACK,			/* potwierdzenie wysania wiadomoci */
-	GG_EVENT_PONG,			/* pakiet pong */
-	GG_EVENT_CONN_FAILED,		/* poczenie si nie udao */
-	GG_EVENT_CONN_SUCCESS,		/* poczenie si powiodo */
-	GG_EVENT_DISCONNECT,		/* serwer zrywa poczenie */
+	GG_EVENT_NONE = 0,		/**< Nie wydarzyło się nic wartego uwagi */
+	GG_EVENT_MSG,			/**< \brief Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. */
+	GG_EVENT_NOTIFY,		/**< \brief Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+	GG_EVENT_NOTIFY_DESCR,		/**< \brief Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+	GG_EVENT_STATUS,		/**< \brief Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+	GG_EVENT_ACK,			/**< Potwierdzenie doręczenia wiadomości */
+	GG_EVENT_PONG,			/**< \brief Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. */
+	GG_EVENT_CONN_FAILED,		/**< \brief Nie udało się połączyć */
+	GG_EVENT_CONN_SUCCESS,		/**< \brief Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. */
+	GG_EVENT_DISCONNECT,		/**< \brief Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. */
+
+	GG_EVENT_DCC_NEW,		/**< Nowe połączenie bezpośrednie (6.x) */
+	GG_EVENT_DCC_ERROR,		/**< Błąd połączenia bezpośredniego (6.x) */
+	GG_EVENT_DCC_DONE,		/**< Zakończono połączenie bezpośrednie (6.x) */
+	GG_EVENT_DCC_CLIENT_ACCEPT,	/**< Moment akceptacji klienta w połączeniu bezpośrednim (6.x) */
+	GG_EVENT_DCC_CALLBACK,		/**< Zwrotne połączenie bezpośrednie (6.x) */
+	GG_EVENT_DCC_NEED_FILE_INFO,	/**< Należy wypełnić \c file_info dla połączenia bezpośredniego (6.x) */
+	GG_EVENT_DCC_NEED_FILE_ACK,	/**< Czeka na potwierdzenie pliku w połączeniu bezpośrednim (6.x) */
+	GG_EVENT_DCC_NEED_VOICE_ACK,	/**< Czeka na potwierdzenie rozmowy w połączeniu bezpośrednim (6.x) */
+	GG_EVENT_DCC_VOICE_DATA, 	/**< Dane bezpośredniego połączenia głosowego (6.x) */
 
-	GG_EVENT_DCC_NEW,		/* nowe poczenie midzy klientami */
-	GG_EVENT_DCC_ERROR,		/* bd poczenia midzy klientami */
-	GG_EVENT_DCC_DONE,		/* zakoczono poczenie */
-	GG_EVENT_DCC_CLIENT_ACCEPT,	/* moment akceptacji klienta */
-	GG_EVENT_DCC_CALLBACK,		/* klient si poczy na danie */
-	GG_EVENT_DCC_NEED_FILE_INFO,	/* naley wypeni file_info */
-	GG_EVENT_DCC_NEED_FILE_ACK,	/* czeka na potwierdzenie pliku */
-	GG_EVENT_DCC_NEED_VOICE_ACK,	/* czeka na potwierdzenie rozmowy */
-	GG_EVENT_DCC_VOICE_DATA, 	/* ramka danych rozmowy gosowej */
+	GG_EVENT_PUBDIR50_SEARCH_REPLY,	/**< Odpowiedź katalogu publicznego */
+	GG_EVENT_PUBDIR50_READ,		/**< Odczytano własne dane z katalogu publicznego */
+	GG_EVENT_PUBDIR50_WRITE,	/**< Zmieniono własne dane w katalogu publicznym */
+
+	GG_EVENT_STATUS60,		/**< Zmiana statusu osoby z listy kontaktów */
+	GG_EVENT_NOTIFY60,		/**< Informacja o statusach osób z listy kontaktów */
+	GG_EVENT_USERLIST,		/**< Wynik importu lub eksportu listy kontaktów */
+	GG_EVENT_IMAGE_REQUEST,		/**< Żądanie przesłania obrazka z wiadommości */
+	GG_EVENT_IMAGE_REPLY,		/**< Przysłano obrazek z wiadomości */
+	GG_EVENT_DCC_ACK,		/**< Potwierdzenie transmisji w połączeniu bezpośrednim (6.x) */
 
-	GG_EVENT_PUBDIR50_SEARCH_REPLY,	/* odpowiedz wyszukiwania */
-	GG_EVENT_PUBDIR50_READ,		/* odczytano wasne dane z katalogu */
-	GG_EVENT_PUBDIR50_WRITE,	/* wpisano wasne dane do katalogu */
+	GG_EVENT_DCC7_NEW,		/**< Nowe połączenie bezpośrednie (7.x) */
+	GG_EVENT_DCC7_ACCEPT,		/**< Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor */
+	GG_EVENT_DCC7_REJECT,		/**< Odrzucono połączenie bezpośrednie (7.x) */
+	GG_EVENT_DCC7_CONNECTED,	/**< Zestawiono połączenie bezpośrednie (7.x), nowy deskryptor */
+	GG_EVENT_DCC7_ERROR,		/**< Błąd połączenia bezpośredniego (7.x) */
+	GG_EVENT_DCC7_DONE,		/**< Zakończono połączenie bezpośrednie (7.x) */
+	GG_EVENT_DCC7_PENDING,		/**< Trwa próba połączenia bezpośredniego (7.x), nowy deskryptor */
 
-	GG_EVENT_STATUS60,		/* kto zmieni stan w GG 6.0 */
-	GG_EVENT_NOTIFY60,		/* kto si pojawi w GG 6.0 */
-	GG_EVENT_USERLIST,		/* odpowied listy kontaktw w GG 6.0 */
-	GG_EVENT_IMAGE_REQUEST,		/* proba o wysanie obrazka GG 6.0 */
-	GG_EVENT_IMAGE_REPLY,		/* podesany obrazek GG 6.0 */
-	GG_EVENT_DCC_ACK		/* potwierdzenie transmisji */
+	GG_EVENT_XML_EVENT,		/**< Otrzymano komunikat systemowy (7.7) */
+	GG_EVENT_DISCONNECT_ACK,	/**< \brief Potwierdzenie zakończenia sesji. Informuje o tym, że zmiana stanu na niedostępny z opisem dotarła do serwera i można zakończyć połączenie TCP. */
 };
 
 #define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
 
-/*
- * enum gg_failure_t
- *
- * okrela powd nieudanego poczenia.
+/**
+ * Powód nieudanego połączenia.
  */
 enum gg_failure_t {
-	GG_FAILURE_RESOLVING = 1,	/* nie znaleziono serwera */
-	GG_FAILURE_CONNECTING,		/* nie mona si poczy */
-	GG_FAILURE_INVALID,		/* serwer zwrci nieprawidowe dane */
-	GG_FAILURE_READING,		/* zerwano poczenie podczas odczytu */
-	GG_FAILURE_WRITING,		/* zerwano poczenie podczas zapisu */
-	GG_FAILURE_PASSWORD,		/* nieprawidowe haso */
-	GG_FAILURE_404, 		/* XXX nieuywane */
-	GG_FAILURE_TLS,			/* bd negocjacji TLS */
-	GG_FAILURE_NEED_EMAIL 		/* serwer rozczy nas z prob o zmian emaila */
+	GG_FAILURE_RESOLVING = 1,	/**< Nie znaleziono serwera */
+	GG_FAILURE_CONNECTING,		/**< Błąd połączenia */
+	GG_FAILURE_INVALID,		/**< Serwer zwrócił nieprawidłowe dane */
+	GG_FAILURE_READING,		/**< Zerwano połączenie podczas odczytu */
+	GG_FAILURE_WRITING,		/**< Zerwano połączenie podczas zapisu */
+	GG_FAILURE_PASSWORD,		/**< Nieprawidłowe hasło */
+	GG_FAILURE_404, 		/**< Nieużywane */
+	GG_FAILURE_TLS,			/**< Błąd negocjacji szyfrowanego połączenia */
+	GG_FAILURE_NEED_EMAIL, 		/**< Serwer rozłączył nas z prośbą o zmianę adresu e-mail */
+	GG_FAILURE_INTRUDER,		/**< Zbyt wiele prób połączenia z nieprawidłowym hasłem */
+	GG_FAILURE_UNAVAILABLE		/**< Serwery są wyłączone */
 };
 
-/*
- * enum gg_error_t
+/**
+ * Kod błędu danej operacji.
  *
- * okrela rodzaj bdu wywoanego przez dan operacj. nie zawiera
- * przesadnie szczegowych informacji o powodzie bdu, by nie komplikowa
- * obsugi bdw. jeli wymagana jest wiksza dokadno, naley sprawdzi
- * zawarto zmiennej errno.
+ * Nie zawiera przesadnie szczegółowych informacji o powodach błędów, by nie
+ * komplikować ich obsługi. Jeśli wymagana jest większa dokładność, należy
+ * sprawdzić zawartość zmiennej systemowej \c errno.
  */
 enum gg_error_t {
-	GG_ERROR_RESOLVING = 1,		/* bd znajdowania hosta */
-	GG_ERROR_CONNECTING,		/* bd aczenia si */
-	GG_ERROR_READING,		/* bd odczytu */
-	GG_ERROR_WRITING,		/* bd wysyania */
+	GG_ERROR_RESOLVING = 1,		/**< Nie znaleziono hosta */
+	GG_ERROR_CONNECTING,		/**< Błąd połączenia */
+	GG_ERROR_READING,		/**< Błąd odczytu/odbierania */
+	GG_ERROR_WRITING,		/**< Błąd zapisu/wysyłania */
 
-	GG_ERROR_DCC_HANDSHAKE,		/* bd negocjacji */
-	GG_ERROR_DCC_FILE,		/* bd odczytu/zapisu pliku */
-	GG_ERROR_DCC_EOF,		/* plik si skoczy? */
-	GG_ERROR_DCC_NET,		/* bd wysyania/odbierania */
-	GG_ERROR_DCC_REFUSED 		/* poczenie odrzucone przez usera */
+	GG_ERROR_DCC_HANDSHAKE,		/**< Błąd negocjacji */
+	GG_ERROR_DCC_FILE,		/**< Błąd odczytu/zapisu pliku */
+	GG_ERROR_DCC_EOF,		/**< Przedwczesny koniec pliku */
+	GG_ERROR_DCC_NET,		/**< Błąd wysyłania/odbierania */
+	GG_ERROR_DCC_REFUSED, 		/**< Połączenie odrzucone */
+
+	GG_ERROR_DCC7_HANDSHAKE,	/**< Błąd negocjacji */
+	GG_ERROR_DCC7_FILE,		/**< Błąd odczytu/zapisu pliku */
+	GG_ERROR_DCC7_EOF,		/**< Przedwczesny koniec pliku */
+	GG_ERROR_DCC7_NET,		/**< Błąd wysyłania/odbierania */
+	GG_ERROR_DCC7_REFUSED 		/**< Połączenie odrzucone */
 };
 
-/*
- * struktury dotyczce wyszukiwania w GG 5.0. NIE NALEY SI DO NICH
- * ODWOYWA BEZPOREDNIO! do dostpu do nich su funkcje gg_pubdir50_*()
+/**
+ * Pole zapytania lub odpowiedzi katalogu publicznego.
  */
 struct gg_pubdir50_entry {
-	int num;
-	char *field;
-	char *value;
-};
+	int num;	/**< Numer wyniku */
+	char *field;	/**< Nazwa pola */
+	char *value;	/**< Wartość pola */
+} /* GG_DEPRECATED */;
 
+/**
+ * Zapytanie lub odpowiedź katalogu publicznego.
+ *
+ * Patrz \c gg_pubdir50_t.
+ */
 struct gg_pubdir50_s {
-	int count;
-	uin_t next;
-	int type;
-	uint32_t seq;
-	struct gg_pubdir50_entry *entries;
-	int entries_count;
-};
+	int count;	/**< Liczba wyników odpowiedzi */
+	uin_t next;	/**< Numer początkowy następnego zapytania */
+	int type;	/**< Rodzaj zapytania */
+	uint32_t seq;	/**< Numer sekwencyjny */
+	struct gg_pubdir50_entry *entries;	/**< Pola zapytania lub odpowiedzi */
+	int entries_count;	/**< Liczba pól */
+} /* GG_DEPRECATED */;
 
-/*
- * typedef gg_pubdir_50_t
+/**
+ * Zapytanie lub odpowiedź katalogu publicznego.
  *
- * typ opisujcy zapytanie lub wynik zapytania katalogu publicznego
- * z protokou GG 5.0. nie naley si odwoywa bezporednio do jego
- * pl -- su do tego funkcje gg_pubdir50_*()
+ * Do pól nie należy się odwoływać bezpośrednio -- wszystkie niezbędne
+ * informacje są dostępne za pomocą funkcji \c gg_pubdir50_*
  */
 typedef struct gg_pubdir50_s *gg_pubdir50_t;
 
-/*
- * struct gg_event
+/**
+ * Opis zdarzenia \c GG_EVENT_MSG.
+ */
+struct gg_event_msg {
+	uin_t sender;		/**< Numer nadawcy */
+	int msgclass;		/**< Klasa wiadomości */
+	time_t time;		/**< Czas nadania */
+	unsigned char *message;	/**< Treść wiadomości */
+
+	int recipients_count;	/**< Liczba odbiorców konferencji */
+	uin_t *recipients;	/**< Odbiorcy konferencji */
+
+	int formats_length;	/**< Długość informacji o formatowaniu tekstu */
+	void *formats;		/**< Informacje o formatowaniu tekstu */
+	uint32_t seq;		/**< Numer sekwencyjny wiadomości */
+
+	char *xhtml_message;	/**< Treść wiadomości w formacie XHTML (może być równe \c NULL, jeśli wiadomość nie zawiera treści XHTML) */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR.
+ */
+struct gg_event_notify_descr {
+	struct gg_notify_reply *notify;	/**< Informacje o liście kontaktów */
+	char *descr;		/**< Opis status */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS.
+ */
+struct gg_event_status {
+	uin_t uin;		/**< Numer Gadu-Gadu */
+	uint32_t status;	/**< Nowy status */
+	char *descr;		/**< Opis */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_STATUS60.
+ */
+struct gg_event_status60 {
+	uin_t uin;		/**< Numer Gadu-Gadu */
+	int status;		/**< Nowy status */
+	uint32_t remote_ip;	/**< Adres IP dla połączeń bezpośrednich */
+	uint16_t remote_port;	/**< Port dla połączeń bezpośrednich */
+	int version;		/**< Wersja protokołu */
+	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+	char *descr;		/**< Opis statusu */
+	time_t time;		/**< Czas powrotu */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60.
+ */
+struct gg_event_notify60 {
+	uin_t uin;		/**< Numer Gadu-Gadu */
+	int status;		/**< Nowy status */
+	uint32_t remote_ip;	/**< Adres IP dla połączeń bezpośrednich */
+	uint16_t remote_port;	/**< Port dla połączeń bezpośrednich */
+	int version;		/**< Wersja protokołu */
+	int image_size;		/**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+	char *descr;		/**< Opis statusu */
+	time_t time;		/**< Czas powrotu */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_ACK.
+ */
+struct gg_event_ack {
+	uin_t recipient;	/**< Numer odbiorcy */
+	int status;		/**< Status doręczenia */
+	int seq;		/**< Numer sekwencyjny wiadomości */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_USERLIST.
+ */
+struct gg_event_userlist {
+	char type;		/**< Rodzaj odpowiedzi */
+	char *reply;		/**< Treść odpowiedzi */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC_VOICE_DATA.
+ */
+struct gg_event_dcc_voice_data {
+	uint8_t *data;		/**< Dane dźwiękowe */
+	int length;		/**< Rozmiar danych dźwiękowych */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST.
+ */
+struct gg_event_image_request {
+	uin_t sender;		/**< Nadawca żądania */
+	uint32_t size;		/**< Rozmiar obrazka */
+	uint32_t crc32;		/**< Suma kontrolna CRC32 */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_IMAGE_REPLY.
+ */
+struct gg_event_image_reply {
+	uin_t sender;		/**< Nadawca obrazka */
+	uint32_t size;		/**< Rozmiar obrazka */
+	uint32_t crc32;		/**< Suma kontrolna CRC32 */
+	char *filename;		/**< Nazwa pliku */
+	char *image;		/**< Bufor z obrazkiem */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_XML_EVENT.
+ */
+struct gg_event_xml_event {
+	char *data;		/**< Bufor z komunikatem */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED.
+ */
+struct gg_event_dcc7_connected {
+	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
+	// XXX czy coś się przyda?
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_REJECT.
+ */
+struct gg_event_dcc7_reject {
+	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
+	int reason;		/**< powód odrzucenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_ACCEPT.
+ */
+struct gg_event_dcc7_accept {
+	struct gg_dcc7 *dcc7;	/**< Struktura połączenia */
+	int type;		/**< Sposób połączenia (P2P, przez serwer) */
+	uint32_t remote_ip;	/**< Adres zdalnego klienta */
+	uint16_t remote_port;	/**< Port zdalnego klienta */
+};
+
+/**
+ * Unia wszystkich zdarzeń zwracanych przez funkcje \c gg_watch_fd(), 
+ * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd().
  *
- * struktura opisujca rodzaj zdarzenia. wychodzi z gg_watch_fd() lub
- * z gg_dcc_watch_fd()
+ * \ingroup events
+ */
+union gg_event_union {
+	enum gg_failure_t failure;	/**< Błąd połączenia (\c GG_EVENT_CONN_FAILED) */
+	struct gg_notify_reply *notify;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY) */
+	struct gg_event_notify_descr notify_descr;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY_DESCR) */
+	struct gg_event_status status;	/**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS) */
+	struct gg_event_status60 status60;	/**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS60) */
+	struct gg_event_notify60 *notify60;	/**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY60) */
+	struct gg_event_msg msg;	/**< Otrzymano wiadomość (\c GG_EVENT_MSG) */
+	struct gg_event_ack ack;	/**< Potwierdzenie wiadomości (\c GG_EVENT_ACK) */
+	struct gg_event_image_request image_request;	/**< Żądanie wysłania obrazka (\c GG_EVENT_IMAGE_REQUEST) */
+	struct gg_event_image_reply image_reply;	/**< Odpowiedź z obrazkiem (\c GG_EVENT_IMAGE_REPLY) */
+	struct gg_event_userlist userlist;	/**< Odpowiedź listy kontaktów (\c GG_EVENT_USERLIST) */
+	gg_pubdir50_t pubdir50;	/**< Odpowiedź katalogu publicznego (\c GG_EVENT_PUBDIR50_*) */
+	struct gg_event_xml_event xml_event;	/**< Zdarzenie systemowe (\c GG_EVENT_XML_EVENT) */
+	struct gg_dcc *dcc_new;	/**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC_NEW) */
+	enum gg_error_t dcc_error;	/**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC_ERROR) */
+	struct gg_event_dcc_voice_data dcc_voice_data;	/**< Dane połączenia głosowego (\c GG_EVENT_DCC_VOICE_DATA) */
+	struct gg_dcc7 *dcc7_new;	/**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC7_NEW) */
+	enum gg_error_t dcc7_error;	/**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC7_ERROR) */
+	struct gg_event_dcc7_connected dcc7_connected;	/**< Informacja o zestawieniu połączenia bezpośredniego (\c GG_EVENT_DCC7_CONNECTED) */
+	struct gg_event_dcc7_reject dcc7_reject;	/**< Odrzucono połączenia bezpośredniego (\c GG_EVENT_DCC7_REJECT) */
+	struct gg_event_dcc7_accept dcc7_accept;	/**< Zaakceptowano połączenie bezpośrednie (\c GG_EVENT_DCC7_ACCEPT) */
+};
+
+/**
+ * Opis zdarzenia.
+ *
+ * Zwracany przez funkcje \c gg_watch_fd(), \c gg_dcc_watch_fd()
+ * i \c gg_dcc7_watch_fd(). Po przeanalizowaniu należy zwolnić
+ * za pomocą \c gg_event_free().
+ *
+ * \ingroup events
  */
 struct gg_event {
-	int type;	/* rodzaj zdarzenia -- gg_event_t */
-	union {		/* @event */
-		struct gg_notify_reply *notify;	/* informacje o licie kontaktw -- GG_EVENT_NOTIFY */
-
-		enum gg_failure_t failure;	/* bd poczenia -- GG_EVENT_FAILURE */
-
-		struct gg_dcc *dcc_new;		/* nowe poczenie bezporednie -- GG_EVENT_DCC_NEW */
-
-		int dcc_error;			/* bd poczenia bezporedniego -- GG_EVENT_DCC_ERROR */
-
-		gg_pubdir50_t pubdir50;		/* wynik operacji zwizanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */
-
-		struct {			/* @msg odebrano wiadomo -- GG_EVENT_MSG */
-			uin_t sender;		/* numer nadawcy */
-			int msgclass;		/* klasa wiadomoci */
-			time_t time;		/* czas nadania */
-			unsigned char *message;	/* tre wiadomoci */
-
-			int recipients_count;	/* ilo odbiorcw konferencji */
-			uin_t *recipients;	/* odbiorcy konferencji */
-
-			int formats_length;	/* dugo informacji o formatowaniu tekstu */
-			void *formats;		/* informacje o formatowaniu tekstu */
-		} msg;
-
-		struct {			/* @notify_descr informacje o licie kontaktw z opisami stanu -- GG_EVENT_NOTIFY_DESCR */
-			struct gg_notify_reply *notify;	/* informacje o licie kontaktw */
-			char *descr;		/* opis stanu */
-		} notify_descr;
-
-		struct {			/* @status zmiana stanu -- GG_EVENT_STATUS */
-			uin_t uin;		/* numer */
-			uint32_t status;	/* nowy stan */
-			char *descr;		/* opis stanu */
-		} status;
-
-		struct {			/* @status60 zmiana stanu -- GG_EVENT_STATUS60 */
-			uin_t uin;		/* numer */
-			int status;	/* nowy stan */
-			uint32_t remote_ip;	/* adres ip */
-			uint16_t remote_port;	/* port */
-			int version;	/* wersja klienta */
-			int image_size;	/* maksymalny rozmiar grafiki w KiB */
-			char *descr;		/* opis stanu */
-			time_t time;		/* czas powrotu */
-		} status60;
-
-		struct {			/* @notify60 informacja o licie kontaktw -- GG_EVENT_NOTIFY60 */
-			uin_t uin;		/* numer */
-			int status;	/* stan */
-			uint32_t remote_ip;	/* adres ip */
-			uint16_t remote_port;	/* port */
-			int version;	/* wersja klienta */
-			int image_size;	/* maksymalny rozmiar grafiki w KiB */
-			char *descr;		/* opis stanu */
-			time_t time;		/* czas powrotu */
-		} *notify60;
-
-		struct {			/* @ack potwierdzenie wiadomoci -- GG_EVENT_ACK */
-			uin_t recipient;	/* numer odbiorcy */
-			int status;		/* stan dorczenia wiadomoci */
-			int seq;		/* numer sekwencyjny wiadomoci */
-		} ack;
-
-		struct {			/* @dcc_voice_data otrzymano dane dwikowe -- GG_EVENT_DCC_VOICE_DATA */
-			uint8_t *data;		/* dane dwikowe */
-			int length;		/* ilo danych dwikowych */
-		} dcc_voice_data;
-
-		struct {			/* @userlist odpowied listy kontaktw serwera */
-			char type;		/* rodzaj odpowiedzi */
-			char *reply;		/* tre odpowiedzi */
-		} userlist;
-
-		struct {			/* @image_request proba o obrazek */
-			uin_t sender;		/* nadawca proby */
-			uint32_t size;		/* rozmiar obrazka */
-			uint32_t crc32;		/* suma kontrolna */
-		} image_request;
-
-		struct {			/* @image_reply odpowied z obrazkiem */
-			uin_t sender;		/* nadawca odpowiedzi */
-			uint32_t size;		/* rozmiar obrazka */
-			uint32_t crc32;		/* suma kontrolna */
-			char *filename;		/* nazwa pliku */
-			char *image;		/* bufor z obrazkiem */
-		} image_reply;
-	} event;
+	int type;			/**< Rodzaj zdarzenia */
+	union gg_event_union event;	/**< Informacja o zdarzeniu */
 };
 
 struct gg_event *gg_watch_fd(struct gg_session *sess);
 void gg_event_free(struct gg_event *e);
-#define gg_free_event gg_event_free
 
-/*
- * funkcje obsugi listy kontaktw.
- */
 int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count);
 int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
 int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type);
@@ -600,76 +936,11 @@
 int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type);
 int gg_remove_notify(struct gg_session *sess, uin_t uin);
 
-/*
- * funkcje obsugi http.
- */
 struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header);
 int gg_http_watch_fd(struct gg_http *h);
 void gg_http_stop(struct gg_http *h);
 void gg_http_free(struct gg_http *h);
-void gg_http_free_fields(struct gg_http *h);
-#define gg_free_http gg_http_free
 
-/*
- * struktury opisujca kryteria wyszukiwania dla gg_search(). nieaktualne,
- * zastpione przez gg_pubdir50_t. pozostawiono je dla zachowania ABI.
- */
-struct gg_search_request {
-	int active;
-	unsigned int start;
-	char *nickname;
-	char *first_name;
-	char *last_name;
-	char *city;
-	int gender;
-	int min_birth;
-	int max_birth;
-	char *email;
-	char *phone;
-	uin_t uin;
-};
-
-struct gg_search {
-	int count;
-	struct gg_search_result *results;
-};
-
-struct gg_search_result {
-	uin_t uin;
-	char *first_name;
-	char *last_name;
-	char *nickname;
-	int born;
-	int gender;
-	char *city;
-	int active;
-};
-
-#define GG_GENDER_NONE 0
-#define GG_GENDER_FEMALE 1
-#define GG_GENDER_MALE 2
-
-/*
- * funkcje wyszukiwania.
- */
-struct gg_http *gg_search(const struct gg_search_request *r, int async);
-int gg_search_watch_fd(struct gg_http *f);
-void gg_free_search(struct gg_http *f);
-#define gg_search_free gg_free_search
-
-const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start);
-const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start);
-const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start);
-const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start);
-void gg_search_request_free(struct gg_search_request *r);
-
-/*
- * funkcje obsugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje
- * zachowuj pewien poziom abstrakcji, eby unikn zmian ABI przy zmianach
- * w protokole.
- *
- * NIE NALEY SI ODWOYWA DO PL gg_pubdir50_t BEZPOREDNIO!
- */
 uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req);
 gg_pubdir50_t gg_pubdir50_new(int type);
 int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value);
@@ -681,6 +952,8 @@
 uint32_t gg_pubdir50_seq(gg_pubdir50_t res);
 void gg_pubdir50_free(gg_pubdir50_t res);
 
+#ifndef DOXYGEN
+
 #define GG_PUBDIR50_UIN "FmNumber"
 #define GG_PUBDIR50_STATUS "FmStatus"
 #define GG_PUBDIR50_FIRSTNAME "firstname"
@@ -699,110 +972,114 @@
 #define GG_PUBDIR50_FAMILYNAME "familyname"
 #define GG_PUBDIR50_FAMILYCITY "familycity"
 
-int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length);
+#else
 
-/*
- * struct gg_pubdir
+/** 
+ * \ingroup pubdir50
  *
- * operacje na katalogu publicznym.
+ * Rodzaj pola zapytania.
  */
-struct gg_pubdir {
-	int success;		/* czy si udao */
-	uin_t uin;		/* otrzymany numerek. 0 jeli bd */
+enum {
+	GG_PUBDIR50_UIN,	/**< Numer Gadu-Gadu */
+	GG_PUBDIR50_STATUS,	/**< Status (tylko wynik wyszukiwania) */
+	GG_PUBDIR50_FIRSTNAME,	/**< Imię */
+	GG_PUBDIR50_LASTNAME,	/**< Nazwisko */
+	GG_PUBDIR50_NICKNAME,	/**< Pseudonim */
+	GG_PUBDIR50_BIRTHYEAR,	/**< Rok urodzenia lub przedział lat oddzielony spacją */
+	GG_PUBDIR50_CITY,	/**< Miejscowość */
+	GG_PUBDIR50_GENDER,	/**< Płeć */
+	GG_PUBDIR50_ACTIVE,	/**< Osoba dostępna (tylko wyszukiwanie) */
+	GG_PUBDIR50_START,	/**< Numer początkowy wyszukiwania (tylko wyszukiwanie) */
+	GG_PUBDIR50_FAMILYNAME,	/**< Nazwisko rodowe (tylko wysyłanie informacji o sobie) */
+	GG_PUBDIR50_FAMILYCITY,	/**< Miejscowość pochodzenia (tylko wysyłanie informacji o sobie) */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolną płeć.
+ */
+enum {
+	GG_PUBDIR50_GENDER_FEMALE,	/**< Kobieta */
+	GG_PUBDIR50_GENDER_MALE,	/**< Mężczyzna */
 };
 
-/* oglne funkcje, nie powinny by uywane */
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_GENDER przy wysyłaniu informacji o sobie.
+ */
+enum {
+	GG_PUBDIR50_GENDER_SET_FEMALE,	/**< Kobieta */
+	GG_PUBDIR50_GENDER_SET_MALE,	/**< Mężczyzna */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_ACTIVE.
+ */
+enum {
+	GG_PUBDIR50_ACTIVE_TRUE,	/**< Wyszukaj tylko osoby dostępne */
+};
+
+#endif	/* DOXYGEN */
+
+/**
+ * Wynik operacji na katalogu publicznym.
+ *
+ * \ingroup http
+ */
+struct gg_pubdir {
+	int success;		/**< Flaga powodzenia operacji */
+	uin_t uin;		/**< Otrzymany numer lub 0 w przypadku błędu */
+};
+
 int gg_pubdir_watch_fd(struct gg_http *f);
 void gg_pubdir_free(struct gg_http *f);
-#define gg_free_pubdir gg_pubdir_free
 
+/**
+ * Token autoryzacji niektórych operacji HTTP.
+ * 
+ * \ingroup token
+ */
 struct gg_token {
-	int width;		/* szeroko obrazka */
-	int height;		/* wysoko obrazka */
-	int length;		/* ilo znakw w tokenie */
-	char *tokenid;		/* id tokenu */
+	int width;		/**< Szerokość obrazka */
+	int height;		/**< Wysokość obrazka */
+	int length;		/**< Liczba znaków w tokenie */
+	char *tokenid;		/**< Identyfikator tokenu */
 };
 
-/* funkcje dotyczce tokenw */
 struct gg_http *gg_token(int async);
 int gg_token_watch_fd(struct gg_http *h);
 void gg_token_free(struct gg_http *h);
 
-/* rejestracja nowego numerka */
-struct gg_http *gg_register(const char *email, const char *password, int async);
-struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async);
 struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
 #define gg_register_watch_fd gg_pubdir_watch_fd
 #define gg_register_free gg_pubdir_free
-#define gg_free_register gg_pubdir_free
+#endif
 
-struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async);
-struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async);
 struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
 #define gg_unregister_watch_fd gg_pubdir_watch_fd
 #define gg_unregister_free gg_pubdir_free
+#endif
 
-/* przypomnienie hasa e-mailem */
-struct gg_http *gg_remind_passwd(uin_t uin, int async);
-struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async);
 struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
 #define gg_remind_passwd_watch_fd gg_pubdir_watch_fd
 #define gg_remind_passwd_free gg_pubdir_free
-#define gg_free_remind_passwd gg_pubdir_free
-
-/* zmiana hasa */
-struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async);
-struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async);
-struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async);
-struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
-#define gg_change_passwd_free gg_pubdir_free
-#define gg_free_change_passwd gg_pubdir_free
-
-/*
- * struct gg_change_info_request
- *
- * opis dania zmiany informacji w katalogu publicznym.
- */
-struct gg_change_info_request {
-	char *first_name;	/* imi */
-	char *last_name;	/* nazwisko */
-	char *nickname;		/* pseudonim */
-	char *email;		/* email */
-	int born;		/* rok urodzenia */
-	int gender;		/* pe */
-	char *city;		/* miasto */
-};
-
-struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city);
-void gg_change_info_request_free(struct gg_change_info_request *r);
+#endif
 
-struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async);
-#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
-#define gg_change_pubdir_free gg_pubdir_free
-#define gg_free_change_pubdir gg_pubdir_free
-
-/*
- * funkcje dotyczce listy kontaktw na serwerze.
- */
-struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async);
-int gg_userlist_get_watch_fd(struct gg_http *f);
-void gg_userlist_get_free(struct gg_http *f);
+struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
+#define gg_change_passwd_watch_fd gg_pubdir_watch_fd
+#define gg_change_passwd_free gg_pubdir_free
+#endif
 
-struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async);
-int gg_userlist_put_watch_fd(struct gg_http *f);
-void gg_userlist_put_free(struct gg_http *f);
-
-struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async);
-int gg_userlist_remove_watch_fd(struct gg_http *f);
-void gg_userlist_remove_free(struct gg_http *f);
-
-
-
-/*
- * funkcje dotyczce komunikacji midzy klientami.
- */
-extern int gg_dcc_port;			/* port, na ktrym nasuchuje klient */
-extern unsigned long gg_dcc_ip;		/* adres, na ktrym nasuchuje klient */
+extern int gg_dcc_port;
+extern unsigned long gg_dcc_ip;
 
 int gg_dcc_request(struct gg_session *sess, uin_t uin);
 
@@ -814,119 +1091,248 @@
 int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename);
 int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length);
 
-#define GG_DCC_VOICE_FRAME_LENGTH 195
-#define GG_DCC_VOICE_FRAME_LENGTH_505 326
+#define GG_DCC_VOICE_FRAME_LENGTH 195		/**< Rozmiar pakietu głosowego przed wersją Gadu-Gadu 5.0.5 */
+#define GG_DCC_VOICE_FRAME_LENGTH_505 326	/**< Rozmiar pakietu głosowego od wersji Gadu-Gadu 5.0.5 */
 
 struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port);
-#define gg_dcc_socket_free gg_free_dcc
+#ifndef DOXYGEN
+#define gg_dcc_socket_free gg_dcc_free
 #define gg_dcc_socket_watch_fd gg_dcc_watch_fd
+#endif
 
 struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d);
 
 void gg_dcc_free(struct gg_dcc *c);
-#define gg_free_dcc gg_dcc_free
-
-/*
- * jeli chcemy sobie podebugowa, wystarczy ustawi `gg_debug_level'.
- * niestety w miar przybywania wpisw `gg_debug(...)' nie chciao mi
- * si ustawia odpowiednich leveli, wic wikszo sza do _MISC.
- */
-extern int gg_debug_level;	/* poziom debugowania. mapa bitowa staych GG_DEBUG_* */
 
-/*
- * mona poda wskanik do funkcji obsugujcej wywoania gg_debug().
- * nieoficjalne, nieudokumentowane, moe si zmieni. jeli kto jest
- * zainteresowany, niech da zna na ekg-devel.
- */
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d);
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash);
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash);
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset);
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason);
+void gg_dcc7_free(struct gg_dcc7 *d);
+
+extern int gg_debug_level;
+
 extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
+extern void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap);
 
-/*
- * mona poda plik, do ktrego bd zapisywane teksty z gg_debug().
- */
 extern FILE *gg_debug_file;
 
-#define GG_DEBUG_NET 1
-#define GG_DEBUG_TRAFFIC 2
-#define GG_DEBUG_DUMP 4
-#define GG_DEBUG_FUNCTION 8
-#define GG_DEBUG_MISC 16
+/**
+ * \ingroup debug
+ * @{
+ */
+#define GG_DEBUG_NET 1		/**< Rejestracja zdarzeń związanych z siecią */
+#define GG_DEBUG_TRAFFIC 2	/**< Rejestracja ruchu sieciowego */
+#define GG_DEBUG_DUMP 4		/**< Rejestracja zawartości pakietów */
+#define GG_DEBUG_FUNCTION 8	/**< Rejestracja wywołań funkcji */
+#define GG_DEBUG_MISC 16	/**< Rejestracja różnych informacji */
+/** @} */
 
 #ifdef GG_DEBUG_DISABLE
 #define gg_debug(x, y...) do { } while(0)
+#define gg_debug_session(z, x, y...) do { } while(0)
 #else
 void gg_debug(int level, const char *format, ...);
+void gg_debug_session(struct gg_session *sess, int level, const char *format, ...);
 #endif
 
 const char *gg_libgadu_version(void);
 
-/*
- * konfiguracja http proxy.
- */
-extern int gg_proxy_enabled;		/* wcza obsug proxy */
-extern char *gg_proxy_host;		/* okrela adres serwera proxy */
-extern int gg_proxy_port;		/* okrela port serwera proxy */
-extern char *gg_proxy_username;		/* okrela nazw uytkownika przy autoryzacji serwera proxy */
-extern char *gg_proxy_password;		/* okrela haso uytkownika przy autoryzacji serwera proxy */
-extern int gg_proxy_http_only;		/* wcza obsug proxy wycznie dla usug HTTP */
+extern int gg_proxy_enabled;
+extern char *gg_proxy_host;
+extern int gg_proxy_port;
+extern char *gg_proxy_username;
+extern char *gg_proxy_password;
+extern int gg_proxy_http_only;
+
+extern unsigned long gg_local_ip;
+
+#define GG_LOGIN_HASH_GG32 0x01	/**< Algorytm Gadu-Gadu */
+#define GG_LOGIN_HASH_SHA1 0x02	/**< Algorytm SHA1 */
 
+#ifndef DOXYGEN
 
-/*
- * adres, z ktrego lemy pakiety (np czymy si z serwerem)
- * uywany przy gg_connect()
+#define GG_PUBDIR50_WRITE 0x01
+#define GG_PUBDIR50_READ 0x02
+#define GG_PUBDIR50_SEARCH 0x03
+#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
+#define GG_PUBDIR50_SEARCH_REPLY 0x05
+
+#else
+
+/**
+ * \ingroup pubdir50
+ * 
+ * Rodzaj zapytania lub odpowiedzi katalogu publicznego.
  */
-extern unsigned long gg_local_ip;
-/*
- * -------------------------------------------------------------------------
- * poniej znajduj si wewntrzne sprawy biblioteki. zwyky klient nie
- * powinien ich w ogle rusza, bo i nie ma po co. wszystko mona zaatwi
- * procedurami wyszego poziomu, ktrych definicje znajduj si na pocztku
- * tego pliku.
- * -------------------------------------------------------------------------
- */
+enum {
+	GG_PUBDIR50_WRITE,	/**< Wysłanie do serwera informacji o sobie */
+	GG_PUBDIR50_READ,	/**< Pobranie z serwera informacji o sobie */
+	GG_PUBDIR50_SEARCH,	/**< Wyszukiwanie w katalogu publicznym */
+	GG_PUBDIR50_SEARCH_REPLY,	/**< Wynik wyszukiwania w katalogu publicznym */
+};
+
+#endif	/* DOXYGEN */
+
+/** \cond obsolete */
+
+#define gg_free_event gg_event_free
+#define gg_free_http gg_http_free
+#define gg_free_pubdir gg_pubdir_free
+#define gg_free_register gg_pubdir_free
+#define gg_free_remind_passwd gg_pubdir_free
+#define gg_free_dcc gg_dcc_free
+#define gg_free_change_passwd gg_pubdir_free
+
+struct gg_search_request {
+	int active;
+	unsigned int start;
+	char *nickname;
+	char *first_name;
+	char *last_name;
+	char *city;
+	int gender;
+	int min_birth;
+	int max_birth;
+	char *email;
+	char *phone;
+	uin_t uin;
+} /* GG_DEPRECATED */;
+
+struct gg_search {
+	int count;
+	struct gg_search_result *results;
+} GG_DEPRECATED;
+
+struct gg_search_result {
+	uin_t uin;
+	char *first_name;
+	char *last_name;
+	char *nickname;
+	int born;
+	int gender;
+	char *city;
+	int active;
+} GG_DEPRECATED;
+
+#define GG_GENDER_NONE 0
+#define GG_GENDER_FEMALE 1
+#define GG_GENDER_MALE 2
+
+struct gg_http *gg_search(const struct gg_search_request *r, int async) GG_DEPRECATED;
+int gg_search_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_free_search(struct gg_http *f) GG_DEPRECATED;
+#define gg_search_free gg_free_search
 
-#ifdef __GG_LIBGADU_HAVE_PTHREAD
-int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
-#elif defined _WIN32
-int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname);
+const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start) GG_DEPRECATED;
+void gg_search_request_free(struct gg_search_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_register(const char *email, const char *password, int async) GG_DEPRECATED;
+struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async) GG_DEPRECATED;
+struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_remind_passwd(uin_t uin, int async) GG_DEPRECATED;
+struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) GG_DEPRECATED;
+
+struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_change_info_request {
+	char *first_name;
+	char *last_name;
+	char *nickname;
+	char *email;
+	int born;
+	int gender;
+	char *city;
+} /* GG_DEPRECATED */;
+
+struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city) GG_DEPRECATED;
+void gg_change_info_request_free(struct gg_change_info_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) GG_DEPRECATED;
+#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
+#define gg_change_pubdir_free gg_pubdir_free
+#define gg_free_change_pubdir gg_pubdir_free
+
+struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_get_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_get_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async) GG_DEPRECATED;
+int gg_userlist_put_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_put_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_remove_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_remove_free(struct gg_http *f) GG_DEPRECATED;
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) GG_DEPRECATED;
+
+/** \endcond */
+
+int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED;
+
+#ifdef __GNUC__
+char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))) GG_DEPRECATED;
+#else
+char *gg_saprintf(const char *format, ...) GG_DEPRECATED;
 #endif
 
-#ifdef _WIN32
-int gg_thread_socket(int thread_id, int socket);
-#endif
-
-int gg_resolve(int *fd, int *pid, const char *hostname);
-
-#if defined __GNUC__ && !defined _WIN32
-char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
-#else
-char *gg_saprintf(const char *format, ...);
-#endif
-
-char *gg_vsaprintf(const char *format, va_list ap);
+char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED;
 
 #define gg_alloc_sprintf gg_saprintf
 
-char *gg_get_line(char **ptr);
+char *gg_get_line(char **ptr) GG_DEPRECATED;
 
-int gg_connect(void *addr, int port, int async);
-struct in_addr *gg_gethostbyname(const char *hostname);
-char *gg_read_line(int sock, char *buf, int length);
-void gg_chomp(char *line);
-char *gg_urlencode(const char *str);
-int gg_http_hash(const char *format, ...);
-int gg_read(struct gg_session *sess, char *buf, int length);
-int gg_write(struct gg_session *sess, const char *buf, int length);
-void *gg_recv_packet(struct gg_session *sess);
-int gg_send_packet(struct gg_session *sess, int type, ...);
-unsigned int gg_login_hash(const unsigned char *password, unsigned int seed);
-uint32_t gg_fix32(uint32_t x);
-uint16_t gg_fix16(uint16_t x);
+int gg_connect(void *addr, int port, int async) GG_DEPRECATED;
+struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED;
+char *gg_read_line(int sock, char *buf, int length) GG_DEPRECATED;
+void gg_chomp(char *line) GG_DEPRECATED;
+char *gg_urlencode(const char *str) GG_DEPRECATED;
+int gg_http_hash(const char *format, ...) GG_DEPRECATED;
+void gg_http_free_fields(struct gg_http *h) GG_DEPRECATED;
+int gg_read(struct gg_session *sess, char *buf, int length) GG_DEPRECATED;
+int gg_write(struct gg_session *sess, const char *buf, int length) GG_DEPRECATED;
+void *gg_recv_packet(struct gg_session *sess) GG_DEPRECATED;
+int gg_send_packet(struct gg_session *sess, int type, ...) GG_DEPRECATED;
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) GG_DEPRECATED;
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) GG_DEPRECATED;
+uint32_t gg_fix32(uint32_t x) GG_DEPRECATED;
+uint16_t gg_fix16(uint16_t x) GG_DEPRECATED;
 #define fix16 gg_fix16
 #define fix32 gg_fix32
-char *gg_proxy_auth(void);
-char *gg_base64_encode(const char *buf);
-char *gg_base64_decode(const char *buf);
-int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq);
+char *gg_proxy_auth(void) GG_DEPRECATED;
+char *gg_base64_encode(const char *buf) GG_DEPRECATED;
+char *gg_base64_decode(const char *buf) GG_DEPRECATED;
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) GG_DEPRECATED;
+
+/**
+ * Kolejka odbieranych obrazków.
+ */
+struct gg_image_queue {
+	uin_t sender;			/**< Nadawca obrazka */
+	uint32_t size;			/**< Rozmiar obrazka */
+	uint32_t crc32;			/**< Suma kontrolna CRC32 */
+	char *filename;			/**< Nazwa pliku */
+	char *image;			/**< Bufor z odebranymi danymi */
+	uint32_t done;			/**< Rozmiar odebranych danych */
+
+	struct gg_image_queue *next;	/**< Kolejny element listy */
+} GG_DEPRECATED;
+
+int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
+int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED;
 
 #define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
 #define GG_APPMSG_PORT 80
@@ -941,63 +1347,111 @@
 #define GG_HTTPS_PORT 443
 #define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)"
 
-#define GG_DEFAULT_CLIENT_VERSION "6, 1, 0, 158"
-#define GG_DEFAULT_PROTOCOL_VERSION 0x24
+#define GG_DEFAULT_CLIENT_VERSION "8.0.0.7669"
+#define GG_DEFAULT_PROTOCOL_VERSION 0x2e
 #define GG_DEFAULT_TIMEOUT 30
 #define GG_HAS_AUDIO_MASK 0x40000000
+#define GG_HAS_AUDIO7_MASK 0x20000000
 #define GG_ERA_OMNIX_MASK 0x04000000
-#define GG_LIBGADU_VERSION "1.5.20050718"
+#undef GG_LIBGADU_VERSION
+
+#ifndef DOXYGEN
+
+#define GG_FEATURE_MSG77		0x01
+#define GG_FEATURE_STATUS77		0x02
+#define GG_FEATURE_DND_FFC		0x10
+#define GG_FEATURE_IMAGE_DESCR		0x20
+
+/* Poniższe makra zostały zachowane dla zgodności API */
+#define GG_FEATURE_MSG80		0
+#define GG_FEATURE_STATUS80		0
+#define GG_FEATURE_STATUS80BETA		0
+#define GG_FEATURE_ALL			(GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR)
+
+#else
+
+/** 
+ * \ingroup login
+ *
+ * Flagi opcji protokołu.
+ */
+enum {
+	GG_FEATURE_MSG77,	/**< Klient życzy sobie otrzymywać wiadomości zgodnie z protokołem 7.7 */
+	GG_FEATURE_STATUS77,	/**< Klient życzy sobie otrzymywać zmiany stanu zgodnie z protokołem 7.7 */
+	GG_FEATURE_DND_FFC,	/**< Klient obsługuje statusy "nie przeszkadzać" i "poGGadaj ze mną" */
+	GG_FEATURE_IMAGE_DESCR,	/**< Klient obsługuje opisy graficzne oraz flagę \c GG_STATUS80_DESCR_MASK */
+};
+
+
+#endif
 
 #define GG_DEFAULT_DCC_PORT 1550
 
 struct gg_header {
 	uint32_t type;			/* typ pakietu */
-	uint32_t length;		/* dugo reszty pakietu */
+	uint32_t length;		/* długość reszty pakietu */
 } GG_PACKED;
 
 #define GG_WELCOME 0x0001
 #define GG_NEED_EMAIL 0x0014
 
 struct gg_welcome {
-	uint32_t key;			/* klucz szyfrowania hasa */
+	uint32_t key;			/* klucz szyfrowania hasła */
 } GG_PACKED;
 
 #define GG_LOGIN 0x000c
 
 struct gg_login {
-	uint32_t uin;			/* mj numerek */
-	uint32_t hash;			/* hash hasa */
-	uint32_t status;		/* status na dzie dobry */
+	uint32_t uin;			/* mój numerek */
+	uint32_t hash;			/* hash hasła */
+	uint32_t status;		/* status na dzień dobry */
 	uint32_t version;		/* moja wersja klienta */
-	uint32_t local_ip;		/* mj adres ip */
-	uint16_t local_port;		/* port, na ktrym sucham */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
 } GG_PACKED;
 
 #define GG_LOGIN_EXT 0x0013
 
 struct gg_login_ext {
-	uint32_t uin;			/* mj numerek */
-	uint32_t hash;			/* hash hasa */
-	uint32_t status;		/* status na dzie dobry */
+	uint32_t uin;			/* mój numerek */
+	uint32_t hash;			/* hash hasła */
+	uint32_t status;		/* status na dzień dobry */
 	uint32_t version;		/* moja wersja klienta */
-	uint32_t local_ip;		/* mj adres ip */
-	uint16_t local_port;		/* port, na ktrym sucham */
-	uint32_t external_ip;		/* zewntrzny adres ip */
-	uint16_t external_port;		/* zewntrzny port */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip */
+	uint16_t external_port;		/* zewnętrzny port */
 } GG_PACKED;
 
 #define GG_LOGIN60 0x0015
 
 struct gg_login60 {
-	uint32_t uin;			/* mj numerek */
-	uint32_t hash;			/* hash hasa */
-	uint32_t status;		/* status na dzie dobry */
+	uint32_t uin;			/* mój numerek */
+	uint32_t hash;			/* hash hasła */
+	uint32_t status;		/* status na dzień dobry */
 	uint32_t version;		/* moja wersja klienta */
 	uint8_t dunno1;			/* 0x00 */
-	uint32_t local_ip;		/* mj adres ip */
-	uint16_t local_port;		/* port, na ktrym sucham */
-	uint32_t external_ip;		/* zewntrzny adres ip */
-	uint16_t external_port;		/* zewntrzny port */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip */
+	uint16_t external_port;		/* zewnętrzny port */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno2;			/* 0xbe */
+} GG_PACKED;
+
+#define GG_LOGIN70 0x0019
+
+struct gg_login70 {
+	uint32_t uin;			/* mój numerek */
+	uint8_t hash_type;		/* rodzaj hashowania hasła */
+	uint8_t hash[64];		/* hash hasła dopełniony zerami */
+	uint32_t status;		/* status na dzień dobry */
+	uint32_t version;		/* moja wersja klienta */
+	uint8_t dunno1;			/* 0x00 */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip (???) */
+	uint16_t external_port;		/* zewnętrzny port (???) */
 	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 	uint8_t dunno2;			/* 0xbe */
 } GG_PACKED;
@@ -1008,70 +1462,135 @@
 
 #define GG_PUBDIR50_REQUEST 0x0014
 
-#define GG_PUBDIR50_WRITE 0x01
-#define GG_PUBDIR50_READ 0x02
-#define GG_PUBDIR50_SEARCH 0x03
-#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
-#define GG_PUBDIR50_SEARCH_REPLY 0x05
-
 struct gg_pubdir50_request {
 	uint8_t type;			/* GG_PUBDIR50_* */
-	uint32_t seq;			/* czas wysania zapytania */
+	uint32_t seq;			/* czas wysłania zapytania */
 } GG_PACKED;
 
 #define GG_PUBDIR50_REPLY 0x000e
 
 struct gg_pubdir50_reply {
 	uint8_t type;			/* GG_PUBDIR50_* */
-	uint32_t seq;			/* czas wysania zapytania */
+	uint32_t seq;			/* czas wysłania zapytania */
 } GG_PACKED;
 
 #define GG_NEW_STATUS 0x0002
 
-#define GG_STATUS_NOT_AVAIL 0x0001		/* niedostpny */
-#define GG_STATUS_NOT_AVAIL_DESCR 0x0015	/* niedostpny z opisem (4.8) */
-#define GG_STATUS_AVAIL 0x0002			/* dostpny */
-#define GG_STATUS_AVAIL_DESCR 0x0004		/* dostpny z opisem (4.9) */
-#define GG_STATUS_BUSY 0x0003			/* zajty */
-#define GG_STATUS_BUSY_DESCR 0x0005		/* zajty z opisem (4.8) */
-#define GG_STATUS_INVISIBLE 0x0014		/* niewidoczny (4.6) */
-#define GG_STATUS_INVISIBLE_DESCR 0x0016	/* niewidoczny z opisem (4.9) */
-#define GG_STATUS_BLOCKED 0x0006		/* zablokowany */
+#ifndef DOXYGEN
+
+#define GG_STATUS_NOT_AVAIL 0x0001
+#define GG_STATUS_NOT_AVAIL_DESCR 0x0015
+#define GG_STATUS_FFC 0x0017
+#define GG_STATUS_FFC_DESCR 0x0018
+#define GG_STATUS_AVAIL 0x0002
+#define GG_STATUS_AVAIL_DESCR 0x0004
+#define GG_STATUS_BUSY 0x0003
+#define GG_STATUS_BUSY_DESCR 0x0005
+#define GG_STATUS_DND 0x0021
+#define GG_STATUS_DND_DESCR 0x0022
+#define GG_STATUS_INVISIBLE 0x0014
+#define GG_STATUS_INVISIBLE_DESCR 0x0016
+#define GG_STATUS_BLOCKED 0x0006
+
+#define GG_STATUS_IMAGE_MASK 0x0100
+#define GG_STATUS_DESCR_MASK 0x4000
+#define GG_STATUS_FRIENDS_MASK 0x8000
+
+#else
 
-#define GG_STATUS_FRIENDS_MASK 0x8000		/* tylko dla znajomych (4.6) */
-
-#define GG_STATUS_DESCR_MAXSIZE 70
+/**
+ * Rodzaje statusów użytkownika.
+ *
+ * \ingroup status
+ */
+enum {
+	GG_STATUS_NOT_AVAIL,		/**< Niedostępny */
+	GG_STATUS_NOT_AVAIL_DESCR,	/**< Niedostępny z opisem */
+	GG_STATUS_FFC,			/**< PoGGadaj ze mną */
+	GG_STATUS_FFC_DESCR,		/**< PoGGadaj ze mną z opisem */
+	GG_STATUS_AVAIL,		/**< Dostępny */
+	GG_STATUS_AVAIL_DESCR,		/**< Dostępny z opisem */
+	GG_STATUS_BUSY,			/**< Zajęty */
+	GG_STATUS_BUSY_DESCR,		/**< Zajęty z opisem */
+	GG_STATUS_DND,			/**< Nie przeszkadzać */
+	GG_STATUS_DND_DESCR,		/**< Nie przeszakdzać z opisem */
+	GG_STATUS_INVISIBLE,		/**< Niewidoczny (tylko własny status) */
+	GG_STATUS_INVISIBLE_DESCR,	/**< Niewidoczny z opisem (tylko własny status) */
+	GG_STATUS_BLOCKED,		/**< Zablokowany (tylko status innych) */
+	GG_STATUS_IMAGE_MASK,		/**< Flaga bitowa oznaczająca opis graficzny (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
+	GG_STATUS_DESCR_MASK,		/**< Flaga bitowa oznaczająca status z opisem (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
+	GG_STATUS_FRIENDS_MASK,		/**< Flaga bitowa dostępności tylko dla znajomych */
+};
 
-/*
- * makra do atwego i szybkiego sprawdzania stanu.
+#endif	/* DOXYGEN */
+
+/**
+ * \ingroup status
+ *
+ * Flaga bitowa dostepnosci informujaca ze mozemy voipowac
  */
 
+#define GG_STATUS_VOICE_MASK 0x20000	/**< czy ma wlaczone audio (7.7) */
+
+/**
+ * \ingroup status
+ *
+ * Maksymalna długośc opisu.
+ */
+#define GG_STATUS_DESCR_MAXSIZE 255
+#define GG_STATUS_DESCR_MAXSIZE_PRE_8_0 70
+
+#define GG_STATUS_MASK 0xff
+
 /* GG_S_F() tryb tylko dla znajomych */
 #define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0)
 
-/* GG_S() stan bez uwzgldnienia trybu tylko dla znajomych */
-#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK)
+/* GG_S() stan bez uwzględnienia dodatkowych flag */
+#define GG_S(x) ((x) & GG_STATUS_MASK)
 
-/* GG_S_A() dostpny */
-#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_FF() chętny do rozmowy */
+#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR)
 
-/* GG_S_NA() niedostpny */
-#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
+/* GG_S_AV() dostępny */
+#define GG_S_AV(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_AW() zaraz wracam */
+#define GG_S_AW(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
 
-/* GG_S_B() zajty */
-#define GG_S_B(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
+/* GG_S_DD() nie przeszkadzać */
+#define GG_S_DD(x) (GG_S(x) == GG_STATUS_DND || GG_S(x) == GG_STATUS_DND_DESCR)
+
+/* GG_S_NA() niedostępny */
+#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
 
 /* GG_S_I() niewidoczny */
 #define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
 
+
+/* GG_S_A() dostępny lub chętny do rozmowy */
+#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x))
+
+/* GG_S_B() zajęty lub nie przeszkadzać */
+#define GG_S_B(x) (GG_S_AW(x) || GG_S_DD(x))
+
+
 /* GG_S_D() stan opisowy */
-#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
+#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || \
+		   GG_S(x) == GG_STATUS_FFC_DESCR || \
+		   GG_S(x) == GG_STATUS_AVAIL_DESCR || \
+		   GG_S(x) == GG_STATUS_BUSY_DESCR || \
+		   GG_S(x) == GG_STATUS_DND_DESCR || \
+		   GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
 
-/* GG_S_BL() blokowany lub blokujcy */
+/* GG_S_BL() blokowany lub blokujący */
 #define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED)
 
+/**
+ * Zmiana statusu (pakiet \c GG_NEW_STATUS i \c GG_NEW_STATUS80BETA)
+ */
 struct gg_new_status {
-	uint32_t status;			/* na jaki zmieni? */
+	uint32_t status;			/**< Nowy status */
 } GG_PACKED;
 
 #define GG_NOTIFY_FIRST 0x000f
@@ -1081,12 +1600,29 @@
 
 struct gg_notify {
 	uint32_t uin;				/* numerek danej osoby */
-	uint8_t dunno1;				/* rodzaj wpisu w licie */
+	uint8_t dunno1;				/* rodzaj wpisu w liście */
 } GG_PACKED;
 
-#define GG_USER_OFFLINE 0x01	/* bdziemy niewidoczni dla uytkownika */
-#define GG_USER_NORMAL 0x03	/* zwyky uytkownik */
-#define GG_USER_BLOCKED 0x04	/* zablokowany uytkownik */
+#ifndef DOXYGEN
+
+#define GG_USER_OFFLINE 0x01
+#define GG_USER_NORMAL 0x03
+#define GG_USER_BLOCKED 0x04
+
+#else
+
+/**
+ * \ingroup contacts
+ *
+ * Rodzaj kontaktu.
+ */
+enum {
+	GG_USER_NORMAL,		/**< Zwykły kontakt */
+	GG_USER_BLOCKED,	/**< Zablokowany */
+	GG_USER_OFFLINE,	/**< Niewidoczny dla kontaktu */
+};
+
+#endif	/* DOXYGEN */
 
 #define GG_LIST_EMPTY 0x0012
 
@@ -1096,7 +1632,7 @@
 	uint32_t uin;			/* numerek */
 	uint32_t status;		/* status danej osoby */
 	uint32_t remote_ip;		/* adres ip delikwenta */
-	uint16_t remote_port;		/* port, na ktrym sucha klient */
+	uint16_t remote_port;		/* port, na którym słucha klient */
 	uint32_t version;		/* wersja klienta */
 	uint16_t dunno2;		/* znowu port? */
 } GG_PACKED;
@@ -1107,7 +1643,7 @@
 	uint32_t uin;			/* numerek plus flagi w MSB */
 	uint8_t status;			/* status danej osoby */
 	uint32_t remote_ip;		/* adres ip delikwenta */
-	uint16_t remote_port;		/* port, na ktrym sucha klient */
+	uint16_t remote_port;		/* port, na którym słucha klient */
 	uint8_t version;		/* wersja klienta */
 	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 	uint8_t dunno1;			/* 0x00 */
@@ -1119,12 +1655,38 @@
 	uint32_t uin;			/* numerek plus flagi w MSB */
 	uint8_t status;			/* status danej osoby */
 	uint32_t remote_ip;		/* adres ip delikwenta */
-	uint16_t remote_port;		/* port, na ktrym sucha klient */
+	uint16_t remote_port;		/* port, na którym słucha klient */
 	uint8_t version;		/* wersja klienta */
 	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
 	uint8_t dunno1;			/* 0x00 */
 } GG_PACKED;
 
+#define GG_NOTIFY_REPLY77 0x0018
+
+struct gg_notify_reply77 {
+	uint32_t uin;			/* numerek plus flagi w MSB */
+	uint8_t status;			/* status danej osoby */
+	uint32_t remote_ip;		/* adres ip delikwenta */
+	uint16_t remote_port;		/* port, na którym słucha klient */
+	uint8_t version;		/* wersja klienta */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno1;			/* 0x00 */
+	uint32_t dunno2;		/* ? */
+} GG_PACKED;
+
+#define GG_STATUS77 0x0017
+
+struct gg_status77 {
+	uint32_t uin;			/* numerek plus flagi w MSB */
+	uint8_t status;			/* status danej osoby */
+	uint32_t remote_ip;		/* adres ip delikwenta */
+	uint16_t remote_port;		/* port, na którym słucha klient */
+	uint8_t version;		/* wersja klienta */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno1;			/* 0x00 */
+	uint32_t dunno2;		/* ? */
+} GG_PACKED;
+
 #define GG_ADD_NOTIFY 0x000d
 #define GG_REMOVE_NOTIFY 0x000e
 
@@ -1142,15 +1704,41 @@
 
 #define GG_SEND_MSG 0x000b
 
+#ifndef DOXYGEN
+
 #define GG_CLASS_QUEUED 0x0001
 #define GG_CLASS_OFFLINE GG_CLASS_QUEUED
 #define GG_CLASS_MSG 0x0004
 #define GG_CLASS_CHAT 0x0008
 #define GG_CLASS_CTCP 0x0010
 #define GG_CLASS_ACK 0x0020
-#define GG_CLASS_EXT GG_CLASS_ACK	/* kompatybilno wstecz */
+#define GG_CLASS_EXT GG_CLASS_ACK	/**< Dla kompatybilności wstecz */
+
+#else
 
-#define GG_MSG_MAXSIZE 2000
+/**
+ * Klasy wiadomości. Wartości są maskami bitowymi, które w większości
+ * przypadków można łączyć (połączenie \c GG_CLASS_MSG i \c GG_CLASS_CHAT
+ * nie ma sensu).
+ *
+ * \ingroup messages
+ */
+enum {
+	GG_CLASS_MSG,		/**< Wiadomość ma pojawić się w osobnym oknie */
+	GG_CLASS_CHAT,		/**< Wiadomość ma pojawić się w oknie rozmowy */
+	GG_CLASS_CTCP,		/**< Wiadomość przeznaczona dla klienta Gadu-Gadu */
+	GG_CLASS_ACK,		/**< Klient nie życzy sobie potwierdzenia */
+	GG_CLASS_QUEUED,	/**< Wiadomość zakolejkowana na serwerze (tylko przy odbieraniu) */
+};
+
+#endif	/* DOXYGEN */
+
+/**
+ * Maksymalna długość wiadomości.
+ *
+ * \ingroup messages
+ */
+#define GG_MSG_MAXSIZE 1989
 
 struct gg_send_msg {
 	uint32_t recipient;
@@ -1163,16 +1751,19 @@
 	uint16_t length;
 } GG_PACKED;
 
+/**
+ * Struktura opisująca formatowanie tekstu. W zależności od wartości pola
+ * \c font, zaraz za tą strukturą może wystąpić \c gg_msg_richtext_color
+ * lub \c gg_msg_richtext_image.
+ *
+ * \ingroup messages
+ */
 struct gg_msg_richtext_format {
-	uint16_t position;
-	uint8_t font;
+	uint16_t position;	/**< Początkowy znak formatowania (liczony od 0) */
+	uint8_t font;		/**< Atrybuty formatowania */
 } GG_PACKED;
 
-struct gg_msg_richtext_image {
-	uint16_t unknown1;
-	uint32_t size;
-	uint32_t crc32;
-} GG_PACKED;
+#ifndef DOXYGEN
 
 #define GG_FONT_BOLD 0x01
 #define GG_FONT_ITALIC 0x02
@@ -1180,10 +1771,44 @@
 #define GG_FONT_COLOR 0x08
 #define GG_FONT_IMAGE 0x80
 
+#else
+
+/**
+ * Atrybuty formatowania wiadomości.
+ *
+ * \ingroup messages
+ */
+enum {
+	GG_FONT_BOLD,
+	GG_FONT_ITALIC,
+	GG_FONT_UNDERLINE,
+	GG_FONT_COLOR,
+	GG_FONT_IMAGE
+};
+
+#endif	/* DOXYGEN */
+
+/**
+ * Struktura opisującą kolor tekstu dla atrybutu \c GG_FONT_COLOR.
+ *
+ * \ingroup messages
+ */
 struct gg_msg_richtext_color {
-	uint8_t red;
-	uint8_t green;
-	uint8_t blue;
+	uint8_t red;		/**< Składowa czerwona koloru */
+	uint8_t green;		/**< Składowa zielona koloru */
+	uint8_t blue;		/**< Składowa niebieska koloru */
+} GG_PACKED;
+
+/**
+ * Strukturya opisująca obrazek wstawiony do wiadomości dla atrubutu
+ * \c GG_FONT_IMAGE.
+ *
+ * \ingroup messages
+ */
+struct gg_msg_richtext_image {
+	uint16_t unknown1;	/**< Nieznane pole o wartości 0x0109 */
+	uint32_t size;		/**< Rozmiar obrazka */
+	uint32_t crc32;		/**< Suma kontrolna CRC32 obrazka */
 } GG_PACKED;
 
 struct gg_msg_recipients {
@@ -1207,12 +1832,32 @@
 
 #define GG_SEND_MSG_ACK 0x0005
 
+#ifndef DOXYGEN
+
 #define GG_ACK_BLOCKED 0x0001
 #define GG_ACK_DELIVERED 0x0002
 #define GG_ACK_QUEUED 0x0003
 #define GG_ACK_MBOXFULL 0x0004
 #define GG_ACK_NOT_DELIVERED 0x0006
 
+#else
+
+/**
+ * Status doręczenia wiadomości.
+ *
+ * \ingroup messages
+ */
+enum
+{
+	GG_ACK_DELIVERED,	/**< Wiadomość dostarczono. */
+	GG_ACK_QUEUED,		/**< Wiadomość zakolejkowano z powodu niedostępności odbiorcy. */
+	GG_ACK_BLOCKED,		/**< Wiadomość zablokowana przez serwer (spam, świąteczne ograniczenia itd.) */
+	GG_ACK_MBOXFULL,	/**< Wiadomości nie dostarczono z powodu zapełnionej kolejki wiadomości odbiorcy. */
+	GG_ACK_NOT_DELIVERED	/**< Wiadomości nie dostarczono (tylko dla \c GG_CLASS_CTCP). */
+};
+
+#endif	/* DOXYGEN */
+
 struct gg_send_msg_ack {
 	uint32_t status;
 	uint32_t recipient;
@@ -1236,29 +1881,59 @@
 
 #define GG_USERLIST_REQUEST 0x0016
 
+#define GG_XML_EVENT 0x0027
+
+#ifndef DOXYGEN
+
 #define GG_USERLIST_PUT 0x00
 #define GG_USERLIST_PUT_MORE 0x01
 #define GG_USERLIST_GET 0x02
 
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj zapytania.
+ */
+enum {
+	GG_USERLIST_PUT,	/**< Eksport listy kontaktów. */
+	GG_USERLIST_GET,	/**< Import listy kontaktów. */
+};
+
+#endif	/* DOXYGEN */
+
 struct gg_userlist_request {
 	uint8_t type;
 } GG_PACKED;
 
 #define GG_USERLIST_REPLY 0x0010
 
+#ifndef DOXYGEN
+
 #define GG_USERLIST_PUT_REPLY 0x00
 #define GG_USERLIST_PUT_MORE_REPLY 0x02
 #define GG_USERLIST_GET_REPLY 0x06
 #define GG_USERLIST_GET_MORE_REPLY 0x04
 
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj odpowiedzi.
+ */
+enum {
+	GG_USERLIST_PUT_REPLY,	/**< Wyeksportowano listy kontaktów. */
+	GG_USERLIST_GET_REPLY,	/**< Zaimportowano listę kontaktów. */
+};
+
+#endif	/* DOXYGEN */
+
 struct gg_userlist_reply {
 	uint8_t type;
 } GG_PACKED;
 
-/*
- * pakiety, stae, struktury dla DCC
- */
-
 struct gg_dcc_tiny_packet {
 	uint8_t type;		/* rodzaj pakietu */
 } GG_PACKED;
@@ -1274,14 +1949,14 @@
 } GG_PACKED;
 
 /*
- * pki co, nie znamy dokadnie protokou. nie wiemy, co czemu odpowiada.
- * nazwy s niepowane i tymczasowe.
+ * póki co, nie znamy dokładnie protokołu. nie wiemy, co czemu odpowiada.
+ * nazwy są niepoważne i tymczasowe.
  */
 #define GG_DCC_WANT_FILE 0x0003		/* peer chce plik */
-#define GG_DCC_HAVE_FILE 0x0001		/* wic mu damy */
+#define GG_DCC_HAVE_FILE 0x0001		/* więc mu damy */
 #define GG_DCC_HAVE_FILEINFO 0x0003	/* niech ma informacje o pliku */
 #define GG_DCC_GIMME_FILE 0x0006	/* peer jest pewny */
-#define GG_DCC_CATCH_FILE 0x0002	/* wysyamy plik */
+#define GG_DCC_CATCH_FILE 0x0002	/* wysyłamy plik */
 
 #define GG_DCC_FILEATTR_READONLY 0x0020
 
@@ -1290,11 +1965,88 @@
 #define GG_DCC_TIMEOUT_FILE_ACK 300	/* 5 minut */
 #define GG_DCC_TIMEOUT_VOICE_ACK 300	/* 5 minut */
 
+#define GG_DCC7_INFO 0x1f
+
+struct gg_dcc7_info {
+	uint32_t uin;			/* numer nadawcy */
+	uint32_t type;			/* sposób połączenia */
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	char info[GG_DCC7_INFO_LEN];	/* informacje o połączeniu "ip port" */
+} GG_PACKED;
+
+#define GG_DCC7_NEW 0x20
+
+struct gg_dcc7_new {
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	uint32_t uin_from;		/* numer nadawcy */
+	uint32_t uin_to;		/* numer odbiorcy */
+	uint32_t type;			/* rodzaj transmisji */
+	unsigned char filename[GG_DCC7_FILENAME_LEN];	/* nazwa pliku */
+	uint32_t size;			/* rozmiar pliku */
+	uint32_t size_hi;		/* rozmiar pliku (starsze bajty) */
+	unsigned char hash[GG_DCC7_HASH_LEN];	/* hash SHA1 */
+} GG_PACKED;
+
+#define GG_DCC7_ACCEPT 0x21
+
+struct gg_dcc7_accept {
+	uint32_t uin;			/* numer przyjmującego połączenie */
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	uint32_t offset;		/* offset przy wznawianiu transmisji */
+	uint32_t dunno1;		/* 0x00000000 */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_TYPE_P2P 0x00000001	/**< Połączenie bezpośrednie */
+#define GG_DCC7_TYPE_SERVER 0x00000002	/**< Połączenie przez serwer */
+
+#define GG_DCC7_REJECT 0x22
+
+struct gg_dcc7_reject {
+	uint32_t uin;			/**< Numer odrzucającego połączenie */
+	gg_dcc7_id_t id;		/**< Identyfikator połączenia */
+	uint32_t reason;		/**< Powód rozłączenia */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_REJECT_BUSY 0x00000001	/**< Połączenie bezpośrednie już trwa, nie umiem obsłużyć więcej */
+#define GG_DCC7_REJECT_USER 0x00000002	/**< Użytkownik odrzucił połączenie */
+#define GG_DCC7_REJECT_VERSION 0x00000006	/**< Druga strona ma wersję klienta nieobsługującą połączeń bezpośrednich tego typu */
+
+#define GG_DCC7_ID_REQUEST 0x23
+
+struct gg_dcc7_id_request {
+	uint32_t type;			/**< Rodzaj tranmisji */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_TYPE_VOICE 0x00000001	/**< Transmisja głosu */
+#define GG_DCC7_TYPE_FILE 0x00000004	/**< transmisja pliku */
+
+#define GG_DCC7_ID_REPLY 0x23
+
+struct gg_dcc7_id_reply {
+	uint32_t type;			/** Rodzaj transmisji */
+	gg_dcc7_id_t id;		/** Przyznany identyfikator */
+} GG_PACKED;
+
+#define GG_DCC7_DUNNO1 0x24
+
+struct gg_dcc7_dunno1 {
+	// XXX
+} GG_PACKED;
+
+#define GG_DCC7_TIMEOUT_CONNECT 10	/* 10 sekund */
+#define GG_DCC7_TIMEOUT_SEND 1800	/* 30 minut */
+#define GG_DCC7_TIMEOUT_GET 1800	/* 30 minut */
+#define GG_DCC7_TIMEOUT_FILE_ACK 300	/* 5 minut */
+#define GG_DCC7_TIMEOUT_VOICE_ACK 300	/* 5 minut */
+
 #ifdef __cplusplus
-#ifdef _MSC_VER
+}
+#ifdef _WIN32
 #pragma pack(pop)
 #endif
-}
 #endif
 
 #endif /* __GG_LIBGADU_H */
--- a/libpurple/protocols/gg/lib/obsolete.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/obsolete.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1,4 +1,4 @@
-/* $Id: obsolete.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */
 
 /*
  *  (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -14,16 +14,23 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-/*
- * Plik zawiera deklaracje funkcji, ktre s ju nieaktualne ze wzgldu
- * na zmiany w protokole, ale s wymagane przez aplikacje linkowane ze
- * starszymi wersjami bibliotek.
+/**
+ * \file obsolete.c
+ *
+ * \brief Nieaktualne funkcje
+ *
+ * Plik zawiera definicje funkcji, które są już nieaktualne ze względu
+ * na zmiany w protokole. Programy konsolidowane ze starszych wersjami
+ * bibliotek powinny nadal mieć możliwość działania, mimo ograniczonej
+ * funkcjonalności.
  */
 
+/** \cond obsolete */
+
 #include <errno.h>
 
 #include "libgadu.h"
@@ -205,3 +212,25 @@
 {
 
 }
+
+int gg_resolve(int *fd, int *pid, const char *hostname)
+{
+	return -1;
+}
+
+void gg_resolve_pthread_cleanup(void *arg, int kill)
+{
+
+}
+
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
+{
+	return -1;
+}
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+{
+	return -1;
+}
+
+/** \endcond */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/protocol.h	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,165 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#ifndef LIBGADU_PROTOCOL_H
+#define LIBGADU_PROTOCOL_H
+
+#include "libgadu.h"
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#endif
+
+#define GG_LOGIN80BETA 0x0029
+
+#define GG_LOGIN80 0x0031
+
+#undef GG_FEATURE_STATUS80BETA
+#undef GG_FEATURE_MSG80
+#undef GG_FEATURE_STATUS80
+#define GG_FEATURE_STATUS80BETA		0x01
+#define GG_FEATURE_MSG80		0x02
+#define GG_FEATURE_STATUS80 		0x05
+
+#define GG8_LANG	"pl"
+#define GG8_VERSION	"Gadu-Gadu Client Build 8.0.0.8731"
+
+struct gg_login80 {
+	uint32_t uin;			/* mój numerek */
+	uint8_t language[2];		/* język: GG8_LANG */
+	uint8_t hash_type;		/* rodzaj hashowania hasła */
+	uint8_t hash[64];		/* hash hasła dopełniony zerami */
+	uint32_t status;		/* status na dzień dobry */
+	uint32_t flags;			/* flagi (przeznaczenie nieznane) */
+	uint32_t features;		/* opcje protokołu (GG8_FEATURES) */
+	uint32_t local_ip;		/* mój adres ip */
+	uint16_t local_port;		/* port, na którym słucham */
+	uint32_t external_ip;		/* zewnętrzny adres ip (???) */
+	uint16_t external_port;		/* zewnętrzny port (???) */
+	uint8_t image_size;		/* maksymalny rozmiar grafiki w KiB */
+	uint8_t dunno2;			/* 0x64 */
+} GG_PACKED;
+
+#define GG_LOGIN_HASH_TYPE_INVALID 0x0016
+
+#define GG_LOGIN80_OK 0x0035
+
+#define GG_NEW_STATUS80BETA 0x0028
+
+#define GG_NEW_STATUS80 0x0038
+
+/**
+ * Zmiana stanu (pakiet \c GG_NEW_STATUS80)
+ */
+struct gg_new_status80 {
+	uint32_t status;			/**< Nowy status */
+	uint32_t flags;				/**< flagi (nieznane przeznaczenie) */
+	uint32_t description_size;		/**< rozmiar opisu */
+} GG_PACKED;
+
+#define GG_STATUS80BETA 0x002a
+#define GG_NOTIFY_REPLY80BETA 0x002b
+
+#define GG_STATUS80 0x0036
+#define GG_NOTIFY_REPLY80 0x0037
+
+struct gg_notify_reply80 {
+	uint32_t uin;		/* numerek plus flagi w najstarszym bajcie */
+	uint32_t status;	/* status danej osoby */
+	uint32_t flags;		/* flagi (przeznaczenie nieznane) */
+	uint32_t remote_ip;	/* adres IP bezpośrednich połączeń */
+	uint16_t remote_port;	/* port bezpośrednich połączeń */
+	uint8_t image_size;	/* maksymalny rozmiar obrazków w KB */
+	uint8_t unknown2;	/* 0x00 */
+	uint32_t unknown3;	/* 0x00000000 */
+	uint32_t descr_len;	/* rozmiar opisu */
+} GG_PACKED;
+
+#define GG_SEND_MSG80 0x002d
+
+struct gg_send_msg80 {
+	uint32_t recipient;
+	uint32_t seq;
+	uint32_t msgclass;
+	uint32_t offset_plain;
+	uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_RECV_MSG80 0x002e
+
+struct gg_recv_msg80 {
+	uint32_t sender;
+	uint32_t seq;
+	uint32_t time;
+	uint32_t msgclass;
+	uint32_t offset_plain;
+	uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_DISCONNECT_ACK 0x000d
+
+#define GG_DCC7_VOICE_RETRIES 0x11	/* 17 powtorzen */
+
+#define GG_DCC7_RESERVED1		0xdeadc0de
+#define GG_DCC7_RESERVED2		0xdeadbeaf
+
+struct gg_dcc7_voice_auth {
+	uint8_t type;			/* 0x00 -> wysylanie ID
+					   0x01 -> potwierdzenie ID
+					*/
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */
+	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */
+} GG_PACKED;
+
+struct gg_dcc7_voice_nodata {	/* wyciszony mikrofon, ten pakiet jest wysylany co 1s (jesli chcemy podtrzymac polaczenie) */
+	uint8_t type;			/* 0x02 */
+	gg_dcc7_id_t id;		/* identyfikator połączenia */
+	uint32_t reserved1;		/* GG_DCC7_RESERVED1 */
+	uint32_t reserved2;		/* GG_DCC7_RESERVED2 */
+} GG_PACKED;
+
+struct gg_dcc7_voice_data {
+	uint8_t type;			/* 0x03 */
+	uint32_t did;			/* XXX: co ile zwieksza sie u nas id pakietu [uzywac 0x28] */
+	uint32_t len;			/* rozmiar strukturki - 1 (sizeof(type)) */
+	uint32_t packet_id;		/* numerek pakietu */
+	uint32_t datalen;		/* rozmiar danych */
+	/* char data[]; */		/* ramki: albo gsm, albo speex, albo melp, albo inne. */
+} GG_PACKED;
+
+struct gg_dcc7_voice_init {
+	uint8_t type;			/* 0x04 */
+	uint32_t id;			/* nr kroku [0x1 - 0x5] */
+	uint32_t protocol;		/* XXX: wersja protokolu (0x29, 0x2a, 0x2b) */
+	uint32_t len;			/* rozmiar sizeof(protocol)+sizeof(len)+sizeof(data) = 0x08 + sizeof(data) */
+	/* char data[]; */		/* reszta danych */
+} GG_PACKED;
+
+struct gg_dcc7_voice_init_confirm {
+	uint8_t type;			/* 0x05 */
+	uint32_t id;			/* id tego co potwierdzamy [0x1 - 0x5] */
+} GG_PACKED;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+
+#endif /* LIBGADU_PROTOCOL_H */
--- a/libpurple/protocols/gg/lib/pubdir.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/pubdir.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1,8 +1,9 @@
-/* $Id: pubdir.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: pubdir.c 502 2008-01-10 23:25:17Z wojtekka $ */
 
 /*
- *  (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ *  (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
  *                          Dawid Jarosz <dawjar@poczta.onet.pl>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU Lesser General Public License Version
@@ -15,11 +16,18 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
+/**
+ * \file pubdir.c
+ *
+ * \brief Obsługa katalogu publicznego
+ */
+
 #include "libgadu.h"
+#include "libgadu-config.h"
 
 #include <ctype.h>
 #include <errno.h>
@@ -29,20 +37,20 @@
 #include <string.h>
 #include <unistd.h>
 
-/*
- * gg_register3()
+/**
+ * Rejestruje nowego użytkownika.
  *
- * rozpoczyna rejestracj uytkownika protokoem GG 6.0. wymaga wczeniejszego
- * pobrania tokenu za pomoc funkcji gg_token().
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
  *
- *  - email - adres e-mail klienta
- *  - password - haso klienta
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto tokenu
- *  - async - poczenie asynchroniczne
+ * \param email Adres e-mail
+ * \param password Hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_register_free(), albo NULL jeli wystpi bd.
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup register
  */
 struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async)
 {
@@ -121,19 +129,59 @@
 	return h;
 }
 
-/*
- * gg_unregister3()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * usuwa konto uytkownika z serwera protokoem GG 6.0
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup register
+ */
+int gg_register_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
  *
- *  - uin - numerek GG
- *  - password - haso klienta
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto tokenu
- *  - async - poczenie asynchroniczne
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_unregister_free(), albo NULL jeli wystpi bd.
+ * \ingroup register
+ */
+void gg_register_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Usuwa użytkownika.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param password Hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup unregister
  */
 struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async)
 {
@@ -145,7 +193,7 @@
 		errno = EFAULT;
 		return NULL;
 	}
-
+    
 	__pwd = gg_saprintf("%ld", random());
 	__fmpwd = gg_urlencode(password);
 	__tokenid = gg_urlencode(tokenid);
@@ -210,22 +258,61 @@
 	return h;
 }
 
-/*
- * gg_change_passwd4()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
  *
- * wysya danie zmiany hasa zgodnie z protokoem GG 6.0. wymaga
- * wczeniejszego pobrania tokenu za pomoc funkcji gg_token().
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup unregister
+ */
+int gg_unregister_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
  *
- *  - uin - numer
- *  - email - adres e-mail
- *  - passwd - stare haso
- *  - newpasswd - nowe haso
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto tokenu
- *  - async - poczenie asynchroniczne
+ * \param h Struktura połączenia
+ *
+ * \ingroup unregister
+ */
+void gg_unregister_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Zmienia hasło użytkownika.
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_change_passwd_free(), albo NULL jeli wystpi bd.
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail
+ * \param passwd Obecne hasło
+ * \param newpasswd Nowe hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup passwd
  */
 struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async)
 {
@@ -309,19 +396,59 @@
 	return h;
 }
 
-/*
- * gg_remind_passwd3()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
  *
- * wysya danie przypomnienia hasa e-mailem.
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup passwd
+ */
+int gg_change_passwd_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
  *
- *  - uin - numer
- *  - email - adres e-mail taki, jak ten zapisany na serwerze
- *  - async - poczenie asynchroniczne
- *  - tokenid - identyfikator tokenu
- *  - tokenval - warto tokenu
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_remind_passwd_free(), albo NULL jeli wystpi bd.
+ * \ingroup passwd
+ */
+void gg_change_passwd_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Wysyła hasło użytkownika na e-mail.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail (podany przy rejestracji)
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup remind
  */
 struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async)
 {
@@ -396,17 +523,55 @@
 	return h;
 }
 
-/*
- * gg_pubdir_watch_fd()
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
  *
- * przy asynchronicznych operacjach na katalogu publicznym naley wywoywa
- * t funkcj przy zmianach na obserwowanym deskryptorze.
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  *
- *  - h - struktura opisujca poczenie
+ * \ingroup remind
+ */
+int gg_remind_watch_fd(struct gg_httpd *h)
+{
+	return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
  *
- * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie
- * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki
- * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error.
+ * \ingroup remind
+ */
+void gg_remind_free(struct gg_http *h)
+{
+	return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
 int gg_pubdir_watch_fd(struct gg_http *h)
 {
@@ -447,7 +612,11 @@
 	
 	gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body);
 
-	if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
+	if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) {
+		p->success = 1;
+		p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0);
+		gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin);
+	} else if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
 		p->success = 1;
 		if (tmp[7] == ':')
 			p->uin = strtol(tmp + 8, NULL, 0);
@@ -458,12 +627,10 @@
 	return 0;
 }
 
-/*
- * gg_pubdir_free()
+/**
+ * Zwalnia zasoby po operacji na katalogu publicznym.
  *
- * zwalnia pami po efektach operacji na katalogu publicznym.
- *
- *  - h - zwalniana struktura
+ * \param h Struktura połączenia
  */
 void gg_pubdir_free(struct gg_http *h)
 {
@@ -474,14 +641,17 @@
 	gg_http_free(h);
 }
 
-/*
- * gg_token()
+/**
+ * Pobiera token do autoryzacji operacji na katalogu publicznym.
+ *
+ * Token jest niezbędny do tworzenia nowego i usuwania użytkownika,
+ * zmiany hasła itd.
  *
- * pobiera z serwera token do autoryzacji zakadania konta, usuwania
- * konta i zmiany hasa.
+ * \param async Flaga połączenia asynchronicznego
  *
- * zaalokowana struct gg_http, ktr poniej naley zwolni
- * funkcj gg_token_free(), albo NULL jeli wystpi bd.
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup token
  */
 struct gg_http *gg_token(int async)
 {
@@ -511,17 +681,18 @@
 	return h;
 }
 
-/*
- * gg_token_watch_fd()
- *
- * przy asynchronicznych operacjach zwizanych z tokenem naley wywoywa
- * t funkcj przy zmianach na obserwowanym deskryptorze.
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
  *
- *  - h - struktura opisujca poczenie
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie się w polu \c error.
  *
- * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie
- * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki
- * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error.
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup token
  */
 int gg_token_watch_fd(struct gg_http *h)
 {
@@ -547,8 +718,8 @@
 	if (h->state != GG_STATE_PARSING)
 		return 0;
 	
-	/* jeli h->data jest puste, to cigalimy tokenid i url do niego,
-	 * ale jeli co tam jest, to znaczy, e mamy drugi etap polegajcy
+	/* jeśli h->data jest puste, to ściągaliśmy tokenid i url do niego,
+	 * ale jeśli coś tam jest, to znaczy, że mamy drugi etap polegający
 	 * na pobieraniu tokenu. */
 	if (!h->data) {
 		int width, height, length;
@@ -573,8 +744,8 @@
 			return -1;
 		}
 		
-		/* dostalimy tokenid i wszystkie niezbdne informacje,
-		 * wic pobierzmy obrazek z tokenem */
+		/* dostaliśmy tokenid i wszystkie niezbędne informacje,
+		 * więc pobierzmy obrazek z tokenem */
 
 		if (strncmp(url, "http://", 7)) {
 			path = gg_saprintf("%s?tokenid=%s", url, tokenid);
@@ -623,6 +794,8 @@
 		free(path);
 		free(url);
 
+		gg_http_free_fields(h);
+
 		memcpy(h, h2, sizeof(struct gg_http));
 		free(h2);
 
@@ -652,12 +825,12 @@
 	return 0;
 }
 
-/*
- * gg_token_free()
+/**
+ * Zwalnia zasoby po operacji pobierania tokenu.
  *
- * zwalnia pami po efektach pobierania tokenu.
+ * \param h Struktura połączenia
  *
- *  - h - zwalniana struktura
+ * \ingroup token
  */
 void gg_token_free(struct gg_http *h)
 {
--- a/libpurple/protocols/gg/lib/pubdir50.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/gg/lib/pubdir50.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1,4 +1,4 @@
-/* $Id: pubdir50.c 16856 2006-08-19 01:13:25Z evands $ */
+/* $Id: pubdir50.c 854 2009-10-12 21:06:28Z wojtekka $ */
 
 /*
  *  (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
@@ -14,23 +14,32 @@
  *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
  *  USA.
  */
 
-#include "libgadu.h"
+/**
+ * \file pubdir50.c
+ *
+ * \brief Obsługa katalogu publicznego od wersji Gadu-Gadu 5.x
+ */
 
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 
-/*
- * gg_pubdir50_new()
+#include "libgadu.h"
+#include "libgadu-internal.h"
+
+/**
+ * Tworzy nowe zapytanie katalogu publicznego.
  *
- * tworzy now zmienn typu gg_pubdir50_t.
+ * \param type Rodzaj zapytania
  *
- * zaalokowana zmienna lub NULL w przypadku braku pamici.
+ * \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku błędu.
+ *
+ * \ingroup pubdir50
  */
 gg_pubdir50_t gg_pubdir50_new(int type)
 {
@@ -50,17 +59,16 @@
 	return res;
 }
 
-/*
- * gg_pubdir50_add_n()  // funkcja wewntrzna
- *
- * funkcja dodaje lub zastpuje istniejce pole do zapytania lub odpowiedzi.
+/**
+ * \internal Dodaje lub zastępuje pole zapytania lub odpowiedzi katalogu
+ * publicznego.
  *
- *  - req - wskanik opisu zapytania,
- *  - num - numer wyniku (0 dla zapytania),
- *  - field - nazwa pola,
- *  - value - warto pola,
+ * \param req Zapytanie lub odpowiedź
+ * \param num Numer wyniku odpowiedzi (0 dla zapytania)
+ * \param field Nazwa pola
+ * \param value Wartość pola
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
 static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
 {
@@ -110,31 +118,31 @@
 	return 0;
 }
 
-/*
- * gg_pubdir50_add()
- *
- * funkcja dodaje pole do zapytania.
+/**
+ * Dodaje pole zapytania.
  *
- *  - req - wskanik opisu zapytania,
- *  - field - nazwa pola,
- *  - value - warto pola,
+ * \param req Zapytanie
+ * \param field Nazwa pola
+ * \param value Wartość pola
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
  */
 int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value)
 {
 	return gg_pubdir50_add_n(req, 0, field, value);
 }
 
-/*
- * gg_pubdir50_seq_set()
- *
- * ustawia numer sekwencyjny zapytania.
+/**
+ * Ustawia numer sekwencyjny zapytania.
  *
- *  - req - zapytanie,
- *  - seq - nowy numer sekwencyjny.
+ * \param req Zapytanie
+ * \param seq Numer sekwencyjny
  *
- * 0/-1.
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
  */
 int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
 {
@@ -151,12 +159,12 @@
 	return 0;
 }
 
-/*
- * gg_pubdir50_free()
+/**
+ * Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego.
  *
- * zwalnia pami po zapytaniu lub rezultacie szukania uytkownika.
+ * \param s Zapytanie lub odpowiedź
  *
- *  - s - zwalniana zmienna,
+ * \ingroup pubdir50
  */
 void gg_pubdir50_free(gg_pubdir50_t s)
 {
@@ -174,15 +182,15 @@
 	free(s);
 }
 
-/*
- * gg_pubdir50()
- *
- * wysya zapytanie katalogu publicznego do serwera.
+/**
+ * Wysyła zapytanie katalogu publicznego do serwera.
  *
- *  - sess - sesja,
- *  - req - zapytanie.
+ * \param sess Struktura sesji
+ * \param req Zapytanie
  *
- * numer sekwencyjny wyszukiwania lub 0 w przypadku bdu.
+ * \return Numer sekwencyjny zapytania lub 0 w przypadku błędu
+ *
+ * \ingroup pubdir50
  */
 uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
 {
@@ -191,16 +199,16 @@
 	char *buf, *p;
 	struct gg_pubdir50_request *r;
 
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
+	gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
 	
 	if (!sess || !req) {
-		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
 		errno = EFAULT;
 		return 0;
 	}
 
 	if (sess->state != GG_STATE_CONNECTED) {
-		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
 		errno = ENOTCONN;
 		return 0;
 	}
@@ -210,30 +218,81 @@
 		if (req->entries[i].num)
 			continue;
 		
-		size += strlen(req->entries[i].field) + 1;
-		size += strlen(req->entries[i].value) + 1;
+		if (sess->encoding == GG_ENCODING_CP1250) {
+			size += strlen(req->entries[i].field) + 1;
+			size += strlen(req->entries[i].value) + 1;
+		} else {
+			char *tmp;
+
+			tmp = gg_utf8_to_cp(req->entries[i].field);
+
+			if (tmp == NULL)
+				return -1;
+
+			size += strlen(tmp) + 1;
+
+			free(tmp);
+
+			tmp = gg_utf8_to_cp(req->entries[i].value);
+
+			if (tmp == NULL)
+				return -1;
+
+			size += strlen(tmp) + 1;
+
+			free(tmp);
+		}
 	}
 
 	if (!(buf = malloc(size))) {
-		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
+		gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
 		return 0;
 	}
 
+	if (!req->seq)
+		req->seq = time(NULL);
+
+	res = req->seq;
+
 	r = (struct gg_pubdir50_request*) buf;
-	res = time(NULL);
 	r->type = req->type;
-	r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL));
-	req->seq = gg_fix32(r->seq);
+	r->seq = gg_fix32(req->seq);
 
 	for (i = 0, p = buf + 5; i < req->entries_count; i++) {
 		if (req->entries[i].num)
 			continue;
 
-		strcpy(p, req->entries[i].field);
-		p += strlen(p) + 1;
+		if (sess->encoding == GG_ENCODING_CP1250) {
+			strcpy(p, req->entries[i].field);
+			p += strlen(p) + 1;
+
+			strcpy(p, req->entries[i].value);
+			p += strlen(p) + 1;
+		} else {
+			char *tmp;
+
+			tmp = gg_utf8_to_cp(req->entries[i].field);
+
+			if (tmp == NULL) {
+				free(buf);
+				return -1;
+			}
 
-		strcpy(p, req->entries[i].value);
-		p += strlen(p) + 1;
+			strcpy(p, tmp);
+			p += strlen(tmp) + 1;
+			free(tmp);
+
+			tmp = gg_utf8_to_cp(req->entries[i].value);
+
+			if (tmp == NULL) {
+				free(buf);
+				return -1;
+			}
+
+			strcpy(p, tmp);
+			p += strlen(tmp) + 1;
+			free(tmp);
+		}
 	}
 
 	if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1)
@@ -245,26 +304,26 @@
 }
 
 /*
- * gg_pubdir50_handle_reply()  // funkcja wewntrzna
- *
- * analizuje przychodzcy pakiet odpowiedzi i zapisuje wynik w struct gg_event.
+ * \internal Analizuje przychodzący pakiet odpowiedzi i zapisuje wynik
+ * w strukturze \c gg_event.
  *
- *  - e - opis zdarzenia
- *  - packet - zawarto pakietu odpowiedzi
- *  - length - dugo pakietu odpowiedzi
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param packet Pakiet odpowiedzi
+ * \param length Długość pakietu odpowiedzi
  *
- * 0/-1
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
  */
-int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length)
 {
 	const char *end = packet + length, *p;
 	struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet;
 	gg_pubdir50_t res;
 	int num = 0;
 	
-	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length);
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);\n", sess, e, packet, length);
 
-	if (!e || !packet) {
+	if (!sess || !e || !packet) {
 		gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
 		errno = EFAULT;
 		return -1;
@@ -299,11 +358,11 @@
 			break;
 	}
 
-	/* brak wynikw? */
+	/* brak wyników? */
 	if (length == 5)
 		return 0;
 
-	/* pomi pocztek odpowiedzi */
+	/* pomiń początek odpowiedzi */
 	p = packet + 5;
 
 	while (p < end) {
@@ -311,7 +370,7 @@
 
 		field = p;
 
-		/* sprawd, czy nie mamy podziau na kolejne pole */
+		/* sprawdź, czy nie mamy podziału na kolejne pole */
 		if (!*field) {
 			num++;
 			field++;
@@ -320,22 +379,22 @@
 		value = NULL;
 		
 		for (p = field; p < end; p++) {
-			/* jeli mamy koniec tekstu... */
+			/* jeśli mamy koniec tekstu... */
 			if (!*p) {
-				/* ...i jeszcze nie mielimy wartoci pola to
-				 * wiemy, e po tym zerze jest warto... */
+				/* ...i jeszcze nie mieliśmy wartości pola to
+				 * wiemy, że po tym zerze jest wartość... */
 				if (!value)
 					value = p + 1;
 				else
 					/* ...w przeciwym wypadku koniec
-					 * wartoci i moemy wychodzi
-					 * grzecznie z ptli */
+					 * wartości i możemy wychodzić
+					 * grzecznie z pętli */
 					break;
 			}
 		}
 		
-		/* sprawdmy, czy pole nie wychodzi poza pakiet, eby nie
-		 * mie segfaultw, jeli serwer przestanie zakacza pakietw
+		/* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie
+		 * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów
 		 * przez \0 */
 
 		if (p == end) {
@@ -345,14 +404,30 @@
 
 		p++;
 
-		/* jeli dostalimy namier na nastpne wyniki, to znaczy e
-		 * mamy koniec wynikw i nie jest to kolejna osoba. */
+		/* jeśli dostaliśmy namier na następne wyniki, to znaczy że
+		 * mamy koniec wyników i nie jest to kolejna osoba. */
 		if (!strcasecmp(field, "nextstart")) {
 			res->next = atoi(value);
 			num--;
 		} else {
-			if (gg_pubdir50_add_n(res, num, field, value) == -1)
-				goto failure;
+			if (sess->encoding == GG_ENCODING_CP1250) {
+				if (gg_pubdir50_add_n(res, num, field, value) == -1)
+					goto failure;
+			} else {
+				char *tmp;
+
+				tmp = gg_cp_to_utf8(value);
+
+				if (tmp == NULL)
+					goto failure;
+
+				if (gg_pubdir50_add_n(res, num, field, tmp) == -1) {
+					free(tmp);
+					goto failure;
+				}
+
+				free(tmp);
+			}
 		}
 	}	
 
@@ -365,16 +440,16 @@
 	return -1;
 }
 
-/*
- * gg_pubdir50_get()
- *
- * pobiera informacj z rezultatu wyszukiwania.
+/**
+ * Pobiera pole z odpowiedzi katalogu publicznego.
  *
- *  - res - rezultat wyszukiwania,
- *  - num - numer odpowiedzi,
- *  - field - nazwa pola (wielko liter nie ma znaczenia).
+ * \param res Odpowiedź
+ * \param num Numer wyniku odpowiedzi
+ * \param field Nazwa pola (wielkość liter nie ma znaczenia)
  *
- * warto pola lub NULL, jeli nie znaleziono.
+ * \return Wartość pola lub \c NULL jeśli nie znaleziono
+ *
+ * \ingroup pubdir50
  */
 const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
 {
@@ -399,57 +474,61 @@
 	return value;
 }
 
-/*
- * gg_pubdir50_count()
+/**
+ * Zwraca liczbę wyników odpowiedzi.
  *
- * zwraca ilo wynikw danego zapytania.
+ * \param res Odpowiedź
  *
- *  - res - odpowied
+ * \return Liczba wyników lub -1 w przypadku błędu
  *
- * ilo lub -1 w przypadku bdu.
+ * \ingroup pubdir50
  */
 int gg_pubdir50_count(gg_pubdir50_t res)
 {
 	return (!res) ? -1 : res->count;
 }
 
-/*
- * gg_pubdir50_type()
+/**
+ * Zwraca rodzaj zapytania lub odpowiedzi.
  *
- * zwraca rodzaj zapytania lub odpowiedzi.
+ * \param res Zapytanie lub odpowiedź
  *
- *  - res - zapytanie lub odpowied
+ * \return Rodzaj lub -1 w przypadku błędu
  *
- * ilo lub -1 w przypadku bdu.
+ * \ingroup pubdir50
  */
 int gg_pubdir50_type(gg_pubdir50_t res)
 {
 	return (!res) ? -1 : res->type;
 }
 
-/*
- * gg_pubdir50_next()
+/**
+ * Zwraca numer, od którego należy rozpocząc kolejne wyszukiwanie.
  *
- * zwraca numer, od ktrego naley rozpocz kolejne wyszukiwanie, jeli
- * zaley nam na kolejnych wynikach.
+ * Dłuższe odpowiedzi katalogu publicznego są wysyłane przez serwer
+ * w mniejszych paczkach. Po otrzymaniu odpowiedzi, jeśli numer kolejnego
+ * wyszukiwania jest większy od zera, dalsze wyniki można otrzymać przez
+ * wywołanie kolejnego zapytania z określonym numerem początkowym.
  *
- *  - res - odpowied
+ * \param res Odpowiedź
  *
- * numer lub -1 w przypadku bdu.
+ * \return Numer lub -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
  */
 uin_t gg_pubdir50_next(gg_pubdir50_t res)
 {
 	return (!res) ? (unsigned) -1 : res->next;
 }
 
-/*
- * gg_pubdir50_seq()
+/**
+ * Zwraca numer sekwencyjny zapytania lub odpowiedzi.
  *
- * zwraca numer sekwencyjny zapytania lub odpowiedzi.
+ * \param res Zapytanie lub odpowiedź
  *
- *  - res - zapytanie lub odpowied
+ * \return Numer sekwencyjny lub -1 w przypadku błędu
  *
- * numer lub -1 w przypadku bdu.
+ * \ingroup pubdir50
  */
 uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/resolver.c	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,1065 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
+ *                          Robert J. Woźny <speedy@ziew.org>
+ *                          Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ *                          Tomasz Chiliński <chilek@chilan.com>
+ *                          Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+/**
+ * \file resolver.c
+ *
+ * \brief Funkcje rozwiązywania nazw
+ */
+
+#ifndef _WIN32
+#  include <sys/wait.h>
+#  include <netdb.h>
+#  include <signal.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+#include "resolver.h"
+#include "compat.h"
+
+/** Sposób rozwiązywania nazw serwerów */
+static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT;
+
+/** Funkcja rozpoczynająca rozwiązywanie nazwy */
+static int (*gg_global_resolver_start)(int *fd, void **private_data, const char *hostname);
+
+/** Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
+static void (*gg_global_resolver_cleanup)(void **private_data, int force);
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+
+#include <pthread.h>
+
+/**
+ * \internal Funkcja pomocnicza zwalniająca zasoby po rozwiązywaniu nazwy
+ * w wątku.
+ *
+ * \param data Wskaźnik na wskaźnik bufora zaalokowanego w wątku
+ */
+static void gg_gethostbyname_cleaner(void *data)
+{
+	char **buf_ptr = (char**) data;
+
+	if (buf_ptr != NULL) {
+		free(*buf_ptr);
+		*buf_ptr = NULL;
+	}
+}
+
+#endif /* GG_CONFIG_HAVE_PTHREAD */
+
+/**
+ * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
+ *
+ * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
+ * nie, to zwykłej \c gethostbyname.
+ *
+ * \param hostname Nazwa serwera
+ * \param addr Wskaźnik na rezultat rozwiązywania nazwy
+ * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthread)
+{
+#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+	char *buf = NULL;
+	char *new_buf = NULL;
+	struct hostent he;
+	struct hostent *he_ptr = NULL;
+	size_t buf_len = 1024;
+	int result = -1;
+	int h_errnop;
+	int ret = 0;
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	int old_state;
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	pthread_cleanup_push(gg_gethostbyname_cleaner, &buf);
+
+	if (pthread)
+		pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+	buf = malloc(buf_len);
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	if (pthread)
+		pthread_setcancelstate(old_state, NULL);
+#endif
+
+	if (buf != NULL) {
+#ifndef sun
+		while ((ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop)) == ERANGE) {
+#else
+		while (((he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop)) == NULL) && (errno == ERANGE)) {
+#endif
+			buf_len *= 2;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+			if (pthread)
+				pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+			new_buf = realloc(buf, buf_len);
+
+			if (new_buf != NULL)
+				buf = new_buf;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+			if (pthread)
+				pthread_setcancelstate(old_state, NULL);
+#endif
+
+			if (new_buf == NULL) {
+				ret = ENOMEM;
+				break;
+			}
+		}
+
+		if (ret == 0 && he_ptr != NULL) {
+			memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr));
+			result = 0;
+		}
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		if (pthread)
+			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
+#endif
+
+		free(buf);
+		buf = NULL;
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		if (pthread)
+			pthread_setcancelstate(old_state, NULL);
+#endif
+	}
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+	pthread_cleanup_pop(1);
+#endif
+
+	return result;
+#else
+	struct hostent *he;
+
+	he = gethostbyname(hostname);
+
+	if (he == NULL)
+		return -1;
+
+	memcpy(addr, he->h_addr, sizeof(struct in_addr));
+
+	return 0;
+#endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
+}
+
+/**
+ * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
+ *
+ * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
+ * nie, to zwykłej \c gethostbyname.
+ *
+ * \param hostname Nazwa serwera
+ *
+ * \return Zaalokowana struktura \c in_addr lub NULL w przypadku błędu.
+ */
+struct in_addr *gg_gethostbyname(const char *hostname)
+{
+	struct in_addr *addr;
+
+	if (!(addr = malloc(sizeof(struct in_addr))))
+		return NULL;
+
+	if (gg_gethostbyname_real(hostname, addr, 0)) {
+		free(addr);
+		return NULL;
+	}
+	return addr;
+}
+
+/**
+ * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
+ */
+struct gg_resolver_fork_data {
+	int pid;		/*< Identyfikator procesu */
+};
+
+
+
+#ifdef _WIN32
+/**
+ *  Deal with the fact that you can't select() on a win32 file fd.
+ *  This makes it practically impossible to tie into purple's event loop.
+ *
+ *  -This is thanks to Tor Lillqvist.
+ *  XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu.
+ */
+static int
+socket_pipe (int *fds)
+{
+	SOCKET temp, socket1 = -1, socket2 = -1;
+	struct sockaddr_in saddr;
+	int len;
+	u_long arg;
+	fd_set read_set, write_set;
+	struct timeval tv;
+
+	temp = socket(AF_INET, SOCK_STREAM, 0);
+
+	if (temp == INVALID_SOCKET) {
+		goto out0;
+	}
+
+	arg = 1;
+	if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) {
+		goto out0;
+	}
+
+	memset(&saddr, 0, sizeof(saddr));
+	saddr.sin_family = AF_INET;
+	saddr.sin_port = 0;
+	saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+	if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) {
+		goto out0;
+	}
+
+	if (listen(temp, 1) == SOCKET_ERROR) {
+		goto out0;
+	}
+
+	len = sizeof(saddr);
+	if (getsockname(temp, (struct sockaddr *)&saddr, &len)) {
+		goto out0;
+	}
+
+	socket1 = socket(AF_INET, SOCK_STREAM, 0);
+
+	if (socket1 == INVALID_SOCKET) {
+		goto out0;
+	}
+
+	arg = 1;
+	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
+		goto out1;
+	}
+
+	if (connect(socket1, (struct sockaddr  *)&saddr, len) != SOCKET_ERROR ||
+			WSAGetLastError() != WSAEWOULDBLOCK) {
+		goto out1;
+	}
+
+	FD_ZERO(&read_set);
+	FD_SET(temp, &read_set);
+
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+
+	if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) {
+		goto out1;
+	}
+
+	if (!FD_ISSET(temp, &read_set)) {
+		goto out1;
+	}
+
+	socket2 = accept(temp, (struct sockaddr *) &saddr, &len);
+	if (socket2 == INVALID_SOCKET) {
+		goto out1;
+	}
+
+	FD_ZERO(&write_set);
+	FD_SET(socket1, &write_set);
+
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+
+	if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) {
+		goto out2;
+	}
+
+	if (!FD_ISSET(socket1, &write_set)) {
+		goto out2;
+	}
+
+	arg = 0;
+	if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) {
+		goto out2;
+	}
+
+	arg = 0;
+	if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) {
+		goto out2;
+	}
+
+	fds[0] = socket1;
+	fds[1] = socket2;
+
+	closesocket (temp);
+
+	return 0;
+
+out2:
+	closesocket (socket2);
+out1:
+	closesocket (socket1);
+out0:
+	closesocket (temp);
+	errno = EIO;            /* XXX */
+
+	return -1;
+}
+#endif
+
+
+
+#ifdef _WIN32
+struct gg_resolve_win32thread_data {
+	char *hostname;
+	int fd;
+};
+
+static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg)
+{
+	struct gg_resolve_win32thread_data *d = arg;
+	struct in_addr a;
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() host: %s, fd: %i called\n", d->hostname, d->fd);
+	
+	if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
+		/* W przypadku błędu gg_gethostbyname_real() zwróci -1
+					 * i nie zmieni &addr. Tam jest już INADDR_NONE,
+					 * więc nie musimy robić nic więcej. */
+		gg_gethostbyname_real(d->hostname, &a, 0);
+	}
+	
+	// if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
+		// struct in_addr *hn;
+		
+		// if (!(hn = gg_gethostbyname(d->hostname)))
+			// a.s_addr = INADDR_NONE;
+		// else {
+			// a.s_addr = hn->s_addr;
+			// free(hn);
+		// }
+	// }
+
+	write(d->fd, &a, sizeof(a));
+	close(d->fd);
+
+	free(d->hostname);
+	d->hostname = NULL;
+
+	free(d);
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() done\n");
+	
+	return 0;
+}
+
+
+static int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname)
+{
+	struct gg_resolve_win32thread_data *d = NULL;
+	HANDLE h;
+	DWORD dwTId;
+	int pipes[2], new_errno;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname);
+	
+	if (!resolver || !fd || !hostname) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	if (socket_pipe(pipes) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+		return -1;
+	}
+
+	if (!(d = malloc(sizeof(*d)))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	d->hostname = NULL;
+
+	if (!(d->hostname = strdup(hostname))) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	d->fd = pipes[1];
+
+	h = CreateThread(NULL, 0, gg_resolve_win32thread_thread,
+		d, 0, &dwTId);
+
+	if (h == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	*resolver = h;
+	*fd = pipes[0];
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() done\n");
+	
+	return 0;
+
+cleanup:
+	if (d) {
+		free(d->hostname);
+		free(d);
+	}
+
+	close(pipes[0]);
+	close(pipes[1]);
+
+	errno = new_errno;
+
+	return -1;
+
+}
+
+static void gg_resolve_win32thread_cleanup(void **priv_data, int force)
+{
+	struct gg_resolve_win32thread_data *data;
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force: %i called\n", force);
+	
+	if (priv_data == NULL || *priv_data == NULL)
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() priv_data: NULL\n");
+		return;
+
+	data = (struct gg_resolve_win32thread_data*) *priv_data;
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() data: %s called\n", data->hostname);
+	*priv_data = NULL;
+
+	if (force) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force called\n", force);
+		//pthread_cancel(data->thread);
+		//pthread_join(data->thread, NULL);
+	}
+
+	free(data->hostname);
+	data->hostname = NULL;
+
+	if (data->fd != -1) {
+		close(data->fd);
+		data->fd = -1;
+	}
+	gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() done\n");
+	free(data);
+}
+#endif
+
+#ifndef _WIN32
+/**
+ * \internal Rozwiązuje nazwę serwera w osobnym procesie.
+ *
+ * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania
+ * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim
+ * przeprowadzane jest rozwiązywanie nazwy. Deskryptor strony do odczytu 
+ * zapisuje się w strukturze sieci i czeka na dane w postaci struktury
+ * \c in_addr. Jeśli nie znaleziono nazwy, zwracana jest \c INADDR_NONE.
+ *
+ * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor
+ *           potoku
+ * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
+ *                  do numeru procesu potomnego rozwiązującego nazwę
+ * \param hostname Nazwa serwera do rozwiązania
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname)
+{
+	struct gg_resolver_fork_data *data = NULL;
+	struct in_addr addr;
+	int pipes[2], new_errno;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
+
+	if (fd == NULL || priv_data == NULL || hostname == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	data = malloc(sizeof(struct gg_resolver_fork_data));
+
+	if (data == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n");
+		return -1;
+	}
+
+	if (pipe(pipes) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	data->pid = fork();
+
+	if (data->pid == -1) {
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	if (data->pid == 0) {
+		close(pipes[0]);
+
+		if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+			/* W przypadku błędu gg_gethostbyname_real() zwróci -1
+                         * i nie zmieni &addr. Tam jest już INADDR_NONE,
+                         * więc nie musimy robić nic więcej. */
+			gg_gethostbyname_real(hostname, &addr, 0);
+		}
+
+		if (write(pipes[1], &addr, sizeof(addr)) != sizeof(addr))
+			exit(1);
+
+		exit(0);
+	}
+
+	close(pipes[1]);
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data);
+
+	*fd = pipes[0];
+	*priv_data = data;
+
+	return 0;
+
+cleanup:
+	free(data);
+	close(pipes[0]);
+	close(pipes[1]);
+
+	errno = new_errno;
+
+	return -1;
+}
+
+/**
+ * \internal Usuwanie zasobów po procesie rozwiązywaniu nazwy.
+ *
+ * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
+ * zasobów sesji podczas rozwiązywania nazwy.
+ *
+ * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
+ *                  danych
+ * \param force Flaga usuwania zasobów przed zakończeniem działania
+ */
+void gg_resolver_fork_cleanup(void **priv_data, int force)
+{
+	struct gg_resolver_fork_data *data;
+
+	if (priv_data == NULL || *priv_data == NULL)
+		return;
+
+	data = (struct gg_resolver_fork_data*) *priv_data;
+	*priv_data = NULL;
+
+	if (force)
+		kill(data->pid, SIGKILL);
+
+	waitpid(data->pid, NULL, WNOHANG);
+
+	free(data);
+}
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+
+/**
+ * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
+ */
+struct gg_resolver_pthread_data {
+	pthread_t thread;	/*< Identyfikator wątku */
+	char *hostname;		/*< Nazwa serwera */
+	int rfd;		/*< Deskryptor do odczytu */
+	int wfd;		/*< Deskryptor do zapisu */
+};
+
+/**
+ * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy.
+ *
+ * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
+ * zasobów sesji podczas rozwiązywania nazwy.
+ *
+ * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
+ *                  danych
+ * \param force Flaga usuwania zasobów przed zakończeniem działania
+ */
+static void gg_resolver_pthread_cleanup(void **priv_data, int force)
+{
+	struct gg_resolver_pthread_data *data;
+
+	if (priv_data == NULL || *priv_data == NULL)
+		return;
+
+	data = (struct gg_resolver_pthread_data *) *priv_data;
+	*priv_data = NULL;
+
+	if (force) {
+		pthread_cancel(data->thread);
+		pthread_join(data->thread, NULL);
+	}
+
+	free(data->hostname);
+	data->hostname = NULL;
+
+	if (data->wfd != -1) {
+		close(data->wfd);
+		data->wfd = -1;
+	}
+
+	free(data);
+}
+
+/**
+ * \internal Wątek rozwiązujący nazwę.
+ *
+ * \param arg Wskaźnik na strukturę \c gg_resolver_pthread_data
+ */
+static void *gg_resolver_pthread_thread(void *arg)
+{
+	struct gg_resolver_pthread_data *data = arg;
+	struct in_addr addr;
+
+	pthread_detach(pthread_self());
+
+	if ((addr.s_addr = inet_addr(data->hostname)) == INADDR_NONE) {
+		/* W przypadku błędu gg_gethostbyname_real() zwróci -1
+                 * i nie zmieni &addr. Tam jest już INADDR_NONE,
+                 * więc nie musimy robić nic więcej. */
+		gg_gethostbyname_real(data->hostname, &addr, 1);
+	}
+
+	if (write(data->wfd, &addr, sizeof(addr)) == sizeof(addr))
+		pthread_exit(NULL);
+	else 
+		pthread_exit((void*) -1);
+
+	return NULL;	/* żeby kompilator nie marudził */
+}
+
+/**
+ * \internal Rozwiązuje nazwę serwera w osobnym wątku.
+ *
+ * Funkcja działa analogicznie do \c gg_resolver_fork_start(), z tą różnicą,
+ * że działa na wątkach, nie procesach. Jest dostępna wyłącznie gdy podczas
+ * kompilacji włączono odpowiednią opcję.
+ *
+ * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor
+ *           potoku
+ * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
+ *                  do prywatnych danych wątku rozwiązującego nazwę
+ * \param hostname Nazwa serwera do rozwiązania
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_resolver_pthread_start(int *fd, void **priv_data, const char *hostname)
+{
+	struct gg_resolver_pthread_data *data = NULL;
+	int pipes[2], new_errno;
+
+	gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
+
+	if (fd == NULL || priv_data == NULL || hostname == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n");
+		errno = EFAULT;
+		return -1;
+	}
+
+	data = malloc(sizeof(struct gg_resolver_pthread_data));
+
+	if (data == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory for resolver data\n");
+		return -1;
+	}
+
+	if (pipe(pipes) == -1) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+		free(data);
+		return -1;
+	}
+
+	data->hostname = strdup(hostname);
+
+	if (data->hostname == NULL) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	data->rfd = pipes[0];
+	data->wfd = pipes[1];
+
+	if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, data)) {
+		gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create thread\n");
+		new_errno = errno;
+		goto cleanup;
+	}
+
+	gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data);
+
+	*fd = pipes[0];
+	*priv_data = data;
+
+	return 0;
+
+cleanup:
+	if (data) {
+		free(data->hostname);
+		free(data);
+	}
+
+	close(pipes[0]);
+	close(pipes[1]);
+
+	errno = new_errno;
+
+	return -1;
+}
+
+#endif /* GG_CONFIG_HAVE_PTHREAD */
+
+/**
+ * Ustawia sposób rozwiązywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)
+{
+	if (gs == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (type == GG_RESOLVER_DEFAULT) {
+		if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
+			gs->resolver_type = gg_global_resolver_type;
+			gs->resolver_start = gg_global_resolver_start;
+			gs->resolver_cleanup = gg_global_resolver_cleanup;
+			return 0;
+		}
+
+#ifdef _WIN32
+		type = GG_RESOLVER_WIN32;
+#else
+		type = GG_RESOLVER_FORK;
+#endif
+
+#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT)
+		type = GG_RESOLVER_PTHREAD;
+#endif
+	}
+
+	switch (type) {
+#ifdef _WIN32
+	case GG_RESOLVER_WIN32:
+			gs->resolver_type = type;
+			gs->resolver_start = gg_resolve_win32thread;
+			gs->resolver_cleanup = gg_resolve_win32thread_cleanup;
+			return 0;
+#else
+	case GG_RESOLVER_FORK:
+			gs->resolver_type = type;
+			gs->resolver_start = gg_resolver_fork_start;
+			gs->resolver_cleanup = gg_resolver_fork_cleanup;
+			return 0;
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		case GG_RESOLVER_PTHREAD:
+			gs->resolver_type = type;
+			gs->resolver_start = gg_resolver_pthread_start;
+			gs->resolver_cleanup = gg_resolver_pthread_cleanup;
+			return 0;
+#endif
+
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+}
+
+/**
+ * Zwraca sposób rozwiązywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ *
+ * \return Sposób rozwiązywania nazw
+ */
+gg_resolver_t gg_session_get_resolver(struct gg_session *gs)
+{
+	if (gs == NULL) {
+		errno = EINVAL;
+		return GG_RESOLVER_INVALID;
+	}
+
+	return gs->resolver_type;
+}
+
+/**
+ * Ustawia własny sposób rozwiązywania nazw w sesji.
+ *
+ * \param gs Struktura sesji
+ * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniająca zasoby
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+	if (gs == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	gs->resolver_type = GG_RESOLVER_CUSTOM;
+	gs->resolver_start = resolver_start;
+	gs->resolver_cleanup = resolver_cleanup;
+
+	return 0;
+}
+
+/**
+ * Ustawia sposób rozwiązywania nazw połączenia HTTP.
+ *
+ * \param gh Struktura połączenia
+ * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type)
+{
+	if (gh == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	if (type == GG_RESOLVER_DEFAULT) {
+		if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
+			gh->resolver_type = gg_global_resolver_type;
+			gh->resolver_start = gg_global_resolver_start;
+			gh->resolver_cleanup = gg_global_resolver_cleanup;
+			return 0;
+		}
+
+#ifdef _WIN32
+		type = GG_RESOLVER_WIN32;
+#else
+		type = GG_RESOLVER_FORK;
+#endif
+
+#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT)
+		type = GG_RESOLVER_PTHREAD;
+#endif
+	}
+
+	switch (type) {
+#ifdef _WIN32
+	case GG_RESOLVER_WIN32:
+			gh->resolver_type = type;
+			gh->resolver_start = gg_resolve_win32thread;
+			gh->resolver_cleanup = gg_resolve_win32thread_cleanup;
+			return 0;
+#else
+	case GG_RESOLVER_FORK:
+			gh->resolver_type = type;
+			gh->resolver_start = gg_resolver_fork_start;
+			gh->resolver_cleanup = gg_resolver_fork_cleanup;
+			return 0;
+#endif
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		case GG_RESOLVER_PTHREAD:
+			gh->resolver_type = type;
+			gh->resolver_start = gg_resolver_pthread_start;
+			gh->resolver_cleanup = gg_resolver_pthread_cleanup;
+			return 0;
+#endif
+
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+}
+
+/**
+ * Zwraca sposób rozwiązywania nazw połączenia HTTP.
+ *
+ * \param gh Struktura połączenia
+ *
+ * \return Sposób rozwiązywania nazw
+ */
+gg_resolver_t gg_http_get_resolver(struct gg_http *gh)
+{
+	if (gh == NULL) {
+		errno = EINVAL;
+		return GG_RESOLVER_INVALID;
+	}
+
+	return gh->resolver_type;
+}
+
+/**
+ * Ustawia własny sposób rozwiązywania nazw połączenia HTTP.
+ *
+ * \param gh Struktura sesji
+ * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniająca zasoby
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+	if (gh == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	gh->resolver_type = GG_RESOLVER_CUSTOM;
+	gh->resolver_start = resolver_start;
+	gh->resolver_cleanup = resolver_cleanup;
+
+	return 0;
+}
+
+/**
+ * Ustawia sposób rozwiązywania nazw globalnie dla biblioteki.
+ *
+ * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_global_set_resolver(gg_resolver_t type)
+{
+	switch (type) {
+		case GG_RESOLVER_DEFAULT:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = NULL;
+			gg_global_resolver_cleanup = NULL;
+			return 0;
+
+#ifndef _WIN32
+		case GG_RESOLVER_FORK:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = gg_resolver_fork_start;
+			gg_global_resolver_cleanup = gg_resolver_fork_cleanup;
+			return 0;
+#endif
+			
+#ifdef _WIN32
+		case GG_RESOLVER_WIN32:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = gg_resolve_win32thread;
+			gg_global_resolver_cleanup = gg_resolve_win32thread_cleanup;
+			return 0;
+#endif			
+		
+#ifdef GG_CONFIG_HAVE_PTHREAD
+		case GG_RESOLVER_PTHREAD:
+			gg_global_resolver_type = type;
+			gg_global_resolver_start = gg_resolver_pthread_start;
+			gg_global_resolver_cleanup = gg_resolver_pthread_cleanup;
+			return 0;
+#endif
+
+		default:
+			errno = EINVAL;
+			return -1;
+	}
+}
+
+/**
+ * Zwraca sposób rozwiązywania nazw globalnie dla biblioteki.
+ *
+ * \return Sposób rozwiązywania nazw
+ */
+gg_resolver_t gg_global_get_resolver(void)
+{
+	return gg_global_resolver_type;
+}
+
+/**
+ * Ustawia własny sposób rozwiązywania nazw globalnie dla biblioteki.
+ *
+ * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
+ * \param resolver_cleanup Funkcja zwalniająca zasoby
+ *
+ * Parametry funkcji rozpoczynającej rozwiązywanie nazwy wyglądają następująco:
+ *  - \c "int *fd" &mdash; wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku
+ *  - \c "void **priv_data" &mdash; wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy
+ *  - \c "const char *name" &mdash; nazwa serwera do rozwiązania
+ *
+ * Parametry funkcji zwalniającej zasoby wyglądają następująco:
+ *  - \c "void **priv_data" &mdash; wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu
+ *  - \c "int force" &mdash; flaga mówiąca o tym, że zasoby są zwalniane przed zakończeniem rozwiązywania nazwy, np. z powodu zamknięcia sesji.
+ *
+ * Własny kod rozwiązywania nazwy powinien stworzyć potok, parę gniazd lub
+ * inny deskryptor pozwalający na co najmniej jednostronną komunikację i 
+ * przekazać go w parametrze \c fd. Po zakończeniu rozwiązywania nazwy,
+ * powinien wysłać otrzymany adres IP w postaci sieciowej (big-endian) do
+ * deskryptora. Jeśli rozwiązywanie nazwy się nie powiedzie, należy wysłać
+ * \c INADDR_NONE. Następnie zostanie wywołana funkcja zwalniająca zasoby
+ * z parametrem \c force równym \c 0. Gdyby sesja została zakończona przed
+ * rozwiązaniem nazwy, np. za pomocą funkcji \c gg_logoff(), funkcja
+ * zwalniająca zasoby zostanie wywołana z parametrem \c force równym \c 1.
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int))
+{
+	if (resolver_start == NULL || resolver_cleanup == NULL) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	gg_global_resolver_type = GG_RESOLVER_CUSTOM;
+	gg_global_resolver_start = resolver_start;
+	gg_global_resolver_cleanup = resolver_cleanup;
+
+	return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/resolver.h	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,30 @@
+/* $Id$ */
+
+/*
+ *  (C) Copyright 2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#ifndef LIBGADU_RESOLVER_H
+#define LIBGADU_RESOLVER_H
+
+#ifndef _WIN32
+#  include <arpa/inet.h>
+#endif
+
+int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread);
+
+#endif /* LIBGADU_RESOLVER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/gg/lib/sha1.c	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,303 @@
+/* $Id: sha1.c 632 2008-07-30 18:40:06Z darkjames $ */
+
+/*
+ *  (C) Copyright 2007 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ *  Public domain SHA-1 implementation by Steve Reid <steve@edmweb.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Lesser General Public License Version
+ *  2.1 as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+/**
+ * \file sha1.c
+ *
+ * \brief Funkcje wyznaczania skrĂłtu SHA1
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+
+/** \cond ignore */
+
+#ifdef GG_CONFIG_HAVE_OPENSSL
+
+#include <openssl/sha.h>
+
+#else
+
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Modified by Wojtek Kaniewski <wojtekka@toxygen.net> for compatibility
+with libgadu and OpenSSL API.
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <string.h>
+
+typedef struct {
+    uint32_t state[5];
+    uint32_t count[2];
+    unsigned char buffer[64];
+} SHA_CTX;
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]);
+static void SHA1_Init(SHA_CTX* context);
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len);
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef GG_CONFIG_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+uint32_t a, b, c, d, e;
+typedef union {
+    unsigned char c[64];
+    uint32_t l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+static unsigned char workspace[64];
+    block = (CHAR64LONG16*)workspace;
+    memcpy(block, buffer, 64);
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+}
+
+
+/* SHA1_Init - Initialize new context */
+
+static void SHA1_Init(SHA_CTX* context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len)
+{
+unsigned int i, j;
+
+    j = (context->count[0] >> 3) & 63;
+    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+    context->count[1] += (len >> 29);
+    if ((j + len) > 63) {
+        memcpy(&context->buffer[j], data, (i = 64-j));
+        SHA1_Transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            SHA1_Transform(context->state, &data[i]);
+        }
+        j = 0;
+    }
+    else i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context)
+{
+uint32_t i, j;
+unsigned char finalcount[8];
+
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+    SHA1_Update(context, (unsigned char *)"\200", 1);
+    while ((context->count[0] & 504) != 448) {
+        SHA1_Update(context, (unsigned char *)"\0", 1);
+    }
+    SHA1_Update(context, finalcount, 8);  /* Should cause a SHA1_Transform() */
+    for (i = 0; i < 20; i++) {
+        digest[i] = (unsigned char)
+         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    i = j = 0;
+    memset(context->buffer, 0, 64);
+    memset(context->state, 0, 20);
+    memset(context->count, 0, 8);
+    memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF  /* make SHA1_Transform overwrite it's own static vars */
+    SHA1_Transform(context->state, context->buffer);
+#endif
+}
+
+#endif /* GG_CONFIG_HAVE_OPENSSL */
+
+/** \endcond */
+
+/** \cond internal */
+
+/**
+ * \internal Liczy skrót SHA1 z ziarna i hasła.
+ *
+ * \param password Hasło
+ * \param seed Ziarno
+ * \param result Bufor na wynik funkcji skrĂłtu (20 bajtĂłw)
+ */
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)
+{
+	SHA_CTX ctx;
+	
+	SHA1_Init(&ctx);
+	SHA1_Update(&ctx, (const unsigned char*) password, strlen(password));
+	seed = gg_fix32(seed);
+	SHA1_Update(&ctx, (uint8_t*) &seed, 4);
+	
+	SHA1_Final(result, &ctx);
+}
+
+/**
+ * \internal Liczy skrĂłt SHA1 z pliku.
+ *
+ * \param fd Deskryptor pliku
+ * \param result WskaĹşnik na skrĂłt
+ *
+ * \return 0 lub -1
+ */
+int gg_file_hash_sha1(int fd, uint8_t *result)
+{
+	unsigned char buf[4096];
+	SHA_CTX ctx;
+	off_t pos, len;
+	int res;
+
+	if ((pos = lseek(fd, 0, SEEK_CUR)) == (off_t) -1)
+		return -1;
+
+	if ((len = lseek(fd, 0, SEEK_END)) == (off_t) -1)
+		return -1;
+
+	if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
+		return -1;
+
+	SHA1_Init(&ctx);
+
+	if (len <= 10485760) {
+		while ((res = read(fd, buf, sizeof(buf))) > 0)
+			SHA1_Update(&ctx, buf, res);
+	} else {
+		int i;
+
+		for (i = 0; i < 9; i++) {
+			int j;
+
+			if (lseek(fd, (len - 1048576) / 9 * i, SEEK_SET) == (off_t) - 1)
+				return -1;
+
+			for (j = 0; j < 1048576 / sizeof(buf); j++) {
+				if ((res = read(fd, buf, sizeof(buf))) != sizeof(buf)) {
+					res = -1;
+					break;
+				}
+
+				SHA1_Update(&ctx, buf, res);
+			}
+
+			if (res == -1)
+				break;
+		}
+	}
+
+	if (res == -1)
+		return -1;
+
+	SHA1_Final(result, &ctx);
+
+	if (lseek(fd, pos, SEEK_SET) == (off_t) -1)
+		return -1;
+
+	return 0;
+}
+
+/** \endcond */
--- a/libpurple/protocols/irc/irc.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/irc/irc.c	Mon Mar 08 22:53:02 2010 +0000
@@ -369,7 +369,7 @@
 	const char *pass = purple_connection_get_password(gc);
 
 	if (pass && *pass) {
-		buf = irc_format(irc, "vv", "PASS", pass);
+		buf = irc_format(irc, "v:", "PASS", pass);
 		if (irc_send(irc, buf) < 0) {
 			g_free(buf);
 			return FALSE;
@@ -944,7 +944,8 @@
 	sizeof(PurplePluginProtocolInfo),    /* struct_size */
 	NULL,                    /* get_account_text_table */
 	NULL,                    /* initiate_media */
-	NULL					 /* can_do_media */
+	NULL,					 /* get_media_caps */
+	NULL					 /* get_moods */
 };
 
 static gboolean load_plugin (PurplePlugin *plugin) {
--- a/libpurple/protocols/jabber/Makefile.mingw	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/Makefile.mingw	Mon Mar 08 22:53:02 2010 +0000
@@ -29,7 +29,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 \
 			-I$(PURPLE_TOP) \
 			-I$(PURPLE_TOP)/win32 \
 			-I$(PIDGIN_TREE_TOP)
--- a/libpurple/protocols/jabber/buddy.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Mon Mar 08 22:53:02 2010 +0000
@@ -57,6 +57,36 @@
 	gchar *last_message;
 } JabberBuddyInfo;
 
+static void
+jabber_buddy_resource_free(JabberBuddyResource *jbr)
+{
+	g_return_if_fail(jbr != NULL);
+
+	jbr->jb->resources = g_list_remove(jbr->jb->resources, jbr);
+
+	while(jbr->commands) {
+		JabberAdHocCommands *cmd = jbr->commands->data;
+		g_free(cmd->jid);
+		g_free(cmd->node);
+		g_free(cmd->name);
+		g_free(cmd);
+		jbr->commands = g_list_delete_link(jbr->commands, jbr->commands);
+	}
+
+	while (jbr->caps.exts) {
+		g_free(jbr->caps.exts->data);
+		jbr->caps.exts = g_list_delete_link(jbr->caps.exts, jbr->caps.exts);
+	}
+
+	g_free(jbr->name);
+	g_free(jbr->status);
+	g_free(jbr->thread_id);
+	g_free(jbr->client.name);
+	g_free(jbr->client.version);
+	g_free(jbr->client.os);
+	g_free(jbr);
+}
+
 void jabber_buddy_free(JabberBuddy *jb)
 {
 	g_return_if_fail(jb != NULL);
@@ -91,6 +121,10 @@
 	return jb;
 }
 
+/* Returns -1 if a is a higher priority resource than b, or is
+ * "more available" than b.  0 if they're the same, and 1 if b is
+ * higher priority/more available than a.
+ */
 static gint resource_compare_cb(gconstpointer a, gconstpointer b)
 {
 	const JabberBuddyResource *jbra = a;
@@ -98,9 +132,10 @@
 	JabberBuddyState state_a, state_b;
 
 	if (jbra->priority != jbrb->priority)
-		return jbra->priority > jbrb->priority ? 1 : -1;
+		return jbra->priority > jbrb->priority ? -1 : 1;
 
 	/* Fold the states for easier comparison */
+	/* TODO: Differentiate online/chat and away/dnd? */
 	switch (jbra->state) {
 		case JABBER_BUDDY_STATE_ONLINE:
 		case JABBER_BUDDY_STATE_CHAT:
@@ -146,105 +181,74 @@
 			return 0;
 		else if ((jbra->idle && !jbrb->idle) ||
 				(jbra->idle && jbrb->idle && jbra->idle < jbrb->idle))
-			return -1;
+			return 1;
 		else
-			return 1;
+			return -1;
 	}
 
 	if (state_a == JABBER_BUDDY_STATE_ONLINE)
-		return 1;
+		return -1;
 	else if (state_a == JABBER_BUDDY_STATE_AWAY &&
 				(state_b == JABBER_BUDDY_STATE_XA ||
 				 state_b == JABBER_BUDDY_STATE_UNAVAILABLE ||
 				 state_b == JABBER_BUDDY_STATE_UNKNOWN))
-		return 1;
+		return -1;
 	else if (state_a == JABBER_BUDDY_STATE_XA &&
 				(state_b == JABBER_BUDDY_STATE_UNAVAILABLE ||
 				 state_b == JABBER_BUDDY_STATE_UNKNOWN))
-		return 1;
+		return -1;
 	else if (state_a == JABBER_BUDDY_STATE_UNAVAILABLE &&
 				state_b == JABBER_BUDDY_STATE_UNKNOWN)
-		return 1;
+		return -1;
 
-	return -1;
+	return 1;
 }
 
 JabberBuddyResource *jabber_buddy_find_resource(JabberBuddy *jb,
 		const char *resource)
 {
-	JabberBuddyResource *jbr = NULL;
 	GList *l;
 
-	if(!jb)
+	if (!jb)
 		return NULL;
 
-	for(l = jb->resources; l; l = l->next)
+	if (resource == NULL)
+		return jb->resources ? jb->resources->data : NULL;
+
+	for (l = jb->resources; l; l = l->next)
 	{
-		JabberBuddyResource *tmp = (JabberBuddyResource *) l->data;
-		if (!jbr && !resource) {
-			jbr = tmp;
-		} else if (!resource) {
-			if (resource_compare_cb(tmp, jbr) > 0)
-				jbr = tmp;
-		} else if(tmp->name) {
-			if(!strcmp(tmp->name, resource)) {
-				jbr = tmp;
-				break;
-			}
-		}
+		JabberBuddyResource *jbr = l->data;
+		if (jbr->name && g_str_equal(resource, jbr->name))
+			return jbr;
 	}
 
-	return jbr;
+	return NULL;
 }
 
 JabberBuddyResource *jabber_buddy_track_resource(JabberBuddy *jb, const char *resource,
 		int priority, JabberBuddyState state, const char *status)
 {
+	/* TODO: Optimization: Only reinsert if priority+state changed */
 	JabberBuddyResource *jbr = jabber_buddy_find_resource(jb, resource);
-	if(!jbr) {
+	if (jbr) {
+		jb->resources = g_list_remove(jb->resources, jbr);
+	} else {
 		jbr = g_new0(JabberBuddyResource, 1);
 		jbr->jb = jb;
 		jbr->name = g_strdup(resource);
 		jbr->capabilities = JABBER_CAP_NONE;
 		jbr->tz_off = PURPLE_NO_TZ_OFF;
-		jb->resources = g_list_append(jb->resources, jbr);
 	}
 	jbr->priority = priority;
 	jbr->state = state;
 	g_free(jbr->status);
 	jbr->status = g_strdup(status);
 
+	jb->resources = g_list_insert_sorted(jb->resources, jbr,
+	                                     resource_compare_cb);
 	return jbr;
 }
 
-void jabber_buddy_resource_free(JabberBuddyResource *jbr)
-{
-	g_return_if_fail(jbr != NULL);
-
-	jbr->jb->resources = g_list_remove(jbr->jb->resources, jbr);
-
-	while(jbr->commands) {
-		JabberAdHocCommands *cmd = jbr->commands->data;
-		g_free(cmd->jid);
-		g_free(cmd->node);
-		g_free(cmd->name);
-		g_free(cmd);
-		jbr->commands = g_list_delete_link(jbr->commands, jbr->commands);
-	}
-
-	if (jbr->caps.exts) {
-		g_list_foreach(jbr->caps.exts, (GFunc)g_free, NULL);
-		g_list_free(jbr->caps.exts);
-	}
-	g_free(jbr->name);
-	g_free(jbr->status);
-	g_free(jbr->thread_id);
-	g_free(jbr->client.name);
-	g_free(jbr->client.version);
-	g_free(jbr->client.os);
-	g_free(jbr);
-}
-
 void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource)
 {
 	JabberBuddyResource *jbr = jabber_buddy_find_resource(jb, resource);
@@ -797,6 +801,8 @@
 		jbr = jabber_buddy_find_resource(jbi->jb, resource_name);
 		add_jbr_info(jbi, resource_name, jbr);
 	} else {
+		/* TODO: This is in priority-ascending order (lowest prio first), because
+		 * everything is prepended.  Is that ok? */
 		for (resources = jbi->jb->resources; resources; resources = resources->next) {
 			jbr = resources->data;
 
@@ -1909,116 +1915,6 @@
 }
 
 
-const char *
-jabber_buddy_state_get_name(JabberBuddyState state)
-{
-	switch(state) {
-		case JABBER_BUDDY_STATE_UNKNOWN:
-			return _("Unknown");
-		case JABBER_BUDDY_STATE_ERROR:
-			return _("Error");
-		case JABBER_BUDDY_STATE_UNAVAILABLE:
-			return _("Offline");
-		case JABBER_BUDDY_STATE_ONLINE:
-			return _("Available");
-		case JABBER_BUDDY_STATE_CHAT:
-			return _("Chatty");
-		case JABBER_BUDDY_STATE_AWAY:
-			return _("Away");
-		case JABBER_BUDDY_STATE_XA:
-			return _("Extended Away");
-		case JABBER_BUDDY_STATE_DND:
-			return _("Do Not Disturb");
-	}
-
-	return _("Unknown");
-}
-
-JabberBuddyState jabber_buddy_status_id_get_state(const char *id) {
-	if(!id)
-		return JABBER_BUDDY_STATE_UNKNOWN;
-	if(!strcmp(id, "available"))
-		return JABBER_BUDDY_STATE_ONLINE;
-	if(!strcmp(id, "freeforchat"))
-		return JABBER_BUDDY_STATE_CHAT;
-	if(!strcmp(id, "away"))
-		return JABBER_BUDDY_STATE_AWAY;
-	if(!strcmp(id, "extended_away"))
-		return JABBER_BUDDY_STATE_XA;
-	if(!strcmp(id, "dnd"))
-		return JABBER_BUDDY_STATE_DND;
-	if(!strcmp(id, "offline"))
-		return JABBER_BUDDY_STATE_UNAVAILABLE;
-	if(!strcmp(id, "error"))
-		return JABBER_BUDDY_STATE_ERROR;
-
-	return JABBER_BUDDY_STATE_UNKNOWN;
-}
-
-const struct {
-	const char *name;
-	JabberBuddyState state;
-} show_state_pairs[] = {
-	{ "available", JABBER_BUDDY_STATE_ONLINE },
-	{ "chat",      JABBER_BUDDY_STATE_CHAT },
-	{ "away",      JABBER_BUDDY_STATE_AWAY },
-	{ "xa",        JABBER_BUDDY_STATE_XA },
-	{ "dnd",       JABBER_BUDDY_STATE_DND },
-	{ "offline",   JABBER_BUDDY_STATE_UNAVAILABLE },
-	{ "error",     JABBER_BUDDY_STATE_ERROR },
-	{ NULL,        JABBER_BUDDY_STATE_UNKNOWN }
-};
-
-JabberBuddyState jabber_buddy_show_get_state(const char *id)
-{
-	int i;
-
-	g_return_val_if_fail(id != NULL, JABBER_BUDDY_STATE_UNKNOWN);
-
-	for (i = 0; show_state_pairs[i].name; ++i)
-		if (g_str_equal(id, show_state_pairs[i].name))
-			return show_state_pairs[i].state;
-
-	purple_debug_warning("jabber", "Invalid value of presence <show/> "
-	                     "attribute: %s\n", id);
-	return JABBER_BUDDY_STATE_UNKNOWN;
-}
-
-const char *
-jabber_buddy_state_get_show(JabberBuddyState state)
-{
-	int i;
-
-	for (i = 0; show_state_pairs[i].name; ++i)
-		if (state == show_state_pairs[i].state)
-			return show_state_pairs[i].name;
-
-/*	purple_debug_warning("jabber", "Unknown buddy state: %d\n", state); */
-	return NULL;
-}
-
-const char *jabber_buddy_state_get_status_id(JabberBuddyState state) {
-	switch(state) {
-		case JABBER_BUDDY_STATE_CHAT:
-			return "freeforchat";
-		case JABBER_BUDDY_STATE_AWAY:
-			return "away";
-		case JABBER_BUDDY_STATE_XA:
-			return "extended_away";
-		case JABBER_BUDDY_STATE_DND:
-			return "dnd";
-		case JABBER_BUDDY_STATE_ONLINE:
-			return "available";
-		case JABBER_BUDDY_STATE_UNKNOWN:
-			return "available";
-		case JABBER_BUDDY_STATE_ERROR:
-			return "error";
-		case JABBER_BUDDY_STATE_UNAVAILABLE:
-			return "offline";
-	}
-	return NULL;
-}
-
 static void user_search_result_add_buddy_cb(PurpleConnection *gc, GList *row, void *user_data)
 {
 	/* XXX find out the jid */
--- a/libpurple/protocols/jabber/buddy.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/buddy.h	Mon Mar 08 22:53:02 2010 +0000
@@ -24,23 +24,20 @@
 #ifndef PURPLE_JABBER_BUDDY_H_
 #define PURPLE_JABBER_BUDDY_H_
 
-typedef enum {
-	JABBER_BUDDY_STATE_UNKNOWN = -2,
-	JABBER_BUDDY_STATE_ERROR = -1,
-	JABBER_BUDDY_STATE_UNAVAILABLE = 0,
-	JABBER_BUDDY_STATE_ONLINE,
-	JABBER_BUDDY_STATE_CHAT,
-	JABBER_BUDDY_STATE_AWAY,
-	JABBER_BUDDY_STATE_XA,
-	JABBER_BUDDY_STATE_DND
-} JabberBuddyState;
-
 typedef struct _JabberBuddy JabberBuddy;
 
 #include "jabber.h"
 #include "caps.h"
+#include "jutil.h"
 
 struct _JabberBuddy {
+	/**
+	 * A sorted list of resources in priority descending order.
+	 * This means that the first resource in the list is the
+	 * "most available" (see resource_compare_cb in buddy.c for
+	 * details).  Don't play with this yourself, let
+	 * jabber_buddy_track_resource and jabber_buddy_remove_resource do it.
+	 */
 	GList *resources;
 	char *error_msg;
 	enum {
@@ -100,7 +97,6 @@
 		const char *resource);
 JabberBuddyResource *jabber_buddy_track_resource(JabberBuddy *jb, const char *resource,
 		int priority, JabberBuddyState state, const char *status);
-void jabber_buddy_resource_free(JabberBuddyResource *jbr);
 void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource);
 void jabber_buddy_get_info(PurpleConnection *gc, const char *who);
 
@@ -110,12 +106,6 @@
 void jabber_setup_set_info(PurplePluginAction *action);
 void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
 
-const char *jabber_buddy_state_get_name(JabberBuddyState state);
-const char *jabber_buddy_state_get_status_id(JabberBuddyState state);
-const char *jabber_buddy_state_get_show(JabberBuddyState state);
-JabberBuddyState jabber_buddy_status_id_get_state(const char *id);
-JabberBuddyState jabber_buddy_show_get_state(const char *id);
-
 void jabber_user_search(JabberStream *js, const char *directory);
 void jabber_user_search_begin(PurplePluginAction *);
 
--- a/libpurple/protocols/jabber/chat.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/chat.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1048,7 +1048,8 @@
 	return TRUE;
 }
 
-gboolean jabber_chat_role_user(JabberChat *chat, const char *who, const char *role)
+gboolean jabber_chat_role_user(JabberChat *chat, const char *who,
+                               const char *role, const char *why)
 {
 	char *to;
 	JabberIq *iq;
@@ -1071,6 +1072,10 @@
 	item = xmlnode_new_child(query, "item");
 	xmlnode_set_attrib(item, "nick", jcm->handle);
 	xmlnode_set_attrib(item, "role", role);
+	if (why) {
+		xmlnode *reason = xmlnode_new_child(item, "reason");
+		xmlnode_insert_data(reason, why, -1);
+	}
 
 	jabber_iq_send(iq);
 
@@ -1138,37 +1143,6 @@
 	return TRUE;
 }
 
-gboolean jabber_chat_kick_user(JabberChat *chat, const char *who, const char *why)
-{
-	JabberIq *iq;
-	JabberChatMember *jcm = g_hash_table_lookup(chat->members, who);
-	char *to;
-	xmlnode *query, *item, *reason;
-
-	if(!jcm || !jcm->jid)
-		return FALSE;
-
-	iq = jabber_iq_new_query(chat->js, JABBER_IQ_SET,
-			"http://jabber.org/protocol/muc#admin");
-
-	to = g_strdup_printf("%s@%s", chat->room, chat->server);
-	xmlnode_set_attrib(iq->node, "to", to);
-	g_free(to);
-
-	query = xmlnode_get_child(iq->node, "query");
-	item = xmlnode_new_child(query, "item");
-	xmlnode_set_attrib(item, "jid", jcm->jid);
-	xmlnode_set_attrib(item, "role", "none");
-	if(why) {
-		reason = xmlnode_new_child(item, "reason");
-		xmlnode_insert_data(reason, why, -1);
-	}
-
-	jabber_iq_send(iq);
-
-	return TRUE;
-}
-
 static void jabber_chat_disco_traffic_cb(JabberStream *js, const char *from,
                                          JabberIqType type, const char *id,
                                          xmlnode *packet, gpointer data)
--- a/libpurple/protocols/jabber/chat.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/chat.h	Mon Mar 08 22:53:02 2010 +0000
@@ -100,7 +100,7 @@
 		const char *affiliation);
 gboolean jabber_chat_affiliation_list(JabberChat *chat, const char *affiliation);
 gboolean jabber_chat_role_user(JabberChat *chat, const char *who,
-		const char *role);
+		const char *role, const char *why);
 gboolean jabber_chat_role_list(JabberChat *chat, const char *role);
 gboolean jabber_chat_kick_user(JabberChat *chat, const char *who,
 		const char *why);
--- a/libpurple/protocols/jabber/disco.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/disco.c	Mon Mar 08 22:53:02 2010 +0000
@@ -517,8 +517,12 @@
 		const char *category, *type, *name;
 		category = xmlnode_get_attrib(child, "category");
 		type = xmlnode_get_attrib(child, "type");
-		if(category && type && !strcmp(category, "pubsub") && !strcmp(type,"pep"))
+		if(category && type && !strcmp(category, "pubsub") && !strcmp(type,"pep")) {
+			PurpleConnection *gc = js->gc;
 			js->pep = TRUE;
+			gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS |
+				PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES;
+		}
 		if (!category || strcmp(category, "server"))
 			continue;
 		if (!type || strcmp(type, "im"))
--- a/libpurple/protocols/jabber/google.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/google.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1407,7 +1407,7 @@
 	JabberStream *js;
 	JabberChat *chat;
 	gchar *room;
-	guint32 tmp, a, b;
+	gchar *uuid = purple_uuid_random();
 
 	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
 
@@ -1416,26 +1416,14 @@
 	g_return_if_fail(gc != NULL);
 	js = purple_connection_get_protocol_data(gc);
 
-	/* Generate a version 4 UUID */
-	tmp = g_random_int();
-	a = 0x4000 | (tmp & 0xFFF); /* 0x4000 to 0x4FFF */
-	tmp >>= 12;
-	b = ((1 << 3) << 12) | (tmp & 0x3FFF); /* 0x8000 to 0xBFFF */
-
-	tmp = g_random_int();
-	room = g_strdup_printf("private-chat-%08x-%04x-%04x-%04x-%04x%08x",
-			g_random_int(),
-			tmp & 0xFFFF,
-			a,
-			b,
-			(tmp >> 16) & 0xFFFF, g_random_int());
-
+	room = g_strdup_printf("private-chat-%s", uuid);	
 	chat = jabber_join_chat(js, room, GOOGLE_GROUPCHAT_SERVER, js->user->node,
 	                        NULL, NULL);
 	if (chat) {
 		chat->muc = TRUE;
-		jabber_chat_invite(gc, chat->id, "", buddy->name);
+		jabber_chat_invite(gc, chat->id, "", purple_buddy_get_name(buddy));
 	}
 
 	g_free(room);
+	g_free(uuid);
 }
--- a/libpurple/protocols/jabber/iq.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/iq.c	Mon Mar 08 22:53:02 2010 +0000
@@ -43,8 +43,8 @@
 #include "utsname.h"
 #endif
 
-GHashTable *iq_handlers = NULL;
-GHashTable *signal_iq_handlers = NULL;
+static GHashTable *iq_handlers = NULL;
+static GHashTable *signal_iq_handlers = NULL;
 
 JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type)
 {
--- a/libpurple/protocols/jabber/jabber.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Mar 08 22:53:02 2010 +0000
@@ -62,6 +62,7 @@
 #include "roster.h"
 #include "ping.h"
 #include "si.h"
+#include "usermood.h"
 #include "xdata.h"
 #include "pep.h"
 #include "adhoccommands.h"
@@ -69,6 +70,8 @@
 #include "jingle/jingle.h"
 #include "jingle/rtp.h"
 
+#define PING_TIMEOUT 60
+
 GList *jabber_features = NULL;
 GList *jabber_identities = NULL;
 static GSList *jabber_cmds = NULL;
@@ -520,9 +523,12 @@
 
 void jabber_keepalive(PurpleConnection *gc)
 {
-	JabberStream *js = gc->proto_data;
-
-	if (js->keepalive_timeout == 0) {
+	JabberStream *js = purple_connection_get_protocol_data(gc);
+	time_t now = time(NULL);
+
+	if (js->keepalive_timeout == 0 && (now - js->last_ping) >= PING_TIMEOUT) {
+		js->last_ping = now;
+
 		jabber_keepalive_ping(js);
 		js->keepalive_timeout = purple_timeout_add_seconds(120,
 				(GSourceFunc)(jabber_keepalive_timeout), gc);
@@ -2062,18 +2068,31 @@
 		if (full) {
 			PurpleStatus *status;
 
-			status = purple_presence_get_active_status(presence);
-			mood = purple_status_get_attr_string(status, "mood");
-			if(mood != NULL) {
+			status = purple_presence_get_status(presence, "mood");
+			mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+			if(mood && *mood) {
 				const char *moodtext;
-				moodtext = purple_status_get_attr_string(status, "moodtext");
-				if(moodtext != NULL) {
-					char *moodplustext = g_strdup_printf("%s (%s)", mood, moodtext);
+				/* find the mood */
+				PurpleMood *moods = jabber_get_moods(account);
+				const char *description = NULL;
+
+				for (; moods->mood ; moods++) {
+					if (purple_strequal(moods->mood, mood)) {
+						description = moods->description;
+						break;
+					}
+				}
+
+				moodtext = purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT);
+				if(moodtext && *moodtext) {
+					char *moodplustext =
+						g_strdup_printf("%s (%s)", description ? _(description) : mood, moodtext);
 
 					purple_notify_user_info_add_pair(user_info, _("Mood"), moodplustext);
 					g_free(moodplustext);
 				} else
-					purple_notify_user_info_add_pair(user_info, _("Mood"), mood);
+					purple_notify_user_info_add_pair(user_info, _("Mood"), 
+					    description ? _(description) : mood);
 			}
 			if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) {
 				PurpleStatus *tune = purple_presence_get_status(presence, "tune");
@@ -2134,7 +2153,15 @@
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), buzz_enabled,
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
+
+	
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD,
+	    "mood", NULL, TRUE, TRUE, TRUE,
+			PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	types = g_list_prepend(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 1);
@@ -2150,7 +2177,7 @@
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), buzz_enabled,
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 0);
@@ -2166,7 +2193,7 @@
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), buzz_enabled,
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 0);
@@ -2182,7 +2209,7 @@
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			"buzz", _("Allow Buzz"), buzz_enabled,
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	priority_value = purple_value_new(PURPLE_TYPE_INT);
 	purple_value_set_int(priority_value, 0);
@@ -2195,11 +2222,11 @@
 			"moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING),
 			"nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	/*
 	if(js->protocol_version == JABBER_PROTO_0_9)
-		m = g_list_append(m, _("Invisible"));
+		"Invisible"
 	*/
 
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE,
@@ -2207,7 +2234,7 @@
 			NULL, TRUE, TRUE, FALSE,
 			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
-	types = g_list_append(types, type);
+	types = g_list_prepend(types, type);
 
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE,
 			"tune", NULL, FALSE, TRUE, TRUE,
@@ -2221,9 +2248,9 @@
 			PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT),
 			PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING),
 			NULL);
-	types = g_list_append(types, type);
-
-	return types;
+	types = g_list_prepend(types, type);
+
+	return g_list_reverse(types);
 }
 
 static void
@@ -2721,9 +2748,10 @@
 		char **nicks = g_strsplit(args[1], " ", -1);
 
 		for (i = 0; nicks[i]; i++)
-			if (!jabber_chat_role_user(chat, nicks[i], args[0])) {
+			if (!jabber_chat_role_user(chat, nicks[i], args[0], NULL)) {
 				*error = g_strdup_printf(_("Unable to set role \"%s\" for user: %s"),
 										 args[0], nicks[i]);
+				g_strfreev(nicks);
 				return PURPLE_CMD_RET_FAILED;
 			}
 
@@ -2778,7 +2806,7 @@
 	if(!chat || !args || !args[0])
 		return PURPLE_CMD_RET_FAILED;
 
-	if(!jabber_chat_kick_user(chat, args[0], args[1])) {
+	if(!jabber_chat_role_user(chat, args[0], "none", args[1])) {
 		*error = g_strdup_printf(_("Unable to kick user %s"), args[0]);
 		return PURPLE_CMD_RET_FAILED;
 	}
@@ -2880,6 +2908,11 @@
 {
 	JabberStream *js = conv->account->gc->proto_data;
 	const gchar *who;
+	gchar *description;
+	PurpleBuddy *buddy;
+	const char *alias;
+	PurpleAttentionType *attn = 
+		purple_get_attention_type_from_code(conv->account, 0);
 
 	if (!args || !args[0]) {
 		/* use the buddy from conversation, if it's a one-to-one conversation */
@@ -2892,27 +2925,18 @@
 		who = args[0];
 	}
 
-	if (_jabber_send_buzz(js, who, error)) {
-		const gchar *alias;
-		gchar *str;
-		PurpleBuddy *buddy =
-			purple_find_buddy(purple_connection_get_account(conv->account->gc),
-				who);
-
-		if (buddy != NULL)
-			alias = purple_buddy_get_contact_alias(buddy);
-		else
-			alias = who;
-
-		str = g_strdup_printf(_("Buzzing %s..."), alias);
-		purple_conversation_write(conv, NULL, str,
-			PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, time(NULL));
-		g_free(str);
-
-		return PURPLE_CMD_RET_OK;
-	} else {
-		return PURPLE_CMD_RET_FAILED;
-	}
+	buddy = purple_find_buddy(conv->account, who);
+	if (buddy != NULL)
+		alias = purple_buddy_get_contact_alias(buddy);
+	else
+		alias = who;
+	
+	description = 
+		g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias);
+	purple_conversation_write(conv, NULL, description, 
+		PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM, time(NULL));
+	g_free(description);
+	return _jabber_send_buzz(js, who, error)  ? PURPLE_CMD_RET_OK : PURPLE_CMD_RET_FAILED;
 }
 
 GList *jabber_attention_types(PurpleAccount *account)
@@ -2933,7 +2957,16 @@
 	gchar *error = NULL;
 
 	if (!_jabber_send_buzz(js, username, &error)) {
+		PurpleAccount *account = purple_connection_get_account(gc);
+		PurpleConversation *conv =
+			purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, username, account);
 		purple_debug_error("jabber", "jabber_send_attention: jabber_cmd_buzz failed with error: %s\n", error ? error : "(NULL)");
+
+		if (conv) {
+			purple_conversation_write(conv, username, error, PURPLE_MESSAGE_ERROR,
+			    time(NULL));
+		}
+
 		g_free(error);
 		return FALSE;
 	}
@@ -3282,6 +3315,29 @@
 	}
 }
 
+static void
+jabber_cmd_mood(PurpleConversation *conv,
+		const char *cmd, char **args, char **error, void *data)
+{
+	JabberStream *js = conv->account->gc->proto_data;
+
+	if (js->pep) {
+		/* if no argument was given, unset mood */
+		if (!args | !args[0]) {
+			jabber_mood_set(js, NULL, NULL);
+		} else if (!args[1]) {
+			jabber_mood_set(js, args[0], NULL);
+		} else {
+			jabber_mood_set(js, args[0], args[1]);
+		}
+	} else {
+		/* account does not support PEP, can't set a mood */
+		purple_conversation_write(conv, NULL,
+		    _("Account does not support PEP, can't set mood"),
+		    PURPLE_MESSAGE_ERROR, time(NULL));
+	}
+}
+
 void jabber_register_commands(void)
 {
 	PurpleCmdId id;
@@ -3395,6 +3451,13 @@
 					  "prpl-jabber", jabber_cmd_buzz,
 					  _("buzz: Buzz a user to get their attention"), NULL);
 	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
+
+	id = purple_cmd_register("mood", "ws", PURPLE_CMD_P_PRPL,
+	    			  PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM |
+	    			  PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS,
+	    			  "prpl-jabber", jabber_cmd_mood,
+	    			  _("mood: Set current user mood"), NULL);
+	jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id));
 }
 
 void jabber_unregister_commands(void)
@@ -3519,6 +3582,9 @@
 	jabber_add_feature(JINGLE_APP_RTP_SUPPORT_VIDEO, jabber_video_enabled);
 	jabber_add_feature(JINGLE_TRANSPORT_RAWUDP, 0);
 	jabber_add_feature(JINGLE_TRANSPORT_ICEUDP, 0);
+
+	g_signal_connect(G_OBJECT(purple_media_manager_get()), "ui-caps-changed",
+			G_CALLBACK(jabber_caps_broadcast_change), NULL);
 #endif
 
 	jabber_auth_init();
--- a/libpurple/protocols/jabber/jabber.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Mon Mar 08 22:53:02 2010 +0000
@@ -166,6 +166,11 @@
 	time_t idle;
 	time_t old_idle;
 
+	/** When we last pinged the server, so we don't ping more
+	 *  often than once every minute.
+	 */
+	time_t last_ping;
+
 	JabberID *user;
 	JabberBuddy *user_jb;
 
--- a/libpurple/protocols/jabber/jingle/rtp.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/jingle/rtp.c	Mon Mar 08 22:53:02 2010 +0000
@@ -468,13 +468,22 @@
 
 	g_return_if_fail(JINGLE_IS_SESSION(session));
 
-	if (type == PURPLE_MEDIA_INFO_HANGUP) {
+	if (type == PURPLE_MEDIA_INFO_HANGUP ||
+			type == PURPLE_MEDIA_INFO_REJECT) {
 		jabber_iq_send(jingle_session_terminate_packet(
-				session, "success"));
-		g_object_unref(session);
-	} else if (type == PURPLE_MEDIA_INFO_REJECT) {
-		jabber_iq_send(jingle_session_terminate_packet(
-				session, "decline"));
+				session, type == PURPLE_MEDIA_INFO_HANGUP ?
+				"success" : "decline"));
+
+		g_signal_handlers_disconnect_by_func(G_OBJECT(media),
+				G_CALLBACK(jingle_rtp_state_changed_cb),
+				session);
+		g_signal_handlers_disconnect_by_func(G_OBJECT(media),
+				G_CALLBACK(jingle_rtp_stream_info_cb),
+				session);
+		g_signal_handlers_disconnect_by_func(G_OBJECT(media),
+				G_CALLBACK(jingle_rtp_new_candidate_cb),
+				session);
+
 		g_object_unref(session);
 	} else if (type == PURPLE_MEDIA_INFO_ACCEPT &&
 			jingle_session_is_initiator(session) == FALSE) {
--- a/libpurple/protocols/jabber/jingle/session.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/jingle/session.c	Mon Mar 08 22:53:02 2010 +0000
@@ -371,7 +371,6 @@
 	return session;
 }
 
-#if GLIB_CHECK_VERSION(2,4,0)
 static gboolean find_by_jid_ghr(gpointer key,
 		gpointer value, gpointer user_data)
 {
@@ -391,58 +390,12 @@
 	return FALSE;
 }
 
-#else /* GLIB_CHECK_VERSION 2.4.0 */
-
-/* Ugly code; g_hash_table_find version above is much nicer */
-struct session_find_jid
-{
-	const gchar *jid;
-	JingleSession *ret;
-	gboolean use_bare;
-};
-
-static void find_by_jid_ghr(gpointer key, gpointer value, gpointer user_data)
-{
-	JingleSession *session = (JingleSession *)value;
-	struct session_find_jid *data = user_data;
-	gchar *remote_jid;
-	gchar *cmp_jid;
-
-	if (data->ret != NULL)
-		return;
-
-	remote_jid = jingle_session_get_remote_jid(session);
-	cmp_jid = data->use_bare ? jabber_get_bare_jid(remote_jid)
-				: g_strdup(remote_jid);
-	g_free(remote_jid);
-
-	if (g_str_equal(data->jid, cmp_jid))
-		data->ret = session;
-
-	g_free(cmp_jid);
-}
-#endif /* GLIB_CHECK_VERSION 2.4.0 */
-
 JingleSession *
 jingle_session_find_by_jid(JabberStream *js, const gchar *jid)
 {
-#if GLIB_CHECK_VERSION(2,4,0)
 	return js->sessions != NULL ?
 			g_hash_table_find(js->sessions,
 			find_by_jid_ghr, (gpointer)jid) : NULL; 
-#else
-	struct session_find_jid data;
-
-	if (js->sessions == NULL)
-		return NULL;
-
-	data.jid = jid;
-	data.ret = NULL;
-	data.use_bare = strchr(jid, '/') == NULL;
-
-	g_hash_table_foreach(js->sessions, find_by_jid_ghr, &data);
-	return data.ret;
-#endif
 }
 
 static xmlnode *
--- a/libpurple/protocols/jabber/jutil.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/jutil.c	Mon Mar 08 22:53:02 2010 +0000
@@ -302,7 +302,7 @@
 	const guchar *c;
 
 	c = (const guchar *)in;
-	while (*c) {
+	for ( ; *c; ++c) {
 		if (*c > 0x7f ||
 				(*c < 0x20 && *c != '\t' && *c != '\n' && *c != '\r'))
 			return NULL;
@@ -651,6 +651,83 @@
 	return equal;
 }
 
+static const struct {
+		const char *status_id; /* link to core */
+		const char *show; /* The show child's cdata in a presence stanza */
+		const char *readable; /* readable representation */
+		JabberBuddyState state;
+} jabber_statuses[] = {
+	{ "offline",       NULL,   N_("Offline"),        JABBER_BUDDY_STATE_UNAVAILABLE },
+	{ "available",     NULL,   N_("Available"),      JABBER_BUDDY_STATE_ONLINE},
+	{ "freeforchat",   "chat", N_("Chatty"),         JABBER_BUDDY_STATE_CHAT },
+	{ "away",          "away", N_("Away"),           JABBER_BUDDY_STATE_AWAY },
+	{ "extended_away", "xa",   N_("Extended Away"),  JABBER_BUDDY_STATE_XA },
+	{ "dnd",           "dnd",  N_("Do Not Disturb"), JABBER_BUDDY_STATE_DND },
+	{ "error",         NULL,   N_("Error"),          JABBER_BUDDY_STATE_ERROR }
+};
+
+const char *
+jabber_buddy_state_get_name(const JabberBuddyState state)
+{
+	int i;
+	for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i)
+		if (jabber_statuses[i].state == state)
+			return _(jabber_statuses[i].readable);
+
+	return _("Unknown");
+}
+
+JabberBuddyState
+jabber_buddy_status_id_get_state(const char *id)
+{
+	int i;
+	if (!id)
+		return JABBER_BUDDY_STATE_UNKNOWN;
+
+	for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i)
+		if (g_str_equal(id, jabber_statuses[i].status_id))
+			return jabber_statuses[i].state;
+
+	return JABBER_BUDDY_STATE_UNKNOWN;
+}
+
+JabberBuddyState jabber_buddy_show_get_state(const char *id)
+{
+	int i;
+
+	g_return_val_if_fail(id != NULL, JABBER_BUDDY_STATE_UNKNOWN);
+
+	for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i)
+		if (jabber_statuses[i].show && g_str_equal(id, jabber_statuses[i].show))
+			return jabber_statuses[i].state;
+
+	purple_debug_warning("jabber", "Invalid value of presence <show/> "
+	                     "attribute: %s\n", id);
+	return JABBER_BUDDY_STATE_UNKNOWN;
+}
+
+const char *
+jabber_buddy_state_get_show(JabberBuddyState state)
+{
+	int i;
+	for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i)
+		if (state == jabber_statuses[i].state)
+			return jabber_statuses[i].show;
+
+	return NULL;
+}
+
+const char *
+jabber_buddy_state_get_status_id(JabberBuddyState state)
+{
+	int i;
+	for (i = 0; i < G_N_ELEMENTS(jabber_statuses); ++i)
+		if (state == jabber_statuses[i].state)
+			return jabber_statuses[i].status_id;
+
+	return NULL;
+}
+
 /* The same as purple_util_get_image_checksum, but guaranteed to remain SHA1 */
 char *
 jabber_calculate_data_sha1sum(gconstpointer data, size_t len)
--- a/libpurple/protocols/jabber/jutil.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/jutil.h	Mon Mar 08 22:53:02 2010 +0000
@@ -30,6 +30,17 @@
 	char *resource;
 } JabberID;
 
+typedef enum {
+	JABBER_BUDDY_STATE_UNKNOWN = -2,
+	JABBER_BUDDY_STATE_ERROR = -1,
+	JABBER_BUDDY_STATE_UNAVAILABLE = 0,
+	JABBER_BUDDY_STATE_ONLINE,
+	JABBER_BUDDY_STATE_CHAT,
+	JABBER_BUDDY_STATE_AWAY,
+	JABBER_BUDDY_STATE_XA,
+	JABBER_BUDDY_STATE_DND
+} JabberBuddyState;
+
 #include "jabber.h"
 
 JabberID* jabber_id_new(const char *str);
@@ -63,5 +74,16 @@
  */
 char *jabber_saslprep(const char *);
 
+/* state -> readable name */
+const char *jabber_buddy_state_get_name(JabberBuddyState state);
+/* state -> core id */
+const char *jabber_buddy_state_get_status_id(JabberBuddyState state);
+/* state -> show attr (for presence stanza) */
+const char *jabber_buddy_state_get_show(JabberBuddyState state);
+/* core id -> state */
+JabberBuddyState jabber_buddy_status_id_get_state(const char *id);
+/* show attr (presence stanza) -> state */
+JabberBuddyState jabber_buddy_show_get_state(const char *id);
+
 char *jabber_calculate_data_sha1sum(gconstpointer data, size_t len);
 #endif /* PURPLE_JABBER_JUTIL_H_ */
--- a/libpurple/protocols/jabber/libxmpp.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Mon Mar 08 22:53:02 2010 +0000
@@ -43,6 +43,7 @@
 #include "presence.h"
 #include "google.h"
 #include "pep.h"
+#include "usermood.h"
 #include "usertune.h"
 #include "caps.h"
 #include "data.h"
@@ -126,6 +127,7 @@
 	NULL, /* get_account_text_table */
 	jabber_initiate_media,          /* initiate_media */
 	jabber_get_media_caps,                  /* get_media_caps */
+	jabber_get_moods  							/* get_moods */
 };
 
 static gboolean load_plugin(PurplePlugin *plugin)
--- a/libpurple/protocols/jabber/message.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/message.c	Mon Mar 08 22:53:02 2010 +0000
@@ -74,24 +74,6 @@
 	jb = jabber_buddy_find(jm->js, jm->from, TRUE);
 	jbr = jabber_buddy_find_resource(jb, jid->resource);
 
-	if (jid->resource) {
-		/*
-		 * We received a message from a specific resource, so we probably want a
-		 * reply to go to this specific resource (i.e. bind/lock the
-		 * conversation to this resource).
-		 *
-		 * This works because purple_conv_im_send gets the name from
-		 * purple_conversation_get_name()
-		 */
-		PurpleConversation *conv;
-
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, jm->from, account);
-		if (conv && !g_str_equal(jm->from, purple_conversation_get_name(conv))) {
-			purple_debug_info("jabber", "Binding conversation to %s\n", jm->from);
-			purple_conversation_set_name(conv, jm->from);
-		}
-	}
-
 	if(!jm->xhtml && !jm->body) {
 		if (jbr) {
 			if (jm->chat_state != JM_STATE_NONE)
@@ -137,6 +119,28 @@
 			serv_got_typing_stopped(gc, jm->from);
 		}
 	} else {
+		if (jid->resource) {
+			/*
+			 * We received a message from a specific resource, so
+			 * we probably want a reply to go to this specific
+			 * resource (i.e. bind/lock the conversation to this
+			 * resource).
+			 *
+			 * This works because purple_conv_im_send gets the name
+			 * from purple_conversation_get_name()
+			 */
+			PurpleConversation *conv;
+
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+			                                             jm->from, account);
+			if (conv && !g_str_equal(jm->from,
+			                         purple_conversation_get_name(conv))) {
+				purple_debug_info("jabber", "Binding conversation to %s\n",
+				                  jm->from);
+				purple_conversation_set_name(conv, jm->from);
+			}
+		}
+
 		if(jbr) {
 			if (jm->chat_state != JM_STATE_NONE)
 				jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
--- a/libpurple/protocols/jabber/parser.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/parser.c	Mon Mar 08 22:53:02 2010 +0000
@@ -31,31 +31,6 @@
 #include "util.h"
 #include "xmlnode.h"
 
-static char *purple_unescape_text(const char *in)
-{
-    GString *ret;
-    const char *c = in;
-
-    if (in == NULL)
-        return NULL;
-
-    ret = g_string_new("");
-    while (*c) {
-        int len;
-        const char *ent;
-
-        if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) {
-            g_string_append(ret, ent);
-            c += len;
-        } else {
-            g_string_append_c(ret, *c);
-            c++;
-        }
-    }
-
-    return g_string_free(ret, FALSE);
-}
-
 static void
 jabber_parser_element_start_libxml(void *user_data,
 				   const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace,
--- a/libpurple/protocols/jabber/pep.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/pep.c	Mon Mar 08 22:53:02 2010 +0000
@@ -56,7 +56,6 @@
 
 void jabber_pep_init_actions(GList **m) {
 	/* register the PEP-specific actions */
-	jabber_mood_init_action(m);
 	jabber_nick_init_action(m);
 }
 
@@ -89,10 +88,11 @@
 	JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
 	xmlnode *pubsub, *items;
 
-	xmlnode_set_attrib(iq->node,"to",to);
+	if (to)
+		xmlnode_set_attrib(iq->node, "to", to);
+
 	pubsub = xmlnode_new_child(iq->node,"pubsub");
-
-	xmlnode_set_namespace(pubsub,"http://jabber.org/protocol/pubsub");
+	xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub");
 
 	items = xmlnode_new_child(pubsub, "items");
 	xmlnode_set_attrib(items,"node",node);
--- a/libpurple/protocols/jabber/presence.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/presence.c	Mon Mar 08 22:53:02 2010 +0000
@@ -40,6 +40,7 @@
 #include "jutil.h"
 #include "adhoccommands.h"
 
+#include "usermood.h"
 #include "usertune.h"
 
 
@@ -131,6 +132,17 @@
 
 	gc = purple_account_get_connection(account);
 	js = purple_connection_get_protocol_data(gc);
+
+	/* it's a mood update */
+	if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) {
+		const char *mood =
+			purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+		const char *mood_text =
+			purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT);
+		jabber_mood_set(js, mood, mood_text);
+		return;
+	}
+
 	jabber_presence_send(js, FALSE);
 }
 
@@ -461,6 +473,10 @@
 	jbr->caps.info = info;
 	jbr->caps.exts = exts;
 
+	purple_prpl_got_media_caps(
+			purple_connection_get_account(userdata->js->gc),
+			userdata->from);
+
 	if (info == NULL)
 		goto out;
 
--- a/libpurple/protocols/jabber/si.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/si.c	Mon Mar 08 22:53:02 2010 +0000
@@ -852,8 +852,11 @@
 	/* If we successfully started listening locally */
 	if (sock >= 0) {
 		gchar *jid;
-		const char *local_ip, *public_ip;
-
+		GList *local_ips =
+			purple_network_get_all_local_system_ips();
+		const char *public_ip;
+		gboolean has_public_ip = FALSE;
+		
 		jsx->local_streamhost_fd = sock;
 
 		jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
@@ -861,19 +864,24 @@
 		xfer->local_port = purple_network_get_port_from_fd(sock);
 		g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
 
-		/* Include the localhost's IP (for in-network transfers) */
-		local_ip = purple_network_get_local_system_ip(jsx->js->fd);
-		if (strcmp(local_ip, "0.0.0.0") != 0) {
+		public_ip = purple_network_get_my_ip(jsx->js->fd);
+
+		/* Include the localhost's IPs (for in-network transfers) */
+		while (local_ips) {
+			gchar *local_ip = local_ips->data;
 			streamhost_count++;
 			streamhost = xmlnode_new_child(query, "streamhost");
 			xmlnode_set_attrib(streamhost, "jid", jid);
 			xmlnode_set_attrib(streamhost, "host", local_ip);
 			xmlnode_set_attrib(streamhost, "port", port);
+			if (purple_strequal(local_ip, public_ip))
+				has_public_ip = TRUE;
+			g_free(local_ip);
+			local_ips = g_list_delete_link(local_ips, local_ips);
 		}
 
 		/* Include the public IP (assuming that there is a port mapped somehow) */
-		public_ip = purple_network_get_my_ip(jsx->js->fd);
-		if (strcmp(public_ip, local_ip) != 0 && strcmp(public_ip, "0.0.0.0") != 0) {
+		if (!has_public_ip && strcmp(public_ip, "0.0.0.0") != 0) {
 			streamhost_count++;
 			streamhost = xmlnode_new_child(query, "streamhost");
 			xmlnode_set_attrib(streamhost, "jid", jid);
--- a/libpurple/protocols/jabber/useravatar.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/useravatar.c	Mon Mar 08 22:53:02 2010 +0000
@@ -239,16 +239,12 @@
 
 void jabber_avatar_fetch_mine(JabberStream *js)
 {
-	char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
-
 	if (js->initial_avatar_hash) {
-		jabber_pep_request_item(js, jid, NS_AVATAR_0_12_METADATA, NULL,
+		jabber_pep_request_item(js, NULL, NS_AVATAR_0_12_METADATA, NULL,
 		                        do_got_own_avatar_0_12_cb);
-		jabber_pep_request_item(js, jid, NS_AVATAR_1_1_METADATA, NULL,
+		jabber_pep_request_item(js, NULL, NS_AVATAR_1_1_METADATA, NULL,
 		                        do_got_own_avatar_cb);
 	}
-
-	g_free(jid);
 }
 
 typedef struct _JabberBuddyAvatarUpdateURLInfo {
--- a/libpurple/protocols/jabber/usermood.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/usermood.c	Mon Mar 08 22:53:02 2010 +0000
@@ -30,100 +30,102 @@
 #include "request.h"
 #include "debug.h"
 
-static const char * const moodstrings[] = {
-	"afraid",
-	"amazed",
-	"amorous",
-	"angry",
-	"annoyed",
-	"anxious",
-	"aroused",
-	"ashamed",
-	"bored",
-	"brave",
-	"calm",
-	"cautious",
-	"cold",
-	"confident",
-	"confused",
-	"contemplative",
-	"contented",
-	"cranky",
-	"crazy",
-	"creative",
-	"curious",
-	"dejected",
-	"depressed",
-	"disappointed",
-	"disgusted",
-	"dismayed",
-	"distracted",
-	"embarrassed",
-	"envious",
-	"excited",
-	"flirtatious",
-	"frustrated",
-	"grumpy",
-	"guilty",
-	"happy",
-	"hopeful",
-	"hot",
-	"humbled",
-	"humiliated",
-	"hungry",
-	"hurt",
-	"impressed",
-	"in_awe",
-	"in_love",
-	"indignant",
-	"interested",
-	"intoxicated",
-	"invincible",
-	"jealous",
-	"lonely",
-	"lucky",
-	"mean",
-	"moody",
-	"nervous",
-	"neutral",
-	"offended",
-	"outraged",
-	"playful",
-	"proud",
-	"relaxed",
-	"relieved",
-	"remorseful",
-	"restless",
-	"sad",
-	"sarcastic",
-	"serious",
-	"shocked",
-	"shy",
-	"sick",
-	"sleepy",
-	"spontaneous",
-	"stressed",
-	"strong",
-	"surprised",
-	"thankful",
-	"thirsty",
-	"tired",
-	"weak",
-	"worried"
+static PurpleMood moods[] = {
+	{"afraid", N_("Afraid"), NULL},
+	{"amazed", N_("Amazed"), NULL},
+	{"amorous", N_("Amorous"), NULL},
+	{"angry", N_("Angry"), NULL},
+	{"annoyed", N_("Annoyed"), NULL},
+	{"anxious", N_("Anxious"), NULL},
+	{"aroused", N_("Aroused"), NULL},
+	{"ashamed", N_("Ashamed"), NULL},
+	{"bored", N_("Bored"), NULL},
+	{"brave", N_("Brave"), NULL},
+	{"calm", N_("Calm"), NULL},
+	{"cautious", N_("Cautious"), NULL},
+	{"cold", N_("Cold"), NULL},
+	{"confident", N_("Confident"), NULL},
+	{"confused", N_("Confused"), NULL},
+	{"contemplative", N_("Contemplative"), NULL},
+	{"contented", N_("Contented"), NULL},
+	{"cranky", N_("Cranky"), NULL},
+	{"crazy", N_("Crazy"), NULL},
+	{"creative", N_("Creative"), NULL},
+	{"curious", N_("Curious"), NULL},
+	{"dejected", N_("Dejected"), NULL},
+	{"depressed", N_("Depressed"), NULL},
+	{"disappointed", N_("Disappointed"), NULL},
+	{"disgusted", N_("Disgusted"), NULL},
+	{"dismayed", N_("Dismayed"), NULL},
+	{"distracted", N_("Distracted"), NULL},
+	{"embarrassed", N_("Embarrassed"), NULL},
+	{"envious", N_("Envious"), NULL},
+	{"excited", N_("Excited"), NULL},
+	{"flirtatious", N_("Flirtatious"), NULL},
+	{"frustrated", N_("Frustrated"), NULL},
+	{"grateful", N_("Grateful"), NULL},
+	{"grieving", N_("Grieving"), NULL},
+	{"grumpy", N_("Grumpy"), NULL},
+	{"guilty", N_("Guilty"), NULL},
+	{"happy", N_("Happy"), NULL},
+	{"hopeful", N_("Hopeful"), NULL},
+	{"hot", N_("Hot"), NULL},
+	{"humbled", N_("Humbled"), NULL},
+	{"humiliated", N_("Humiliated"), NULL},
+	{"hungry", N_("Hungry"), NULL},
+	{"hurt", N_("Hurt"), NULL},
+	{"impressed", N_("Impressed"), NULL},
+	{"in_awe", N_("In awe"), NULL},
+	{"in_love", N_("In love"), NULL},
+	{"indignant", N_("Indignant"), NULL},
+	{"interested", N_("Interested"), NULL},
+	{"intoxicated", N_("Intoxicated"), NULL},
+	{"invincible", N_("Invincible"), NULL},
+	{"jealous", N_("Jealous"), NULL},
+	{"lonely", N_("Lonely"), NULL},
+	{"lost", N_("Lost"), NULL},
+	{"lucky", N_("Lucky"), NULL},
+	{"mean", N_("Mean"), NULL},
+	{"moody", N_("Moody"), NULL},
+	{"nervous", N_("Nervous"), NULL},
+	{"neutral", N_("Neutral"), NULL},
+	{"offended", N_("Offended"), NULL},
+	{"outraged", N_("Outraged"), NULL},
+	{"playful", N_("Playful"), NULL},
+	{"proud", N_("Proud"), NULL},
+	{"relaxed", N_("Relaxed"), NULL},
+	{"relieved", N_("Relieved"), NULL},
+	{"remorseful", N_("Remorseful"), NULL},
+	{"restless", N_("Restless"), NULL},
+	{"sad", N_("Sad"), NULL},
+	{"sarcastic", N_("Sarcastic"), NULL},
+	{"satisfied", N_("Satisfied"), NULL},
+	{"serious", N_("Serious"), NULL},
+	{"shocked", N_("Shocked"), NULL},
+	{"shy", N_("Shy"), NULL},
+	{"sick", N_("Sick"), NULL},
+	{"sleepy", N_("Sleepy"), NULL},
+	{"spontaneous", N_("Spontaneous"), NULL},
+	{"stressed", N_("Stressed"), NULL},
+	{"strong", N_("Strong"), NULL},
+	{"surprised", N_("Surprised"), NULL},
+	{"thankful", N_("Thankful"), NULL},
+	{"thirsty", N_("Thirsty"), NULL},
+	{"tired", N_("Tired"), NULL},
+	{"undefined", N_("Undefined"), NULL},
+	{"weak", N_("Weak"), NULL},
+	{"worried", N_("Worried"), NULL},
+	/* Mark the last record. */
+	{NULL, NULL, NULL}
 };
 
-static void
-jabber_mood_cb(JabberStream *js, const char *from, xmlnode *items)
-{
-	xmlnode *item;
-	JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE);
+static void jabber_mood_cb(JabberStream *js, const char *from, xmlnode *items) {
+	/* it doesn't make sense to have more than one item here, so let's just pick the first one */
+	xmlnode *item = xmlnode_get_child(items, "item");
 	const char *newmood = NULL;
 	char *moodtext = NULL;
-	xmlnode *child, *mood;
-
-	/* it doesn't make sense to have more than one item here, so let's just pick the first one */
-	item = xmlnode_get_child(items, "item");
-
+	JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE);
+	xmlnode *moodinfo, *mood;
 	/* ignore the mood of people not on our buddy list */
 	if (!buddy || !item)
 		return;
@@ -131,39 +133,34 @@
 	mood = xmlnode_get_child_with_namespace(item, "mood", "http://jabber.org/protocol/mood");
 	if (!mood)
 		return;
-	for (child = mood->child; child; child = child->next) {
-		if (child->type != XMLNODE_TYPE_TAG)
-			continue;
-
-		if (g_str_equal("text", child->name) && moodtext == NULL)
-				moodtext = xmlnode_get_data(child);
-		else {
-			int i;
-			for (i = 0; i < G_N_ELEMENTS(moodstrings); ++i) {
-				/* verify that the mood is known (valid) */
-				if (g_str_equal(child->name, moodstrings[i])) {
-					newmood = moodstrings[i];
-					break;
+	for (moodinfo = mood->child; moodinfo; moodinfo = moodinfo->next) {
+		if (moodinfo->type == XMLNODE_TYPE_TAG) {
+			if (!strcmp(moodinfo->name, "text")) {
+				if (!moodtext) /* only pick the first one */
+					moodtext = xmlnode_get_data(moodinfo);
+			} else {
+				int i;
+				for (i = 0; moods[i].mood; ++i) {
+					/* verify that the mood is known (valid) */
+					if (!strcmp(moodinfo->name, moods[i].mood)) {
+						newmood = moods[i].mood;
+						break;
+					}
 				}
 			}
+			if (newmood != NULL && moodtext != NULL)
+			   break;
 		}
 		if (newmood != NULL && moodtext != NULL)
 		   break;
 	}
 	if (newmood != NULL) {
-		PurpleAccount *account;
-		const char *status_id;
-		JabberBuddyResource *resource = jabber_buddy_find_resource(buddy, NULL);
-		if (!resource) { /* huh? */
-			g_free(moodtext);
-			return;
-		}
-		status_id = jabber_buddy_state_get_status_id(resource->state);
-
-		account = purple_connection_get_account(js->gc);
-		purple_prpl_got_user_status(account, from, status_id, "mood",
-		                            _(newmood), "moodtext",
-		                            moodtext ? moodtext : "", NULL);
+		purple_prpl_got_user_status(js->gc->account, from, "mood",
+				PURPLE_MOOD_NAME, newmood,
+				PURPLE_MOOD_COMMENT, moodtext,
+				NULL);
+	} else {
+		purple_prpl_got_user_status_deactive(js->gc->account, from, "mood");
 	}
 	g_free(moodtext);
 }
@@ -173,77 +170,17 @@
 	jabber_pep_register_handler("http://jabber.org/protocol/mood", jabber_mood_cb);
 }
 
-static void do_mood_set_from_fields(PurpleConnection *gc, PurpleRequestFields *fields) {
-	JabberStream *js;
-	int selected_mood = purple_request_fields_get_choice(fields, "mood");
-
-	if (!PURPLE_CONNECTION_IS_VALID(gc)) {
-		purple_debug_error("jabber", "Unable to set mood; account offline.\n");
-		return;
-	}
-
-	js = gc->proto_data;
-
-	if (selected_mood < 0 || selected_mood >= G_N_ELEMENTS(moodstrings)) {
-		purple_debug_error("jabber", "Invalid mood index (%d) selected.\n", selected_mood);
-		return;
-	}
-
-	jabber_mood_set(js, moodstrings[selected_mood], purple_request_fields_get_string(fields, "text"));
-}
-
-static void do_mood_set_mood(PurplePluginAction *action) {
-	PurpleConnection *gc = (PurpleConnection *) action->context;
-
-	PurpleRequestFields *fields;
-	PurpleRequestFieldGroup *group;
-	PurpleRequestField *field;
-	int i;
-
-	fields = purple_request_fields_new();
-	group = purple_request_field_group_new(NULL);
-	purple_request_fields_add_group(fields, group);
-
-	field = purple_request_field_choice_new("mood",
-											_("Mood"), 0);
-
-	for(i = 0; i < G_N_ELEMENTS(moodstrings); ++i)
-		purple_request_field_choice_add(field, _(moodstrings[i]));
-
-	purple_request_field_set_required(field, TRUE);
-	purple_request_field_group_add_field(group, field);
-
-	field = purple_request_field_string_new("text",
-											_("Description"), NULL,
-											FALSE);
-	purple_request_field_group_add_field(group, field);
-
-	purple_request_fields(gc, _("Edit User Mood"),
-						  _("Edit User Mood"),
-						  _("Please select your mood from the list."),
-						  fields,
-						  _("Set"), G_CALLBACK(do_mood_set_from_fields),
-						  _("Cancel"), NULL,
-						  purple_connection_get_account(gc), NULL, NULL,
-						  gc);
-
-}
-
-void jabber_mood_init_action(GList **m) {
-	PurplePluginAction *act = purple_plugin_action_new(_("Set Mood..."), do_mood_set_mood);
-	*m = g_list_append(*m, act);
-}
-
 void jabber_mood_set(JabberStream *js, const char *mood, const char *text) {
 	xmlnode *publish, *moodnode;
 
-	g_return_if_fail(mood != NULL);
-
 	publish = xmlnode_new("publish");
 	xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/mood");
 	moodnode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "mood");
 	xmlnode_set_namespace(moodnode, "http://jabber.org/protocol/mood");
-	xmlnode_new_child(moodnode, mood);
+	if (mood) {
+		/* if mood is NULL, set an empty mood node, meaning: unset mood */
+	    xmlnode_new_child(moodnode, mood);
+	}
 
 	if (text && text[0] != '\0') {
 		xmlnode *textnode = xmlnode_new_child(moodnode, "text");
@@ -254,3 +191,8 @@
 	/* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free
 	   (yay for well-defined memory management rules) */
 }
+
+PurpleMood *jabber_get_moods(PurpleAccount *account)
+{
+	return moods;
+}
\ No newline at end of file
--- a/libpurple/protocols/jabber/usermood.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/usermood.h	Mon Mar 08 22:53:02 2010 +0000
@@ -30,10 +30,10 @@
 
 void jabber_mood_init(void);
 
-void jabber_mood_init_action(GList **m);
-
 void jabber_mood_set(JabberStream *js,
 		     const char *mood, /* must be one of the valid strings defined in the XEP */
 		     const char *text /* might be NULL */);
 
+PurpleMood *jabber_get_moods(PurpleAccount *account);
+
 #endif /* PURPLE_JABBER_USERMOOD_H_ */
--- a/libpurple/protocols/jabber/usernick.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/usernick.c	Mon Mar 08 22:53:02 2010 +0000
@@ -86,14 +86,12 @@
 }
 
 static void do_nick_set_nick(PurplePluginAction *action) {
-	PurpleConnection *gc = (PurpleConnection *) action->context;
-	JabberStream *js = gc->proto_data;
-	char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
+	PurpleConnection *gc = action->context;
+	JabberStream *js = purple_connection_get_protocol_data(gc);
 
 	/* since the nickname might have been changed by another resource of this account, we always have to request the old one
 		from the server to present as the default for the new one */
-	jabber_pep_request_item(js, jid, "http://jabber.org/protocol/nick", NULL, do_nick_got_own_nick_cb);
-	g_free(jid);
+	jabber_pep_request_item(js, NULL, "http://jabber.org/protocol/nick", NULL, do_nick_got_own_nick_cb);
 }
 
 void jabber_nick_init(void) {
--- a/libpurple/protocols/jabber/xdata.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/jabber/xdata.c	Mon Mar 08 22:53:02 2010 +0000
@@ -305,7 +305,7 @@
 
 				data->values = g_slist_prepend(data->values, value);
 
-				purple_request_field_list_add(field, lbl, value);
+				purple_request_field_list_add_icon(field, lbl, NULL, value);
 				if(g_list_find_custom(selected, value, (GCompareFunc)strcmp))
 					purple_request_field_list_add_selected(field, lbl);
 			}
--- a/libpurple/protocols/msn/msg.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/msn/msg.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1043,7 +1043,6 @@
 
 			else
 				purple_prpl_got_attention(account->gc, user, MSN_NUDGE);
-
 		} else {
 			purple_prpl_got_attention(account->gc, user, MSN_NUDGE);
 		}
--- a/libpurple/protocols/msn/msn.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/msn/msn.c	Mon Mar 08 22:53:02 2010 +0000
@@ -2732,7 +2732,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	msn_get_account_text_table,             /* get_account_text_table */
 	NULL,                                   /* initiate_media */
-	NULL                                    /* can_do_media */
+	NULL,                                   /* get_media_caps */
+	NULL                                    /* get_moods */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/msn/nexus.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/msn/nexus.c	Mon Mar 08 22:53:02 2010 +0000
@@ -244,15 +244,6 @@
 	gpointer data;
 };
 
-#if !GLIB_CHECK_VERSION(2, 12, 0)
-static gboolean
-nexus_remove_all_cb(gpointer key, gpointer val, gpointer data)
-{
-	return TRUE;
-}
-#endif
-
-
 static gboolean
 nexus_parse_token(MsnNexus *nexus, int id, xmlnode *node)
 {
@@ -281,12 +272,7 @@
 	if (token_str == NULL)
 		return FALSE;
 
-#if GLIB_CHECK_VERSION(2, 12, 0)
 	g_hash_table_remove_all(nexus->tokens[id].token);
-#else
-	g_hash_table_foreach_remove(nexus->tokens[id].token,
-		nexus_remove_all_cb, NULL);
-#endif
 
 	elems = g_strsplit(token_str, "&", 0);
 
--- a/libpurple/protocols/msn/notification.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/msn/notification.c	Mon Mar 08 22:53:02 2010 +0000
@@ -361,81 +361,6 @@
 	msn_cmdproc_send_trans(cmdproc, trans);
 }
 
-#if 0
-static void
-ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
-			 size_t len)
-{
-	MsnMessage *msg;
-	PurpleConnection *gc;
-	const char *passport;
-	const char *content_type;
-
-	purple_debug_info("msn", "Process UBM payload:%.*s\n", (guint)len, payload);
-	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
-
-	msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM);
-	if (purple_debug_is_verbose())
-		msn_message_show_readable(msg, "Notification", TRUE);
-
-	gc = cmdproc->session->account->gc;
-	passport = msg->remote_user;
-
-	content_type = msn_message_get_content_type(msg);
-	purple_debug_info("msn", "type:%s\n", content_type);
-	if(!strcmp(content_type,"text/plain")){
-		const char *value;
-		const char *body;
-		char *body_enc;
-		char *body_final = NULL;
-		size_t body_len;
-
-		body = msn_message_get_bin_data(msg, &body_len);
-		body_enc = g_markup_escape_text(body, body_len);
-
-		if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)	{
-			char *pre, *post;
-
-			msn_parse_format(value, &pre, &post);
-			body_final = g_strdup_printf("%s%s%s", pre ? pre : "",
-							body_enc ? body_enc : "", post ? post : "");
-			g_free(pre);
-			g_free(post);
-		}
-		g_free(body_enc);
-		serv_got_im(gc, passport, body_final, 0, time(NULL));
-		g_free(body_final);
-	}
-	if(!strcmp(content_type,"text/x-msmsgscontrol")){
-		if(msn_message_get_attr(msg, "TypingUser") != NULL){
-			serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
-						PURPLE_TYPING);
-		}
-	}
-	if(!strcmp(content_type,"text/x-msnmsgr-datacast")){
-		char *username, *str;
-		PurpleAccount *account;
-		PurpleBuddy *buddy;
-		const char *user;
-
-		account = cmdproc->session->account;
-		user = msg->remote_user;
-
-		if ((buddy = purple_find_buddy(account, user)) != NULL){
-			username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
-		}else{
-			username = g_markup_escape_text(user, -1);
-		}
-
-		str = g_strdup_printf(_("%s just sent you a Nudge!"), username);
-		g_free(username);
-		msn_session_report_user(cmdproc->session,user,str,PURPLE_MESSAGE_SYSTEM);
-		g_free(str);
-	}
-	msn_message_destroy(msg);
-}
-#endif
-
 /*Yahoo msg process*/
 static void
 ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
@@ -1624,11 +1549,7 @@
 	 *  6: Album
 	 *  7: ?
 	 */
-#if GLIB_CHECK_VERSION(2,6,0)
 	strings  = g_strv_length(cmedia_array);
-#else
-	while (cmedia_array[++strings] != NULL);
-#endif
 
 	if (strings >= 4 && !strcmp(cmedia_array[2], "1")) {
 		if (user->extinfo == NULL)
--- a/libpurple/protocols/msn/slp.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/msn/slp.c	Mon Mar 08 22:53:02 2010 +0000
@@ -308,8 +308,6 @@
 	return NULL;
 }
 
-#define MAX_FILE_NAME_LEN 0x226
-
 static void
 got_sessionreq(MsnSlpCall *slpcall, const char *branch,
 			   const char *euf_guid, const char *context)
@@ -382,7 +380,7 @@
 		/* File Transfer */
 		PurpleAccount *account;
 		PurpleXfer *xfer;
-		char *bin;
+		MsnFileContext *header;
 		gsize bin_len;
 		guint32 file_size;
 		char *file_name;
@@ -396,16 +394,18 @@
 
 		xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE,
 							 slpcall->slplink->remote_user);
-		if (xfer)
-		{
-			bin = (char *)purple_base64_decode(context, &bin_len);
-			file_size = GUINT32_FROM_LE(*(gsize *)(bin + 8));
 
-			file_name = g_convert(bin + 20, MAX_FILE_NAME_LEN, "UTF-8", "UTF-16LE",
+		header = (MsnFileContext *)purple_base64_decode(context, &bin_len);
+		if (bin_len >= sizeof(MsnFileContext) - 1 &&
+			(header->version == 2 ||
+			 (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) {
+			file_size = GUINT64_FROM_LE(header->file_size);
+
+			file_name = g_convert((const gchar *)&header->file_name,
+			                      MAX_FILE_NAME_LEN * 2,
+			                      "UTF-8", "UTF-16LE",
 			                      NULL, NULL, NULL);
 
-			g_free(bin);
-
 			purple_xfer_set_filename(xfer, file_name ? file_name : "");
 			g_free(file_name);
 			purple_xfer_set_size(xfer, file_size);
@@ -424,6 +424,7 @@
 
 			purple_xfer_request(xfer);
 		}
+		g_free(header);
 
 		accepted = TRUE;
 
--- a/libpurple/protocols/msn/slp.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/msn/slp.h	Mon Mar 08 22:53:02 2010 +0000
@@ -30,6 +30,25 @@
 #include "session.h"
 #include "slpcall.h"
 
+#define MAX_FILE_NAME_LEN 260 /* MAX_PATH in Windows */
+
+/**
+ * The context data for a file transfer request
+ */
+#pragma pack(push,1) /* Couldn't they have made it the right size? */
+typedef struct
+{
+	guint32   length;       /*< Length of header */
+	guint32   version;      /*< MSN version */
+	guint64   file_size;    /*< Size of file */
+	guint32   type;         /*< Transfer type */
+	gunichar2 file_name[MAX_FILE_NAME_LEN]; /*< Self-explanatory */
+	gchar     unknown1[30]; /*< Used somehow for background sharing */
+	guint32   unknown2;     /*< Possibly for background sharing as well */
+	gchar     preview[1];   /*< File preview data, 96x96 PNG */
+} MsnFileContext;
+#pragma pack(pop)
+
 MsnSlpCall * msn_slp_sip_recv(MsnSlpLink *slplink,
 							  const char *body);
 
--- a/libpurple/protocols/msn/slplink.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/msn/slplink.c	Mon Mar 08 22:53:02 2010 +0000
@@ -307,6 +307,8 @@
 
 	slpmsg->offset += msg->msnslp_header.length;
 
+	slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
+
 	if (slpmsg->offset < real_size)
 	{
 		if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED)
@@ -331,8 +333,6 @@
 			}
 		}
 	}
-
-	slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
 }
 
 /* We have received the message nak. */
@@ -658,74 +658,51 @@
 	}
 }
 
-typedef struct
-{
-	guint32 length;
-	guint32 unk1;
-	guint32 file_size;
-	guint32 unk2;
-	guint32 unk3;
-} MsnContextHeader;
-
-#define MAX_FILE_NAME_LEN 0x226
-
 static gchar *
 gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
 {
 	gsize size = 0;
-	MsnContextHeader header;
+	MsnFileContext header;
 	gchar *u8 = NULL;
-	guchar *base;
-	guchar *n;
 	gchar *ret;
 	gunichar2 *uni = NULL;
 	glong currentChar = 0;
-	glong uni_len = 0;
-	gsize len;
+	glong len = 0;
 
 	size = purple_xfer_get_size(xfer);
 
-	if(!file_name) {
+	if (!file_name) {
 		gchar *basename = g_path_get_basename(file_path);
 		u8 = purple_utf8_try_convert(basename);
 		g_free(basename);
 		file_name = u8;
 	}
 
-	uni = g_utf8_to_utf16(file_name, -1, NULL, &uni_len, NULL);
+	uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL);
 
-	if(u8) {
+	if (u8) {
 		g_free(u8);
 		file_name = NULL;
 		u8 = NULL;
 	}
 
-	len = sizeof(MsnContextHeader) + MAX_FILE_NAME_LEN + 4;
-
-	header.length = GUINT32_TO_LE(len);
-	header.unk1 = GUINT32_TO_LE(2);
-	header.file_size = GUINT32_TO_LE(size);
-	header.unk2 = GUINT32_TO_LE(0);
-	header.unk3 = GUINT32_TO_LE(0);
-
-	base = g_malloc(len + 1);
-	n = base;
+	header.length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1);
+	header.version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */
+	header.file_size = GUINT64_TO_LE(size);
+	header.type = GUINT32_TO_LE(1);    /* No file preview */
 
-	memcpy(n, &header, sizeof(MsnContextHeader));
-	n += sizeof(MsnContextHeader);
+	len = MIN(len, MAX_FILE_NAME_LEN);
+	for (currentChar = 0; currentChar < len; currentChar++) {
+		header.file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]);
+	}
+	memset(&header.file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2);
 
-	memset(n, 0x00, MAX_FILE_NAME_LEN);
-	for(currentChar = 0; currentChar < uni_len; currentChar++) {
-		*((gunichar2 *)n + currentChar) = GUINT16_TO_LE(uni[currentChar]);
-	}
-	n += MAX_FILE_NAME_LEN;
-
-	memset(n, 0xFF, 4);
-	n += 4;
+	memset(&header.unknown1, 0, sizeof(header.unknown1));
+	header.unknown2 = GUINT32_TO_LE(0xffffffff);
+	header.preview[0] = '\0';
 
 	g_free(uni);
-	ret = purple_base64_encode(base, len);
-	g_free(base);
+	ret = purple_base64_encode((const guchar *)&header, sizeof(MsnFileContext));
 	return ret;
 }
 
--- a/libpurple/protocols/msnp9/msn.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/msnp9/msn.c	Mon Mar 08 22:53:02 2010 +0000
@@ -775,7 +775,8 @@
 
 	gc->proto_data = session;
 	gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR |
-		PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY;
+		PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY |
+		PURPLE_CONNECTION_ALLOW_ATTENTION;
 
 	msn_session_set_login_step(session, MSN_LOGIN_STEP_START);
 
--- a/libpurple/protocols/mxit/multimx.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/mxit/multimx.c	Mon Mar 08 22:53:02 2010 +0000
@@ -304,7 +304,7 @@
 	serv_got_joined_chat(gc, multimx->chatid, multimx->roomname);
 
 	/* Send ".list" command to GroupChat server to retrieve current member-list */
-	mxit_send_message(session, multimx->roomid, ".list", FALSE);
+	mxit_send_message(session, multimx->roomid, ".list", FALSE, FALSE);
 }
 
 
@@ -579,7 +579,7 @@
 	}
 
 	/* Send packet to MXit */
-	mxit_send_message(session, multimx->roomid, message, TRUE);
+	mxit_send_message(session, multimx->roomid, message, TRUE, FALSE);
 	
 	/* Determine our nickname to display */
 	if (session->profile && (session->profile->nickname[0] != '\0'))		/* default is profile name (since that's what everybody else sees) */
--- a/libpurple/protocols/mxit/mxit.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/mxit/mxit.c	Mon Mar 08 22:53:02 2010 +0000
@@ -58,9 +58,10 @@
 {
 	PurpleAccount*		account;
 	PurpleConnection*	con;
-	gchar**				parts	= NULL;
-	gchar*				link	= NULL;
+	gchar**				parts		= NULL;
+	gchar*				link		= NULL;
 	gsize				len;
+	gboolean			is_command	= FALSE;
 
 	purple_debug_info( MXIT_PLUGIN_ID, "mxit_link_click (%s)\n", link64 );
 
@@ -92,7 +93,7 @@
 	con = purple_account_get_connection( account );
 
 	/* send click message back to MXit */
-	mxit_send_message( con->proto_data, parts[3], parts[4], FALSE );
+	mxit_send_message( con->proto_data, parts[3], parts[4], FALSE, is_command );
 
 	g_free( link );
 	link = NULL;
@@ -188,7 +189,11 @@
 
 	/* find the buddy object */
 	buddy = purple_find_buddy( session->acc, who );
-	if ( ( !buddy ) || ( !buddy->proto_data ) )
+	if ( !buddy )
+		return;
+
+	contact = purple_buddy_get_protocol_data(buddy);
+	if ( !contact )
 		return;
 
 	/* we ignore all conversations with which we have chatted with in this session */
@@ -196,7 +201,6 @@
 		return;
 
 	/* determite if this buddy is a MXit service */
-	contact = buddy->proto_data;
 	switch ( contact->type ) {
 		case MXIT_TYPE_BOT :
 		case MXIT_TYPE_CHATROOM :
@@ -205,7 +209,7 @@
 				tmp = g_strdup_printf("<font color=\"#999999\">%s</font>\n", _( "Loading menu..." ));
 				serv_got_im( session->con, who, tmp, PURPLE_MESSAGE_NOTIFY, time( NULL ) );
 				g_free(tmp);
-				mxit_send_message( session, who, " ", FALSE );
+				mxit_send_message( session, who, " ", FALSE, FALSE );
 		default :
 				break;
 	}
@@ -258,11 +262,15 @@
  */
 static const char* mxit_list_emblem( PurpleBuddy* buddy )
 {
-	struct contact*	contact = buddy->proto_data;
+	struct contact*	contact = purple_buddy_get_protocol_data(buddy);
 
 	if ( !contact )
 		return NULL;
 
+	/* subscription state is Pending, Rejected or Deleted */
+	if ( contact->subtype != MXIT_SUBTYPE_BOTH )
+		return "not-authorized";
+
 	switch ( contact-> type ) {
 		case MXIT_TYPE_JABBER :			/* external contacts via MXit */
 		case MXIT_TYPE_MSN :
@@ -295,7 +303,7 @@
  */
 char* mxit_status_text( PurpleBuddy* buddy )
 {
-	struct contact*	contact = buddy->proto_data;
+	struct contact*	contact = purple_buddy_get_protocol_data(buddy);
 
 	if ( !contact )
 		return NULL;
@@ -320,7 +328,7 @@
  */
 static void mxit_tooltip( PurpleBuddy* buddy, PurpleNotifyUserInfo* info, gboolean full )
 {
-	struct contact*	contact = buddy->proto_data;
+	struct contact*	contact = purple_buddy_get_protocol_data(buddy);
 
 	if ( !contact )
 		return;
@@ -390,7 +398,7 @@
 {
 	purple_debug_info( MXIT_PLUGIN_ID, "Sending message '%s' to buddy '%s'\n", message, who );
 
-	mxit_send_message( gc->proto_data, who, message, TRUE );
+	mxit_send_message( gc->proto_data, who, message, TRUE, FALSE );
 
 	return 1;		/* echo to conversation window */
 }
@@ -423,6 +431,7 @@
 
 	statusmsg1 = purple_markup_strip_html( purple_status_get_attr_string( status, "message" ) );
 	statusmsg2 = g_strndup( statusmsg1, CP_MAX_STATUS_MSG );
+
 	purple_debug_info( MXIT_PLUGIN_ID, "mxit_set_status: '%s'\n", statusmsg2 );
 
 	/* update presence state */
@@ -455,7 +464,7 @@
 
 	purple_debug_info( MXIT_PLUGIN_ID, "mxit_free_buddy\n" );
 
-	contact = buddy->proto_data;
+	contact = purple_buddy_get_protocol_data(buddy);
 	if ( contact ) {
 		if ( contact->statusMsg )
 			g_free( contact->statusMsg );
@@ -463,7 +472,8 @@
 			g_free( contact->avatarId );
 		g_free( contact );
 	}
-	buddy->proto_data = NULL;
+
+	purple_buddy_set_protocol_data(buddy, NULL);
 }
 
 
@@ -623,8 +633,9 @@
 	NULL,					/* attention_types */
 	sizeof( PurplePluginProtocolInfo ),		/* struct_size */
 	mxit_get_text_table,	/* get_account_text_table */
-	NULL,
-	NULL
+	NULL,					/* initiate_media */
+	NULL,					/* get_media_caps */
+	NULL					/* get_moods */
 };
 
 
--- a/libpurple/protocols/mxit/mxit.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/mxit/mxit.h	Mon Mar 08 22:53:02 2010 +0000
@@ -37,7 +37,9 @@
 #endif
 #elif defined( _WIN32 )
 /* windows architecture */
+#ifndef HOST_NAME_MAX
 #define		HOST_NAME_MAX				512
+#endif
 #include	"libc_interface.h"
 #elif defined( __linux__ )
 /* linux architecture */
--- a/libpurple/protocols/mxit/profile.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/mxit/profile.c	Mon Mar 08 22:53:02 2010 +0000
@@ -115,9 +115,9 @@
 
 	buddy = purple_find_buddy( session->acc, username );
 	if ( buddy ) {
-		purple_notify_user_info_add_pair( info, _( "Alias" ), buddy->alias );
+		purple_notify_user_info_add_pair( info, _( "Alias" ), purple_buddy_get_alias( buddy ) );
 		purple_notify_user_info_add_section_break( info );
-		contact = buddy->proto_data;
+		contact = purple_buddy_get_protocol_data(buddy);
 	}
 
 	purple_notify_user_info_add_pair( info, _( "Nick Name" ), profile->nickname );
--- a/libpurple/protocols/mxit/protocol.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/mxit/protocol.c	Mon Mar 08 22:53:02 2010 +0000
@@ -695,12 +695,12 @@
  *  @param to			The username of the recipient
  *  @param msg			The message text
  */
-void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup )
+void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup, gboolean is_command )
 {
 	char		data[CP_MAX_PACKET];
 	char*		markuped_msg;
 	int			datalen;
-	int			msgtype = CP_MSGTYPE_NORMAL;
+	int			msgtype = ( is_command ? CP_MSGTYPE_COMMAND : CP_MSGTYPE_NORMAL );
 
 	/* first we need to convert the markup from libPurple to MXit format */
 	if ( parse_markup )
@@ -1254,6 +1254,7 @@
 {
 	PurpleStatus*	status;
 	int				presence;
+	const char*		statusmsg;
 	const char*		profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_HIDENUMBER, CP_PROFILE_FULLNAME,
 									CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL,
 									CP_PROFILE_MOBILENR };
@@ -1272,13 +1273,21 @@
 	/* update presence status */
 	status = purple_account_get_active_status( session->acc );
 	presence = mxit_convert_presence( purple_status_get_id( status ) );
-	if ( presence != MXIT_PRESENCE_ONLINE ) {
+	statusmsg = purple_status_get_attr_string( status, "message" );
+
+	if ( ( presence != MXIT_PRESENCE_ONLINE ) || ( statusmsg ) ) {
 		/* when logging into MXit, your default presence is online. but with the UI, one can change
 		 * the presence to whatever. in the case where its changed to a different presence setting
 		 * we need to send an update to the server, otherwise the user's presence will be out of
 		 * sync between the UI and MXit.
 		 */
-		mxit_send_presence( session, presence, purple_status_get_attr_string( status, "message" ) );
+		char* statusmsg1 = purple_markup_strip_html( statusmsg );
+		char* statusmsg2 = g_strndup( statusmsg1, CP_MAX_STATUS_MSG );
+
+		mxit_send_presence( session, presence, statusmsg2 );
+
+		g_free( statusmsg1 );
+		g_free( statusmsg2 );
 	}
 
 	/* save extra info if this is a HTTP connection */
--- a/libpurple/protocols/mxit/protocol.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/mxit/protocol.h	Mon Mar 08 22:53:02 2010 +0000
@@ -273,7 +273,7 @@
 
 void mxit_send_presence( struct MXitSession* session, int presence, const char* statusmsg );
 void mxit_send_mood( struct MXitSession* session, int mood );
-void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup );
+void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup, gboolean is_command );
 
 void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes );
 void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] );
--- a/libpurple/protocols/mxit/roster.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/mxit/roster.c	Mon Mar 08 22:53:02 2010 +0000
@@ -318,7 +318,7 @@
 
 		/* create new buddy */
 		buddy = purple_buddy_new( session->acc, contact->username, contact->alias );
-		buddy->proto_data = contact;
+		purple_buddy_set_protocol_data(buddy, contact);
 
 		/* add new buddy to list */
 		purple_blist_add_buddy( buddy, NULL, group, NULL );
@@ -326,13 +326,15 @@
 	else {
 		/* buddy was found in the group */
 
+		gpointer data = NULL;
+
 		/* now update the buddy's alias */
 		purple_blist_alias_buddy( buddy, contact->alias );
 
 		/* replace the buddy's contact struct */
-		if ( buddy->proto_data )
-			free( buddy->proto_data );
-		buddy->proto_data = contact;
+		if ( ( data = purple_buddy_get_protocol_data( buddy ) ) )
+			free( data );
+		purple_buddy_set_protocol_data( buddy, contact );
 	}
 
 	/* load buddy's avatar id */
@@ -379,7 +381,7 @@
 		return;
 	}
 
-	contact = buddy->proto_data;
+	contact = purple_buddy_get_protocol_data( buddy );
 	if ( !contact )
 		return;
 
@@ -439,9 +441,12 @@
 	for ( i = 0; i < g_slist_length( list ); i++ ) {
 		buddy = g_slist_nth_data( list, i );
 
-		if ( !buddy->proto_data ) {
+		if ( !purple_buddy_get_protocol_data( buddy ) ) {
+			const gchar *alias = purple_buddy_get_alias( buddy );
+			const gchar *name = purple_buddy_get_name( buddy );
+
 			/* this buddy should be removed, because we did not receive him in our roster update from MXit */
-			purple_debug_info( MXIT_PLUGIN_ID, "Removed 'old' buddy from the blist '%s' (%s)\n", buddy->alias, buddy->name );
+			purple_debug_info( MXIT_PLUGIN_ID, "Removed 'old' buddy from the blist '%s' (%s)\n", alias, name );
 			purple_blist_remove_buddy( buddy );
 		}
 	}
@@ -535,7 +540,7 @@
 		return FALSE;
 	}
 
-	contact = buddy->proto_data;
+	contact = purple_buddy_get_protocol_data( buddy );
 	if ( !contact )
 		return FALSE;
 
@@ -560,10 +565,13 @@
 	GSList*				list	= NULL;
 	PurpleBuddy*		mxbuddy	= NULL;
 	unsigned int		i;
+	const gchar *		buddy_name = purple_buddy_get_name( buddy );
+	const gchar *		buddy_alias = purple_buddy_get_alias( buddy );
+	const gchar *		group_name = purple_group_get_name( group );
 
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_add_buddy '%s' (group='%s')\n", buddy->name, group->name );
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_add_buddy '%s' (group='%s')\n", buddy_name, group_name );
 
-	list = purple_find_buddies( session->acc, buddy->name );
+	list = purple_find_buddies( session->acc, buddy_name );
 	if ( g_slist_length( list ) == 1 ) {
 		purple_debug_info( MXIT_PLUGIN_ID, "mxit_add_buddy (scenario 1) (list:%i)\n", g_slist_length( list ) );
 		/*
@@ -572,7 +580,7 @@
 		 * you accept an invite.  so in that case the user is already
 		 * in our blist and ready to be chatted to.
 		 */
-		mxit_send_invite( session, buddy->name, buddy->alias, group->name );
+		mxit_send_invite( session, buddy_name, buddy_alias, group_name );
 	}
 	else {
 		purple_debug_info( MXIT_PLUGIN_ID, "mxit_add_buddy (scenario 2) (list:%i)\n", g_slist_length( list ) );
@@ -585,17 +593,17 @@
 		for ( i = 0; i < g_slist_length( list ); i++ ) {
 			mxbuddy = g_slist_nth_data( list, i );
 
-			if ( mxbuddy->proto_data != NULL ) {
+			if ( purple_buddy_get_protocol_data( mxbuddy ) != NULL ) {
 				/* this is our REAL MXit buddy! */
 
 				/* now update the buddy's alias */
-				purple_blist_alias_buddy( mxbuddy, buddy->alias );
+				purple_blist_alias_buddy( mxbuddy, buddy_alias );
 
 				/* now update the buddy's group */
 //				mxbuddy = mxit_update_buddy_group( session, mxbuddy, group );
 
 				/* send the update to the MXit server */
-				mxit_send_update_contact( session, mxbuddy->name, mxbuddy->alias, group->name );
+				mxit_send_update_contact( session, purple_buddy_get_name( mxbuddy ), purple_buddy_get_alias( mxbuddy ), group_name );
 			}
 		}
 	}
@@ -623,10 +631,11 @@
 void mxit_remove_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* group )
 {
 	struct MXitSession*	session	= (struct MXitSession*) gc->proto_data;
+	const gchar *		buddy_name = purple_buddy_get_name( buddy );
 
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_remove_buddy '%s'\n", buddy->name );
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_remove_buddy '%s'\n", buddy_name );
 
-	mxit_send_remove( session, buddy->name );
+	mxit_send_remove( session, buddy_name );
 }
 
 
@@ -659,7 +668,7 @@
 		return;
 	}
 
-	mxit_send_update_contact( session, who, alias, group->name );
+	mxit_send_update_contact( session, who, alias, purple_group_get_name( group ) );
 }
 
 
@@ -685,7 +694,7 @@
 		return;
 	}
 
-	mxit_send_update_contact( session, who, buddy->alias, new_group );
+	mxit_send_update_contact( session, who, purple_buddy_get_alias( buddy ), new_group );
 }
 
 
@@ -704,7 +713,7 @@
 	PurpleBuddy*		buddy	= NULL;
 	GList*				item	= NULL;
 
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_rename_group from '%s' to '%s\n", old_name, group->name );
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_rename_group from '%s' to '%s\n", old_name, purple_group_get_name( group ) );
 
 	//  TODO: Might be more efficient to use the "rename group" command (cmd=29).
 
@@ -712,7 +721,7 @@
 	item = moved_buddies;
 	while ( item ) {
 		buddy = item->data;
-		mxit_send_update_contact( session, buddy->name, buddy->alias, group->name );
+		mxit_send_update_contact( session, purple_buddy_get_name( buddy ), purple_buddy_get_alias( buddy ), purple_group_get_name( group ) );
 		item = g_list_next( item );
 	}
 }
--- a/libpurple/protocols/myspace/myspace.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Mon Mar 08 22:53:02 2010 +0000
@@ -3093,7 +3093,8 @@
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
 	msim_get_account_text_table,              /* get_account_text_table */
 	NULL,                   /* initiate_media */
-	NULL                    /* can_do_media */
+	NULL,                   /* get_media_caps */
+	NULL                    /* get_moods */
 };
 
 /**
--- a/libpurple/protocols/myspace/user.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/myspace/user.c	Mon Mar 08 22:53:02 2010 +0000
@@ -54,11 +54,13 @@
 
 	user = purple_buddy_get_protocol_data(buddy);
 	if (create && !user) {
+		PurpleBlistNode *node = PURPLE_BLIST_NODE(buddy);
+
 		/* No MsimUser for this buddy; make one. */
 
 		user = g_new0(MsimUser, 1);
 		user->buddy = buddy;
-		user->id = purple_blist_node_get_int(&buddy->node, "UserID");
+		user->id = purple_blist_node_get_int(node, "UserID");
 		purple_buddy_set_protocol_data(buddy, user);
 	}
 
--- a/libpurple/protocols/novell/novell.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/novell/novell.c	Mon Mar 08 22:53:02 2010 +0000
@@ -3529,7 +3529,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,						/* get_account_text_table */
 	NULL,						/* initiate_media */
-	NULL						/* can_do_media */
+	NULL,						/* get_media_caps */
+	NULL						/* get_moods */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/protocols/null/nullprpl.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1117,9 +1117,10 @@
   NULL,                                /* send_attention */
   NULL,                                /* get_attention_types */
   sizeof(PurplePluginProtocolInfo),    /* struct_size */
-  NULL,
-  NULL,                                 /* initiate_media */
-  NULL                                  /* can_do_media */	
+  NULL,				       /* get_account_text_table */
+  NULL,                                /* initiate_media */
+  NULL,                                /* get_media_caps */
+  NULL				       /* get_moods */
 };
 
 static void nullprpl_init(PurplePlugin *plugin)
--- a/libpurple/protocols/oscar/clientlogin.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/clientlogin.c	Mon Mar 08 22:53:02 2010 +0000
@@ -43,7 +43,7 @@
 #include "core.h"
 
 #define URL_CLIENT_LOGIN "https://api.screenname.aol.com/auth/clientLogin"
-#define URL_START_OSCAR_SESSION "http://api.oscar.aol.com/aim/startOSCARSession"
+#define URL_START_OSCAR_SESSION "https://api.oscar.aol.com/aim/startOSCARSession"
 
 /*
  * Using clientLogin requires a developer ID.  This key is for libpurple.
@@ -177,10 +177,23 @@
 	code = atoi(tmp);
 	if (code != 200)
 	{
+		xmlnode *status_detail_node;
+		guint status_detail = 0;
+
+		status_detail_node = xmlnode_get_child(response_node,
+		                                       "statusDetailCode");
+		if (status_detail_node) {
+			gchar *data = xmlnode_get_data(status_detail_node);
+			if (data) {
+				status_detail = atoi(data);
+				g_free(data);
+			}
+		}
+
 		purple_debug_error("oscar", "startOSCARSession response statusCode "
 				"was %s: %s\n", tmp, response);
 
-		if (code == 401 || code == 607)
+		if ((code == 401 && status_detail != 1014) || code == 607)
 			purple_connection_error_reason(gc,
 					PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 					_("You have been connecting and disconnecting too "
@@ -293,7 +306,11 @@
 static void send_start_oscar_session(OscarData *od, const char *token, const char *session_key, time_t hosttime)
 {
 	char *query_string, *signature, *url;
-	gboolean use_tls = purple_account_get_bool(purple_connection_get_account(od->gc), "use_ssl", OSCAR_DEFAULT_USE_SSL);
+	PurpleAccount *account;
+	gboolean use_tls;
+
+	account = purple_connection_get_account(od->gc);
+	use_tls = purple_account_get_bool(account, "use_ssl", OSCAR_DEFAULT_USE_SSL);
 
 	/*
 	 * Construct the GET parameters.  0x00000611 is the distid given to
@@ -317,7 +334,8 @@
 	g_free(signature);
 
 	/* Make the request */
-	od->url_data = purple_util_fetch_url(url, TRUE, NULL, FALSE,
+	od->url_data = purple_util_fetch_url_request_len_with_account(account,
+			url, TRUE, NULL, FALSE, NULL, FALSE, -1,
 			start_oscar_session_cb, od);
 	g_free(url);
 }
@@ -573,8 +591,9 @@
 	g_string_free(body, TRUE);
 
 	/* Send the POST request  */
-	od->url_data = purple_util_fetch_url_request(URL_CLIENT_LOGIN,
-			TRUE, NULL, FALSE, request->str, FALSE,
+	od->url_data = purple_util_fetch_url_request_len_with_account(
+			purple_connection_get_account(gc), URL_CLIENT_LOGIN,
+			TRUE, NULL, FALSE, request->str, FALSE, -1,
 			client_login_cb, od);
 	g_string_free(request, TRUE);
 }
--- a/libpurple/protocols/oscar/family_buddy.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/family_buddy.c	Mon Mar 08 22:53:02 2010 +0000
@@ -221,6 +221,18 @@
 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 		ret = userfunc(od, conn, frame, &userinfo);
 
+	if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING &&
+	    userinfo.capabilities & OSCAR_CAPABILITY_XTRAZ) {
+		PurpleAccount *account = purple_connection_get_account(od->gc);
+		PurpleBuddy *buddy = purple_find_buddy(account, userinfo.bn);
+
+		if (buddy) {
+			PurplePresence *presence = purple_buddy_get_presence(buddy);
+
+			if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
+				icq_im_xstatus_request(od, userinfo.bn);
+		}
+	}
 	aim_info_free(&userinfo);
 
 	return ret;
--- a/libpurple/protocols/oscar/family_icbm.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Mon Mar 08 22:53:02 2010 +0000
@@ -2624,6 +2624,15 @@
 	char *bn;
 	guchar *cookie;
 	guint8 bnlen;
+	char *xml = NULL;
+	int hdrlen;
+	int curpos;
+	int num1,num2;
+	char *desc, *title, *temp;
+	PurpleAccount *account;
+	PurpleBuddy *buddy;
+	PurplePresence *presence;
+	PurpleStatus *status;
 
 	cookie = byte_stream_getraw(bs, 8);
 	channel = byte_stream_get16(bs);
@@ -2633,14 +2642,54 @@
 
 	if (channel == 0x0002)
 	{
-		if (reason == 0x0003) /* channel-specific */
-			/* parse status note text */
-			parse_status_note_text(od, cookie, bn, bs);
-
-		byte_stream_get16(bs); /* Unknown */
-		byte_stream_get16(bs); /* Unknown */
-		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
-			ret = userfunc(od, conn, frame, channel, bn, reason, cookie);
+	 	hdrlen = byte_stream_getle16(bs);
+	 	if ( ((hdrlen == 27 ) && (bs->len > (27 + 51)))) {
+	 		byte_stream_advance(bs, 51);
+	 		num1 = byte_stream_getle16(bs); 
+	 		num2 = byte_stream_getle16(bs);
+	 		purple_debug_misc("oscar", "X-Status: Num1 %i, num2 %i\n",num1, num2);
+	 		
+	 		if(((num1 == 0x4f00)&&(num2 == 0x3b00))) {
+		 		byte_stream_advance(bs, 86);	
+		 		curpos = byte_stream_curpos(bs);
+		 		xml = byte_stream_getstr(bs, bs->len - curpos);
+		 		purple_debug_misc("oscar", "X-Status: Received XML reply\n");
+		 		if(xml) {
+ 				/* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", (const char*) xml); */
+ 					if ((desc=strstr(xml,"&lt;desc&gt;")) != NULL) {
+ 						temp=strstr(xml,"&lt;/desc&gt;");
+ 						temp[0]=0;
+ 						desc=desc+12;
+ 					}
+ 					if ((title=strstr(xml,"&lt;title&gt;")) != NULL) {
+ 						temp=strstr(xml,"&lt;/title&gt;");
+ 						temp[0]=0;
+ 						title=title+13;
+ 					} else {
+ 						title="";
+ 					}
+ 					strcpy(xml,title);
+					if (desc) {
+ 						strcat(xml, " - ");
+ 						strcat(xml, desc);
+					}
+ 					purple_debug_misc("oscar", "X-Status reply: %s\n", (const char*)xml);
+ 					account = purple_connection_get_account(od->gc);
+ 					buddy = purple_find_buddy(account, bn);
+ 					presence = purple_buddy_get_presence(buddy);
+ 					status = purple_presence_get_active_status(presence);
+ 					purple_prpl_got_user_status(account, bn,
+  					    purple_status_get_id(status), "message", xml, NULL);
+		   		} else {
+			 		purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
+				}
+	 		} else {
+		 		purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n" );
+		 /*		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
+		 		ret = userfunc(od, conn, frame, channel, sn, reason); */
+			}
+		
+	 	}
 
 	} else if (channel == 0x0004) { /* ICQ message */
 		switch (reason) {
@@ -2699,6 +2748,7 @@
 
 	g_free(cookie);
 	g_free(bn);
+	g_free(xml);
 
 	return ret;
 }
@@ -2807,6 +2857,181 @@
 }
 
 /*
+ * Subtype 0x0006 - Send eXtra Status request
+ */
+int icq_im_xstatus_request(OscarData *od, const char *sn)
+{
+	FlapConnection *conn;
+	aim_snacid_t snacid;
+	guchar cookie[8];
+	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
+	ByteStream bs, header, plugindata;
+	PurpleAccount *account;
+	const char *fmt;
+	char *statxml;
+	int xmllen;
+
+	static const guint8 pluginid[] = 
+	{
+	0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1, 
+	0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00
+	};
+	
+	static const guint8 c_plugindata[] =
+	{
+	0x1B, 0x00, 0x0A,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00,
+	0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C,
+	0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70,
+	0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
+	0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41,
+	0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00
+	};
+	
+	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+		return -EINVAL;
+
+	if (!sn)
+		return -EINVAL;
+
+	fmt =  "<N><QUERY>&lt;Q&gt;&lt;PluginID&gt;srvMng&lt;/PluginID&gt;&lt;/Q&gt;</QUERY><NOTIFY>&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;req&gt;&lt;id&gt;AwayStat&lt;/id&gt;&lt;trans&gt;2&lt;/trans&gt;&lt;senderId&gt;%s&lt;/senderId&gt;&lt;/req&gt;&lt;/srv&gt;</NOTIFY></N>\r\n";	
+
+	account = purple_connection_get_account(od->gc);
+	xmllen = strlen(fmt) - 2 + strlen(account->username);
+
+	statxml = (char*) g_malloc(xmllen);
+	snprintf(statxml, xmllen, fmt, account->username);
+
+	aim_icbm_makecookie(cookie);
+
+	byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2
+					  + 2 + 2 + 8 + 16 + 2 + 2 + 2 + 2 + 2
+					  + 2 + 2 + sizeof(c_plugindata) + xmllen
+					  + 2 + 2);
+ 
+	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+
+	byte_stream_new(&header, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata) + xmllen); /* TLV 0x0005 Stream + Size */
+	byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen));
+
+	byte_stream_put16(&header, 0x0000); /* Message Type: Request */
+	byte_stream_putraw(&header, cookie, sizeof(cookie)); /* Message ID */
+	byte_stream_putraw(&header, pluginid, sizeof(pluginid)); /* Plugin ID */
+	
+	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
+	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
+	
+	/* Add Plugin Specific Data */
+	byte_stream_putraw(&plugindata, c_plugindata, sizeof(c_plugindata)); /* Content of TLV 0x2711 */
+	byte_stream_putstr(&plugindata, statxml);
+
+	aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data);
+	
+	aim_tlvlist_write(&header, &inner_tlvlist);
+	
+	
+	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&header), header.data);
+	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);		/* Empty TLV 0x0003 */
+	
+	aim_tlvlist_write(&bs, &outer_tlvlist);
+	
+	purple_debug_misc("oscar", "X-Status Request\n");
+	flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs, TRUE);
+
+	aim_tlvlist_free(inner_tlvlist);
+	aim_tlvlist_free(outer_tlvlist);
+	byte_stream_destroy(&header);
+	byte_stream_destroy(&plugindata);
+	byte_stream_destroy(&bs);
+	g_free(statxml);
+
+	return 0;
+}
+
+int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
+{
+	FlapConnection *conn;
+	ByteStream bs;
+	aim_snacid_t snacid;
+	PurpleAccount *account;
+	PurpleStatus *status;
+   const char *fmt;
+   const char *formatted_msg;
+   char *msg;
+   char *statxml;
+	const char *title;
+	int len;
+	
+	static const guint8 plugindata[] = {
+	0x1B, 0x00,
+	0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+	0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F, 
+	0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 
+	0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 
+	0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75, 
+	0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74, 
+	0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+	0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00
+	};	
+
+	fmt = "<NR><RES>&lt;ret event='OnRemoteNotification'&gt;&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;val srv_id='cAwaySrv'&gt;&lt;Root&gt;&lt;CASXtraSetAwayMessage&gt;&lt;/CASXtraSetAwayMessage&gt;&l t;uin&gt;%s&lt;/uin&gt;&lt;index&gt;1&lt;/index&gt;&lt;title&gt;%s&lt;/title&gt;&lt;desc&gt;%s&lt;/desc&gt;&lt;/Root&gt;&lt;/val&gt;&lt;/srv&gt;&lt;srv&gt;&lt;id&gt;cRandomizerSrv&lt;/id&gt;&lt;val srv_id='cRandomizerSrv'&gt;undefined&lt;/val&gt;&lt;/srv&gt;&lt;/ret&gt;</RES></NR>\r\n";
+	
+	
+	if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
+		return -EINVAL;
+
+	if (!sn)
+		return -EINVAL;
+		
+	account = purple_connection_get_account(od->gc);
+	if(!account) return -EINVAL;
+	
+/*	if (!strcmp(account->username, sn))
+		icq_im_xstatus_request(od, sn); */
+	
+	status  = purple_presence_get_active_status(account->presence);
+	if (!status) return -EINVAL;
+   title = purple_status_get_name(status);
+   if (!title) return -EINVAL;
+	formatted_msg = purple_status_get_attr_string(status, "message");
+	if (!formatted_msg) return -EINVAL;
+   msg = purple_markup_strip_html(formatted_msg);
+	if (!msg) return -EINVAL;
+	len = strlen(fmt)-6+strlen(account->username)+strlen(title)+strlen(msg);
+	statxml = (char*) g_malloc(len);
+
+	snprintf(statxml, len, fmt, 
+		account->username, title, msg);
+
+	purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg);
+
+	byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + sizeof(plugindata) + strlen(statxml)); /* 16 extra */
+
+	snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0);
+	aim_im_puticbm(&bs, cookie, 0x0002, sn);
+	byte_stream_put16(&bs, 0x0003);
+	byte_stream_putraw(&bs, plugindata, sizeof(plugindata));
+	byte_stream_putraw(&bs, (const guint8*)statxml, strlen(statxml));
+	
+	flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, 0x0000, snacid, &bs, TRUE);
+
+	g_free(statxml);
+	g_free(msg);
+	byte_stream_destroy(&bs);
+
+	return 0;
+}
+
+/*
  * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
  *
  * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
--- a/libpurple/protocols/oscar/family_locate.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/family_locate.c	Mon Mar 08 22:53:02 2010 +0000
@@ -45,7 +45,7 @@
  * But, eh.
  */
 static const struct {
-	guint32 flag;
+	guint64 flag;
 	guint8 data[16];
 } aim_caps[] = {
 
@@ -166,6 +166,16 @@
 	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
 
+	/* New format of caps (xtraz icons) */
+	{OSCAR_CAPABILITY_NEWCAPS,
+	 {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/* Support xtraz statuses */
+	{OSCAR_CAPABILITY_XTRAZ,
+	 {0x1a, 0x09, 0x3c, 0x6c, 0xd7, 0xFD, 0x4e, 0xc5, 
+	  0x9d, 0x51, 0xa6, 0x47, 0x4e, 0x34, 0xf5, 0xa0}},
+
 	{OSCAR_CAPABILITY_SENDBUDDYLIST,
 	 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
 	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
@@ -240,6 +250,202 @@
 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
 };
 
+/* Keep this array synchronized with icq_purple_moods. */
+static const struct {
+	const char *mood;
+	guint8 data[16];
+} icq_custom_icons[] = {
+
+	{"thinking",
+	 {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60,
+	  0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}},
+
+	{"busy",
+	 {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08,
+	  0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}},
+
+	{"shopping",
+	 {0x63, 0x62, 0x73, 0x37, 0xa0, 0x3f, 0x49, 0xff,
+	  0x80, 0xe5, 0xf7, 0x09, 0xcd, 0xe0, 0xa4, 0xee}},
+
+	/* This was in the original patch, but isn't what the official client
+	 * (ICQ 6) sets when you choose its typewriter icon. */
+	{"typing",
+	 {0x63, 0x4f, 0x6b, 0xd8 ,0xad, 0xd2, 0x4a, 0xa1,
+	  0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}},
+
+	{"question",
+	 {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0,
+	  0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}},
+
+	{"angry",
+	 {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a,
+	  0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}},
+
+	{"plate",
+	 {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42,
+	  0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}},
+
+	{"cinema",
+	 {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4,
+	  0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}},
+
+	{"sick",
+	 {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60,
+	  0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}},
+
+	{"typing",
+	 {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70,
+	  0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}},
+
+	{"suit",
+	 {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27,
+	  0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}},
+
+	{"bathing",
+	 {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c,
+	  0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}},
+
+	{"tv",
+	 {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76,
+	  0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}},
+
+	{"excited",
+	 {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff,
+	  0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}},
+
+	{"sleeping",
+	 {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65,
+	  0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}},
+
+	{"hiptop",
+	 {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9,
+	  0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}},
+
+	{"in_love",
+	 {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48,
+	  0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80}},
+
+	{"sleepy",
+	 {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78,
+	  0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}},
+
+	{"meeting",
+	 {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d,
+	  0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}},
+
+	{"phone",
+	 {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66,
+	  0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}},
+
+	{"surfing",
+	 {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4,
+	  0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}},
+
+	{"mobile",
+	 {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3,
+	  0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}},
+
+	{"search",
+	 {0xd4, 0xe2, 0xb0, 0xba, 0x33, 0x4e, 0x4f, 0xa5,
+	  0x98, 0xd0, 0x11, 0x7d, 0xbf, 0x4d, 0x3c, 0xc8}},
+
+	{"party",
+	 {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1,
+	  0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}},
+
+	{"coffee",
+	 {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38,
+	  0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}},
+
+	{"console",
+	 {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0,
+	  0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}},
+
+	{"internet",
+	 {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e,
+	  0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}},
+
+	{"cigarette",
+	 {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17,
+	  0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}},
+
+	{"writing",
+	 {0x00, 0x72, 0xd9, 0x08, 0x4a, 0xd1, 0x43, 0xdd,
+	  0x91, 0x99, 0x6f, 0x02, 0x69, 0x66, 0x02, 0x6f}},
+
+	{"beer",
+	 {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86,
+	  0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}},
+
+	{"music",
+	 {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d,
+	  0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}},
+
+	{"studying",
+	 {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6,
+	  0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}},
+
+	{"working",
+	 {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b,
+	  0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}},
+
+	{"restroom",
+	 {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35,
+	  0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}},
+
+	{NULL,
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}
+};
+
+/* Keep this array synchronized with icq_custom_icons. */
+static PurpleMood icq_purple_moods[] = {
+	{"thinking", N_("Thinking"), NULL},
+	{"busy", N_("Busy"), NULL},
+	{"shopping", N_("Shopping"), NULL},
+	/* This was in the original patch, but isn't what the official client
+	 * (ICQ 6) sets when you choose its typewriter icon. */
+	{"typing", NULL, NULL},
+	{"question", N_("Questioning"), NULL},
+	{"angry", N_("Angry"), NULL},
+	{"plate", N_("Eating"), NULL},
+	{"cinema", N_("Watching a movie"), NULL},
+	{"sick", N_("Sick"), NULL},
+	{"typing", N_("Typing"), NULL},
+	{"suit", N_("At the office"), NULL},
+	{"bathing", N_("Taking a bath"), NULL},
+	{"tv", N_("Watching TV"), NULL},
+	{"excited", N_("Having fun"), NULL},
+	{"sleeping", N_("Sleeping"), NULL},
+	{"hiptop", N_("Using a PDA"), NULL},
+	{"in_love", N_("In love"), NULL},
+	/* Sleepy / Tired */
+	{"sleepy", N_("Sleepy"), NULL},
+	{"meeting", N_("Meeting friends"), NULL},
+	{"phone", N_("On the phone"), NULL},
+	{"surfing", N_("Surfing"), NULL},
+	/* "I am mobile." / "John is mobile." */
+	{"mobile", N_("Mobile"), NULL},
+	{"search", N_("Searching the web"), NULL},
+	{"party", N_("At a party"), NULL},
+	{"coffee", N_("Having Coffee"), NULL},
+	/* Playing video games */
+	{"console", N_("Gaming"), NULL},
+	{"internet", N_("Browsing the web"), NULL},
+	{"cigarette", N_("Smoking"), NULL},
+	{"writing", N_("Writing"), NULL},
+	/* Drinking [Alcohol] */
+	{"beer", N_("Drinking"), NULL},
+	{"music", N_("Listening to music"), NULL},
+	{"studying", N_("Studying"), NULL},
+	{"working", N_("Working"), NULL},
+	{"restroom", N_("In the restroom"), NULL},
+	/* Mark the last record. */
+	{NULL, NULL, NULL},
+};
+
+
 /*
  * Add the userinfo to our linked list.  If we already have userinfo
  * for this buddy, then just overwrite parts of the old data.
@@ -274,6 +480,7 @@
 		cur->sessionlen = userinfo->sessionlen;
 	if (userinfo->capabilities != 0)
 		cur->capabilities = userinfo->capabilities;
+
 	cur->present |= userinfo->present;
 
 	if (userinfo->iconcsumlen > 0) {
@@ -398,13 +605,38 @@
 					cap[8], cap[9],
 					cap[10], cap[11], cap[12], cap[13],
 					cap[14], cap[15]);
-
 		g_free(cap);
 	}
 
 	return flags;
 }
 
+static const char *
+aim_receive_custom_icon(OscarData *od, ByteStream *bs, int len)
+{
+	int offset;
+	const char *result = NULL;
+
+	for (offset = 0; byte_stream_empty(bs) && (offset < len); offset += 0x10) {
+		/* check wheather this capability is a custom user icon */
+		guint8 *cap;
+		int i;
+
+		cap = byte_stream_getraw(bs, 0x10);
+
+		for (i = 0; icq_custom_icons[i].mood; i++) {
+			if (memcmp(&icq_custom_icons[i].data, cap, 0x10) == 0) {
+				purple_debug_misc("oscar", "Custom status icon: %s\n", icq_purple_moods[i].description);		
+				result = icq_custom_icons[i].mood;
+				break; /* should only match once... */
+			}
+		}
+		g_free(cap);
+	}
+
+	return result;
+}
+
 guint32
 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len)
 {
@@ -495,6 +727,38 @@
 	g_free(info->away_encoding);
 }
 
+static const struct {
+	char *icqmood;
+	const char *mood;
+} icqmoods[] = {
+	{"icqmood0",  "shopping"},
+	{"icqmood1",  "bathing"},
+	{"icqmood2",  "sleepy"},
+	{"icqmood3",  "party"},
+	{"icqmood4",  "beer"},
+	{"icqmood5",  "thinking"},
+	{"icqmood6",  "plate"},
+	{"icqmood7",  "tv"},
+	{"icqmood8",  "meeting"},
+	{"icqmood9",  "coffee"},
+	{"icqmood10", "music"},
+	{"icqmood11", "suit"},
+	{"icqmood12", "cinema"},
+	{"icqmood13", "smile-big"},
+	{"icqmood14", "phone"},
+	{"icqmood15", "console"},
+	{"icqmood16", "studying"},
+	{"icqmood17", "sick"},
+	{"icqmood18", "sleeping"},
+	{"icqmood19", "surfing"},
+	{"icqmood20", "internet"},
+	{"icqmood21", "working"},
+	{"icqmood22", "typing"},
+	{"icqmood23", "angry"},
+	{NULL, 0}
+
+};
+
 /*
  * AIM is fairly regular about providing user info.  This is a generic
  * routine to extract it in its standard form.
@@ -535,11 +799,12 @@
 	for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
 		guint16 type, length;
 		int endpos;
+		int curpos;
 
 		type = byte_stream_get16(bs);
 		length = byte_stream_get16(bs);
-
-		endpos = byte_stream_curpos(bs) + MIN(length, byte_stream_empty(bs));
+		curpos = byte_stream_curpos(bs);
+		endpos = curpos + MIN(length, byte_stream_empty(bs));
 
 		if (type == 0x0001) {
 			/*
@@ -651,11 +916,23 @@
 			outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
 
 		} else if (type == 0x000d) {
+			PurpleAccount *account = purple_connection_get_account(od->gc);
+			const char *mood;
+
 			/*
 			 * OSCAR Capability information
 			 */
 			outinfo->capabilities |= aim_locate_getcaps(od, bs, length);
 			outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
+			byte_stream_setpos(bs, curpos);
+
+			mood = aim_receive_custom_icon(od, bs, length);
+			if (mood)
+				purple_prpl_got_user_status(account, outinfo->bn, "mood",
+						PURPLE_MOOD_NAME, mood,
+						NULL);
+			else
+				purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood");
 
 		} else if (type == 0x000e) {
 			/*
@@ -791,6 +1068,37 @@
 							outinfo->itmsurl_encoding = NULL;
 						}
 					} break;
+
+					case 0x000e: { /* ICQ mood */
+						PurpleAccount *account = purple_connection_get_account(od->gc);
+						char *icqmood;
+						gint32 i;
+						const char *mood = NULL;
+
+						icqmood = byte_stream_getstr(bs, length2);
+
+						/* icqmood = "" means X-Status
+						 * with no mood icon. */
+						if (*icqmood) {
+							for (i = 0; icqmoods[i].icqmood; i++) {
+								if (!strcmp(icqmood, icqmoods[i].icqmood)) {
+									mood = icqmoods[i].mood;
+									break; /* should only match once... */
+								}
+							}
+
+							if (!mood)
+								purple_debug_warning("oscar", "Unknown icqmood: %s\n", icqmood);
+						}
+						g_free(icqmood);
+
+						if (mood)
+							purple_prpl_got_user_status(account, outinfo->bn, "mood",
+									PURPLE_MOOD_NAME, mood,
+									NULL);
+						else
+							purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood");
+					} break;
 				}
 
 				/* Save ourselves. */
@@ -857,6 +1165,10 @@
 	return 0;
 }
 
+/* Apparently, this is never called.
+ * If you activate it, figure out a way to know what mood to pass to
+ * aim_tlvlist_add_caps() below. --rlaager */
+#if 0
 /*
  * Inverse of aim_info_extract()
  */
@@ -892,8 +1204,9 @@
 	}
 #endif
 
-	if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
-		aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities);
+	if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) {
+		aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities, NULL);
+	}
 
 	if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
 		aim_tlvlist_add_32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
@@ -904,6 +1217,7 @@
 
 	return 0;
 }
+#endif
 
 /*
  * Subtype 0x0001
@@ -1089,6 +1403,10 @@
 aim_locate_setcaps(OscarData *od, guint32 caps)
 {
 	FlapConnection *conn;
+	PurpleAccount *account = purple_connection_get_account(od->gc);
+	PurplePresence *presence = purple_account_get_presence(account);
+	PurpleStatus *status = purple_presence_get_status(presence, "mood");
+	const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
 	ByteStream bs;
 	aim_snacid_t snacid;
 	GSList *tlvlist = NULL;
@@ -1096,7 +1414,7 @@
 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE)))
 		return -EINVAL;
 
-	aim_tlvlist_add_caps(&tlvlist, 0x0005, caps);
+	aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, mood);
 
 	byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
 
@@ -1179,9 +1497,21 @@
 	/* Caps will be 5 */
 	if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) {
 		ByteStream cbs;
+		PurpleAccount *account = purple_connection_get_account(od->gc);
+		const char *mood;
+
 		byte_stream_init(&cbs, tlv->value, tlv->length);
 		userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length);
+		byte_stream_rewind(&cbs);
 		userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES;
+
+		mood = aim_receive_custom_icon(od, &cbs, tlv->length);
+		if (mood)
+			purple_prpl_got_user_status(account, userinfo->bn, "mood",
+					PURPLE_MOOD_NAME, mood,
+					NULL);
+		else
+			purple_prpl_got_user_status_deactive(account, userinfo->bn, "mood");
 	}
 	aim_tlvlist_free(tlvlist);
 
@@ -1399,3 +1729,57 @@
 
 	return 0;
 }
+
+#if 0 //rlaager
+const char* aim_get_custom_icon_mood(gint32 no)
+{
+	if (no >= G_N_ELEMENTS(aim_custom_icons) || no < 1)
+		return NULL;
+	return aim_custom_icons[no].mood.mood;
+}
+#endif
+
+const char*
+icq_get_custom_icon_description(const char *mood)
+{
+	int i;
+
+	if (!(mood && *mood))
+		return NULL;
+
+	for (i = 0; icq_custom_icons[i].mood; i++) {
+		/* We check that description is not NULL to exclude
+		 * duplicates, like the typing duplicate. */
+		if (icq_purple_moods[i].description &&
+		    !strcmp(mood, icq_custom_icons[i].mood)) {
+			return icq_purple_moods[i].description;
+		}
+	}
+
+	return NULL;
+}
+
+guint8*
+icq_get_custom_icon_data(const char *mood)
+{
+	int i;
+
+	if (!(mood && *mood))
+		return NULL;
+
+	for (i = 0; icq_custom_icons[i].mood; i++) {
+		/* We check that description is not NULL to exclude
+		 * duplicates, like the typing duplicate. */
+		if (icq_purple_moods[i].description &&
+		    !strcmp(mood, icq_custom_icons[i].mood)) {
+			return (guint8 *)icq_custom_icons[i].data;
+		}
+	}
+	return NULL;
+}
+
+PurpleMood*
+icq_get_purple_moods(PurpleAccount *account)
+{
+	return icq_purple_moods;
+}
--- a/libpurple/protocols/oscar/flap_connection.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/flap_connection.c	Mon Mar 08 22:53:02 2010 +0000
@@ -364,6 +364,12 @@
 		conn->connect_data = NULL;
 	}
 
+	if (conn->gsc != NULL && conn->gsc->connect_data != NULL)
+	{
+		purple_ssl_close(conn->gsc);
+		conn->gsc = NULL;
+	}
+
 	if (conn->new_conn_data != NULL)
 	{
 		if (conn->type == SNAC_FAMILY_CHAT)
--- a/libpurple/protocols/oscar/libaim.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/libaim.c	Mon Mar 08 22:53:02 2010 +0000
@@ -97,7 +97,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,					/* get_account_text_table */
 	NULL,					/* initiate_media */
-	NULL					/* can_do_media */
+	NULL,					/* get_media_caps */
+	NULL					/* get_moods */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/oscar/libicq.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/libicq.c	Mon Mar 08 22:53:02 2010 +0000
@@ -108,7 +108,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	icq_get_account_text_table, /* get_account_text_table */
 	NULL,					/* initiate_media */
-	NULL					/* can_do_media */
+	NULL,					/* can_do_media */
+	oscar_get_purple_moods, /* get_moods */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/oscar/oscar.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.c	Mon Mar 08 22:53:02 2010 +0000
@@ -61,6 +61,11 @@
 #define OSCAR_STATUS_ID_FREE4CHAT   "free4chat"
 #define OSCAR_STATUS_ID_CUSTOM      "custom"
 #define OSCAR_STATUS_ID_MOBILE	    "mobile"
+#define OSCAR_STATUS_ID_EVIL        "evil"
+#define OSCAR_STATUS_ID_DEPRESSION	 "depression"
+#define OSCAR_STATUS_ID_ATHOME      "athome"
+#define OSCAR_STATUS_ID_ATWORK      "atwork"
+#define OSCAR_STATUS_ID_LUNCH       "lunch"
 
 #define AIMHASHDATA "http://pidgin.im/aim_data.php3"
 
@@ -68,7 +73,7 @@
 
 static OscarCapability purple_caps = (OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON | OSCAR_CAPABILITY_DIRECTIM |
 									  OSCAR_CAPABILITY_SENDFILE | OSCAR_CAPABILITY_UNICODE | OSCAR_CAPABILITY_INTEROPERATE |
-									  OSCAR_CAPABILITY_SHORTCAPS | OSCAR_CAPABILITY_TYPING);
+									  OSCAR_CAPABILITY_SHORTCAPS | OSCAR_CAPABILITY_TYPING |  OSCAR_CAPABILITY_ICQSERVERRELAY | OSCAR_CAPABILITY_NEWCAPS | OSCAR_CAPABILITY_XTRAZ);
 
 static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
 static guint8 features_icq[] = {0x01, 0x06};
@@ -672,7 +677,7 @@
 {
 	GString *str;
 	const gchar *tmp;
-	guint bit = 1;
+	guint64 bit = 1;
 
 	str = g_string_new("");
 
@@ -703,6 +708,10 @@
 			case OSCAR_CAPABILITY_GAMES2:
 				tmp = _("Games");
 				break;
+			case OSCAR_CAPABILITY_XTRAZ:
+			case OSCAR_CAPABILITY_NEWCAPS:
+				tmp = _("ICQ Xtraz");
+				break;
 			case OSCAR_CAPABILITY_ADDINS:
 				tmp = _("Add-Ins");
 				break;
@@ -784,6 +793,16 @@
 		return g_strdup(_("Web Aware"));
 	else if (state & AIM_ICQ_STATE_INVISIBLE)
 		return g_strdup(_("Invisible"));
+	else if (state & AIM_ICQ_STATE_EVIL)
+		return g_strdup(_("Evil"));
+	else if (state & AIM_ICQ_STATE_DEPRESSION)
+		return g_strdup(_("Depression"));
+	else if (state & AIM_ICQ_STATE_ATHOME)
+		return g_strdup(_("At home"));
+	else if (state & AIM_ICQ_STATE_ATWORK)
+		return g_strdup(_("At work"));
+	else if (state & AIM_ICQ_STATE_LUNCH)
+		return g_strdup(_("At lunch"));
 	else
 		return g_strdup(_("Online"));
 }
@@ -961,6 +980,16 @@
 		}
 	}
 
+	if (presence) {
+		const char *mood;
+		const char *description;
+		status = purple_presence_get_status(presence, "mood");
+		mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+		description = icq_get_custom_icon_description(mood);
+		if (description && *description)
+			purple_notify_user_info_add_pair(user_info, _("Mood"), _(description));
+	}
+
 	purple_notify_user_info_add_pair(user_info, _("Status"), message);
 	g_free(message);
 }
@@ -1544,6 +1573,7 @@
 
 	if (oscar_util_valid_name_icq((purple_account_get_username(account)))) {
 		od->icq = TRUE;
+		gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS;
 	} else {
 		gc->flags |= PURPLE_CONNECTION_HTML;
 		gc->flags |= PURPLE_CONNECTION_AUTO_RESP;
@@ -2240,6 +2270,16 @@
 			status_id = OSCAR_STATUS_ID_AWAY;
 		else if (type & AIM_ICQ_STATE_INVISIBLE)
 			status_id = OSCAR_STATUS_ID_INVISIBLE;
+		else if (type & AIM_ICQ_STATE_EVIL)
+			status_id = OSCAR_STATUS_ID_EVIL;
+		else if (type & AIM_ICQ_STATE_DEPRESSION)
+			status_id = OSCAR_STATUS_ID_DEPRESSION;
+		else if (type & AIM_ICQ_STATE_ATHOME)
+			status_id = OSCAR_STATUS_ID_ATHOME;
+		else if (type & AIM_ICQ_STATE_ATWORK)
+			status_id = OSCAR_STATUS_ID_ATWORK;
+		else if (type & AIM_ICQ_STATE_LUNCH)
+			status_id = OSCAR_STATUS_ID_LUNCH;
 		else
 			status_id = OSCAR_STATUS_ID_AVAILABLE;
 	} else {
@@ -2552,7 +2592,9 @@
 {
 	PurpleConnection *gc;
 	PurpleAccount *account;
+	PurpleMessageFlags flags = 0;
 	char *message = NULL;
+	char *rtfmsg = NULL;
 
 	g_return_val_if_fail(od != NULL, 0);
 	g_return_val_if_fail(od->gc != NULL, 0);
@@ -2582,6 +2624,20 @@
 		}
 	}
 
+	if (args->info.rtfmsg.rtfmsg != NULL)
+	{
+		if (args->encoding != NULL)
+		{
+			char *encoding = NULL;
+			encoding = oscar_encoding_extract(args->encoding);
+			rtfmsg = oscar_encoding_to_utf8(account, encoding, args->info.rtfmsg.rtfmsg,
+			                                 strlen(args->info.rtfmsg.rtfmsg));
+			g_free(encoding);
+		} else {
+			if (g_utf8_validate(args->info.rtfmsg.rtfmsg, strlen(args->info.rtfmsg.rtfmsg), NULL))
+				rtfmsg = g_strdup(args->info.rtfmsg.rtfmsg);
+		}
+	}
 	if (args->type & OSCAR_CAPABILITY_CHAT)
 	{
 		char *encoding, *utf8name, *tmp;
@@ -2667,10 +2723,28 @@
 
 	else if (args->type & OSCAR_CAPABILITY_ICQSERVERRELAY)
 	{
-		purple_debug_error("oscar", "Got an ICQ Server Relay message of "
+		purple_debug_info("oscar", "Got an ICQ Server Relay message of "
 				"type %d\n", args->info.rtfmsg.msgtype);
-	}
-
+		purple_debug_info("oscar", "Sending X-Status Reply\n");
+
+		if(args->info.rtfmsg.msgtype == 26)
+			icq_relay_xstatus(od, userinfo->bn, args->cookie);
+		
+		if(args->info.rtfmsg.msgtype == 1)
+		{
+			if(rtfmsg)
+			{
+				serv_got_im(gc, userinfo->bn, rtfmsg, flags,
+				            time(NULL));
+			}
+			else
+			{
+				serv_got_im(gc, userinfo->bn,
+				            args->info.rtfmsg.rtfmsg, flags,
+				            time(NULL));
+			}
+		}
+	}
 	else
 	{
 		purple_debug_error("oscar", "Unknown request class %hu\n",
@@ -3250,6 +3324,29 @@
 
 		} break;
 
+		case 0x0006: { /* Reply from an ICQ status message request */
+			char *statusmsg, **splitmsg;
+			PurpleNotifyUserInfo *user_info;
+
+			/* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
+			statusmsg = oscar_icqstatus(state);
+			splitmsg = g_strsplit(msg, "\r\n", 0);
+			
+			user_info = purple_notify_user_info_new();
+				
+			purple_notify_user_info_add_pair(user_info, _("UIN"), who);
+			purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
+			purple_notify_user_info_add_section_break(user_info);
+			purple_notify_user_info_add_pair(user_info, NULL, g_strjoinv("<BR>", splitmsg));
+
+			g_free(statusmsg);
+			g_strfreev(splitmsg);
+
+			purple_notify_userinfo(gc, who, user_info, NULL, NULL);
+			purple_notify_user_info_destroy(user_info);
+
+		} break;
+
 		default: {
 			purple_debug_warning("oscar",
 					   "Received an unknown client auto-response from %s.  "
@@ -3701,7 +3798,7 @@
 	if (utf8 == NULL)
 		/* The conversion failed! */
 		utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]"));
-	serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time((time_t)NULL));
+	serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time(NULL));
 	g_free(utf8);
 
 	return 1;
@@ -4848,6 +4945,16 @@
 		data |= AIM_ICQ_STATE_CHAT;
 	else if (!strcmp(status_id, OSCAR_STATUS_ID_INVISIBLE))
 		data |= AIM_ICQ_STATE_INVISIBLE;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_EVIL))
+		data |= AIM_ICQ_STATE_EVIL;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_DEPRESSION))
+		data |= AIM_ICQ_STATE_DEPRESSION;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_ATWORK))
+		data |= AIM_ICQ_STATE_ATWORK;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_ATHOME))
+		data |= AIM_ICQ_STATE_ATHOME;
+	else if (!strcmp(status_id, OSCAR_STATUS_ID_LUNCH))
+		data |= AIM_ICQ_STATE_LUNCH;
 	else if (!strcmp(status_id, OSCAR_STATUS_ID_CUSTOM))
 		data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
 
@@ -5009,6 +5116,12 @@
 	pc = purple_account_get_connection(account);
 	od = purple_connection_get_protocol_data(pc);
 
+	/* There's no need to do the stuff below for mood updates. */
+	if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) {
+		aim_locate_setcaps(od, purple_caps);
+		return;
+	}
+
 	/* Set the AIM-style away message for both AIM and ICQ accounts */
 	oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
 
@@ -5961,6 +6074,11 @@
 	return 0;
 }
 
+PurpleMood* oscar_get_purple_moods(PurpleAccount *account)
+{
+	return icq_get_purple_moods(account);
+}
+
 const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b)
 {
 	const char *name = b ? purple_buddy_get_name(b) : NULL;
@@ -6031,12 +6149,17 @@
 			return "admin";
 		if (userinfo->flags & AIM_FLAG_ACTIVEBUDDY)
 			return "bot";
-		if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP)
-			return "hiptop";
 		if (userinfo->capabilities & OSCAR_CAPABILITY_SECUREIM)
 			return "secure";
 		if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY)
 			return "birthday";
+
+		/* Make the mood icon override anything below this. */
+		if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
+			return NULL;
+
+		if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP)
+			return "hiptop";
 	}
 	return NULL;
 }
@@ -6259,9 +6382,53 @@
 										   purple_value_new(PURPLE_TYPE_STRING), NULL);
 	status_types = g_list_prepend(status_types, type);
 
-	type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE,
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
 									 OSCAR_STATUS_ID_FREE4CHAT,
-									 _("Free For Chat"), TRUE, is_icq, FALSE);
+									 _("Free For Chat"), TRUE, is_icq, FALSE,
+									 "message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+
+	status_types = g_list_prepend(status_types, type);
+	
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_EVIL,
+									 _("Evil"), TRUE, is_icq, FALSE,
+				 "message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+	status_types = g_list_prepend(status_types, type);
+
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_DEPRESSION,
+									 _("Depression"), TRUE, is_icq, FALSE,
+				 "message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+	status_types = g_list_prepend(status_types, type);
+
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_ATHOME,
+									 _("At home"), TRUE, is_icq, FALSE,
+				"message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+	status_types = g_list_prepend(status_types, type);
+
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_ATWORK,
+									 _("At work"), TRUE, is_icq, FALSE,
+				"message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+
+	status_types = g_list_prepend(status_types, type);
+
+
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
+									 OSCAR_STATUS_ID_LUNCH,
+									 _("Lunch"), TRUE, is_icq, FALSE,
+				"message", _("Message"),
+				purple_value_new(PURPLE_TYPE_STRING), NULL);
+
 	status_types = g_list_prepend(status_types, type);
 
 	type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
@@ -6271,9 +6438,12 @@
 										   purple_value_new(PURPLE_TYPE_STRING), NULL);
 	status_types = g_list_prepend(status_types, type);
 
-	type = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
 									 OSCAR_STATUS_ID_INVISIBLE,
-									 NULL, TRUE, TRUE, FALSE);
+									 NULL, TRUE, TRUE, FALSE,
+									 "message", _("Message"),
+									  purple_value_new(PURPLE_TYPE_STRING), NULL);
+
 	status_types = g_list_prepend(status_types, type);
 
 	type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, OSCAR_STATUS_ID_MOBILE, NULL, FALSE, FALSE, TRUE);
@@ -6306,9 +6476,14 @@
 									 NULL, TRUE, TRUE, FALSE);
 	status_types = g_list_prepend(status_types, type);
 
-	status_types = g_list_reverse(status_types);
-
-	return status_types;
+	type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD,
+			"mood", NULL, TRUE, is_icq, TRUE,
+			PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(PURPLE_TYPE_STRING),
+			PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
+	status_types = g_list_prepend(status_types, type);
+
+	return g_list_reverse(status_types);
 }
 
 static void oscar_ssi_editcomment(struct name_data *data, const char *text) {
@@ -6480,6 +6655,23 @@
 	}
 }
 
+static void oscar_get_icqxstatusmsg (PurpleBlistNode *node, gpointer ignore)
+{
+	PurpleBuddy *buddy;
+	PurpleConnection *gc;
+	PurpleAccount *account;
+	
+	
+	g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+	buddy = (PurpleBuddy *)node;
+	gc = purple_account_get_connection(buddy->account);
+	account = purple_connection_get_account(gc);
+	purple_debug_info("oscar",   "Manual X-Status Get From %s to %s:\n", purple_buddy_get_name(buddy), account->username);
+
+	icq_im_xstatus_request(gc->proto_data, purple_buddy_get_name(buddy));
+}
+
 static void
 oscar_get_aim_info_cb(PurpleBlistNode *node, gpointer ignore)
 {
@@ -6529,15 +6721,13 @@
 		menu = g_list_prepend(menu, act);
 	}
 
-#if 0
 	if (od->icq)
 	{
-		act = purple_menu_action_new(_("Get Status Msg"),
-		                           PURPLE_CALLBACK(oscar_get_icqstatusmsg),
+		act = purple_menu_action_new(_("Get X-Status Msg"),
+		                           PURPLE_CALLBACK(oscar_get_icqxstatusmsg),
 		                           NULL, NULL);
 		menu = g_list_prepend(menu, act);
 	}
-#endif
 
 	if (userinfo &&
 		oscar_util_name_compare(purple_account_get_username(account), bname) &&
--- a/libpurple/protocols/oscar/oscar.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Mon Mar 08 22:53:02 2010 +0000
@@ -375,8 +375,12 @@
 	OSCAR_CAPABILITY_CAMERA               = 0x04000000,
 	OSCAR_CAPABILITY_ICHAT_SCREENSHARE    = 0x08000000,
 	OSCAR_CAPABILITY_TYPING               = 0x10000000,
-	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x20000000,
-	OSCAR_CAPABILITY_LAST                 = 0x40000000
+	OSCAR_CAPABILITY_NEWCAPS              = 0x20000000,
+	OSCAR_CAPABILITY_XTRAZ                = 0x40000000,
+	OSCAR_CAPABILITY_GENERICUNKNOWN       = 0x80000000,
+#warning Fix OSCAR_CAPABILITY_LAST situation
+	// TODO: We're out of bits.  Rework things that depend on this or remove some capability. (Or, ensure this is a 64-bit type.)
+	OSCAR_CAPABILITY_LAST                 = 0x100000000	
 } OscarCapability;
 
 /*
@@ -572,6 +576,12 @@
 #define AIM_ICQ_STATE_BUSY              0x00000010
 #define AIM_ICQ_STATE_CHAT              0x00000020
 #define AIM_ICQ_STATE_INVISIBLE         0x00000100
+#define AIM_ICQ_STATE_EVIL              0x00003000
+#define AIM_ICQ_STATE_DEPRESSION        0x00004000
+#define AIM_ICQ_STATE_ATHOME            0x00005000
+#define AIM_ICQ_STATE_ATWORK            0x00006000
+#define AIM_ICQ_STATE_LUNCH             0x00002001
+#define AIM_ICQ_STATE_EVIL              0x00003000
 #define AIM_ICQ_STATE_WEBAWARE          0x00010000
 #define AIM_ICQ_STATE_HIDEIP            0x00020000
 #define AIM_ICQ_STATE_BIRTHDAY          0x00080000
@@ -1012,7 +1022,8 @@
 /* 0x0008 */ int aim_im_warn(OscarData *od, FlapConnection *conn, const char *destbn, guint32 flags);
 /* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code);
 /* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od);
-/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 channel, const char *bn, guint16 event);
+/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *bn, guint16 type2);
+/* 0x000b */ int icq_relay_xstatus (OscarData *od, const char *sn, const guchar* cookie);
 void aim_icbm_makecookie(guchar* cookie);
 gchar *oscar_encoding_extract(const char *encoding);
 gchar *oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen);
@@ -1144,8 +1155,10 @@
 void aim_info_free(aim_userinfo_t *);
 int aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *);
 int aim_putuserinfo(ByteStream *bs, aim_userinfo_t *info);
-
-
+PurpleMood* icq_get_purple_moods(PurpleAccount *account);
+const char* icq_get_custom_icon_description(const char *mood);
+guint8* icq_get_custom_icon_data(const char *mood);
+int icq_im_xstatus_request(OscarData *od, const char *sn);
 
 /* 0x0003 - family_buddy.c */
 /* 0x0002 */ void aim_buddylist_reqrights(OscarData *, FlapConnection *);
@@ -1474,7 +1487,7 @@
 int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value);
 int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value);
 int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value);
-int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps);
+int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, const char *mood);
 int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo);
 int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance);
 int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tl);
--- a/libpurple/protocols/oscar/oscarcommon.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/oscarcommon.h	Mon Mar 08 22:53:02 2010 +0000
@@ -30,6 +30,7 @@
 #include "prpl.h"
 #include "version.h"
 #include "notify.h"
+#include "status.h"
 
 #define OSCAR_DEFAULT_LOGIN_SERVER "login.messaging.aol.com"
 #define OSCAR_DEFAULT_LOGIN_PORT 5190
@@ -51,6 +52,7 @@
 #ifdef _WIN32
 const char *oscar_get_locale_charset(void);
 #endif
+PurpleMood* oscar_get_purple_moods(PurpleAccount *account);
 const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b);
 const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b);
 const char* oscar_list_emblem(PurpleBuddy *b);
--- a/libpurple/protocols/oscar/tlv.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/oscar/tlv.c	Mon Mar 08 22:53:02 2010 +0000
@@ -407,10 +407,11 @@
  * @param caps Bitfield of capability flags to send
  * @return The size of the value added.
  */
-int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps)
+int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, const char *mood)
 {
 	guint8 buf[256]; /* TODO: Don't use a fixed length buffer */
 	ByteStream bs;
+	guint8 *data;
 
 	if (caps == 0)
 		return 0; /* nothing there anyway */
@@ -418,6 +419,11 @@
 	byte_stream_init(&bs, buf, sizeof(buf));
 
 	byte_stream_putcaps(&bs, caps);
+	
+	/* adding of custom icon GUID */
+	data = icq_get_custom_icon_data(mood);
+	if (data != NULL)
+		byte_stream_putraw(&bs, data, 16);
 
 	return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf);
 }
--- a/libpurple/protocols/qq/buddy_memo.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/qq/buddy_memo.c	Mon Mar 08 22:53:02 2010 +0000
@@ -119,7 +119,7 @@
 
 	who = uid_to_purple_name(bd_uid);
 	buddy = purple_find_buddy(account, who);
-	if (buddy == NULL || buddy->proto_data == NULL) {
+	if (buddy == NULL || purple_buddy_get_protocol_data(buddy) == NULL) {
 		g_free(who);
 		purple_debug_info("QQ", "Error...Can NOT find %d!\n", bd_uid);
 		return;
--- a/libpurple/protocols/qq/qq.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/qq/qq.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1037,7 +1037,8 @@
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
 	NULL,							/* get_account_text_table */
 	NULL,							/* initiate_media */
-	NULL                            /* can_do_media */
+	NULL,							/* get_media_caps */
+	NULL							/* get_moods */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/protocols/sametime/sametime.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Mon Mar 08 22:53:02 2010 +0000
@@ -3527,10 +3527,10 @@
   purple_request_field_list_set_multi_select(f, FALSE);
   for(; confs; confs = confs->next) {
     struct mwConference *c = confs->data;
-    purple_request_field_list_add(f, mwConference_getTitle(c), c);
+    purple_request_field_list_add_icon(f, mwConference_getTitle(c), NULL, c);
   }
-  purple_request_field_list_add(f, _("Create New Conference..."),
-			      GINT_TO_POINTER(0x01));
+  purple_request_field_list_add_icon(f, _("Create New Conference..."),
+			      NULL, GINT_TO_POINTER(0x01));
   purple_request_field_group_add_field(g, f);
   
   f = purple_request_field_string_new(CHAT_KEY_INVITE, "Message", NULL, FALSE);
@@ -5458,7 +5458,7 @@
     res->id = g_strdup(match->id);
     res->name = g_strdup(match->name);
 
-    purple_request_field_list_add(f, res->name, res);
+    purple_request_field_list_add_icon(f, res->name, NULL, res);
   }
 
   purple_request_field_group_add_field(g, f);
--- a/libpurple/protocols/silc/buddy.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/silc/buddy.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1183,7 +1183,7 @@
 			   client_entry->username, *client_entry->hostname ?
 			   client_entry->hostname : "",
 			   fingerprint ? tmp2 : "");
-		purple_request_field_list_add(f, tmp, client_entry);
+		purple_request_field_list_add_icon(f, tmp, NULL, client_entry);
 		silc_free(fingerprint);
 	}
 
--- a/libpurple/protocols/silc/chat.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/silc/chat.c	Mon Mar 08 22:53:02 2010 +0000
@@ -475,7 +475,7 @@
 		g_snprintf(tmp2, sizeof(tmp2), "%s\n  %s\n  %s",
 			   ident->realname ? ident->realname : ident->username ?
 			   ident->username : "", fingerprint, babbleprint);
-		purple_request_field_list_add(f, tmp2, public_key);
+		purple_request_field_list_add_icon(f, tmp2, NULL, public_key);
 
 		silc_free(fingerprint);
 		silc_free(babbleprint);
--- a/libpurple/protocols/silc/silc.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/silc/silc.c	Mon Mar 08 22:53:02 2010 +0000
@@ -2116,7 +2116,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,				        /* get_account_text_table */
 	NULL,				        /* initiate_media */
-	NULL                        /* can_do_media */
+	NULL,				        /* get_media_caps */
+	NULL				        /* get_moods */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/silc10/buddy.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/silc10/buddy.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1176,7 +1176,7 @@
 			   clients[i]->username, clients[i]->hostname ?
 			   clients[i]->hostname : "",
 			   fingerprint ? tmp2 : "");
-		purple_request_field_list_add(f, tmp, clients[i]);
+		purple_request_field_list_add_icon(f, tmp, NULL, clients[i]);
 		silc_free(fingerprint);
 	}
 
--- a/libpurple/protocols/silc10/chat.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/silc10/chat.c	Mon Mar 08 22:53:02 2010 +0000
@@ -449,7 +449,7 @@
 		g_snprintf(tmp2, sizeof(tmp2), "%s\n  %s\n  %s",
 			   ident->realname ? ident->realname : ident->username ?
 			   ident->username : "", fingerprint, babbleprint);
-		purple_request_field_list_add(f, tmp2, pubkey);
+		purple_request_field_list_add_icon(f, tmp2, NULL, pubkey);
 
 		silc_free(fingerprint);
 		silc_free(babbleprint);
--- a/libpurple/protocols/simple/simple.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/simple/simple.c	Mon Mar 08 22:53:02 2010 +0000
@@ -2086,7 +2086,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,					/* get_account_text_table */
 	NULL,					/* initiate_media */
-	NULL					/* can_do_media */
+	NULL,					/* get_media_caps */
+	NULL					/* get_moods */
 };
 
 
--- a/libpurple/protocols/yahoo/libyahoo.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/yahoo/libyahoo.c	Mon Mar 08 22:53:02 2010 +0000
@@ -264,7 +264,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	yahoo_get_account_text_table,    /* get_account_text_table */
 	NULL, /* initiate_media */
-	NULL  /* can_do_media */
+	NULL,  /* get_media_caps */
+	NULL   /* get_moods */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/yahoo/libyahoojp.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/yahoo/libyahoojp.c	Mon Mar 08 22:53:02 2010 +0000
@@ -160,7 +160,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	yahoojp_get_account_text_table,    /* get_account_text_table */
 	NULL, /* initiate_media */
-	NULL  /* can_do_media */
+	NULL, /* get_media_caps */
+	NULL  /* get_moods */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/yahoo/libymsg.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/yahoo/libymsg.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1125,9 +1125,12 @@
 		m = m2;
 		purple_util_chrreplace(m, '\r', '\n');
 		if (!strcmp(m, "<ding>")) {
+			PurpleConversation *conv = NULL;
 			char *username;
 
 			username = g_markup_escape_text(im->fed_from, -1);
+			conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY,
+				username, account);
 			purple_prpl_got_attention(gc, username, YAHOO_BUZZ);
 			g_free(username);
 			g_free(m);
@@ -1773,11 +1776,8 @@
 		char *crumb = NULL;
 		char *crypt = NULL;
 
-#if GLIB_CHECK_VERSION(2,6,0)
 		totalelements = g_strv_length(split_data);
-#else
-		while (split_data[++totalelements] != NULL);
-#endif
+
 		if (totalelements >= 4) {
 			response_no = strtol(split_data[0], NULL, 10);
 			crumb = g_strdup(split_data[1] + strlen("crumb="));
@@ -1859,11 +1859,8 @@
 		int response_no = -1;
 		char *token = NULL;
 
-#if GLIB_CHECK_VERSION(2,6,0)
 		totalelements = g_strv_length(split_data);
-#else
-		while (split_data[++totalelements] != NULL);
-#endif
+
 		if(totalelements == 1)
 			response_no = strtol(split_data[0], NULL, 10);
 		else if(totalelements >= 2) {
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/yahoo/yahoo_filexfer.c	Mon Mar 08 22:53:02 2010 +0000
@@ -509,9 +509,6 @@
 		return 0;
 	}
 
-	if ((purple_xfer_get_bytes_sent(xfer) + len) >= purple_xfer_get_size(xfer))
-		purple_xfer_set_completed(xfer, TRUE);
-
 	return len;
 }
 
--- a/libpurple/protocols/zephyr/zephyr.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/protocols/zephyr/zephyr.c	Mon Mar 08 22:53:02 2010 +0000
@@ -2908,7 +2908,8 @@
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL,					/* get_account_text_table */
 	NULL,					/* initate_media */
-	NULL					/* can_do_media */
+	NULL,					/* get_media_caps */
+	NULL					/* get_moods */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/prpl.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/prpl.c	Mon Mar 08 22:53:02 2010 +0000
@@ -288,8 +288,10 @@
 
 	/* The buddy is no longer online, they are therefore by definition not
 	 * still typing to us. */
-	if (!purple_status_is_online(status))
+	if (!purple_status_is_online(status)) {
 		serv_got_typing_stopped(purple_account_get_connection(account), name);
+		purple_prpl_got_media_caps(account, name);
+	}
 }
 
 void purple_prpl_got_user_status_deactive(PurpleAccount *account, const char *name,
@@ -407,6 +409,16 @@
 	return statuses;
 }
 
+static void
+purple_prpl_attention(PurpleConversation *conv, const char *who,
+	guint type, PurpleMessageFlags flags, time_t mtime)
+{
+	PurpleAccount *account = purple_conversation_get_account(conv);
+	purple_signal_emit(purple_conversations_get_handle(),
+		flags == PURPLE_MESSAGE_SEND ? "sent-attention" : "got-attention",
+		account, who, conv, type);
+}
+
 void
 purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code)
 {
@@ -452,6 +464,7 @@
 
 	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who);
 	purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime);
+	purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_SEND, time(NULL));
 
 	g_free(description);
 }
@@ -503,7 +516,15 @@
 void
 purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code)
 {
+	PurpleConversation *conv = NULL;
+	PurpleAccount *account = purple_connection_get_account(gc);
+
 	got_attention(gc, -1, who, type_code);
+	conv = 
+		purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, account);
+	if (conv)
+		purple_prpl_attention(conv, who, type_code, PURPLE_MESSAGE_RECV,
+			time(NULL));
 }
 
 void
@@ -560,6 +581,39 @@
 	return PURPLE_MEDIA_CAPS_NONE;
 }
 
+void
+purple_prpl_got_media_caps(PurpleAccount *account, const char *name)
+{
+#ifdef USE_VV
+	GSList *list;
+
+	g_return_if_fail(account != NULL);
+	g_return_if_fail(name    != NULL);
+
+	if ((list = purple_find_buddies(account, name)) == NULL)
+		return;
+
+	while (list) {
+		PurpleBuddy *buddy = list->data;
+		PurpleMediaCaps oldcaps = purple_buddy_get_media_caps(buddy);
+		PurpleMediaCaps newcaps = 0;
+		const gchar *bname = purple_buddy_get_name(buddy);
+		list = g_slist_delete_link(list, list);
+
+		
+		newcaps = purple_prpl_get_media_caps(account, bname);
+		purple_buddy_set_media_caps(buddy, newcaps);
+
+		if (oldcaps == newcaps)
+			continue;
+
+		purple_signal_emit(purple_blist_get_handle(),
+				"buddy-caps-changed", buddy,
+				newcaps, oldcaps);
+	}
+#endif
+}
+
 /**************************************************************************
  * Protocol Plugin Subsystem API
  **************************************************************************/
--- a/libpurple/prpl.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/prpl.h	Mon Mar 08 22:53:02 2010 +0000
@@ -569,6 +569,12 @@
 	 */
 	PurpleMediaCaps (*get_media_caps)(PurpleAccount *account,
 					  const char *who);
+
+	/**
+	 * Returns an array of "PurpleMood"s, with the last one having
+	 * "mood" set to @c NULL.
+	 */
+	PurpleMood *(*get_moods)(PurpleAccount *account);
 };
 
 #define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \
@@ -903,6 +909,17 @@
 					const char *who,
 					PurpleMediaSessionType type);
 
+/**
+ * Signals that the prpl received capabilities for the given contact.
+ *
+ * This function is intended to be used only by prpls.
+ *
+ * @param account The account the user is on.
+ * @param who The name of the contact for which capabilities have been received.
+ * @since 2.7.0
+ */
+void purple_prpl_got_media_caps(PurpleAccount *account, const char *who);
+
 /*@}*/
 
 /**************************************************************************/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purple-2-uninstalled.pc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,22 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+datarootdir=@datarootdir@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+
+abs_srcdir=@abs_srcdir@
+abs_builddir=@abs_builddir@
+
+abs_top_srcdir=@abs_top_srcdir@
+abs_top_builddir=@abs_top_builddir@
+
+plugindir=${libdir}/purple-@PURPLE_MAJOR_VERSION@
+
+Name: libpurple
+Description: libpurple is a GLib-based instant messenger library.
+Version: @VERSION@
+Requires: glib-2.0
+Cflags: -I${abs_top_srcdir} -I${abs_top_builddir}
+Libs: ${abs_builddir}/libpurple.la
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/purple-2.pc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,16 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+datarootdir=@datarootdir@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+
+plugindir=${libdir}/purple-@PURPLE_MAJOR_VERSION@
+
+Name: libpurple
+Description: libpurple is a GLib-based instant messenger library.
+Version: @VERSION@
+Requires: glib-2.0
+Cflags: -I${includedir}
+Libs: -L${libdir} -lpurple
--- a/libpurple/purple-uninstalled.pc.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/purple-uninstalled.pc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -9,6 +9,8 @@
 abs_srcdir=@abs_srcdir@
 abs_builddir=@abs_builddir@
 
+plugindir=${libdir}/purple-@PURPLE_MAJOR_VERSION@
+
 Name: libpurple
 Description: libpurple is a GLib-based instant messenger library.
 Version: @VERSION@
--- a/libpurple/purple.pc.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/purple.pc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -6,6 +6,8 @@
 datadir=@datadir@
 sysconfdir=@sysconfdir@
 
+plugindir=${libdir}/purple-@PURPLE_MAJOR_VERSION@
+
 Name: libpurple
 Description: libpurple is a GLib-based instant messenger library.
 Version: @VERSION@
--- a/libpurple/request.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/request.c	Mon Mar 08 22:53:02 2010 +0000
@@ -850,13 +850,40 @@
 purple_request_field_list_add(PurpleRequestField *field, const char *item,
 							void *data)
 {
+	purple_request_field_list_add_icon(field, item, NULL, data);
+}
+
+void
+purple_request_field_list_add_icon(PurpleRequestField *field, const char *item, const char* icon_path,
+							void *data)
+{
 	g_return_if_fail(field != NULL);
 	g_return_if_fail(item  != NULL);
 	g_return_if_fail(data  != NULL);
 	g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST);
 
+	if (icon_path)
+	{
+		if (field->u.list.icons == NULL)
+		{
+			GList *l;
+			for (l = field->u.list.items ; l != NULL ; l = l->next)
+			{
+				/* Order doesn't matter, because we're just
+				 * filing in blank items.  So, we use
+				 * g_list_prepend() because it's faster. */
+				field->u.list.icons = g_list_prepend(field->u.list.icons, NULL);
+			}
+		}
+		field->u.list.icons = g_list_append(field->u.list.icons, g_strdup(icon_path));
+	}
+	else if (field->u.list.icons)
+	{
+		/* Keep this even with the items list. */
+		field->u.list.icons = g_list_append(field->u.list.icons, NULL);
+	}
+
 	field->u.list.items = g_list_append(field->u.list.items, g_strdup(item));
-
 	g_hash_table_insert(field->u.list.item_data, g_strdup(item), data);
 }
 
@@ -962,6 +989,15 @@
 	return field->u.list.items;
 }
 
+GList *
+purple_request_field_list_get_icons(const PurpleRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, NULL);
+	g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST, NULL);
+
+	return field->u.list.icons;
+}
+
 PurpleRequestField *
 purple_request_field_label_new(const char *id, const char *text)
 {
--- a/libpurple/request.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/request.h	Mon Mar 08 22:53:02 2010 +0000
@@ -150,6 +150,7 @@
 		struct
 		{
 			GList *items;
+			GList *icons;
 			GHashTable *item_data;
 			GList *selected;
 			GHashTable *selected_table;
@@ -953,11 +954,24 @@
  * @param field The list field.
  * @param item  The list item.
  * @param data  The associated data.
+ *
+ * @deprecated Use purple_request_field_list_add_icon() instead.
  */
 void purple_request_field_list_add(PurpleRequestField *field,
 								 const char *item, void *data);
 
 /**
+ * Adds an item to a list field.
+ *
+ * @param field The list field.
+ * @param item  The list item.
+ * @param icon_path The path to icon file, or @c NULL for no icon.
+ * @param data  The associated data.
+ */
+void purple_request_field_list_add_icon(PurpleRequestField *field,
+								 const char *item, const char* icon_path, void* data);
+
+/**
  * Adds a selected item to the list field.
  *
  * @param field The field.
@@ -1015,6 +1029,18 @@
  */
 GList *purple_request_field_list_get_items(const PurpleRequestField *field);
 
+/**
+ * Returns a list of icons in a list field.
+ *
+ * The icons will correspond with the items, in order.
+ *
+ * @param field The field.
+ *
+ * @constreturn The list of icons or @c NULL (i.e. the empty GList) if no
+ *              items have icons.
+ */
+GList *purple_request_field_list_get_icons(const PurpleRequestField *field);
+
 /*@}*/
 
 /**************************************************************************/
--- a/libpurple/signals.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/signals.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1009,6 +1009,21 @@
 }
 
 void
+purple_marshal_POINTER__POINTER(
+                                    PurpleCallback cb, va_list args, void *data,
+                                    void **return_val)
+{
+	gpointer ret_val;
+	void *arg1 = va_arg(args, void *);
+
+	ret_val = ((gpointer(*)(void *, void *))cb)(arg1, data);
+
+	if (return_val != NULL)
+		*return_val = ret_val;
+}
+
+
+void
 purple_marshal_POINTER__POINTER_INT(
                                     PurpleCallback cb, va_list args, void *data,
                                     void **return_val)
--- a/libpurple/signals.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/signals.h	Mon Mar 08 22:53:02 2010 +0000
@@ -355,6 +355,8 @@
 void purple_marshal_BOOLEAN__INT_POINTER(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 
+void purple_marshal_POINTER__POINTER(
+		PurpleCallback cb, va_list args, void *data, void **return_val);
 void purple_marshal_POINTER__POINTER_INT(
 		PurpleCallback cb, va_list args, void *data, void **return_val);
 void purple_marshal_POINTER__POINTER_INT64(
--- a/libpurple/sound.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/sound.h	Mon Mar 08 22:53:02 2010 +0000
@@ -51,6 +51,7 @@
 	PURPLE_SOUND_CHAT_SAY,         /**< Someone else says somthing in a chat. */
 	PURPLE_SOUND_POUNCE_DEFAULT,   /**< Default sound for a buddy pounce.     */
 	PURPLE_SOUND_CHAT_NICK,        /**< Someone says your name in a chat.     */
+	PURPLE_SOUND_GOT_ATTENTION,	   /**< Got an attention					  */
 	PURPLE_NUM_SOUNDS              /**< Total number of sounds.               */
 
 } PurpleSoundEventID;
--- a/libpurple/status.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/status.c	Mon Mar 08 22:53:02 2010 +0000
@@ -137,6 +137,7 @@
 	-200,   /* extended away            */
 	-400,   /* mobile                   */
 	0,      /* tune                     */
+	0,      /* mood                     */
 	-10,    /* idle, special case.      */
 	-5,     /* idle time, special case. */
 	10      /* Offline messageable      */
@@ -157,15 +158,16 @@
 
 } const status_primitive_map[] =
 {
-	{ PURPLE_STATUS_UNSET,           "unset",           N_("Unset")           },
-	{ PURPLE_STATUS_OFFLINE,         "offline",         N_("Offline")         },
-	{ PURPLE_STATUS_AVAILABLE,       "available",       N_("Available")       },
-	{ PURPLE_STATUS_UNAVAILABLE,     "unavailable",     N_("Do not disturb")     },
-	{ PURPLE_STATUS_INVISIBLE,       "invisible",       N_("Invisible")       },
-	{ PURPLE_STATUS_AWAY,            "away",            N_("Away")            },
-	{ PURPLE_STATUS_EXTENDED_AWAY,   "extended_away",   N_("Extended away")   },
-	{ PURPLE_STATUS_MOBILE,          "mobile",          N_("Mobile")          },
-	{ PURPLE_STATUS_TUNE,            "tune",            N_("Listening to music") }
+	{ PURPLE_STATUS_UNSET,           "unset",           N_("Unset")               },
+	{ PURPLE_STATUS_OFFLINE,         "offline",         N_("Offline")             },
+	{ PURPLE_STATUS_AVAILABLE,       "available",       N_("Available")           },
+	{ PURPLE_STATUS_UNAVAILABLE,     "unavailable",     N_("Do not disturb")      },
+	{ PURPLE_STATUS_INVISIBLE,       "invisible",       N_("Invisible")           },
+	{ PURPLE_STATUS_AWAY,            "away",            N_("Away")                },
+	{ PURPLE_STATUS_EXTENDED_AWAY,   "extended_away",   N_("Extended away")       },
+	{ PURPLE_STATUS_MOBILE,          "mobile",          N_("Mobile")              },
+	{ PURPLE_STATUS_TUNE,            "tune",            N_("Listening to music"), },
+	{ PURPLE_STATUS_MOOD,            "mood",            N_("Feeling")             },
 };
 
 const char *
--- a/libpurple/status.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/status.h	Mon Mar 08 22:53:02 2010 +0000
@@ -87,6 +87,12 @@
 typedef struct _PurplePresence        PurplePresence;
 typedef struct _PurpleStatus          PurpleStatus;
 
+typedef struct _PurpleMood {
+	const char *mood;
+	const char *description;
+	gpointer *padding;
+} PurpleMood;
+
 /**
  * A context for a presence.
  *
@@ -106,8 +112,7 @@
  */
 /*
  * If you add a value to this enum, make sure you update
- * the status_primitive_map array in status.c and the special-cases for idle
- * and offline-messagable just below it.
+ * the status_primitive_map and primitive_scores arrays in status.c.
  */
 typedef enum
 {
@@ -120,6 +125,7 @@
 	PURPLE_STATUS_EXTENDED_AWAY,
 	PURPLE_STATUS_MOBILE,
 	PURPLE_STATUS_TUNE,
+	PURPLE_STATUS_MOOD,
 	PURPLE_STATUS_NUM_PRIMITIVES
 } PurpleStatusPrimitive;
 
@@ -139,6 +145,9 @@
 #define PURPLE_TUNE_URL		"tune_url"
 #define PURPLE_TUNE_FULL	"tune_full"
 
+#define PURPLE_MOOD_NAME	"mood"
+#define PURPLE_MOOD_COMMENT	"moodtext"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
--- a/libpurple/upnp.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/upnp.c	Mon Mar 08 22:53:02 2010 +0000
@@ -542,7 +542,7 @@
 		len = recv(dd->fd, buf,
 			sizeof(buf) - 1, 0);
 
-		if(len > 0) {
+		if(len >= 0) {
 			buf[len] = '\0';
 			break;
 		} else if(errno != EINTR) {
--- a/libpurple/util.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/util.c	Mon Mar 08 22:53:02 2010 +0000
@@ -220,50 +220,12 @@
 gchar *
 purple_base64_encode(const guchar *data, gsize len)
 {
-#if GLIB_CHECK_VERSION(2,12,0)
 	return g_base64_encode(data, len);
-#else
-	char *out, *rv;
-
-	g_return_val_if_fail(data != NULL, NULL);
-	g_return_val_if_fail(len > 0,  NULL);
-
-	rv = out = g_malloc(((len/3)+1)*4 + 1);
-
-	for (; len >= 3; len -= 3)
-	{
-		*out++ = alphabet[data[0] >> 2];
-		*out++ = alphabet[((data[0] << 4) & 0x30) | (data[1] >> 4)];
-		*out++ = alphabet[((data[1] << 2) & 0x3c) | (data[2] >> 6)];
-		*out++ = alphabet[data[2] & 0x3f];
-		data += 3;
-	}
-
-	if (len > 0)
-	{
-		unsigned char fragment;
-
-		*out++ = alphabet[data[0] >> 2];
-		fragment = (data[0] << 4) & 0x30;
-
-		if (len > 1)
-			fragment |= data[1] >> 4;
-
-		*out++ = alphabet[fragment];
-		*out++ = (len < 2) ? '=' : alphabet[(data[1] << 2) & 0x3c];
-		*out++ = '=';
-	}
-
-	*out = '\0';
-
-	return rv;
-#endif /* GLIB < 2.12.0 */
 }
 
 guchar *
 purple_base64_decode(const char *str, gsize *ret_len)
 {
-#if GLIB_CHECK_VERSION(2,12,0)
 	/*
 	 * We want to allow ret_len to be NULL for backward compatibility,
 	 * but g_base64_decode() requires a valid length variable.  So if
@@ -271,69 +233,6 @@
 	 */
 	gsize unused;
 	return g_base64_decode(str, ret_len != NULL ? ret_len : &unused);
-#else
-	guchar *out = NULL;
-	char tmp = 0;
-	const char *c;
-	gint32 tmp2 = 0;
-	int len = 0, n = 0;
-
-	g_return_val_if_fail(str != NULL, NULL);
-
-	c = str;
-
-	while (*c) {
-		if (*c >= 'A' && *c <= 'Z') {
-			tmp = *c - 'A';
-		} else if (*c >= 'a' && *c <= 'z') {
-			tmp = 26 + (*c - 'a');
-		} else if (*c >= '0' && *c <= 57) {
-			tmp = 52 + (*c - '0');
-		} else if (*c == '+') {
-			tmp = 62;
-		} else if (*c == '/') {
-			tmp = 63;
-		} else if (*c == '\r' || *c == '\n') {
-			c++;
-			continue;
-		} else if (*c == '=') {
-			if (n == 3) {
-				out = g_realloc(out, len + 2);
-				out[len] = (guchar)(tmp2 >> 10) & 0xff;
-				len++;
-				out[len] = (guchar)(tmp2 >> 2) & 0xff;
-				len++;
-			} else if (n == 2) {
-				out = g_realloc(out, len + 1);
-				out[len] = (guchar)(tmp2 >> 4) & 0xff;
-				len++;
-			}
-			break;
-		}
-		tmp2 = ((tmp2 << 6) | (tmp & 0xff));
-		n++;
-		if (n == 4) {
-			out = g_realloc(out, len + 3);
-			out[len] = (guchar)((tmp2 >> 16) & 0xff);
-			len++;
-			out[len] = (guchar)((tmp2 >> 8) & 0xff);
-			len++;
-			out[len] = (guchar)(tmp2 & 0xff);
-			len++;
-			tmp2 = 0;
-			n = 0;
-		}
-		c++;
-	}
-
-	out = g_realloc(out, len + 1);
-	out[len] = 0;
-
-	if (ret_len != NULL)
-		*ret_len = len;
-
-	return out;
-#endif /* GLIB < 2.12.0 */
 }
 
 /**************************************************************************
@@ -2468,6 +2367,31 @@
 	return g_string_free(ret, FALSE);
 }
 
+char *purple_unescape_text(const char *in)
+{
+    GString *ret;
+    const char *c = in;
+
+    if (in == NULL)
+        return NULL;
+
+    ret = g_string_new("");
+    while (*c) {
+        int len;
+        const char *ent;
+
+        if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) {
+            g_string_append(ret, ent);
+            c += len;
+        } else {
+            g_string_append_c(ret, *c);
+            c++;
+        }
+    }
+
+    return g_string_free(ret, FALSE);
+}
+
 char *purple_unescape_html(const char *html)
 {
 	GString *ret;
@@ -2656,56 +2580,7 @@
 
 int purple_build_dir (const char *path, int mode)
 {
-#if GLIB_CHECK_VERSION(2,8,0)
 	return g_mkdir_with_parents(path, mode);
-#else
-	char *dir, **components, delim[] = { G_DIR_SEPARATOR, '\0' };
-	int cur, len;
-
-	g_return_val_if_fail(path != NULL, -1);
-
-	dir = g_new0(char, strlen(path) + 1);
-	components = g_strsplit(path, delim, -1);
-	len = 0;
-	for (cur = 0; components[cur] != NULL; cur++) {
-		/* If you don't know what you're doing on both
-		 * win32 and *NIX, stay the hell away from this code */
-		if(cur > 1)
-			dir[len++] = G_DIR_SEPARATOR;
-		strcpy(dir + len, components[cur]);
-		len += strlen(components[cur]);
-		if(cur == 0)
-			dir[len++] = G_DIR_SEPARATOR;
-
-		if(g_file_test(dir, G_FILE_TEST_IS_DIR)) {
-			continue;
-#ifdef _WIN32
-		/* allow us to create subdirs on UNC paths
-		 * (\\machinename\path\to\blah)
-		 * g_file_test() doesn't work on "\\machinename" */
-		} else if (cur == 2 && dir[0] == '\\' && dir[1] == '\\'
-				&& components[cur + 1] != NULL) {
-			continue;
-#endif
-		} else if(g_file_test(dir, G_FILE_TEST_EXISTS)) {
-			purple_debug_warning("build_dir", "bad path: %s\n", path);
-			g_strfreev(components);
-			g_free(dir);
-			return -1;
-		}
-
-		if (g_mkdir(dir, mode) < 0) {
-			purple_debug_warning("build_dir", "mkdir: %s\n", g_strerror(errno));
-			g_strfreev(components);
-			g_free(dir);
-			return -1;
-		}
-	}
-
-	g_strfreev(components);
-	g_free(dir);
-	return 0;
-#endif
 }
 
 /*
@@ -3218,30 +3093,13 @@
 gboolean
 purple_str_has_prefix(const char *s, const char *p)
 {
-#if GLIB_CHECK_VERSION(2,2,0)
 	return g_str_has_prefix(s, p);
-#else
-	g_return_val_if_fail(s != NULL, FALSE);
-	g_return_val_if_fail(p != NULL, FALSE);
-
-	return (!strncmp(s, p, strlen(p)));
-#endif
 }
 
 gboolean
 purple_str_has_suffix(const char *s, const char *x)
 {
-#if GLIB_CHECK_VERSION(2,2,0)
 	return g_str_has_suffix(s, x);
-#else
-	int off;
-
-	g_return_val_if_fail(s != NULL, FALSE);
-	g_return_val_if_fail(x != NULL, FALSE);
-
-	off = strlen(s) - strlen(x);
-	return (off >= 0 && purple_strequal(s + off, x));
-#endif
 }
 
 char *
@@ -5138,18 +4996,25 @@
 const gchar *
 purple_get_host_name(void)
 {
-#if GLIB_CHECK_VERSION(2,8,0)
 	return g_get_host_name();
-#else
-	static char hostname[256];
-	int ret = gethostname(hostname, sizeof(hostname));
-	hostname[sizeof(hostname) - 1] = '\0';
-
-	if (ret == -1 || hostname[0] == '\0') {
-		purple_debug_info("purple_get_host_name: ", "could not find host name");
-		return "localhost";
-	} else {
-		return hostname;
-	}
-#endif
 }
+
+gchar *
+purple_uuid_random(void)
+{
+	guint32 tmp, a, b;
+
+	tmp = g_random_int();
+	a = 0x4000 | (tmp & 0xFFF); /* 0x4000 to 0x4FFF */
+	tmp >>= 12;
+	b = ((1 << 3) << 12) | (tmp & 0x3FFF); /* 0x8000 to 0xBFFF */
+
+	tmp = g_random_int();
+
+	return g_strdup_printf("%08x-%04x-%04x-%04x-%04x%08x",
+			g_random_int(),
+			tmp & 0xFFFF,
+			a,
+			b,
+			(tmp >> 16) & 0xFFFF, g_random_int());
+}
--- a/libpurple/util.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/util.h	Mon Mar 08 22:53:02 2010 +0000
@@ -42,6 +42,7 @@
 typedef struct _PurpleKeyValuePair PurpleKeyValuePair;
 
 #include "account.h"
+#include "signals.h"
 #include "xmlnode.h"
 #include "notify.h"
 
@@ -515,18 +516,35 @@
 char *purple_markup_linkify(const char *str);
 
 /**
- * Unescapes HTML entities to their literal characters. Also translates
- * "<br>" to "\n".
- * For example "&amp;" is replaced by '&' and so on.
+ * Unescapes HTML entities to their literal characters in the text.
+ * For example "&amp;" is replaced by '&' and so on.  Also converts
+ * numerical entities (e.g. "&#38;" is also '&').
+ *
+ * This function currently supports the following named entities:
+ *     "&amp;", "&lt;", "&gt;", "&copy;", "&quot;", "&reg;", "&apos;"
+ *
+ * purple_unescape_html() is similar, but also converts "<br>" into "\n".
+ *
+ * @param text The string in which to unescape any HTML entities
  *
- * The following named entities are supported (in addition to numerical
- * entities):
- *    "&amp;", "&lt;", "&gt;", "&copy;", "&quot;", "&reg;", "&apos;"
+ * @return The text with HTML entities literalized.  You must g_free
+ *         this string when finished with it.
+ *
+ * @see purple_unescape_html()
+ * @since 2.7.0
+ */
+char *purple_unescape_text(const char *text);
+
+/**
+ * Unescapes HTML entities to their literal characters and converts
+ * "<br>" to "\n".  See purple_unescape_text() for more details.
  *
  * @param html The string in which to unescape any HTML entities
  *
  * @return The text with HTML entities literalized.  You must g_free
  *         this string when finished with it.
+ *
+ * @see purple_unescape_text()
  */
 char *purple_unescape_html(const char *html);
 
@@ -1429,6 +1447,14 @@
  */
 const gchar *purple_get_host_name(void);
 
+/**
+ * Returns a type 4 (random) UUID
+ *
+ * @return A UUID, caller is responsible for freeing it
+ * @since 2.7.0
+ */
+gchar *purple_uuid_random(void);
+
 #ifdef __cplusplus
 }
 #endif
--- a/libpurple/win32/global.mak	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/win32/global.mak	Mon Mar 08 22:53:02 2010 +0000
@@ -10,15 +10,14 @@
 
 # Locations of our various dependencies
 WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev
-ASPELL_TOP ?= $(WIN32_DEV_TOP)/aspell-dev-0-50-3-3
-GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.11-daa1
-GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0
+GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.16
+ENCHANT_TOP ?= $(WIN32_DEV_TOP)/enchant_1.5.0-2_win32
+GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0-2.14
 GTK_BIN ?= $(GTK_TOP)/bin
 BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK
-LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.6.30
+LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.7.4
 MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa2
-NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4
-NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.11.4
+NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.12.5-nspr-4.8.2
 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10.0
 SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.8
 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5
@@ -103,7 +102,7 @@
 ifeq "$(origin CC)" "default"
   CC := gcc.exe
 endif
-GMSGFMT ?= $(GTK_BIN)/msgfmt
+GMSGFMT ?= $(WIN32_DEV_TOP)/gettext-0.17/bin/msgfmt
 MAKENSIS ?= makensis.exe
 MAKENSISOPT ?= /
 PERL ?= /cygdrive/c/perl/bin/perl
--- a/libpurple/win32/libc_interface.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/win32/libc_interface.c	Mon Mar 08 22:53:02 2010 +0000
@@ -33,6 +33,7 @@
 #include "libc_internal.h"
 #include <glib/gstdio.h>
 
+/** This is redefined here because we can't include internal.h */
 #ifdef ENABLE_NLS
 #  include <locale.h>
 #  include <libintl.h>
@@ -61,7 +62,7 @@
 /* helpers */
 static int wpurple_is_socket( int fd ) {
 	int optval;
-	unsigned int optlen = sizeof(int);
+	int optlen = sizeof(int);
 
 	if( (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&optval, &optlen)) == SOCKET_ERROR ) {
 		int error = WSAGetLastError();
@@ -469,65 +470,7 @@
 /* stdio.h */
 
 int wpurple_rename (const char *oldname, const char *newname) {
-
-#if GLIB_CHECK_VERSION(2,8,5)
-
 	return g_rename(oldname, newname);
-
-#else
-
-	/* This is a ugly, but we still compile with 2.6.10 but use newer runtimes */
-	struct stat oldstat, newstat;
-
-	/* As of Glib 2.8.5, g_rename() uses MoveFileEx() with MOVEFILE_REPLACE_EXISTING to behave more sanely */
-	if (glib_check_version(2, 8, 5) == NULL) {
-		return g_rename(oldname, newname);
-	}
-
-	if(g_stat(oldname, &oldstat) == 0) {
-		/* newname exists */
-		if(g_stat(newname, &newstat) == 0) {
-			/* oldname is a dir */
-			if(S_ISDIR(oldstat.st_mode)) {
-				if(!S_ISDIR(newstat.st_mode)) {
-					return g_rename(oldname, newname);
-				}
-				/* newname is a dir */
-				else {
-					/* This is not quite right.. If newname is empty and
-					   is not a sub dir of oldname, newname should be
-					   deleted and oldname should be renamed.
-					*/
-					purple_debug(PURPLE_DEBUG_WARNING, "wpurple", "wpurple_rename does not behave here as it should\n");
-					return g_rename(oldname, newname);
-				}
-			}
-			/* oldname is not a dir */
-			else {
-				/* newname is a dir */
-				if(S_ISDIR(newstat.st_mode)) {
-					errno = EISDIR;
-					return -1;
-				}
-				/* newname is not a dir */
-				else {
-					g_remove(newname);
-					return g_rename(oldname, newname);
-				}
-			}
-		}
-		/* newname doesn't exist */
-		else
-			return g_rename(oldname, newname);
-	}
-	else {
-		/* oldname doesn't exist */
-		errno = ENOENT;
-		return -1;
-	}
-
-#endif
-
 }
 
 /* time.h */
@@ -1030,7 +973,7 @@
 
 		memset(zonename, 0, sizeof(zonename));
 		namesize = sizeof(zonename);
-		if ((r = RegQueryValueEx(key, "Std", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
+		if ((r = RegQueryValueEx(key, "Std", NULL, NULL, (LPBYTE)zonename, &namesize)) != ERROR_SUCCESS)
 		{
 			purple_debug_warning("wpurple", "could not query value for 'std' to identify Windows timezone: %i\n", (int) r);
 			RegCloseKey(key);
@@ -1045,7 +988,7 @@
 		}
 		memset(zonename, 0, sizeof(zonename));
 		namesize = sizeof(zonename);
-		if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
+		if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, (LPBYTE)zonename, &namesize)) != ERROR_SUCCESS)
 		{
 			purple_debug_warning("wpurple", "could not query value for 'dlt' to identify Windows timezone: %i\n", (int) r);
 			RegCloseKey(key);
@@ -1105,78 +1048,14 @@
 	return "";
 }
 
+int wpurple_g_access (const gchar *filename, int mode);
 /**
- * g_access:
- * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
- * @mode: as in access()
- *
- * A wrapper for the POSIX access() function. This function is used to
- * test a pathname for one or several of read, write or execute
- * permissions, or just existence. On Windows, the underlying access()
- * function in the C library only checks the READONLY attribute, and
- * does not look at the ACL at all. Software that needs to handle file
- * permissions on Windows more exactly should use the Win32 API.
- *
- * See the C library manual for more details about access().
- *
- * Returns: zero if the pathname refers to an existing file system
- * object that has all the tested permissions, or -1 otherwise or on
- * error.
- *
- * Since: 2.8
+ * @deprecated - remove for 3.0.0
  */
 int
-wpurple_g_access (const gchar *filename,
-	  int          mode)
+wpurple_g_access (const gchar *filename, int mode)
 {
-#if GLIB_CHECK_VERSION(2,8,0)
-
 	return g_access(filename, mode);
-
-#else
-
-  if (G_WIN32_HAVE_WIDECHAR_API ())
-    {
-      wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
-      int retval;
-      int save_errno;
-
-      if (wfilename == NULL)
-	{
-	  errno = EINVAL;
-	  return -1;
-	}
-
-      retval = _waccess (wfilename, mode);
-      save_errno = errno;
-
-      g_free (wfilename);
-
-      errno = save_errno;
-      return retval;
-    }
-  else
-    {
-      gchar *cp_filename = g_locale_from_utf8 (filename, -1, NULL, NULL, NULL);
-      int retval;
-      int save_errno;
-
-      if (cp_filename == NULL)
-	{
-	  errno = EINVAL;
-	  return -1;
-	}
-
-      retval = access (cp_filename, mode);
-      save_errno = errno;
-
-      g_free (cp_filename);
-
-      errno = save_errno;
-      return retval;
-    }
-
-#endif
 }
 
 
--- a/libpurple/win32/libc_interface.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/win32/libc_interface.h	Mon Mar 08 22:53:02 2010 +0000
@@ -115,11 +115,6 @@
 #define close( fd ) \
 wpurple_close( fd )
 
-#if !GLIB_CHECK_VERSION(2,8,0)
-#define g_access( filename, mode) \
-wpurple_g_access( filename, mode )
-#endif
-
 #ifndef sleep
 #define sleep(x) Sleep((x)*1000)
 #endif
@@ -134,21 +129,14 @@
 wpurple_gettimeofday( timeval, timezone )
 
 /* stdio.h */
+#undef snprintf
 #define snprintf _snprintf
+#undef vsnprintf
 #define vsnprintf _vsnprintf
 
 #define rename( oldname, newname ) \
 wpurple_rename( oldname, newname )
 
-#if GLIB_CHECK_VERSION(2,6,0)
-#ifdef g_rename
-# undef g_rename
-#endif
-/* This is necessary because we want rename on win32 to be able to overwrite an existing file, it is done in internal.h if GLib < 2.6*/
-#define g_rename(oldname, newname) \
-wpurple_rename(oldname, newname)
-#endif
-
 /* sys/stat.h */
 #define fchmod(a,b)
 
--- a/libpurple/win32/libc_internal.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/win32/libc_internal.h	Mon Mar 08 22:53:02 2010 +0000
@@ -141,10 +141,6 @@
 int wpurple_gethostname(char *name, size_t size);
 
 
-#if !GLIB_CHECK_VERSION(2,8,0)
-int wpurple_g_access(const gchar *filename, int mode);
-#endif
-
 /* stdio.h */
 int wpurple_rename(const char *oldname, const char *newname);
 
--- a/libpurple/win32/libpurplerc.rc.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/win32/libpurplerc.rc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -18,7 +18,7 @@
         VALUE "FileDescription", "LibPurple Library"
         VALUE "FileVersion", "@PURPLE_VERSION@"
         VALUE "InternalName", "libpurple"
-        VALUE "LegalCopyright", "Copyright (C) 1998-2007 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
+        VALUE "LegalCopyright", "Copyright (C) 1998-2010 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
         VALUE "OriginalFilename", "libpurple.dll"
         VALUE "ProductName", "LibPurple"
         VALUE "ProductVersion", "@PURPLE_VERSION@"
--- a/libpurple/win32/win32dep.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/win32/win32dep.c	Mon Mar 08 22:53:02 2010 +0000
@@ -22,7 +22,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  *
  */
-#define _WIN32_IE 0x500
+#define _WIN32_IE 0x501
 #include "internal.h"
 #include <winuser.h>
 
@@ -30,14 +30,6 @@
 #include "notify.h"
 
 /*
- *  DEFINES & MACROS
- */
-
-/* For shfolder.dll */
-typedef HRESULT (CALLBACK* LPFNSHGETFOLDERPATHA)(HWND, int, HANDLE, DWORD, LPSTR);
-typedef HRESULT (CALLBACK* LPFNSHGETFOLDERPATHW)(HWND, int, HANDLE, DWORD, LPWSTR);
-
-/*
  * LOCALS
  */
 static char *app_data_dir = NULL, *install_dir = NULL,
@@ -115,37 +107,12 @@
 
 /* Get paths to special Windows folders. */
 gchar *wpurple_get_special_folder(int folder_type) {
-	static LPFNSHGETFOLDERPATHA MySHGetFolderPathA = NULL;
-	static LPFNSHGETFOLDERPATHW MySHGetFolderPathW = NULL;
 	gchar *retval = NULL;
-
-	if (!MySHGetFolderPathW) {
-		MySHGetFolderPathW = (LPFNSHGETFOLDERPATHW)
-			wpurple_find_and_loadproc("shfolder.dll", "SHGetFolderPathW");
-	}
-
-	if (MySHGetFolderPathW) {
-		wchar_t utf_16_dir[MAX_PATH + 1];
+	wchar_t utf_16_dir[MAX_PATH + 1];
 
-		if (SUCCEEDED(MySHGetFolderPathW(NULL, folder_type, NULL,
-						SHGFP_TYPE_CURRENT, utf_16_dir))) {
-			retval = g_utf16_to_utf8(utf_16_dir, -1, NULL, NULL, NULL);
-		}
-	}
-
-	if (!retval) {
-		if (!MySHGetFolderPathA) {
-			MySHGetFolderPathA = (LPFNSHGETFOLDERPATHA)
-				wpurple_find_and_loadproc("shfolder.dll", "SHGetFolderPathA");
-		}
-		if (MySHGetFolderPathA) {
-			char locale_dir[MAX_PATH + 1];
-
-			if (SUCCEEDED(MySHGetFolderPathA(NULL, folder_type, NULL,
-							SHGFP_TYPE_CURRENT, locale_dir))) {
-				retval = g_locale_to_utf8(locale_dir, -1, NULL, NULL, NULL);
-			}
-		}
+	if (SUCCEEDED(SHGetFolderPathW(NULL, folder_type, NULL,
+					SHGFP_TYPE_CURRENT, utf_16_dir))) {
+		retval = g_utf16_to_utf8(utf_16_dir, -1, NULL, NULL, NULL);
 	}
 
 	return retval;
@@ -156,20 +123,11 @@
 
 	if (!initialized) {
 		char *tmp = NULL;
-		if (G_WIN32_HAVE_WIDECHAR_API()) {
-			wchar_t winstall_dir[MAXPATHLEN];
-			if (GetModuleFileNameW(NULL, winstall_dir,
-					MAXPATHLEN) > 0) {
-				tmp = g_utf16_to_utf8(winstall_dir, -1,
-					NULL, NULL, NULL);
-			}
-		} else {
-			gchar cpinstall_dir[MAXPATHLEN];
-			if (GetModuleFileNameA(NULL, cpinstall_dir,
-					MAXPATHLEN) > 0) {
-				tmp = g_locale_to_utf8(cpinstall_dir,
-					-1, NULL, NULL, NULL);
-			}
+		wchar_t winstall_dir[MAXPATHLEN];
+		if (GetModuleFileNameW(NULL, winstall_dir,
+				MAXPATHLEN) > 0) {
+			tmp = g_utf16_to_utf8(winstall_dir, -1,
+				NULL, NULL, NULL);
 		}
 
 		if (tmp == NULL) {
@@ -246,61 +204,33 @@
 	HKEY reg_key;
 	gboolean success = FALSE;
 
-	if(G_WIN32_HAVE_WIDECHAR_API()) {
-		wchar_t *wc_subkey = g_utf8_to_utf16(subkey, -1, NULL,
-			NULL, NULL);
-
-		if(RegOpenKeyExW(rootkey, wc_subkey, 0,
-				KEY_SET_VALUE, &reg_key) == ERROR_SUCCESS) {
-			wchar_t *wc_valname = NULL;
-
-			if (valname)
-				wc_valname = g_utf8_to_utf16(valname, -1,
-					NULL, NULL, NULL);
+	wchar_t *wc_subkey = g_utf8_to_utf16(subkey, -1, NULL,
+		NULL, NULL);
 
-			if(value) {
-				wchar_t *wc_value = g_utf8_to_utf16(value, -1,
-					NULL, NULL, NULL);
-				int len = (wcslen(wc_value) * sizeof(wchar_t)) + 1;
-				if(RegSetValueExW(reg_key, wc_valname, 0, REG_SZ,
-						(LPBYTE)wc_value, len
-						) == ERROR_SUCCESS)
-					success = TRUE;
-				g_free(wc_value);
-			} else
-				if(RegDeleteValueW(reg_key, wc_valname) ==  ERROR_SUCCESS)
-					success = TRUE;
+	if(RegOpenKeyExW(rootkey, wc_subkey, 0,
+			KEY_SET_VALUE, &reg_key) == ERROR_SUCCESS) {
+		wchar_t *wc_valname = NULL;
+
+		if (valname)
+			wc_valname = g_utf8_to_utf16(valname, -1,
+				NULL, NULL, NULL);
 
-			g_free(wc_valname);
-		}
-		g_free(wc_subkey);
-	} else {
-		char *cp_subkey = g_locale_from_utf8(subkey, -1, NULL,
-			NULL, NULL);
-		if(RegOpenKeyExA(rootkey, cp_subkey, 0,
-				KEY_SET_VALUE, &reg_key) == ERROR_SUCCESS) {
-			char *cp_valname = NULL;
-			if(valname)
-				cp_valname = g_locale_from_utf8(valname, -1,
-					NULL, NULL, NULL);
+		if(value) {
+			wchar_t *wc_value = g_utf8_to_utf16(value, -1,
+				NULL, NULL, NULL);
+			int len = (wcslen(wc_value) * sizeof(wchar_t)) + 1;
+			if(RegSetValueExW(reg_key, wc_valname, 0, REG_SZ,
+					(LPBYTE)wc_value, len
+					) == ERROR_SUCCESS)
+				success = TRUE;
+			g_free(wc_value);
+		} else
+			if(RegDeleteValueW(reg_key, wc_valname) ==  ERROR_SUCCESS)
+				success = TRUE;
 
-			if (value) {
-				char *cp_value = g_locale_from_utf8(value, -1,
-					NULL, NULL, NULL);
-				int len = strlen(cp_value) + 1;
-				if(RegSetValueExA(reg_key, cp_valname, 0, REG_SZ,
-						cp_value, len
-						) == ERROR_SUCCESS)
-					success = TRUE;
-				g_free(cp_value);
-			} else
-				if(RegDeleteValueA(reg_key, cp_valname) ==  ERROR_SUCCESS)
-					success = TRUE;
-
-			g_free(cp_valname);
-		}
-		g_free(cp_subkey);
+		g_free(wc_valname);
 	}
+	g_free(wc_subkey);
 
 	if(reg_key != NULL)
 		RegCloseKey(reg_key);
@@ -312,17 +242,11 @@
 	HKEY reg_key = NULL;
 	LONG rv;
 
-	if(G_WIN32_HAVE_WIDECHAR_API()) {
-		wchar_t *wc_subkey = g_utf8_to_utf16(subkey, -1, NULL,
-			NULL, NULL);
-		rv = RegOpenKeyExW(rootkey, wc_subkey, 0, access, &reg_key);
-		g_free(wc_subkey);
-	} else {
-		char *cp_subkey = g_locale_from_utf8(subkey, -1, NULL,
-			NULL, NULL);
-		rv = RegOpenKeyExA(rootkey, cp_subkey, 0, access, &reg_key);
-		g_free(cp_subkey);
-	}
+	wchar_t *wc_subkey = g_utf8_to_utf16(subkey, -1, NULL,
+		NULL, NULL);
+	rv = RegOpenKeyExW(rootkey, wc_subkey, 0, access, &reg_key);
+
+	g_free(wc_subkey);
 
 	if (rv != ERROR_SUCCESS) {
 		char *errmsg = g_win32_error_message(rv);
@@ -340,19 +264,11 @@
 static gboolean _reg_read(HKEY reg_key, const char *valname, LPDWORD type, LPBYTE data, LPDWORD data_len) {
 	LONG rv;
 
-	if(G_WIN32_HAVE_WIDECHAR_API()) {
-		wchar_t *wc_valname = NULL;
-		if (valname)
-			wc_valname = g_utf8_to_utf16(valname, -1, NULL, NULL, NULL);
-		rv = RegQueryValueExW(reg_key, wc_valname, 0, type, data, data_len);
-		g_free(wc_valname);
-	} else {
-		char *cp_valname = NULL;
-		if(valname)
-			cp_valname = g_locale_from_utf8(valname, -1, NULL, NULL, NULL);
-		rv = RegQueryValueExA(reg_key, cp_valname, 0, type, data, data_len);
-		g_free(cp_valname);
-	}
+	wchar_t *wc_valname = NULL;
+	if (valname)
+		wc_valname = g_utf8_to_utf16(valname, -1, NULL, NULL, NULL);
+	rv = RegQueryValueExW(reg_key, wc_valname, 0, type, data, data_len);
+	g_free(wc_valname);
 
 	if (rv != ERROR_SUCCESS) {
 		char *errmsg = g_win32_error_message(rv);
@@ -389,24 +305,13 @@
 
 	if(reg_key) {
 		if(_reg_read(reg_key, valname, &type, NULL, &nbytes) && type == REG_SZ) {
-			LPBYTE data;
-			if(G_WIN32_HAVE_WIDECHAR_API())
-				data = (LPBYTE) g_new(wchar_t, ((nbytes + 1) / sizeof(wchar_t)) + 1);
-			else
-				data = (LPBYTE) g_malloc(nbytes + 1);
+			LPBYTE data = (LPBYTE) g_new(wchar_t, ((nbytes + 1) / sizeof(wchar_t)) + 1);
 
 			if(_reg_read(reg_key, valname, &type, data, &nbytes)) {
-				if(G_WIN32_HAVE_WIDECHAR_API()) {
-					wchar_t *wc_temp = (wchar_t*) data;
-					wc_temp[nbytes / sizeof(wchar_t)] = '\0';
-					result = g_utf16_to_utf8(wc_temp, -1,
-						NULL, NULL, NULL);
-				} else {
-					char *cp_temp = (char*) data;
-					cp_temp[nbytes] = '\0';
-					result = g_locale_to_utf8(cp_temp, -1,
-						NULL, NULL, NULL);
-				}
+				wchar_t *wc_temp = (wchar_t*) data;
+				wc_temp[nbytes / sizeof(wchar_t)] = '\0';
+				result = g_utf16_to_utf8(wc_temp, -1,
+					NULL, NULL, NULL);
 			}
 			g_free(data);
 		}
--- a/libpurple/xmlnode.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/libpurple/xmlnode.c	Mon Mar 08 22:53:02 2010 +0000
@@ -545,31 +545,6 @@
 	return xml_with_declaration;
 }
 
-static char *purple_unescape_text(const char *in)
-{
-    GString *ret;
-    const char *c = in;
-
-    if (in == NULL)
-        return NULL;
-
-    ret = g_string_new("");
-    while (*c) {
-        int len;
-        const char *ent;
-
-        if ((ent = purple_markup_unescape_entity(c, &len)) != NULL) {
-            g_string_append(ret, ent);
-            c += len;
-        } else {
-            g_string_append_c(ret, *c);
-            c++;
-        }
-    }
-
-    return g_string_free(ret, FALSE);
-}
-
 struct _xmlnode_parser_data {
 	xmlnode *current;
 	gboolean error;
--- a/pidgin/Makefile.am	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/Makefile.am	Mon Mar 08 22:53:02 2010 +0000
@@ -21,6 +21,7 @@
 		win32/winpidgin.c \
 		win32/wspell.c \
 		win32/wspell.h \
+		win32/nsis/generate_gtk_zip.sh \
 		win32/nsis/pixmaps/pidgin-header.bmp \
 		win32/nsis/pixmaps/pidgin-intro.bmp \
 		win32/nsis/pixmaps/pidgin-install.ico \
@@ -48,6 +49,7 @@
 		win32/nsis/translations/kurdish.nsh \
 		win32/nsis/translations/lithuanian.nsh \
 		win32/nsis/translations/norwegian.nsh \
+		win32/nsis/translations/norwegian_nynorsk.nsh \
 		win32/nsis/translations/persian.nsh \
 		win32/nsis/translations/polish.nsh \
 		win32/nsis/translations/portuguese.nsh \
@@ -74,17 +76,12 @@
 
 pidgin_SOURCES = \
 	eggtrayicon.c \
-	pidgincombobox.c \
 	pidginstock.c \
 	gtkaccount.c \
 	gtkblist.c \
 	gtkblist-theme.c \
 	gtkblist-theme-loader.c \
-	gtkcelllayout.c \
 	gtkcellrendererexpander.c \
-	gtkcellrendererprogress.c \
-	gtkcellview.c \
-	gtkcellviewmenuitem.c \
 	gtkcertmgr.c \
 	gtkconn.c \
 	gtkconv.c \
@@ -92,9 +89,8 @@
 	gtkdialogs.c \
 	gtkdnd-hints.c \
 	gtkdocklet.c \
-	gtkdocklet-x11.c \
+	gtkdocklet-gtk.c \
 	gtkeventloop.c \
-	gtkexpander.c \
 	gtkft.c \
 	gtkicon-theme.c \
 	gtkicon-theme-loader.c \
@@ -135,13 +131,8 @@
 	gtkblist.h \
 	gtkblist-theme.h \
 	gtkblist-theme-loader.h \
-	gtkcelllayout.h \
 	gtkcellrendererexpander.h \
-	gtkcellrendererprogress.h \
-	gtkcellview.h \
-	gtkcellviewmenuitem.h \
 	gtkcertmgr.h \
-	pidgincombobox.h \
 	gtkconn.h \
 	gtkconv.h \
 	gtkconvwin.h \
@@ -150,7 +141,6 @@
 	gtkdnd-hints.h \
 	gtkdocklet.h \
 	gtkeventloop.h \
-	gtkexpander.h \
 	gtkft.h \
 	gtkicon-theme.h \
 	gtkicon-theme-loader.h \
--- a/pidgin/Makefile.mingw	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/Makefile.mingw	Mon Mar 08 22:53:02 2010 +0000
@@ -9,8 +9,6 @@
 
 DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES))
 
-NEEDED_DLLS = $(GTKSPELL_TOP)/gtkspell/libgtkspell.dll
-
 ##
 ## VARIABLE DEFINITIONS
 ##
@@ -43,14 +41,12 @@
 			-I$(GTK_TOP)/include/atk-1.0 \
 			-I$(GTK_TOP)/include/cairo \
 			-I$(GTK_TOP)/lib/gtk-2.0/include \
-			-I$(GTKSPELL_TOP) \
-			-I$(ASPELL_TOP)/include
+			-I$(GTKSPELL_TOP)/include/gtkspell-2.0
 
 LIB_PATHS +=		-L$(GTK_TOP)/lib \
 			-L$(PURPLE_TOP) \
 			-L$(PIDGIN_TOP) \
-			-L$(PIDGIN_IDLETRACK_TOP) \
-			-L$(ASPELL_TOP)/lib
+			-L$(PIDGIN_IDLETRACK_TOP)
 
 ##
 ##  SOURCES, OBJECTS
@@ -61,7 +57,6 @@
 			gtkblist-theme.c \
 			gtkblist.c \
 			gtkcellrendererexpander.c \
-			gtkcellrendererprogress.c \
 			gtkcertmgr.c \
 			gtkconn.c \
 			gtkconv.c \
@@ -70,7 +65,6 @@
 			gtkdnd-hints.c \
 			gtkdocklet.c \
 			gtkeventloop.c \
-			gtkexpander.c \
 			gtkft.c \
 			gtkicon-theme-loader.c \
 			gtkicon-theme.c \
@@ -153,7 +147,6 @@
 
 install_shallow: $(PIDGIN_INSTALL_DIR) $(EXE_TARGET).exe $(PIDGIN_TARGET).dll
 	cp $(EXE_TARGET).exe $(PIDGIN_TARGET).dll $(PIDGIN_INSTALL_DIR)
-	cp $(NEEDED_DLLS) $(PIDGIN_INSTALL_DIR)
 
 install: install_shallow all
 	$(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE) install
--- a/pidgin/gtkaccount.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkaccount.c	Mon Mar 08 22:53:02 2010 +0000
@@ -474,9 +474,7 @@
 
 	/* Username */
 	dialog->username_entry = gtk_entry_new();
-#if GTK_CHECK_VERSION(2,10,0)
 	g_object_set(G_OBJECT(dialog->username_entry), "truncate-multiline", TRUE, NULL);
-#endif
 
 	add_pref_box(dialog, vbox, _("_Username:"), dialog->username_entry);
 
@@ -2081,25 +2079,12 @@
 	return ret;
 }
 
-#if !GTK_CHECK_VERSION(2,2,0)
-static void
-get_selected_helper(GtkTreeModel *model, GtkTreePath *path,
-					GtkTreeIter *iter, gpointer user_data)
-{
-	*((gboolean *)user_data) = TRUE;
-}
-#endif
-
 static void
 account_selected_cb(GtkTreeSelection *sel, AccountsWindow *dialog)
 {
 	gboolean selected = FALSE;
 
-#if GTK_CHECK_VERSION(2,2,0)
 	selected = (gtk_tree_selection_count_selected_rows(sel) > 0);
-#else
-	gtk_tree_selection_selected_foreach(sel, get_selected_helper, &selected);
-#endif
 
 	gtk_widget_set_sensitive(dialog->modify_button, selected);
 	gtk_widget_set_sensitive(dialog->delete_button, selected);
--- a/pidgin/gtkblist.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkblist.c	Mon Mar 08 22:53:02 2010 +0000
@@ -137,13 +137,9 @@
 static struct pidgin_blist_sort_method *current_sort_method = NULL;
 static void sort_method_none(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
 
-/* The functions we use for sorting aren't available in gtk 2.0.x, and
- * segfault in 2.2.0.  2.2.1 is known to work, so I'll require that */
-#if GTK_CHECK_VERSION(2,2,1)
 static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
 static void sort_method_status(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
 static void sort_method_log_activity(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter);
-#endif
 static PidginBuddyList *gtkblist = NULL;
 
 static GList *groups_tree(void);
@@ -201,7 +197,6 @@
 
 static gboolean gtk_blist_window_state_cb(GtkWidget *w, GdkEventWindowState *event, gpointer data)
 {
-#if GTK_CHECK_VERSION(2,2,0)
 	if(event->changed_mask & GDK_WINDOW_STATE_WITHDRAWN) {
 		if(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)
 			purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", FALSE);
@@ -223,28 +218,6 @@
 		if (!(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED))
 			pidgin_blist_refresh_timer(purple_get_blist());
 	}
-#else
-	/* At least gtk+ 2.0.6 does not properly set the change_mask when unsetting a
-	 * GdkWindowState flag. To work around, the window state will be explicitly
-	 * queried on these older versions of gtk+. See pidgin ticket #739.
-	 */
-	GdkWindowState new_window_state = gdk_window_get_state(G_OBJECT(gtkblist->window->window));
-
-	if(new_window_state & GDK_WINDOW_STATE_WITHDRAWN) {
-		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", FALSE);
-	} else {
-		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", TRUE);
-		pidgin_blist_refresh_timer(purple_get_blist());
-	}
-
-	if(new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
-		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized", TRUE);
-	else
-		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_maximized", FALSE);
-
-	if (!(new_window_state & GDK_WINDOW_STATE_ICONIFIED))
-		pidgin_blist_refresh_timer(purple_get_blist());
-#endif
 
 	return FALSE;
 }
@@ -434,7 +407,6 @@
 	gtk_blist_join_chat(chat);
 }
 
-#if GTK_CHECK_VERSION(2,6,0)
 static void gtk_blist_renderer_editing_cancelled_cb(GtkCellRenderer *renderer, PurpleBuddyList *list)
 {
 	editing_blist = FALSE;
@@ -483,7 +455,6 @@
 	}
 	editing_blist = TRUE;
 }
-#endif
 
 static void
 gtk_blist_do_personize(GList *merges)
@@ -763,12 +734,8 @@
 	g_object_set(G_OBJECT(gtkblist->text_rend), "editable", TRUE, NULL);
 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW(gtkblist->treeview), FALSE);
 	gtk_widget_grab_focus(gtkblist->treeview);
-#if GTK_CHECK_VERSION(2,2,0)
 	gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(gtkblist->treeview), path,
 			gtkblist->text_column, gtkblist->text_rend, TRUE);
-#else
-	gtk_tree_view_set_cursor(GTK_TREE_VIEW(gtkblist->treeview), path, gtkblist->text_column, TRUE);
-#endif
 	gtk_tree_path_free(path);
 }
 
@@ -1241,9 +1208,9 @@
 
 		purple_blist_node_set_bool(node, "collapsed", TRUE);
 
-		for(cnode = node->child; cnode; cnode = cnode->next) {
+		for(cnode = purple_blist_node_get_first_child(node); cnode; cnode = purple_blist_node_get_sibling_next(cnode)) {
 			if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) {
-				gtknode = cnode->ui_data;
+				gtknode = purple_blist_node_get_ui_data(cnode);
 				if (!gtknode->contact_expanded)
 					continue;
 				gtknode->contact_expanded = FALSE;
@@ -1275,7 +1242,7 @@
 		else
 			buddy = (PurpleBuddy*)node;
 
-		pidgin_dialogs_im_with_user(buddy->account, buddy->name);
+		pidgin_dialogs_im_with_user(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy));
 	} else if (PURPLE_BLIST_NODE_IS_CHAT(node)) {
 		gtk_blist_join_chat((PurpleChat *)node);
 	} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
@@ -1295,9 +1262,9 @@
 	if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
 		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
 		if (PURPLE_BLIST_NODE_IS_BUDDY(node))
-			purple_blist_request_add_chat(NULL, (PurpleGroup*)node->parent->parent, NULL, NULL);
+			purple_blist_request_add_chat(NULL, purple_buddy_get_group(PURPLE_BUDDY(node)), NULL, NULL);
 		if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_CHAT(node))
-			purple_blist_request_add_chat(NULL, (PurpleGroup*)node->parent, NULL, NULL);
+			purple_blist_request_add_chat(NULL, purple_contact_get_group(PURPLE_CONTACT(node)), NULL, NULL);
 		else if (PURPLE_BLIST_NODE_IS_GROUP(node))
 			purple_blist_request_add_chat(NULL, (PurpleGroup*)node, NULL, NULL);
 	}
@@ -1315,13 +1282,13 @@
 	if(gtk_tree_selection_get_selected(sel, NULL, &iter)){
 		gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
 		if (PURPLE_BLIST_NODE_IS_BUDDY(node)) {
-			purple_blist_request_add_buddy(NULL, NULL, ((PurpleGroup*)node->parent->parent)->name,
-					NULL);
-		} else if (PURPLE_BLIST_NODE_IS_CONTACT(node)
-				|| PURPLE_BLIST_NODE_IS_CHAT(node)) {
-			purple_blist_request_add_buddy(NULL, NULL, ((PurpleGroup*)node->parent)->name, NULL);
+			PurpleGroup *group = purple_buddy_get_group(PURPLE_BUDDY(node));
+			purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(group), NULL);
+		} else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || PURPLE_BLIST_NODE_IS_CHAT(node)) {
+			PurpleGroup *group = purple_contact_get_group(PURPLE_CONTACT(node));
+			purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(group), NULL);
 		} else if (PURPLE_BLIST_NODE_IS_GROUP(node)) {
-			purple_blist_request_add_buddy(NULL, NULL, ((PurpleGroup*)node)->name, NULL);
+			purple_blist_request_add_buddy(NULL, NULL, purple_group_get_name(PURPLE_GROUP(node)), NULL);
 		}
 	}
 	else {
@@ -1373,11 +1340,11 @@
 	if(!PURPLE_BLIST_NODE_IS_CONTACT(node))
 		return;
 
-	gtknode = (struct _pidgin_blist_node *)node->ui_data;
+	gtknode = purple_blist_node_get_ui_data(node);
 
 	gtknode->contact_expanded = TRUE;
 
-	for(bnode = node->child; bnode; bnode = bnode->next) {
+	for(bnode = purple_blist_node_get_first_child(node); bnode; bnode = purple_blist_node_get_sibling_next(bnode)) {
 		pidgin_blist_update(NULL, bnode);
 	}
 
@@ -1392,7 +1359,7 @@
 		/* Let the treeview draw so it knows where to scroll */
 		ex->treeview = GTK_TREE_VIEW(gtkblist->treeview);
 		ex->path = path;
-		ex->node = node->child;
+		ex->node = purple_blist_node_get_first_child(node);
 		g_idle_add(scroll_to_expanded_cell, ex);
 	}
 }
@@ -1406,11 +1373,11 @@
 	if(!PURPLE_BLIST_NODE_IS_CONTACT(node))
 		return;
 
-	gtknode = (struct _pidgin_blist_node *)node->ui_data;
+	gtknode = purple_blist_node_get_ui_data(node);
 
 	gtknode->contact_expanded = FALSE;
 
-	for(bnode = node->child; bnode; bnode = bnode->next) {
+	for(bnode = purple_blist_node_get_first_child(node); bnode; bnode = purple_blist_node_get_sibling_next(bnode)) {
 		pidgin_blist_update(NULL, bnode);
 	}
 }
@@ -1501,10 +1468,10 @@
 	submenu = gtk_menu_new();
 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
 
-	for (group = purple_blist_get_root(); group; group = group->next) {
-		if (group->type != PURPLE_BLIST_GROUP_NODE)
+	for (group = purple_blist_get_root(); group; group = purple_blist_node_get_sibling_next(group)) {
+		if (!PURPLE_BLIST_NODE_IS_GROUP(group))
 			continue;
-		if (group == node->parent)
+		if (group == purple_blist_node_get_parent(node))
 			continue;
 		menuitem = pidgin_new_item_from_stock(submenu, purple_group_get_name((PurpleGroup *)group), NULL,
 						      G_CALLBACK(gtk_blist_menu_move_to_cb), node, 0, 0, NULL);
@@ -1515,6 +1482,8 @@
 
 void
 pidgin_blist_make_buddy_menu(GtkWidget *menu, PurpleBuddy *buddy, gboolean sub) {
+	PurpleAccount *account = NULL;
+	PurpleConnection *pc = NULL;
 	PurplePluginProtocolInfo *prpl_info;
 	PurpleContact *contact;
 	PurpleBlistNode *node;
@@ -1523,13 +1492,16 @@
 	g_return_if_fail(menu);
 	g_return_if_fail(buddy);
 
-	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(buddy->account->gc->prpl);
-
-	node = (PurpleBlistNode*)buddy;
+	account = purple_buddy_get_account(buddy);
+	pc = purple_account_get_connection(account);
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(pc));
+
+	node = PURPLE_BLIST_NODE(buddy);
 
 	contact = purple_buddy_get_contact(buddy);
 	if (contact) {
-		contact_expanded = ((struct _pidgin_blist_node *)(((PurpleBlistNode*)contact)->ui_data))->contact_expanded;
+		PidginBlistNode *node = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(contact));
+		contact_expanded = node->contact_expanded;
 	}
 
 	if (prpl_info && prpl_info->get_info) {
@@ -2688,39 +2660,30 @@
 
 /* Altered from do_colorshift in gnome-panel */
 static void
-do_alphashift (GdkPixbuf *dest, GdkPixbuf *src, int shift)
+do_alphashift(GdkPixbuf *pixbuf, int shift)
 {
 	gint i, j;
-	gint width, height, has_alpha, srcrowstride, destrowstride;
-	guchar *target_pixels;
-	guchar *original_pixels;
-	guchar *pixsrc;
-	guchar *pixdest;
+	gint width, height, padding;
+	guchar *pixels;
 	int val;
-	guchar a;
-
-	has_alpha = gdk_pixbuf_get_has_alpha (src);
-	if (!has_alpha)
+
+	if (!gdk_pixbuf_get_has_alpha(pixbuf))
 	  return;
 
-	width = gdk_pixbuf_get_width (src);
-	height = gdk_pixbuf_get_height (src);
-	srcrowstride = gdk_pixbuf_get_rowstride (src);
-	destrowstride = gdk_pixbuf_get_rowstride (dest);
-	target_pixels = gdk_pixbuf_get_pixels (dest);
-	original_pixels = gdk_pixbuf_get_pixels (src);
+	width = gdk_pixbuf_get_width(pixbuf);
+	height = gdk_pixbuf_get_height(pixbuf);
+	padding = gdk_pixbuf_get_rowstride(pixbuf) - width * 4;
+	pixels = gdk_pixbuf_get_pixels(pixbuf);
 
 	for (i = 0; i < height; i++) {
-		pixdest = target_pixels + i*destrowstride;
-		pixsrc = original_pixels + i*srcrowstride;
 		for (j = 0; j < width; j++) {
-			*(pixdest++) = *(pixsrc++);
-			*(pixdest++) = *(pixsrc++);
-			*(pixdest++) = *(pixsrc++);
-			a = *(pixsrc++);
-			val = a - shift;
-			*(pixdest++) = CLAMP(val, 0, 255);
-		}
+			pixels++;
+			pixels++;
+			pixels++;
+			val = *pixels - shift;
+			*(pixels++) = CLAMP(val, 0, 255);
+		}
+		pixels += padding;
 	}
 }
 
@@ -3065,7 +3028,6 @@
 						current_height-1,td->avatar_width+2, td->avatar_height+2);
 		}
 
-#if GTK_CHECK_VERSION(2,2,0)
 		if (td->status_icon) {
 			if (dir == GTK_TEXT_DIR_RTL)
 				gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, td->status_icon,
@@ -3092,16 +3054,6 @@
 					current_height + ((td->name_height / 2) - (PRPL_SIZE / 2)),
 					-1 , -1, GDK_RGB_DITHER_NONE, 0, 0);
 
-#else
-		if (td->status_icon) {
-			gdk_pixbuf_render_to_drawable(td->status_icon, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 12, current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
-		}
-		if(td->avatar)
-			gdk_pixbuf_render_to_drawable(td->avatar,
-					GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0,
-					max_width - (td->avatar_width + TOOLTIP_BORDER),
-					current_height, -1, -1, GDK_RGB_DITHER_NONE, 0, 0);
-#endif
 		if (td->name_layout) {
 			if (dir == GTK_TEXT_DIR_RTL) {
 				gtk_paint_layout(style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, FALSE,
@@ -3480,12 +3432,13 @@
 	/* Help */
 	{ N_("/_Help"), NULL, NULL, 0, "<Branch>", NULL },
 	{ N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "<StockItem>", GTK_STOCK_HELP },
+	{ "/Help/sep1", NULL, NULL, 0, "<Separator>", NULL },
+	{ N_("/Help/_Build Information"), NULL, pidgin_dialogs_buildinfo, 0, "<Item>", NULL },
 	{ N_("/Help/_Debug Window"), NULL, toggle_debug, 0, "<Item>", NULL },
-#if GTK_CHECK_VERSION(2,6,0)
+	{ N_("/Help/De_veloper Information"), NULL, pidgin_dialogs_developers, 0, "<Item>", NULL },
+	{ N_("/Help/_Translator Information"), NULL, pidgin_dialogs_translators, 0, "<Item>", NULL },
+	{ "/Help/sep2", NULL, NULL, 0, "<Separator>", NULL },
 	{ N_("/Help/_About"), NULL, pidgin_dialogs_about, 4,  "<StockItem>", GTK_STOCK_ABOUT },
-#else
-	{ N_("/Help/_About"), NULL, pidgin_dialogs_about, 4,  "<Item>", NULL },
-#endif
 };
 
 /*********************************************************
@@ -3705,7 +3658,8 @@
 
 
 		/* Offline? */
-		/* FIXME: Why is this status special-cased by the core? -- rlaager */
+		/* FIXME: Why is this status special-cased by the core? --rlaager
+		 * FIXME: Alternatively, why not have the core do all of them? --rlaager */
 		if (!PURPLE_BUDDY_IS_ONLINE(b)) {
 			purple_notify_user_info_add_pair(user_info, _("Status"), _("Offline"));
 		}
@@ -3800,6 +3754,24 @@
 	return pb;
 }
 
+static char *get_mood_icon_path(const char *mood)
+{
+	char *path;
+
+	if (!strcmp(mood, "busy")) {
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin",
+		                        "status", "16", "busy.png", NULL);
+	} else if (!strcmp(mood, "hiptop")) {
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin",
+		                        "emblems", "16", "hiptop.png", NULL);
+	} else {
+		char *filename = g_strdup_printf("%s.png", mood);
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin",
+		                        "emotes", "small", filename, NULL);
+		g_free(filename);
+	}
+	return path;
+}
 
 GdkPixbuf *
 pidgin_blist_get_emblem(PurpleBlistNode *node)
@@ -3811,7 +3783,7 @@
 	PurplePluginProtocolInfo *prpl_info;
 	const char *name = NULL;
 	char *filename, *path;
-	PurplePresence *p;
+	PurplePresence *p = NULL;
 	PurpleStatus *tune;
 
 	if(PURPLE_BLIST_NODE_IS_CONTACT(node)) {
@@ -3824,14 +3796,17 @@
 		gtkbuddynode = node->ui_data;
 		p = purple_buddy_get_presence(buddy);
 		if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
-			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems",
-						"16", "mobile.png", NULL);
+			/* This emblem comes from the small emoticon set now,
+			 * to reduce duplication. */
+			path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes",
+						"small", "mobile.png", NULL);
 			return _pidgin_blist_get_cached_emblem(path);
 		}
 
 		if (((struct _pidgin_blist_node*)(node->parent->ui_data))->contact_expanded) {
-			if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"))
-				return pidgin_create_prpl_icon(((PurpleBuddy*)node)->account, PIDGIN_PRPL_ICON_SMALL);
+			if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons"))
+				return NULL;
+			return pidgin_create_prpl_icon(((PurpleBuddy*)node)->account, PIDGIN_PRPL_ICON_SMALL);
 		}
 	} else {
 		return NULL;
@@ -3844,9 +3819,14 @@
 		return _pidgin_blist_get_cached_emblem(path);
 	}
 
-	p = purple_buddy_get_presence(buddy);
+	/* If we came through the contact code flow above, we didn't need
+	 * to get the presence until now. */
+	if (p == NULL)
+		p = purple_buddy_get_presence(buddy);
+
 	if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
-		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "mobile.png", NULL);
+		/* This emblem comes from the small emoticon set now, to reduce duplication. */
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", "small", "mobile.png", NULL);
 		return _pidgin_blist_get_cached_emblem(path);
 	}
 
@@ -3865,7 +3845,8 @@
 			return _pidgin_blist_get_cached_emblem(path);
 		}
 		/* Regular old "tune" is the only one in all protocols. */
-		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL);
+		/* This emblem comes from the small emoticon set now, to reduce duplication. */
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", "small", "music.png", NULL);
 		return _pidgin_blist_get_cached_emblem(path);
 	}
 
@@ -3877,13 +3858,24 @@
 	if (prpl_info && prpl_info->list_emblem)
 		name = prpl_info->list_emblem(buddy);
 
-	if (name == NULL)
-		return NULL;
-
-	filename = g_strdup_printf("%s.png", name);
-
-	path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", filename, NULL);
-	g_free(filename);
+	if (name == NULL) {
+		PurpleStatus *status;
+
+		if (!purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOOD))
+			return NULL;
+
+		status = purple_presence_get_status(p, "mood");
+		name = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+		
+		if (!(name && *name))
+			return NULL;
+
+		path = get_mood_icon_path(name);
+	} else {
+		filename = g_strdup_printf("%s.png", name);
+		path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", filename, NULL);
+		g_free(filename);
+	}
 
 	/* _pidgin_blist_get_cached_emblem() assumes ownership of path */
 	return _pidgin_blist_get_cached_emblem(path);
@@ -4075,44 +4067,11 @@
 				g_free(tmp);
 				tmp = new;
 			}
-			/* add ... to messages that are too long, GTK 2.6+ does it automatically */
-#if !GTK_CHECK_VERSION(2,6,0)
-			if(tmp) {
-				char buf[32];
-				char *c = tmp;
-				int length = 0, vis=0;
-				gboolean inside = FALSE;
-				g_strdelimit(tmp, "\n", ' ');
-				purple_str_strip_char(tmp, '\r');
-
-				while(*c && vis < 20) {
-					if(*c == '&')
-						inside = TRUE;
-					else if(*c == ';')
-						inside = FALSE;
-					if(!inside)
-						vis++;
-					c = g_utf8_next_char(c); /* this is fun */
-				}
-
-				length = c - tmp;
-
-				if(vis == 20)
-					g_snprintf(buf, sizeof(buf), "%%.%ds...", length);
-				else
-					g_snprintf(buf, sizeof(buf), "%%s ");
-
-				statustext = g_strdup_printf(buf, tmp);
-
-				g_free(tmp);
-			}
-#else
 			if(tmp) {
 				g_strdelimit(tmp, "\n", ' ');
 				purple_str_strip_char(tmp, '\r');
 			}
 			statustext = tmp;
-#endif
 		}
 
 		if(!purple_presence_is_online(presence) && !statustext)
@@ -4671,11 +4630,9 @@
 	const char *id;
 
 	pidgin_blist_sort_method_reg("none", _("Manually"), sort_method_none);
-#if GTK_CHECK_VERSION(2,2,1)
 	pidgin_blist_sort_method_reg("alphabetical", _("Alphabetically"), sort_method_alphabetical);
 	pidgin_blist_sort_method_reg("status", _("By status"), sort_method_status);
 	pidgin_blist_sort_method_reg("log_size", _("By recent log activity"), sort_method_log_activity);
-#endif
 
 	id = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/sort_type");
 	if (id == NULL) {
@@ -4699,9 +4656,7 @@
 	}
 
 	redo_buddy_list(purple_get_blist(), FALSE, FALSE);
-#if GTK_CHECK_VERSION(2,6,0)
 	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview));
-#endif
 
 	if (node)
 	{
@@ -4737,11 +4692,7 @@
 
 	/* this is far too ugly thanks to me not wanting to fix #3989 properly right now */
 	if (priv->error_scrollbook != NULL) {
-#if GTK_CHECK_VERSION(2,2,0)
 		errors = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->error_scrollbook->notebook));
-#else
-		errors = g_list_length(GTK_NOTEBOOK(priv->error_scrollbook->notebook)->children);
-#endif
 	}
 	if ((list = purple_accounts_get_all_active()) != NULL || errors) {
 		gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkblist->notebook), 1);
@@ -4818,23 +4769,11 @@
 	gdk_window_set_cursor(widget->window, gtkblist->hand_cursor);
 
 	if (gtkblist->headline_close) {
-#if GTK_CHECK_VERSION(2,2,0)
 		gdk_draw_pixbuf(widget->window, NULL, gtkblist->headline_close,
-#else
-		gdk_pixbuf_render_to_drawable(gtkblist->headline_close,
-				GDK_DRAWABLE(widget->window), NULL,
-#endif
 					0, 0,
 					widget->allocation.width - 2 - HEADLINE_CLOSE_SIZE, 2,
 					HEADLINE_CLOSE_SIZE, HEADLINE_CLOSE_SIZE,
 					GDK_RGB_DITHER_NONE, 0, 0);
-#if 0
-		/* The presence of one opening paren in each branch of
-		 * GTK_CHECK_VERSION confuses vim's bracket matching for the
-		 * rest of the file.
-		 */
-		)
-#endif
 		gtk_paint_focus(widget->style, widget->window, GTK_STATE_PRELIGHT,
 				NULL, widget, NULL,
 				widget->allocation.width - HEADLINE_CLOSE_SIZE - 3, 1,
@@ -5193,9 +5132,7 @@
 	gtk_label_set_markup(GTK_LABEL(label), markup);
 	g_free(markup);
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-#if GTK_CHECK_VERSION(2,6,0)
 	g_object_set(G_OBJECT(label), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 #if GTK_CHECK_VERSION(2,12,0)
 	{ /* avoid unused variable warnings on pre-2.12 Gtk */
 		char *description =
@@ -5398,12 +5335,7 @@
 		return;
 
 	tooltips = gtk_tooltips_new ();
-#if GLIB_CHECK_VERSION(2,10,0)
 	g_object_ref_sink (tooltips);
-#else
-	g_object_ref(tooltips);
-	gtk_object_sink(GTK_OBJECT(tooltips));
-#endif
 
 	gtk_tooltips_force_window (tooltips);
 #if GTK_CHECK_VERSION(2, 12, 0)
@@ -5511,10 +5443,8 @@
 	gtk_tree_view_column_set_attributes(column, rend,
 					    "visible", GROUP_EXPANDER_VISIBLE_COLUMN,
 					    "expander-visible", GROUP_EXPANDER_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
 					    "sensitive", GROUP_EXPANDER_COLUMN,
 					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
 					    NULL);
 
 	/* contact */
@@ -5523,10 +5453,8 @@
 	gtk_tree_view_column_set_attributes(column, rend,
 					    "visible", CONTACT_EXPANDER_VISIBLE_COLUMN,
 					    "expander-visible", CONTACT_EXPANDER_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
 					    "sensitive", CONTACT_EXPANDER_COLUMN,
 					    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
 					    NULL);
 
 	for (i = 0; i < 5; i++) {
@@ -5538,9 +5466,7 @@
 			gtk_tree_view_column_set_attributes(column, rend,
 							    "pixbuf", STATUS_ICON_COLUMN,
 							    "visible", STATUS_ICON_VISIBLE_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
 							    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
 							    NULL);
 			g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
 
@@ -5549,20 +5475,14 @@
 			gtkblist->text_rend = rend = gtk_cell_renderer_text_new();
 			gtk_tree_view_column_pack_start(column, rend, TRUE);
 			gtk_tree_view_column_set_attributes(column, rend,
-#if GTK_CHECK_VERSION(2,6,0)
 							    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
 							    "markup", NAME_COLUMN,
 							    NULL);
-#if GTK_CHECK_VERSION(2,6,0)
 			g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL);
 			g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list);
-#endif
 			g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list);
 			g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
-#if GTK_CHECK_VERSION(2,6,0)
 			g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 
 			/* idle */
 			rend = gtk_cell_renderer_text_new();
@@ -5571,9 +5491,7 @@
 			gtk_tree_view_column_set_attributes(column, rend,
 							    "markup", IDLE_COLUMN,
 							    "visible", IDLE_VISIBLE_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
 							    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
 							    NULL);
 		} else if (emblem == i) {
 			/* emblem */
@@ -5581,9 +5499,7 @@
 			g_object_set(rend, "xalign", 1.0, "yalign", 0.5, "ypad", 0, "xpad", 3, NULL);
 			gtk_tree_view_column_pack_start(column, rend, FALSE);
 			gtk_tree_view_column_set_attributes(column, rend, "pixbuf", EMBLEM_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
 									  "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
 									  "visible", EMBLEM_VISIBLE_COLUMN, NULL);
 
 		} else if (protocol_icon == i) {
@@ -5593,9 +5509,7 @@
 			gtk_tree_view_column_set_attributes(column, rend,
 							   "pixbuf", PROTOCOL_ICON_COLUMN,
 							   "visible", PROTOCOL_ICON_VISIBLE_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
 							   "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
 							  NULL);
 			g_object_set(rend, "xalign", 0.0, "xpad", 3, "ypad", 0, NULL);
 
@@ -5605,9 +5519,7 @@
 			g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL);
 			gtk_tree_view_column_pack_start(column, rend, FALSE);
 			gtk_tree_view_column_set_attributes(column, rend, "pixbuf", BUDDY_ICON_COLUMN,
-#if GTK_CHECK_VERSION(2,6,0)
 							    "cell-background-gdk", BGCOLOR_COLUMN,
-#endif
 							    "visible", BUDDY_ICON_VISIBLE_COLUMN,
 							    NULL);
 		}
@@ -6446,7 +6358,7 @@
 		g_object_ref(G_OBJECT(gtkblist->empty_avatar));
 		avatar = gtkblist->empty_avatar;
 	} else if ((!PURPLE_BUDDY_IS_ONLINE(buddy) || purple_presence_is_idle(presence))) {
-		do_alphashift(avatar, avatar, 77);
+		do_alphashift(avatar, 77);
 	}
 
 	emblem = pidgin_blist_get_emblem((PurpleBlistNode*) buddy);
@@ -6794,9 +6706,6 @@
 			return;
 	}
 
-#if !GTK_CHECK_VERSION(2,6,0)
-	gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview));
-#endif
 }
 
 static void pidgin_blist_destroy(PurpleBuddyList *list)
@@ -7595,8 +7504,6 @@
 			sibling ? &sibling_iter : NULL);
 }
 
-#if GTK_CHECK_VERSION(2,2,1)
-
 static void sort_method_alphabetical(PurpleBlistNode *node, PurpleBuddyList *blist, GtkTreeIter groupiter, GtkTreeIter *cur, GtkTreeIter *iter)
 {
 	GtkTreeIter more_z;
@@ -7840,8 +7747,6 @@
 	}
 }
 
-#endif
-
 static void
 plugin_act(GtkObject *obj, PurplePluginAction *pam)
 {
@@ -7910,6 +7815,101 @@
 	purple_account_set_enabled(account, PIDGIN_UI, FALSE);
 }
 
+static void
+edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields)
+{
+	PurpleRequestField *mood_field, *text_field;
+	GList *l;
+
+	mood_field = purple_request_fields_get_field(fields, "mood");
+	text_field = purple_request_fields_get_field(fields, "text");
+	l = purple_request_field_list_get_selected(mood_field);
+
+	if (l) {
+		const char *mood = purple_request_field_list_get_data(mood_field, l->data);
+		const char *text = purple_request_field_string_get_value(text_field);
+		PurpleAccount *account = purple_connection_get_account(gc);
+
+		if (mood != NULL && !purple_strequal(mood, "")) {
+			if (text) {
+				purple_account_set_status(account, "mood", TRUE,
+			                          	PURPLE_MOOD_NAME, mood,
+				    				  	PURPLE_MOOD_COMMENT, text,
+			                          	NULL);
+			} else {
+				purple_account_set_status(account, "mood", TRUE,
+			                          	PURPLE_MOOD_NAME, mood,
+			                          	NULL);
+			}
+		} else {
+			purple_account_set_status(account, "mood", FALSE, NULL);
+		}
+	}
+}
+
+static void
+set_mood_cb(GtkWidget *widget, PurpleAccount *account)
+{
+	PurplePresence *presence = purple_account_get_presence(account);
+	PurpleStatus *status = purple_presence_get_status(presence, "mood");
+	const char *current_mood;
+	PurpleRequestFields *fields;
+	PurpleRequestFieldGroup *g;
+	PurpleRequestField *f;
+	PurpleConnection *gc = purple_account_get_connection(account);
+	PurplePluginProtocolInfo *prpl_info;
+	PurpleMood *mood;
+	
+	g_return_if_fail(gc->prpl != NULL);
+	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+	current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME);
+
+	fields = purple_request_fields_new();
+	g = purple_request_field_group_new(NULL);
+	f = purple_request_field_list_new("mood", _("Please select your mood from the list"));
+
+	purple_request_field_list_add(f, _("None"), "");
+	if (current_mood == NULL)
+		purple_request_field_list_add_selected(f, _("None"));
+
+	/* TODO: rlaager wants this sorted. */
+	for (mood = prpl_info->get_moods(account);
+	     mood->mood != NULL ; mood++) {
+		char *path;
+
+		if (mood->mood == NULL || mood->description == NULL)
+			continue;
+
+		path = get_mood_icon_path(mood->mood);
+		purple_request_field_list_add_icon(f, _(mood->description),
+				path, (gpointer)mood->mood);
+		g_free(path);
+
+		if (current_mood && !strcmp(current_mood, mood->mood))
+			purple_request_field_list_add_selected(f, _(mood->description));
+	}
+	purple_request_field_group_add_field(g, f);
+
+	purple_request_fields_add_group(fields, g);
+
+	/* if the connection allows setting a mood message */
+	if (gc->flags & PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES) {
+		g = purple_request_field_group_new(NULL);
+		f = purple_request_field_string_new("text",
+		    _("Message (optional)"), NULL, FALSE);
+		purple_request_field_group_add_field(g, f);
+		purple_request_fields_add_group(fields, g);
+	}
+
+	purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"),
+                              NULL, fields,
+                              _("OK"), G_CALLBACK(edit_mood_cb),
+                              _("Cancel"), NULL,
+                              purple_connection_get_account(gc),
+                              NULL, NULL, gc);
+}
+
 void
 pidgin_blist_update_accounts_menu(void)
 {
@@ -7989,6 +7989,7 @@
 		PurpleAccount *account = NULL;
 		GdkPixbuf *pixbuf = NULL;
 		PurplePlugin *plugin = NULL;
+		PurplePluginProtocolInfo *prpl_info;
 
 		account = accounts->data;
 
@@ -8028,8 +8029,34 @@
 
 		gc = purple_account_get_connection(account);
 		plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? gc->prpl : NULL;
-		if (plugin && PURPLE_PLUGIN_HAS_ACTIONS(plugin)) {
-			build_plugin_actions(submenu, plugin, gc);
+		prpl_info = plugin ? PURPLE_PLUGIN_PROTOCOL_INFO(plugin) : NULL;
+		
+		if (prpl_info &&
+		    (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) ||
+			 PURPLE_PLUGIN_HAS_ACTIONS(plugin))) {
+			if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) &&
+			    gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) {
+				GList *types;
+
+				for (types = purple_account_get_status_types(account);
+			     	types != NULL ; types = types->next) {
+					PurpleStatusType *type = types->data;
+
+					if (strcmp(purple_status_type_get_id(type), "mood") != 0)
+						continue;
+
+					menuitem = gtk_menu_item_new_with_mnemonic(_("Set _Mood..."));
+					g_signal_connect(G_OBJECT(menuitem), "activate",
+					         	G_CALLBACK(set_mood_cb), account);
+					gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+
+					/* Be safe.  It shouldn't match more than once anyway */
+					break;
+				}
+			}
+			if (PURPLE_PLUGIN_HAS_ACTIONS(plugin)) {
+				build_plugin_actions(submenu, plugin, gc);
+			}
 		} else {
 			menuitem = gtk_menu_item_new_with_label(_("No actions available"));
 			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
--- a/pidgin/gtkblist.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkblist.h	Mon Mar 08 22:53:02 2010 +0000
@@ -132,9 +132,9 @@
 	gpointer priv;                   /**< Pointer to opaque private data */
 };
 
-#define PIDGIN_BLIST(list) ((PidginBuddyList *)(list)->ui_data)
+#define PIDGIN_BLIST(list) ((PidginBuddyList *)purple_blist_get_ui_data())
 #define PIDGIN_IS_PIDGIN_BLIST(list) \
-	((list)->ui_ops == pidgin_blist_get_ui_ops())
+	(purple_blist_get_ui_ops() == pidgin_blist_get_ui_ops())
 
 /**************************************************************************
  * @name GTK+ Buddy List API
@@ -196,11 +196,14 @@
 void pidgin_blist_update_refresh_timeout(void);
 
 /**
- * Returns the blist emblem
+ * Returns the blist emblem.
+ *
+ * This may be an existing pixbuf that has been given an additional ref,
+ * so it shouldn't be modified.
  *
  * @param node   The node to return an emblem for
  *
- * @return  A newly created GdkPixbuf, or NULL
+ * @return  A GdkPixbuf for the emblem to show, or NULL
  */
 GdkPixbuf *
 pidgin_blist_get_emblem(PurpleBlistNode *node);
--- a/pidgin/gtkcelllayout.c	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-/* gtkcelllayout.c
- * Copyright (C) 2003  Kristian Rietveld  <kris@gtk.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- */
-
-/*
-#include <config.h>
-*/
-#include <gtk/gtkversion.h>
-#if !GTK_CHECK_VERSION(2,4,0)
-#include "gtkcelllayout.h"
-
-GType
-gtk_cell_layout_get_type (void)
-{
-  static GType cell_layout_type = 0;
-
-  if (! cell_layout_type)
-    {
-      static const GTypeInfo cell_layout_info =
-      {
-        sizeof (GtkCellLayoutIface),
-        NULL,
-        NULL,
-        NULL,
-        NULL,
-        NULL,
-        0,
-        0,
-        NULL
-      };
-
-      cell_layout_type =
-        g_type_register_static (G_TYPE_INTERFACE, "PidginCellLayout",
-                                &cell_layout_info, 0);
-
-      g_type_interface_add_prerequisite (cell_layout_type, G_TYPE_OBJECT);
-    }
-
-  return cell_layout_type;
-}
-
-/**
- * gtk_cell_layout_pack_start:
- * @cell_layout: A #GtkCellLayout.
- * @cell: A #GtkCellRenderer.
- * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout.
- *
- * Packs the @cell into the beginning of @cell_layout. If @expand is %FALSE,
- * then the @cell is allocated no more space than it needs. Any unused space
- * is divided evenly between cells for which @expand is %TRUE.
- *
- * Since: 2.4
- */
-void
-gtk_cell_layout_pack_start (GtkCellLayout   *cell_layout,
-                            GtkCellRenderer *cell,
-                            gboolean         expand)
-{
-  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_start) (cell_layout,
-                                                           cell,
-                                                           expand);
-}
-
-/**
- * gtk_cell_layout_pack_end:
- * @cell_layout: A #GtkCellLayout.
- * @cell: A #GtkCellRenderer.
- * @expand: %TRUE if @cell is to be given extra space allocated to @cell_layout.
- *
- * Adds the @cell to the end of @cell_layout. If @expand is %FALSE, then the
- * @cell is allocated no more space than it needs. Any unused space is
- * divided evenly between cells for which @expand is %TRUE.
- *
- * Since: 2.4
- */
-void
-gtk_cell_layout_pack_end (GtkCellLayout   *cell_layout,
-                          GtkCellRenderer *cell,
-                          gboolean         expand)
-{
-  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->pack_end) (cell_layout,
-                                                         cell,
-                                                         expand);
-}
-
-/**
- * gtk_cell_layout_clear:
- * @cell_layout: A #GtkCellLayout.
- *
- * Unsets all the mappings on all renderers on @cell_layout and
- * removes all renderers from @cell_layout.
- *
- * Since: 2.4
- */
-void
-gtk_cell_layout_clear (GtkCellLayout *cell_layout)
-{
-  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
-
-  (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear) (cell_layout);
-}
-
-static void
-gtk_cell_layout_set_attributesv (GtkCellLayout   *cell_layout,
-                                 GtkCellRenderer *cell,
-                                 va_list          args)
-{
-  gchar *attribute;
-  gint column;
-  GtkCellLayoutIface *iface;
-
-  attribute = va_arg (args, gchar *);
-
-  iface = GTK_CELL_LAYOUT_GET_IFACE (cell_layout);
-
-  (* iface->clear_attributes) (cell_layout, cell);
-
-  while (attribute != NULL)
-    {
-      column = va_arg (args, gint);
-      (* iface->add_attribute) (cell_layout, cell, attribute, column);
-      attribute = va_arg (args, gchar *);
-    }
-}
-
-/**
- * gtk_cell_layout_set_attributes:
- * @cell_layout: A #GtkCellLayout.
- * @cell: A #GtkCellRenderer.
- * @Varargs: A %NULL-terminated list of attributes.
- *
- * Sets the attributes in list as the attributes of @cell_layout. The
- * attributes should be in attribute/column order, as in
- * gtk_cell_layout_add_attribute(). All existing attributes are removed, and
- * replaced with the new attributes.
- *
- * Since: 2.4
- */
-void
-gtk_cell_layout_set_attributes (GtkCellLayout   *cell_layout,
-                                GtkCellRenderer *cell,
-                                ...)
-{
-  va_list args;
-
-  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  va_start (args, cell);
-  gtk_cell_layout_set_attributesv (cell_layout, cell, args);
-  va_end (args);
-}
-
-/**
- * gtk_cell_layout_add_attribute:
- * @cell_layout: A #GtkCellLayout.
- * @cell: A #GtkCellRenderer.
- * @attribute: An attribute on the renderer.
- * @column: The column position on the model to get the attribute from.
- *
- * Adds an attribute mapping to the list in @cell_layout. The @column is the
- * column of the model to get a value from, and the @attribute is the
- * parameter on @cell to be set from the value. So for example if column 2
- * of the model contains strings, you could have the "text" attribute of a
- * #GtkCellRendererText get its values from column 2.
- *
- * Since: 2.4
- */
-void
-gtk_cell_layout_add_attribute (GtkCellLayout   *cell_layout,
-                               GtkCellRenderer *cell,
-                               const gchar     *attribute,
-                               gint             column)
-{
-  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-  g_return_if_fail (attribute != NULL);
-  g_return_if_fail (column >= 0);
-
-  (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->add_attribute) (cell_layout,
-                                                              cell,
-                                                              attribute,
-                                                              column);
-}
-
-/**
- * gtk_cell_layout_set_cell_data_func:
- * @cell_layout: A #GtkCellLayout.
- * @cell: A #GtkCellRenderer.
- * @func: The #GtkCellLayoutDataFunc to use.
- * @func_data: The user data for @func.
- * @destroy: The destroy notification for @func_data.
- *
- * Sets the #GtkCellLayoutDataFunc to use for @cell_layout. This function
- * is used instead of the standard attributes mapping for setting the
- * column value, and should set the value of @cell_layout's cell renderer(s)
- * as appropriate. @func may be %NULL to remove and older one.
- *
- * Since: 2.4
- */
-void
-gtk_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
-                                    GtkCellRenderer       *cell,
-                                    GtkCellLayoutDataFunc  func,
-                                    gpointer               func_data,
-                                    GDestroyNotify         destroy)
-{
-  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->set_cell_data_func) (cell_layout,
-                                                                   cell,
-                                                                   func,
-                                                                   func_data,
-                                                                   destroy);
-}
-
-/**
- * gtk_cell_layout_clear_attributes:
- * @cell_layout: A #GtkCellLayout.
- * @cell: A #GtkCellRenderer to clear the attribute mapping on.
- *
- * Clears all existing attributes previously set with
- * gtk_cell_layout_set_attributes().
- *
- * Since: 2.4
- */
-void
-gtk_cell_layout_clear_attributes (GtkCellLayout   *cell_layout,
-                                  GtkCellRenderer *cell)
-{
-  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->clear_attributes) (cell_layout,
-                                                                 cell);
-}
-
-/**
- * gtk_cell_layout_reorder:
- * @cell_layout: A #GtkCellLayout.
- * @cell: A #GtkCellRenderer to reorder.
- * @position: New position to insert @cell at.
- *
- * Re-inserts @cell at @position. Note that @cell has already to be packed
- * into @cell_layout for this to function properly.
- *
- * Since: 2.4
- */
-void
-gtk_cell_layout_reorder (GtkCellLayout   *cell_layout,
-                         GtkCellRenderer *cell,
-                         gint             position)
-{
-  g_return_if_fail (GTK_IS_CELL_LAYOUT (cell_layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  (* GTK_CELL_LAYOUT_GET_IFACE (cell_layout)->reorder) (cell_layout,
-                                                        cell,
-                                                        position);
-}
-#endif /* Gtk 2.4 */
--- a/pidgin/gtkcelllayout.h	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-/* gtkcelllayout.h
- * Copyright (C) 2003  Kristian Rietveld  <kris@gtk.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- */
-
-#ifndef __GTK_CELL_LAYOUT_H__
-#define __GTK_CELL_LAYOUT_H__
-
-#include <glib-object.h>
-
-#include <gtk/gtkcellrenderer.h>
-#include <gtk/gtktreeviewcolumn.h>
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_CELL_LAYOUT            (gtk_cell_layout_get_type ())
-#define GTK_CELL_LAYOUT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_LAYOUT, GtkCellLayout))
-#define GTK_IS_CELL_LAYOUT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_LAYOUT))
-#define GTK_CELL_LAYOUT_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_CELL_LAYOUT, GtkCellLayoutIface))
-
-typedef struct _GtkCellLayout           GtkCellLayout; /* dummy typedef */
-typedef struct _GtkCellLayoutIface      GtkCellLayoutIface;
-
-/* keep in sync with GtkTreeCellDataFunc */
-typedef void (* GtkCellLayoutDataFunc) (GtkCellLayout   *cell_layout,
-                                        GtkCellRenderer *cell,
-                                        GtkTreeModel    *tree_model,
-                                        GtkTreeIter     *iter,
-                                        gpointer         data);
-
-struct _GtkCellLayoutIface
-{
-  GTypeInterface g_iface;
-
-  /* Virtual Table */
-  void (* pack_start)         (GtkCellLayout         *cell_layout,
-                               GtkCellRenderer       *cell,
-                               gboolean               expand);
-  void (* pack_end)           (GtkCellLayout         *cell_layout,
-                               GtkCellRenderer       *cell,
-                               gboolean               expand);
-  void (* clear)              (GtkCellLayout         *cell_layout);
-  void (* add_attribute)      (GtkCellLayout         *cell_layout,
-                               GtkCellRenderer       *cell,
-                               const gchar           *attribute,
-                               gint                   column);
-  void (* set_cell_data_func) (GtkCellLayout         *cell_layout,
-                               GtkCellRenderer       *cell,
-                               GtkCellLayoutDataFunc  func,
-                               gpointer               func_data,
-                               GDestroyNotify         destroy);
-  void (* clear_attributes)   (GtkCellLayout         *cell_layout,
-                               GtkCellRenderer       *cell);
-  void (* reorder)            (GtkCellLayout         *cell_layout,
-                               GtkCellRenderer       *cell,
-                               gint                   position);
-};
-
-GType gtk_cell_layout_get_type           (void);
-void  gtk_cell_layout_pack_start         (GtkCellLayout         *cell_layout,
-                                          GtkCellRenderer       *cell,
-                                          gboolean               expand);
-void  gtk_cell_layout_pack_end           (GtkCellLayout         *cell_layout,
-                                          GtkCellRenderer       *cell,
-                                          gboolean               expand);
-void  gtk_cell_layout_clear              (GtkCellLayout         *cell_layout);
-void  gtk_cell_layout_set_attributes     (GtkCellLayout         *cell_layout,
-                                          GtkCellRenderer       *cell,
-                                          ...);
-void  gtk_cell_layout_add_attribute      (GtkCellLayout         *cell_layout,
-                                          GtkCellRenderer       *cell,
-                                          const gchar           *attribute,
-                                          gint                   column);
-void  gtk_cell_layout_set_cell_data_func (GtkCellLayout         *cell_layout,
-                                          GtkCellRenderer       *cell,
-                                          GtkCellLayoutDataFunc  func,
-                                          gpointer               func_data,
-                                          GDestroyNotify         destroy);
-void  gtk_cell_layout_clear_attributes   (GtkCellLayout         *cell_layout,
-                                          GtkCellRenderer       *cell);
-void  gtk_cell_layout_reorder            (GtkCellLayout         *cell_layout,
-                                          GtkCellRenderer       *cell,
-                                          gint                   position);
-
-
-G_END_DECLS
-
-#endif /* __GTK_CELL_LAYOUT_H__ */
--- a/pidgin/gtkcellrendererexpander.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkcellrendererexpander.c	Mon Mar 08 22:53:02 2010 +0000
@@ -246,13 +246,8 @@
 	width = cell_area->width;
 	height = cell_area->height;
 
-#if GTK_CHECK_VERSION(2,6,0)
 	if (!cell->sensitive)
 		state = GTK_STATE_INSENSITIVE;
-#else
-	if (GTK_WIDGET_STATE(widget) == GTK_STATE_INSENSITIVE)
-		state = GTK_STATE_INSENSITIVE;
-#endif
 	else if (flags & GTK_CELL_RENDERER_PRELIT)
 		state = GTK_STATE_PRELIGHT;
 	else if (GTK_WIDGET_HAS_FOCUS (widget) && flags & GTK_CELL_RENDERER_SELECTED)
--- a/pidgin/gtkcellrendererprogress.c	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,296 +0,0 @@
-/*
- * @file gtkcellrendererprogress.c GTK+ Cell Renderer Progress
- * @ingroup pidgin
- */
-
-/* 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
- *
- */
-
-/* This is taken largely from GtkCellRenderer[Text|Pixbuf|Toggle] by
- * Jonathon Blandford <jrb@redhat.com> for RedHat, Inc.
- */
-
-#include "gtkcellrendererprogress.h"
-
-static void pidgin_cell_renderer_progress_get_property  (GObject                    *object,
-						      guint                       param_id,
-						      GValue                     *value,
-						      GParamSpec                 *pspec);
-static void pidgin_cell_renderer_progress_set_property  (GObject                    *object,
-						      guint                       param_id,
-						      const GValue               *value,
-						      GParamSpec                 *pspec);
-static void pidgin_cell_renderer_progress_init       (PidginCellRendererProgress      *cellprogress);
-static void pidgin_cell_renderer_progress_class_init (PidginCellRendererProgressClass *class);
-static void pidgin_cell_renderer_progress_get_size   (GtkCellRenderer            *cell,
-						   GtkWidget                  *widget,
-						   GdkRectangle               *cell_area,
-						   gint                       *x_offset,
-						   gint                       *y_offset,
-						   gint                       *width,
-						   gint                       *height);
-static void pidgin_cell_renderer_progress_render     (GtkCellRenderer            *cell,
-						   GdkWindow                  *window,
-						   GtkWidget                  *widget,
-						   GdkRectangle               *background_area,
-						   GdkRectangle               *cell_area,
-						   GdkRectangle               *expose_area,
-						   guint                       flags);
-#if 0
-static gboolean pidgin_cell_renderer_progress_activate  (GtkCellRenderer            *cell,
-						      GdkEvent                   *event,
-						      GtkWidget                  *widget,
-						      const gchar                *path,
-						      GdkRectangle               *background_area,
-						      GdkRectangle               *cell_area,
-						      guint                       flags);
-#endif
-static void  pidgin_cell_renderer_progress_finalize (GObject *gobject);
-
-enum {
-	LAST_SIGNAL
-};
-
-enum {
-	PROP_0,
-	PROP_PERCENTAGE,
-	PROP_TEXT,
-	PROP_SHOW_TEXT
-};
-
-static gpointer parent_class;
-/* static guint progress_cell_renderer_signals [LAST_SIGNAL]; */
-
-GType  pidgin_cell_renderer_progress_get_type (void)
-{
-	static GType cell_progress_type = 0;
-
-	if (!cell_progress_type)
-		{
-			static const GTypeInfo cell_progress_info =
-				{
-					sizeof (PidginCellRendererProgressClass),
-					NULL,           /* base_init */
-					NULL,           /* base_finalize */
-					(GClassInitFunc) pidgin_cell_renderer_progress_class_init,
-					NULL,           /* class_finalize */
-					NULL,           /* class_data */
-					sizeof (PidginCellRendererProgress),
-					0,              /* n_preallocs */
-					(GInstanceInitFunc) pidgin_cell_renderer_progress_init,
-					NULL		/* value_table */
-				};
-
-			cell_progress_type =
-				g_type_register_static (GTK_TYPE_CELL_RENDERER,
-										"PidginCellRendererProgress",
-										&cell_progress_info, 0);
-		}
-
-	return cell_progress_type;
-}
-
-static void pidgin_cell_renderer_progress_init (PidginCellRendererProgress *cellprogress)
-{
-	GTK_CELL_RENDERER(cellprogress)->mode = GTK_CELL_RENDERER_MODE_INERT;
-	GTK_CELL_RENDERER(cellprogress)->xpad = 2;
-	GTK_CELL_RENDERER(cellprogress)->ypad = 2;
-}
-
-static void pidgin_cell_renderer_progress_class_init (PidginCellRendererProgressClass *class)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS(class);
-	GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(class);
-
-	parent_class = g_type_class_peek_parent (class);
-	object_class->finalize = pidgin_cell_renderer_progress_finalize;
-
-	object_class->get_property = pidgin_cell_renderer_progress_get_property;
-	object_class->set_property = pidgin_cell_renderer_progress_set_property;
-
-	cell_class->get_size = pidgin_cell_renderer_progress_get_size;
-	cell_class->render   = pidgin_cell_renderer_progress_render;
-
-	g_object_class_install_property (object_class,
-					 PROP_PERCENTAGE,
-					 g_param_spec_double ("percentage",
-							      "Percentage",
-							      "The fractional progress to display",
-							      0, 1, 0,
-							      G_PARAM_READWRITE));
-	g_object_class_install_property (object_class,
-					 PROP_TEXT,
-					 g_param_spec_string ("text",
-							      "Text",
-							      "Text to overlay over progress bar",
-							      NULL,
-							      G_PARAM_READWRITE));
-	g_object_class_install_property(object_class,
-					PROP_SHOW_TEXT,
-					g_param_spec_string("text_set",
-							    "Text set",
-							    "Whether to overlay text on the progress bar",
-							    FALSE,
-							    G_PARAM_READABLE | G_PARAM_WRITABLE));
-}
-
-static void pidgin_cell_renderer_progress_finalize (GObject *object)
-{
-/*
-	PidginCellRendererProgress *cellprogress = PIDGIN_CELL_RENDERER_PROGRESS(object);
-*/
-
-	(* G_OBJECT_CLASS (parent_class)->finalize) (object);
-}
-
-static void pidgin_cell_renderer_progress_get_property (GObject    *object,
-						     guint      param_id,
-						     GValue     *value,
-						     GParamSpec *psec)
-{
-	PidginCellRendererProgress *cellprogress = PIDGIN_CELL_RENDERER_PROGRESS(object);
-
-	switch (param_id)
-		{
-		case PROP_PERCENTAGE:
-			g_value_set_double(value, cellprogress->progress);
-			break;
-		case PROP_TEXT:
-			g_value_set_string(value, cellprogress->text);
-			break;
-		case PROP_SHOW_TEXT:
-			g_value_set_boolean(value, cellprogress->text_set);
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, psec);
-			break;
-		}
-}
-
-static void pidgin_cell_renderer_progress_set_property (GObject      *object,
-						     guint        param_id,
-						     const GValue *value,
-						     GParamSpec   *pspec)
-{
-	PidginCellRendererProgress *cellprogress = PIDGIN_CELL_RENDERER_PROGRESS (object);
-
-	switch (param_id)
-		{
-		case PROP_PERCENTAGE:
-			cellprogress->progress = g_value_get_double(value);
-			break;
-		case PROP_TEXT:
-			if (cellprogress->text)
-				g_free(cellprogress->text);
-			cellprogress->text = g_strdup(g_value_get_string(value));
-			g_object_notify(object, "text");
-			break;
-		case PROP_SHOW_TEXT:
-			cellprogress->text_set = g_value_get_boolean(value);
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
-			break;
-		}
-}
-
-GtkCellRenderer *pidgin_cell_renderer_progress_new(void)
-{
-	return g_object_new(PIDGIN_TYPE_GTK_CELL_RENDERER_PROGRESS, NULL);
-}
-
-static void pidgin_cell_renderer_progress_get_size (GtkCellRenderer *cell,
-						 GtkWidget       *widget,
-						 GdkRectangle    *cell_area,
-						 gint            *x_offset,
-						 gint            *y_offset,
-						 gint            *width,
-						 gint            *height)
-{
-	gint calc_width;
-	gint calc_height;
-
-	calc_width = (gint) cell->xpad * 2 + 50;
-	calc_height = (gint) cell->ypad * 2 + 12;
-
-	if (width)
-		*width = calc_width;
-
-	if (height)
-		*height = calc_height;
-
-	if (cell_area)
-		{
-			if (x_offset)
-				{
-					*x_offset = cell->xalign * (cell_area->width - calc_width);
-					*x_offset = MAX (*x_offset, 0);
-				}
-			if (y_offset)
-				{
-					*y_offset = cell->yalign * (cell_area->height - calc_height);
-					*y_offset = MAX (*y_offset, 0);
-				}
-		}
-}
-
-
-static void pidgin_cell_renderer_progress_render (GtkCellRenderer *cell,
-					       GdkWindow       *window,
-					       GtkWidget       *widget,
-					       GdkRectangle    *background_area,
-					       GdkRectangle    *cell_area,
-					       GdkRectangle    *expose_area,
-					       guint            flags)
-{
-	PidginCellRendererProgress *cellprogress = (PidginCellRendererProgress *) cell;
-
-	gint width, height;
-	GtkStateType state;
-
-	width = cell_area->width;
-	height = cell_area->height;
-
-	if (GTK_WIDGET_HAS_FOCUS (widget))
-		state = GTK_STATE_ACTIVE;
-	else
-		state = GTK_STATE_NORMAL;
-
-	width -= cell->xpad*2;
-	height -= cell->ypad*2;
-
-	gtk_paint_box (widget->style,
-		       window,
-		       GTK_STATE_NORMAL, GTK_SHADOW_IN,
-		       NULL, widget, "trough",
-		       cell_area->x + cell->xpad,
-		       cell_area->y + cell->ypad,
-		       width - 1, height - 1);
-	gtk_paint_box (widget->style,
-		       window,
-		       state, GTK_SHADOW_OUT,
-		       NULL, widget, "bar",
-		       cell_area->x + cell->xpad + 1,
-		       cell_area->y + cell->ypad + 1,
-		       (width - 3) * cellprogress->progress,
-		       height - 3);
-}
--- a/pidgin/gtkcellrendererprogress.h	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/* gtkxcellrendererprogress.h
- * 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 _PIDGINCELLRENDERERPROGRESS_H_
-#define _PIDGINCELLRENDERERPROGRESS_H_
-
-#include <gtk/gtk.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-#define PIDGIN_TYPE_GTK_CELL_RENDERER_PROGRESS         (pidgin_cell_renderer_progress_get_type())
-#define PIDGIN_CELL_RENDERER_PROGRESS(obj)         (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_GTK_CELL_RENDERER_PROGRESS, PidginCellRendererProgress))
-#define PIDGIN_CELL_RENDERER_PROGRESS_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_GTK_CELL_RENDERER_PROGRESS, PidginCellRendererProgressClass))
-#define PIDGIN_IS_GTK_CELL_PROGRESS_PROGRESS(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_GTK_CELL_RENDERER_PROGRESS))
-#define PIDGIN_IS_GTK_CELL_PROGRESS_PROGRESS_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_GTK_CELL_RENDERER_PROGRESS))
-#define PIDGIN_CELL_RENDERER_PROGRESS_GET_CLASS(obj)         (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_GTK_CELL_RENDERER_PROGRESS, PidginCellRendererProgressClass))
-
-typedef struct _PidginCellRendererProgress PidginCellRendererProgress;
-typedef struct _PidginCellRendererProgressClass PidginCellRendererProgressClass;
-
-struct _PidginCellRendererProgress {
-	GtkCellRenderer parent;
-
-	gdouble progress;
-	gchar *text;
-	gboolean text_set;
-};
-
-struct _PidginCellRendererProgressClass {
-	GtkCellRendererClass parent_class;
-};
-
-GType            pidgin_cell_renderer_progress_get_type     (void);
-GtkCellRenderer  *pidgin_cell_renderer_progress_new          (void);
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* _PIDGINCELLRENDERERPROGRESS_H_ */
--- a/pidgin/gtkcellview.c	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1002 +0,0 @@
-/* gtkellview.c
- * Copyright (C) 2002, 2003  Kristian Rietveld <kris@gtk.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- */
-
-/*
-#include <config.h>
-*/
-#include "gtkcellview.h"
-#include <gtk/gtkversion.h>
-#if !GTK_CHECK_VERSION(2,6,0)
-#if GTK_CHECK_VERSION(2,4,0)
-#include <gtk/gtkcelllayout.h>
-#else
-#include "gtkcelllayout.h"
-#endif
-#include <gtk/gtksignal.h>
-#include <gtk/gtkcellrenderertext.h>
-#include <gtk/gtkcellrendererpixbuf.h>
-#include <gobject/gmarshal.h>
-
-#define P_(x) (x)
-
-typedef struct _GtkCellViewCellInfo GtkCellViewCellInfo;
-struct _GtkCellViewCellInfo
-{
-  GtkCellRenderer *cell;
-
-  gint requested_width;
-  gint real_width;
-  guint expand : 1;
-  guint pack : 1;
-
-  GSList *attributes;
-
-  GtkCellLayoutDataFunc func;
-  gpointer func_data;
-  GDestroyNotify destroy;
-};
-
-struct _GtkCellViewPrivate
-{
-  GtkTreeModel *model;
-  GtkTreeRowReference *displayed_row;
-  GList *cell_list;
-  gint spacing;
-
-  GdkColor background;
-  gboolean background_set;
-};
-
-
-static void        gtk_cell_view_class_init               (GtkCellViewClass *klass);
-static void        gtk_cell_view_cell_layout_init         (GtkCellLayoutIface *iface);
-static void        gtk_cell_view_get_property             (GObject           *object,
-                                                           guint             param_id,
-                                                           GValue           *value,
-                                                           GParamSpec       *pspec);
-static void        gtk_cell_view_set_property             (GObject          *object,
-                                                           guint             param_id,
-                                                           const GValue     *value,
-                                                           GParamSpec       *pspec);
-static void        gtk_cell_view_init                     (GtkCellView      *cellview);
-static void        gtk_cell_view_finalize                 (GObject          *object);
-static void        gtk_cell_view_style_set                (GtkWidget        *widget,
-                                                           GtkStyle         *previous_style);
-static void        gtk_cell_view_size_request             (GtkWidget        *widget,
-                                                           GtkRequisition   *requisition);
-static void        gtk_cell_view_size_allocate            (GtkWidget        *widget,
-                                                           GtkAllocation    *allocation);
-static gboolean    gtk_cell_view_expose                   (GtkWidget        *widget,
-                                                           GdkEventExpose   *event);
-static void        gtk_cell_view_set_valuesv              (GtkCellView      *cellview,
-                                                           GtkCellRenderer  *renderer,
-                                                           va_list           args);
-static GtkCellViewCellInfo *gtk_cell_view_get_cell_info   (GtkCellView      *cellview,
-                                                           GtkCellRenderer  *renderer);
-static void        gtk_cell_view_set_cell_data            (GtkCellView      *cellview);
-
-
-static void        gtk_cell_view_cell_layout_pack_start        (GtkCellLayout         *layout,
-                                                                GtkCellRenderer       *renderer,
-                                                                gboolean               expand);
-static void        gtk_cell_view_cell_layout_pack_end          (GtkCellLayout         *layout,
-                                                                GtkCellRenderer       *renderer,
-                                                                gboolean               expand);
-static void        gtk_cell_view_cell_layout_add_attribute     (GtkCellLayout         *layout,
-                                                                GtkCellRenderer       *renderer,
-                                                                const gchar           *attribute,
-                                                                gint                   column);
-static void       gtk_cell_view_cell_layout_clear              (GtkCellLayout         *layout);
-static void       gtk_cell_view_cell_layout_clear_attributes   (GtkCellLayout         *layout,
-                                                                GtkCellRenderer       *renderer);
-static void       gtk_cell_view_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
-                                                                GtkCellRenderer       *cell,
-                                                                GtkCellLayoutDataFunc  func,
-                                                                gpointer               func_data,
-                                                                GDestroyNotify         destroy);
-static void       gtk_cell_view_cell_layout_reorder            (GtkCellLayout         *layout,
-                                                                GtkCellRenderer       *cell,
-                                                                gint                   position);
-
-
-enum
-{
-  PROP_0,
-  PROP_BACKGROUND,
-  PROP_BACKGROUND_GDK,
-  PROP_BACKGROUND_SET
-};
-
-static GtkObjectClass *parent_class = NULL;
-
-
-GType
-gtk_cell_view_get_type (void)
-{
-  static GType cell_view_type = 0;
-
-  if (!cell_view_type)
-    {
-      static const GTypeInfo cell_view_info =
-        {
-          sizeof (GtkCellViewClass),
-          NULL, /* base_init */
-          NULL, /* base_finalize */
-          (GClassInitFunc) gtk_cell_view_class_init,
-          NULL, /* class_finalize */
-          NULL, /* class_data */
-          sizeof (GtkCellView),
-          0,
-          (GInstanceInitFunc) gtk_cell_view_init
-        };
-
-      static const GInterfaceInfo cell_layout_info =
-       {
-         (GInterfaceInitFunc) gtk_cell_view_cell_layout_init,
-         NULL,
-         NULL
-       };
-
-      cell_view_type = g_type_register_static (GTK_TYPE_WIDGET, "PidginCellView",
-                                               &cell_view_info, 0);
-
-      g_type_add_interface_static (cell_view_type, GTK_TYPE_CELL_LAYOUT,
-                                   &cell_layout_info);
-    }
-
-  return cell_view_type;
-}
-
-static void
-gtk_cell_view_class_init (GtkCellViewClass *klass)
-{
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-
-  parent_class = g_type_class_peek_parent (klass);
-
-  gobject_class->get_property = gtk_cell_view_get_property;
-  gobject_class->set_property = gtk_cell_view_set_property;
-  gobject_class->finalize = gtk_cell_view_finalize;
-
-  widget_class->expose_event = gtk_cell_view_expose;
-  widget_class->size_allocate = gtk_cell_view_size_allocate;
-  widget_class->size_request = gtk_cell_view_size_request;
-  widget_class->style_set = gtk_cell_view_style_set;
-
-  /* properties */
-  g_object_class_install_property (gobject_class,
-                                   PROP_BACKGROUND,
-                                   g_param_spec_string ("background",
-                                                        P_("Background color name"),
-                                                        P_("Background color as a string"),
-                                                        NULL,
-                                                        G_PARAM_WRITABLE));
-  g_object_class_install_property (gobject_class,
-                                   PROP_BACKGROUND_GDK,
-                                   g_param_spec_boxed ("background_gdk",
-                                                      P_("Background color"),
-                                                      P_("Background color as a GdkColor"),
-                                                      GDK_TYPE_COLOR,
-                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
-
-#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (gobject_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE))
-
-  ADD_SET_PROP ("background_set", PROP_BACKGROUND_SET,
-                P_("Background set"),
-                P_("Whether this tag affects the background color"));
-}
-
-static void
-gtk_cell_view_cell_layout_init (GtkCellLayoutIface *iface)
-{
-  iface->pack_start = gtk_cell_view_cell_layout_pack_start;
-  iface->pack_end = gtk_cell_view_cell_layout_pack_end;
-  iface->clear = gtk_cell_view_cell_layout_clear;
-  iface->add_attribute = gtk_cell_view_cell_layout_add_attribute;
-  iface->set_cell_data_func = gtk_cell_view_cell_layout_set_cell_data_func;
-  iface->clear_attributes = gtk_cell_view_cell_layout_clear_attributes;
-  iface->reorder = gtk_cell_view_cell_layout_reorder;
-}
-
-static void
-gtk_cell_view_get_property (GObject    *object,
-                            guint       param_id,
-                            GValue     *value,
-                            GParamSpec *pspec)
-{
-  GtkCellView *view = GTK_CELL_VIEW (object);
-
-  switch (param_id)
-    {
-      case PROP_BACKGROUND_GDK:
-        {
-          GdkColor color;
-
-          color = view->priv->background;
-
-          g_value_set_boxed (value, &color);
-        }
-        break;
-      case PROP_BACKGROUND_SET:
-        g_value_set_boolean (value, view->priv->background_set);
-        break;
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-        break;
-    }
-}
-
-static void
-gtk_cell_view_set_property (GObject      *object,
-                            guint         param_id,
-                            const GValue *value,
-                            GParamSpec   *pspec)
-{
-  GtkCellView *view = GTK_CELL_VIEW (object);
-
-  switch (param_id)
-    {
-      case PROP_BACKGROUND:
-        {
-          GdkColor color;
-
-          if (!g_value_get_string (value))
-            gtk_cell_view_set_background_color (view, NULL);
-          else if (gdk_color_parse (g_value_get_string (value), &color))
-            gtk_cell_view_set_background_color (view, &color);
-          else
-            g_warning ("Don't know color `%s'", g_value_get_string (value));
-
-          g_object_notify (object, "background_gdk");
-        }
-        break;
-      case PROP_BACKGROUND_GDK:
-        gtk_cell_view_set_background_color (view, g_value_get_boxed (value));
-        break;
-      case PROP_BACKGROUND_SET:
-        view->priv->background_set = g_value_get_boolean (value);
-        break;
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
-        break;
-    }
-}
-
-static void
-gtk_cell_view_init (GtkCellView *cellview)
-{
-  GTK_WIDGET_SET_FLAGS (cellview, GTK_NO_WINDOW);
-
-  cellview->priv = g_new0(GtkCellViewPrivate,1);
-}
-
-static void
-gtk_cell_view_style_set (GtkWidget *widget,
-                         GtkStyle  *previous_style)
-{
-  if (previous_style && GTK_WIDGET_REALIZED (widget))
-    gdk_window_set_background (widget->window,
-                               &widget->style->base[GTK_WIDGET_STATE (widget)]);
-}
-
-static void
-gtk_cell_view_finalize (GObject *object)
-{
-  GtkCellView *cellview = GTK_CELL_VIEW (object);
-
-  gtk_cell_view_cell_layout_clear (GTK_CELL_LAYOUT (cellview));
-
-  if (cellview->priv->model)
-     g_object_unref (cellview->priv->model);
-
-  if (cellview->priv->displayed_row)
-     gtk_tree_row_reference_free (cellview->priv->displayed_row);
-
-  if (G_OBJECT_CLASS (parent_class)->finalize)
-    (* G_OBJECT_CLASS (parent_class)->finalize) (object);
-
-  g_free (cellview->priv);
-}
-
-static void
-gtk_cell_view_size_request (GtkWidget      *widget,
-                            GtkRequisition *requisition)
-{
-  GList *i;
-  gboolean first_cell = TRUE;
-  GtkCellView *cellview;
-
-  cellview = GTK_CELL_VIEW (widget);
-
-  requisition->width = 0;
-  requisition->height = 0;
-
-  if (cellview->priv->displayed_row)
-    gtk_cell_view_set_cell_data (cellview);
-
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      gint width, height;
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (!info->cell->visible)
-        continue;
-
-      if (!first_cell)
-        requisition->width += cellview->priv->spacing;
-
-      gtk_cell_renderer_get_size (info->cell, widget, NULL, NULL, NULL,
-                                  &width, &height);
-
-      info->requested_width = width;
-      requisition->width += width;
-      requisition->height = MAX (requisition->height, height);
-
-      first_cell = FALSE;
-    }
-}
-
-static void
-gtk_cell_view_size_allocate (GtkWidget     *widget,
-                             GtkAllocation *allocation)
-{
-  GList *i;
-  gint expand_cell_count = 0;
-  gint full_requested_width = 0;
-  gint extra_space;
-  GtkCellView *cellview;
-
-  widget->allocation = *allocation;
-
-  cellview = GTK_CELL_VIEW (widget);
-
-  /* checking how much extra space we have */
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (!info->cell->visible)
-        continue;
-
-      if (info->expand)
-        expand_cell_count++;
-
-      full_requested_width += info->requested_width;
-    }
-
-  extra_space = widget->allocation.width - full_requested_width;
-  if (extra_space < 0)
-    extra_space = 0;
-  else if (extra_space > 0 && expand_cell_count > 0)
-    extra_space /= expand_cell_count;
-
-  /* iterate list for PACK_START cells */
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (info->pack == GTK_PACK_END)
-        continue;
-
-      if (!info->cell->visible)
-        continue;
-
-      info->real_width = info->requested_width + (info->expand ? extra_space : 0);
-    }
-
-  /* iterate list for PACK_END cells */
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (info->pack == GTK_PACK_START)
-        continue;
-
-      if (!info->cell->visible)
-        continue;
-
-      info->real_width = info->requested_width + (info->expand ? extra_space : 0);
-    }
-}
-
-static gboolean
-gtk_cell_view_expose (GtkWidget      *widget,
-                      GdkEventExpose *event)
-{
-  GList *i;
-  GtkCellView *cellview;
-  GdkRectangle area;
-  GtkCellRendererState state;
-  gboolean rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
-
-  cellview = GTK_CELL_VIEW (widget);
-
-  if (! GTK_WIDGET_DRAWABLE (widget))
-    return FALSE;
-
-  /* "blank" background */
-  if (cellview->priv->background_set)
-    {
-      GdkGC *gc;
-
-      gc = gdk_gc_new (GTK_WIDGET (cellview)->window);
-      gdk_gc_set_rgb_fg_color (gc, &cellview->priv->background);
-
-      gdk_draw_rectangle (GTK_WIDGET (cellview)->window,
-                          gc,
-                          TRUE,
-
-                          /*0, 0,*/
-                          widget->allocation.x,
-                          widget->allocation.y,
-
-                          widget->allocation.width,
-                          widget->allocation.height);
-
-      g_object_unref (G_OBJECT (gc));
-    }
-
-  /* set cell data (if available) */
-  if (cellview->priv->displayed_row)
-    gtk_cell_view_set_cell_data (cellview);
-  else if (cellview->priv->model)
-    return FALSE;
-
-  /* render cells */
-  area = widget->allocation;
-
-  /* we draw on our very own window, initialize x and y to zero */
-  area.x = widget->allocation.x + (rtl ? widget->allocation.width : 0);
-  area.y = widget->allocation.y;
-
-  if (GTK_WIDGET_STATE (widget) == GTK_STATE_PRELIGHT)
-    state = GTK_CELL_RENDERER_PRELIT;
-  else
-    state = 0;
-
-  /* PACK_START */
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (info->pack == GTK_PACK_END)
-        continue;
-
-      if (!info->cell->visible)
-        continue;
-
-      area.width = info->real_width;
-      if (rtl)
-         area.x -= area.width;
-
-      gtk_cell_renderer_render (info->cell,
-                                event->window,
-                                widget,
-                                /* FIXME! */
-                                &area, &area, &event->area, state);
-
-      if (!rtl)
-         area.x += info->real_width;
-    }
-
-   area.x = rtl ? widget->allocation.x : (widget->allocation.x + widget->allocation.width);
-
-  /* PACK_END */
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (info->pack == GTK_PACK_START)
-        continue;
-
-      if (!info->cell->visible)
-        continue;
-
-      area.width = info->real_width;
-      if (!rtl)
-         area.x -= area.width;
-
-      gtk_cell_renderer_render (info->cell,
-                                widget->window,
-                                widget,
-                                /* FIXME ! */
-                                &area, &area, &event->area, state);
-      if (rtl)
-         area.x += info->real_width;
-    }
-
-  return FALSE;
-}
-
-static GtkCellViewCellInfo *
-gtk_cell_view_get_cell_info (GtkCellView     *cellview,
-                             GtkCellRenderer *renderer)
-{
-  GList *i;
-
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data;
-
-      if (info->cell == renderer)
-        return info;
-    }
-
-  return NULL;
-}
-
-static void
-gtk_cell_view_set_cell_data (GtkCellView *cellview)
-{
-  GList *i;
-  GtkTreeIter iter;
-  GtkTreePath *path;
-
-  g_return_if_fail (cellview->priv->displayed_row != NULL);
-
-  path = gtk_tree_row_reference_get_path (cellview->priv->displayed_row);
-  gtk_tree_model_get_iter (cellview->priv->model, &iter, path);
-  gtk_tree_path_free (path);
-
-  for (i = cellview->priv->cell_list; i; i = i->next)
-    {
-      GSList *j;
-      GtkCellViewCellInfo *info = i->data;
-
-      g_object_freeze_notify (G_OBJECT (info->cell));
-
-      for (j = info->attributes; j && j->next; j = j->next->next)
-        {
-          gchar *property = j->data;
-          gint column = GPOINTER_TO_INT (j->next->data);
-          GValue value = {0, };
-
-          gtk_tree_model_get_value (cellview->priv->model, &iter,
-                                    column, &value);
-          g_object_set_property (G_OBJECT (info->cell),
-                                 property, &value);
-          g_value_unset (&value);
-        }
-
-      if (info->func)
-	(* info->func) (GTK_CELL_LAYOUT (cellview),
-			info->cell,
-			cellview->priv->model,
-			&iter,
-			info->func_data);
-
-      g_object_thaw_notify (G_OBJECT (info->cell));
-    }
-}
-
-/* GtkCellLayout implementation */
-static void
-gtk_cell_view_cell_layout_pack_start (GtkCellLayout   *layout,
-                                      GtkCellRenderer *renderer,
-                                      gboolean         expand)
-{
-  GtkCellViewCellInfo *info;
-  GtkCellView *cellview = GTK_CELL_VIEW (layout);
-
-  g_return_if_fail (GTK_IS_CELL_VIEW (cellview));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
-  g_return_if_fail (!gtk_cell_view_get_cell_info (cellview, renderer));
-
-  g_object_ref (G_OBJECT (renderer));
-  gtk_object_sink (GTK_OBJECT (renderer));
-
-  info = g_new0 (GtkCellViewCellInfo, 1);
-  info->cell = renderer;
-  info->expand = expand ? TRUE : FALSE;
-  info->pack = GTK_PACK_START;
-
-  cellview->priv->cell_list = g_list_append (cellview->priv->cell_list, info);
-}
-
-static void
-gtk_cell_view_cell_layout_pack_end (GtkCellLayout   *layout,
-                                    GtkCellRenderer *renderer,
-                                    gboolean         expand)
-{
-  GtkCellViewCellInfo *info;
-  GtkCellView *cellview = GTK_CELL_VIEW (layout);
-
-  g_return_if_fail (GTK_IS_CELL_VIEW (cellview));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
-  g_return_if_fail (!gtk_cell_view_get_cell_info (cellview, renderer));
-
-  g_object_ref (G_OBJECT (renderer));
-  gtk_object_sink (GTK_OBJECT (renderer));
-
-  info = g_new0 (GtkCellViewCellInfo, 1);
-  info->cell = renderer;
-  info->expand = expand ? TRUE : FALSE;
-  info->pack = GTK_PACK_END;
-
-  cellview->priv->cell_list = g_list_append (cellview->priv->cell_list, info);
-}
-
-static void
-gtk_cell_view_cell_layout_add_attribute (GtkCellLayout   *layout,
-                                         GtkCellRenderer *renderer,
-                                         const gchar     *attribute,
-                                         gint             column)
-{
-  GtkCellViewCellInfo *info;
-  GtkCellView *cellview = GTK_CELL_VIEW (layout);
-
-  g_return_if_fail (GTK_IS_CELL_VIEW (cellview));
-  info = gtk_cell_view_get_cell_info (cellview, renderer);
-  g_return_if_fail (info != NULL);
-
-  info->attributes = g_slist_prepend (info->attributes,
-                                      GINT_TO_POINTER (column));
-  info->attributes = g_slist_prepend (info->attributes,
-                                      g_strdup (attribute));
-}
-
-static void
-gtk_cell_view_cell_layout_clear (GtkCellLayout *layout)
-{
-  GtkCellView *cellview = GTK_CELL_VIEW (layout);
-
-  g_return_if_fail (GTK_IS_CELL_VIEW (cellview));
-
-  while (cellview->priv->cell_list)
-    {
-      GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)cellview->priv->cell_list->data;
-
-      gtk_cell_view_cell_layout_clear_attributes (layout, info->cell);
-      g_object_unref (G_OBJECT (info->cell));
-      g_free (info);
-      cellview->priv->cell_list = g_list_delete_link (cellview->priv->cell_list,
-						      cellview->priv->cell_list);
-    }
-}
-
-static void
-gtk_cell_view_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
-                                              GtkCellRenderer       *cell,
-                                              GtkCellLayoutDataFunc  func,
-                                              gpointer               func_data,
-                                              GDestroyNotify         destroy)
-{
-  GtkCellView *cellview = GTK_CELL_VIEW (layout);
-  GtkCellViewCellInfo *info;
-
-  g_return_if_fail (GTK_IS_CELL_VIEW (cellview));
-
-  info = gtk_cell_view_get_cell_info (cellview, cell);
-  g_return_if_fail (info != NULL);
-
-  if (info->destroy)
-    {
-      GDestroyNotify d = info->destroy;
-
-      info->destroy = NULL;
-      d (info->func_data);
-    }
-
-  info->func = func;
-  info->func_data = func_data;
-  info->destroy = destroy;
-}
-
-static void
-gtk_cell_view_cell_layout_clear_attributes (GtkCellLayout   *layout,
-                                            GtkCellRenderer *renderer)
-{
-  GtkCellViewCellInfo *info;
-  GtkCellView *cellview = GTK_CELL_VIEW (layout);
-  GSList *list;
-
-  g_return_if_fail (GTK_IS_CELL_VIEW (cellview));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
-
-  info = gtk_cell_view_get_cell_info (cellview, renderer);
-  if (info != NULL)
-    {
-      list = info->attributes;
-      while (list && list->next)
-	{
-	  g_free (list->data);
-	  list = list->next->next;
-	}
-
-      g_slist_free (info->attributes);
-      info->attributes = NULL;
-    }
-}
-
-static void
-gtk_cell_view_cell_layout_reorder (GtkCellLayout   *layout,
-                                   GtkCellRenderer *cell,
-                                   gint             position)
-{
-  GList *link;
-  GtkCellViewCellInfo *info;
-  GtkCellView *cellview = GTK_CELL_VIEW (layout);
-
-  g_return_if_fail (GTK_IS_CELL_VIEW (cellview));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  info = gtk_cell_view_get_cell_info (cellview, cell);
-
-  g_return_if_fail (info != NULL);
-  g_return_if_fail (position >= 0);
-
-  link = g_list_find (cellview->priv->cell_list, info);
-
-  g_return_if_fail (link != NULL);
-
-  cellview->priv->cell_list = g_list_remove_link (cellview->priv->cell_list,
-                                                  link);
-  cellview->priv->cell_list = g_list_insert (cellview->priv->cell_list,
-                                             info, position);
-
-  gtk_widget_queue_draw (GTK_WIDGET (cellview));
-}
-
-/* public API */
-GtkWidget *
-gtk_cell_view_new (void)
-{
-  GtkCellView *cellview;
-
-  cellview = GTK_CELL_VIEW (g_object_new (gtk_cell_view_get_type (), NULL));
-
-  return GTK_WIDGET (cellview);
-}
-
-GtkWidget *
-gtk_cell_view_new_with_text (const gchar *text)
-{
-  GtkCellView *cellview;
-  GtkCellRenderer *renderer;
-  GValue value = {0, };
-
-  cellview = GTK_CELL_VIEW (gtk_cell_view_new ());
-
-  renderer = gtk_cell_renderer_text_new ();
-  gtk_cell_view_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview),
-                                        renderer, TRUE);
-
-  g_value_init (&value, G_TYPE_STRING);
-  g_value_set_string (&value, text);
-  gtk_cell_view_set_values (cellview, renderer, "text", &value, NULL);
-  g_value_unset (&value);
-
-  return GTK_WIDGET (cellview);
-}
-
-GtkWidget *
-gtk_cell_view_new_with_markup (const gchar *markup)
-{
-  GtkCellView *cellview;
-  GtkCellRenderer *renderer;
-  GValue value = {0, };
-
-  cellview = GTK_CELL_VIEW (gtk_cell_view_new ());
-
-  renderer = gtk_cell_renderer_text_new ();
-  gtk_cell_view_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview),
-                                        renderer, TRUE);
-
-  g_value_init (&value, G_TYPE_STRING);
-  g_value_set_string (&value, markup);
-  gtk_cell_view_set_values (cellview, renderer, "markup", &value, NULL);
-  g_value_unset (&value);
-
-  return GTK_WIDGET (cellview);
-}
-
-GtkWidget *
-gtk_cell_view_new_with_pixbuf (GdkPixbuf *pixbuf)
-{
-  GtkCellView *cellview;
-  GtkCellRenderer *renderer;
-  GValue value = {0, };
-
-  cellview = GTK_CELL_VIEW (gtk_cell_view_new ());
-
-  renderer = gtk_cell_renderer_pixbuf_new ();
-  gtk_cell_view_cell_layout_pack_start (GTK_CELL_LAYOUT (cellview),
-                                        renderer, TRUE);
-
-  g_value_init (&value, GDK_TYPE_PIXBUF);
-  g_value_set_object (&value, pixbuf);
-  gtk_cell_view_set_values (cellview, renderer, "pixbuf", &value, NULL);
-  g_value_unset (&value);
-
-  return GTK_WIDGET (cellview);
-}
-
-void
-gtk_cell_view_set_value (GtkCellView     *cell_view,
-                         GtkCellRenderer *renderer,
-                         gchar           *property,
-                         GValue          *value)
-{
-  g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
-
-  g_object_set_property (G_OBJECT (renderer), property, value);
-
-  /* force resize and redraw */
-  gtk_widget_queue_resize (GTK_WIDGET (cell_view));
-  gtk_widget_queue_draw (GTK_WIDGET (cell_view));
-}
-
-static void
-gtk_cell_view_set_valuesv (GtkCellView     *cell_view,
-                           GtkCellRenderer *renderer,
-                           va_list          args)
-{
-  gchar *attribute;
-  GValue *value;
-
-  attribute = va_arg (args, gchar *);
-
-  while (attribute)
-    {
-      value = va_arg (args, GValue *);
-      gtk_cell_view_set_value (cell_view, renderer, attribute, value);
-      attribute = va_arg (args, gchar *);
-    }
-}
-
-void
-gtk_cell_view_set_values (GtkCellView     *cell_view,
-                          GtkCellRenderer *renderer,
-                          ...)
-{
-  va_list args;
-
-  g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
-  g_return_if_fail (gtk_cell_view_get_cell_info (cell_view, renderer));
-
-  va_start (args, renderer);
-  gtk_cell_view_set_valuesv (cell_view, renderer, args);
-  va_end (args);
-}
-
-void
-gtk_cell_view_set_model (GtkCellView  *cell_view,
-                         GtkTreeModel *model)
-{
-  g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
-  g_return_if_fail (GTK_IS_TREE_MODEL (model));
-
-  if (cell_view->priv->model)
-    {
-      if (cell_view->priv->displayed_row)
-        gtk_tree_row_reference_free (cell_view->priv->displayed_row);
-      cell_view->priv->displayed_row = NULL;
-
-      g_object_unref (G_OBJECT (cell_view->priv->model));
-      cell_view->priv->model = NULL;
-    }
-
-  cell_view->priv->model = model;
-
-  if (cell_view->priv->model)
-    g_object_ref (G_OBJECT (cell_view->priv->model));
-}
-
-/**
- * gtk_cell_view_set_displayed_row:
- * @cell_view: a #GtkCellView
- * @path: a #GtkTreePath or %NULL to unset.
- *
- * Sets the row of the model that is currently displayed
- * by the #GtkCellView. If the path is unset, then the
- * contents of the cellview "stick" at their last value;
- * this is not normally a desired result, but may be
- * a needed intermediate state if say, the model for
- * the #GtkCellView becomes temporarily empty.
- **/
-void
-gtk_cell_view_set_displayed_row (GtkCellView *cell_view,
-                                 GtkTreePath *path)
-{
-  g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
-  g_return_if_fail (GTK_IS_TREE_MODEL (cell_view->priv->model));
-
-  if (cell_view->priv->displayed_row)
-    gtk_tree_row_reference_free (cell_view->priv->displayed_row);
-
-  if (path)
-    {
-      cell_view->priv->displayed_row =
-	gtk_tree_row_reference_new (cell_view->priv->model, path);
-    }
-  else
-    cell_view->priv->displayed_row = NULL;
-
-  /* force resize and redraw */
-  gtk_widget_queue_resize (GTK_WIDGET (cell_view));
-  gtk_widget_queue_draw (GTK_WIDGET (cell_view));
-}
-
-GtkTreePath *
-gtk_cell_view_get_displayed_row (GtkCellView *cell_view)
-{
-  g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), NULL);
-
-  if (!cell_view->priv->displayed_row)
-    return NULL;
-
-  return gtk_tree_row_reference_get_path (cell_view->priv->displayed_row);
-}
-
-gboolean
-gtk_cell_view_get_size_of_row (GtkCellView    *cell_view,
-                               GtkTreePath    *path,
-                               GtkRequisition *requisition)
-{
-  GtkTreeRowReference *tmp;
-  GtkRequisition req;
-
-  g_return_val_if_fail (GTK_IS_CELL_VIEW (cell_view), FALSE);
-  g_return_val_if_fail (path != NULL, FALSE);
-  g_return_val_if_fail (requisition != NULL, FALSE);
-
-  tmp = cell_view->priv->displayed_row;
-  cell_view->priv->displayed_row =
-    gtk_tree_row_reference_new (cell_view->priv->model, path);
-
-  gtk_cell_view_size_request (GTK_WIDGET (cell_view), requisition);
-
-  gtk_tree_row_reference_free (cell_view->priv->displayed_row);
-  cell_view->priv->displayed_row = tmp;
-
-  /* restore actual size info */
-  gtk_cell_view_size_request (GTK_WIDGET (cell_view), &req);
-
-  return TRUE;
-}
-
-void
-gtk_cell_view_set_background_color (GtkCellView    *view,
-                                    const GdkColor *color)
-{
-  g_return_if_fail (GTK_IS_CELL_VIEW (view));
-
-  if (color)
-    {
-      if (!view->priv->background_set)
-        {
-          view->priv->background_set = TRUE;
-          g_object_notify (G_OBJECT (view), "background_set");
-        }
-
-      view->priv->background = *color;
-    }
-  else
-    {
-      if (view->priv->background_set)
-        {
-          view->priv->background_set = FALSE;
-          g_object_notify (G_OBJECT (view), "background_set");
-        }
-    }
-}
-#endif /* Gtk 2.6 */
--- a/pidgin/gtkcellview.h	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/* gtkcellview.h
- * Copyright (C) 2002, 2003  Kristian Rietveld <kris@gtk.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- */
-
-#ifndef __GTK_CELL_VIEW_H__
-#define __GTK_CELL_VIEW_H__
-
-#include <gtk/gtkwidget.h>
-#include <gtk/gtkcellrenderer.h>
-#include <gtk/gtktreemodel.h>
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_CELL_VIEW                (gtk_cell_view_get_type ())
-#define GTK_CELL_VIEW(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_VIEW, GtkCellView))
-#define GTK_CELL_VIEW_CLASS(vtable)       (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_CELL_VIEW, GtkCellViewClass))
-#define GTK_IS_CELL_VIEW(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_VIEW))
-#define GTK_IS_CELL_VIEW_CLASS(vtable)    (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_CELL_VIEW))
-#define GTK_CELL_VIEW_GET_CLASS(inst)     (G_TYPE_INSTANCE_GET_CLASS ((inst), GTK_TYPE_CELL_VIEW, GtkCellViewClass))
-
-typedef struct _GtkCellView             GtkCellView;
-typedef struct _GtkCellViewClass        GtkCellViewClass;
-typedef struct _GtkCellViewPrivate      GtkCellViewPrivate;
-
-struct _GtkCellView
-{
-  GtkWidget parent_instance;
-
-  /*< private >*/
-  GtkCellViewPrivate *priv;
-};
-
-struct _GtkCellViewClass
-{
-  GtkWidgetClass parent_class;
-};
-
-GType             gtk_cell_view_get_type               (void);
-GtkWidget        *gtk_cell_view_new                    (void);
-GtkWidget        *gtk_cell_view_new_with_text          (const gchar     *text);
-GtkWidget        *gtk_cell_view_new_with_markup        (const gchar     *markup);
-GtkWidget        *gtk_cell_view_new_with_pixbuf        (GdkPixbuf       *pixbuf);
-
-
-void              gtk_cell_view_set_value               (GtkCellView     *cell_view,
-                                                         GtkCellRenderer *renderer,
-                                                         gchar           *property,
-                                                         GValue          *value);
-void              gtk_cell_view_set_values              (GtkCellView     *cell_view,
-                                                         GtkCellRenderer *renderer,
-                                                         ...);
-
-void              gtk_cell_view_set_model               (GtkCellView     *cell_view,
-                                                         GtkTreeModel    *model);
-void              gtk_cell_view_set_displayed_row       (GtkCellView     *cell_view,
-                                                         GtkTreePath     *path);
-GtkTreePath      *gtk_cell_view_get_displayed_row       (GtkCellView     *cell_view);
-gboolean          gtk_cell_view_get_size_of_row         (GtkCellView     *cell_view,
-                                                         GtkTreePath     *path,
-                                                         GtkRequisition  *requisition);
-
-void              gtk_cell_view_set_background_color    (GtkCellView     *cell_view,
-                                                         const GdkColor  *color);
-
-G_END_DECLS
-
-#endif /* __GTK_CELL_VIEW_H__ */
--- a/pidgin/gtkcellviewmenuitem.c	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,163 +0,0 @@
-/* gtkcellviewmenuitem.c
- * Copyright (C) 2003  Kristian Rietveld <kris@gtk.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- */
-
-/*
-#include <config.h>
-*/
-#include <gtk/gtkversion.h>
-#if !GTK_CHECK_VERSION(2,6,0)
-#include "gtkcellviewmenuitem.h"
-#include "gtkcellview.h"
-
-struct _GtkCellViewMenuItemPrivate
-{
-  GtkWidget *cell_view;
-};
-
-static void gtk_cell_view_menu_item_init       (GtkCellViewMenuItem      *item);
-static void gtk_cell_view_menu_item_class_init (GtkCellViewMenuItemClass *klass);
-static void gtk_cell_view_menu_item_finalize   (GObject                  *object);
-
-
-GType
-gtk_cell_view_menu_item_get_type (void)
-{
-  static GType cell_view_menu_item_type = 0;
-
-  if (!cell_view_menu_item_type)
-    {
-      static const GTypeInfo cell_view_menu_item_info =
-        {
-          sizeof (GtkCellViewMenuItemClass),
-          NULL,
-          NULL,
-          (GClassInitFunc) gtk_cell_view_menu_item_class_init,
-          NULL,
-          NULL,
-          sizeof (GtkCellViewMenuItem),
-          0,
-          (GInstanceInitFunc) gtk_cell_view_menu_item_init
-        };
-
-      cell_view_menu_item_type =
-        g_type_register_static (GTK_TYPE_MENU_ITEM, "PidginCellViewMenuItem",
-                                &cell_view_menu_item_info, 0);
-    }
-
-  return cell_view_menu_item_type;
-}
-
-static void
-gtk_cell_view_menu_item_class_init (GtkCellViewMenuItemClass *klass)
-{
-  GObjectClass *object_class;
-
-  object_class = (GObjectClass *)klass;
-  object_class->finalize = gtk_cell_view_menu_item_finalize;
-}
-
-static void
-gtk_cell_view_menu_item_init (GtkCellViewMenuItem *item)
-{
-  item->priv = g_new0(GtkCellViewMenuItemPrivate,1);
-}
-
-GtkWidget *
-gtk_cell_view_menu_item_new (void)
-{
-  GtkCellViewMenuItem *item;
-
-  item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL);
-
-  item->priv->cell_view = gtk_cell_view_new ();
-  gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view);
-  gtk_widget_show (item->priv->cell_view);
-
-  return GTK_WIDGET (item);
-}
-
-GtkWidget *
-gtk_cell_view_menu_item_new_with_pixbuf (GdkPixbuf *pixbuf)
-{
-  GtkCellViewMenuItem *item;
-
-  item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL);
-
-  item->priv->cell_view = gtk_cell_view_new_with_pixbuf (pixbuf);
-  gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view);
-  gtk_widget_show (item->priv->cell_view);
-
-  return GTK_WIDGET (item);
-}
-
-GtkWidget *
-gtk_cell_view_menu_item_new_with_text (const gchar *text)
-{
-  GtkCellViewMenuItem *item;
-
-  item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL);
-
-  item->priv->cell_view = gtk_cell_view_new_with_text (text);
-  gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view);
-  gtk_widget_show (item->priv->cell_view);
-
-  return GTK_WIDGET (item);
-}
-
-GtkWidget *
-gtk_cell_view_menu_item_new_with_markup (const gchar *markup)
-{
-  GtkCellViewMenuItem *item;
-
-  item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL);
-
-  item->priv->cell_view = gtk_cell_view_new_with_markup (markup);
-  gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view);
-  gtk_widget_show (item->priv->cell_view);
-
-  return GTK_WIDGET (item);
-}
-
-GtkWidget *
-gtk_cell_view_menu_item_new_from_model (GtkTreeModel *model,
-                                        GtkTreePath  *path)
-{
-  GtkCellViewMenuItem *item;
-
-  item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL);
-
-  item->priv->cell_view = gtk_cell_view_new ();
-  gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view);
-
-  gtk_cell_view_set_model (GTK_CELL_VIEW (item->priv->cell_view), model);
-  gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (item->priv->cell_view), path);
-
-  gtk_widget_show (item->priv->cell_view);
-
-  return GTK_WIDGET (item);
-}
-
-static void
-gtk_cell_view_menu_item_finalize (GObject *object)
-{
-  GtkCellViewMenuItem *item = GTK_CELL_VIEW_MENU_ITEM (object);
-
-  g_free (item->priv);
-}
-#endif /* Gtk 2.6 */
--- a/pidgin/gtkcellviewmenuitem.h	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/* gtkcellviewmenuitem.h
- * Copyright (C) 2003  Kristian Rietveld <kris@gtk.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- */
-
-#ifndef __GTK_CELL_VIEW_MENU_ITEM_H__
-#define __GTK_CELL_VIEW_MENU_ITEM_H__
-
-#include <gtk/gtkmenuitem.h>
-#include <gtk/gtktreemodel.h>
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_CELL_VIEW_MENU_ITEM              (gtk_cell_view_menu_item_get_type ())
-#define GTK_CELL_VIEW_MENU_ITEM(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_VIEW_MENU_ITEM, GtkCellViewMenuItem))
-#define GTK_CELL_VIEW_MENU_ITEM_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_VIEW_MENU_ITEM, GtkCellViewMenuItemClass))
-#define GTK_IS_CELL_VIEW_MENU_ITEM(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_VIEW_MENU_ITEM))
-#define GTK_IS_CELL_VIEW_MENU_ITEM_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_VIEW_MENU_ITEM))
-#define GTK_CELL_VIEW_MENU_ITEM_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_VIEW_MENU_ITEM, GtkCellViewMenuItemClass))
-
-
-typedef struct _GtkCellViewMenuItem        GtkCellViewMenuItem;
-typedef struct _GtkCellViewMenuItemClass   GtkCellViewMenuItemClass;
-typedef struct _GtkCellViewMenuItemPrivate GtkCellViewMenuItemPrivate;
-
-struct _GtkCellViewMenuItem
-{
-  GtkMenuItem parent_instance;
-
-  /*< private >*/
-  GtkCellViewMenuItemPrivate *priv;
-};
-
-struct _GtkCellViewMenuItemClass
-{
-  GtkMenuItemClass parent_class;
-};
-
-
-GType      gtk_cell_view_menu_item_get_type        (void);
-GtkWidget *gtk_cell_view_menu_item_new             (void);
-
-GtkWidget *gtk_cell_view_menu_item_new_with_pixbuf (GdkPixbuf   *pixbuf);
-GtkWidget *gtk_cell_view_menu_item_new_with_text   (const gchar *text);
-GtkWidget *gtk_cell_view_menu_item_new_with_markup (const gchar *markup);
-
-GtkWidget *gtk_cell_view_menu_item_new_from_model  (GtkTreeModel *model,
-                                                    GtkTreePath  *path);
-
-
-G_END_DECLS
-
-#endif /* __GTK_CELL_VIEW_MENU_ITEM_H__ */
--- a/pidgin/gtkconv.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkconv.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1075,11 +1075,7 @@
 	gdk_window_set_cursor(gtkblist->window->window, cursor);
 	gdk_window_set_cursor(win->window->window, cursor);
 	gdk_cursor_unref(cursor);
-#if GTK_CHECK_VERSION(2,4,0)
 	gdk_display_flush(gdk_drawable_get_display(GDK_DRAWABLE(widget->window)));
-#else
-	gdk_flush();
-#endif
 
 	name = purple_conversation_get_name(conv);
 	account = purple_conversation_get_account(conv);
@@ -1115,114 +1111,13 @@
 	clear_conversation_scrollback(conv);
 }
 
-struct _search {
-	PidginWindow *gtkwin;
-	GtkWidget *entry;
-};
-
-static void do_search_cb(GtkWidget *widget, gint resp, struct _search *s)
-{
-	PurpleConversation *conv;
-	PidginConversation *gtk_active_conv;
-	GList *iter;
-
-	conv = pidgin_conv_window_get_active_conversation(s->gtkwin);
-	gtk_active_conv = PIDGIN_CONVERSATION(conv);
-
-	switch (resp)
-	{
-		case GTK_RESPONSE_OK:
-			/* clear highlighting except the active conversation window
-			 * highlight the keywords in the active conversation window */
-			for (iter = pidgin_conv_window_get_gtkconvs(s->gtkwin) ; iter ; iter = iter->next)
-			{
-				PidginConversation *gtkconv = iter->data;
-
-				if (gtkconv != gtk_active_conv)
-				{
-					gtk_imhtml_search_clear(GTK_IMHTML(gtkconv->imhtml));
-				}
-				else
-				{
-					gtk_imhtml_search_find(GTK_IMHTML(gtk_active_conv->imhtml),
-					                       gtk_entry_get_text(GTK_ENTRY(s->entry)));
-				}
-			}
-			break;
-
-		case GTK_RESPONSE_DELETE_EVENT:
-		case GTK_RESPONSE_CLOSE:
-			/* clear the keyword highlighting in all the conversation windows */
-			for (iter = pidgin_conv_window_get_gtkconvs(s->gtkwin); iter; iter=iter->next)
-			{
-				PidginConversation *gconv = iter->data;
-				gtk_imhtml_search_clear(GTK_IMHTML(gconv->imhtml));
-			}
-
-			gtk_widget_destroy(s->gtkwin->dialogs.search);
-			s->gtkwin->dialogs.search = NULL;
-			g_free(s);
-			break;
-	}
-}
-
 static void
 menu_find_cb(gpointer data, guint action, GtkWidget *widget)
 {
 	PidginWindow *gtkwin = data;
-	GtkWidget *hbox;
-	GtkWidget *img = gtk_image_new_from_stock(PIDGIN_STOCK_DIALOG_QUESTION,
-						  gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_HUGE));
-	GtkWidget *label;
-	struct _search *s;
-
-	if (gtkwin->dialogs.search) {
-		gtk_window_present(GTK_WINDOW(gtkwin->dialogs.search));
-		return;
-	}
-
-	s = g_malloc(sizeof(struct _search));
-	s->gtkwin = gtkwin;
-
-	gtkwin->dialogs.search = gtk_dialog_new_with_buttons(_("Find"),
-			GTK_WINDOW(gtkwin->window), GTK_DIALOG_DESTROY_WITH_PARENT,
-			GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
-			GTK_STOCK_FIND, GTK_RESPONSE_OK, NULL);
-	gtk_dialog_set_default_response(GTK_DIALOG(gtkwin->dialogs.search),
-									GTK_RESPONSE_OK);
-	g_signal_connect(G_OBJECT(gtkwin->dialogs.search), "response",
-					 G_CALLBACK(do_search_cb), s);
-
-	gtk_container_set_border_width(GTK_CONTAINER(gtkwin->dialogs.search), PIDGIN_HIG_BOX_SPACE);
-	gtk_window_set_resizable(GTK_WINDOW(gtkwin->dialogs.search), FALSE);
-	gtk_dialog_set_has_separator(GTK_DIALOG(gtkwin->dialogs.search), FALSE);
-	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(gtkwin->dialogs.search)->vbox), PIDGIN_HIG_BORDER);
-	gtk_container_set_border_width(
-		GTK_CONTAINER(GTK_DIALOG(gtkwin->dialogs.search)->vbox), PIDGIN_HIG_BOX_SPACE);
-
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER);
-	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(gtkwin->dialogs.search)->vbox),
-					  hbox);
-	gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
-
-	gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
-	gtk_dialog_set_response_sensitive(GTK_DIALOG(gtkwin->dialogs.search),
-									  GTK_RESPONSE_OK, FALSE);
-
-	label = gtk_label_new(NULL);
-	gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Search for:"));
-	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-
-	s->entry = gtk_entry_new();
-	gtk_entry_set_activates_default(GTK_ENTRY(s->entry), TRUE);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(s->entry));
-	g_signal_connect(G_OBJECT(s->entry), "changed",
-					 G_CALLBACK(pidgin_set_sensitive_if_input),
-					 gtkwin->dialogs.search);
-	gtk_box_pack_start(GTK_BOX(hbox), s->entry, FALSE, FALSE, 0);
-
-	gtk_widget_show_all(gtkwin->dialogs.search);
-	gtk_widget_grab_focus(s->entry);
+	PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(gtkwin);
+	gtk_widget_show_all(gtkconv->quickfind.container);
+	gtk_widget_grab_focus(gtkconv->quickfind.entry);
 }
 
 #ifdef USE_VV
@@ -1255,6 +1150,18 @@
 }
 
 static void
+menu_get_attention_cb(gpointer data, guint action, GtkWidget *widget)
+{
+	PidginWindow *win = data;
+	PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
+
+	if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+		purple_prpl_send_attention(purple_conversation_get_gc(conv), 
+			purple_conversation_get_name(conv), 0);
+	}
+}
+
+static void
 menu_add_pounce_cb(gpointer data, guint action, GtkWidget *widget)
 {
 	PidginWindow *win = data;
@@ -2048,11 +1955,7 @@
 			case GDK_period:
 				gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook),
 						gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv),
-#if GTK_CHECK_VERSION(2,2,0)
 						(curconv + 1) % gtk_notebook_get_n_pages(GTK_NOTEBOOK(win->notebook)));
-#else
-						(curconv + 1) % g_list_length(GTK_NOTEBOOK(win->notebook)->children));
-#endif
 				return TRUE;
 				break;
 			case GDK_F6:
@@ -2320,6 +2223,11 @@
 	gtkconv = PIDGIN_CONVERSATION(conv);
 	old_conv = gtkconv->active_conv;
 
+	purple_debug_info("gtkconv", "setting active conversation on toolbar %p\n",
+		conv);
+	gtk_imhtmltoolbar_switch_active_conversation(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), 
+		conv);
+
 	if (old_conv == conv)
 		return;
 
@@ -3183,6 +3091,7 @@
 #endif
 
 	{ N_("/Conversation/Se_nd File..."), NULL, menu_send_file_cb, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SEND_FILE },
+	{ N_("/Conversation/Get _Attention"), NULL, menu_get_attention_cb, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION },
 	{ N_("/Conversation/Add Buddy _Pounce..."), NULL, menu_add_pounce_cb,
 			0, "<Item>", NULL },
 	{ N_("/Conversation/_Get Info"), "<CTL>O", menu_get_info_cb, 0,
@@ -3567,6 +3476,9 @@
 		gtk_item_factory_get_widget(win->menu.item_factory,
 		                            N_("/Conversation/Send File..."));
 
+	g_object_set_data(G_OBJECT(win->window), "get_attention",
+		gtk_item_factory_get_widget(win->menu.item_factory,
+			                    N_("/Conversation/Get Attention")));
 	win->menu.add_pounce =
 		gtk_item_factory_get_widget(win->menu.item_factory,
 		                            N_("/Conversation/Add Buddy Pounce..."));
@@ -3885,7 +3797,7 @@
 	gtk_size_group_add_widget(sg, image);
 
 	/* Make our menu item */
-	text = g_strdup_printf("%s (%s)", name, purple_account_get_username(account));
+	text = g_strdup_printf("%s (%s)", name, purple_account_get_name_for_display(account));
 	menuitem = gtk_radio_menu_item_new_with_label(*group, text);
 	g_free(text);
 	*group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
@@ -4114,7 +4026,6 @@
 		color = (GdkColor*)get_nick_color(gtkconv, name);
 	}
 
-#if GTK_CHECK_VERSION(2,6,0)
 	gtk_list_store_insert_with_values(ls, &iter,
 /*
 * The GTK docs are mute about the effects of the "row" value for performance.
@@ -4132,18 +4043,6 @@
 			CHAT_USERS_COLOR_COLUMN, color,
 			CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
 			-1);
-#else
-	gtk_list_store_append(ls, &iter);
-	gtk_list_store_set(ls, &iter,
-			CHAT_USERS_ICON_STOCK_COLUMN,  stock,
-			CHAT_USERS_ALIAS_COLUMN, alias,
-			CHAT_USERS_ALIAS_KEY_COLUMN, alias_key,
-			CHAT_USERS_NAME_COLUMN,  name,
-			CHAT_USERS_FLAGS_COLUMN, flags,
-			CHAT_USERS_COLOR_COLUMN, color,
-			CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
-			-1);
-#endif
 
 	if (is_me && color)
 		gdk_color_free(color);
@@ -4816,9 +4715,7 @@
 
 	/* Setup the label telling how many people are in the room. */
 	gtkchat->count = gtk_label_new(_("0 people in room"));
-#if GTK_CHECK_VERSION(2,6,0)
 	gtk_label_set_ellipsize(GTK_LABEL(gtkchat->count), PANGO_ELLIPSIZE_END);
-#endif
 	gtk_box_pack_start(GTK_BOX(lbox), gtkchat->count, FALSE, FALSE, 0);
 	gtk_widget_show(gtkchat->count);
 
@@ -4888,10 +4785,8 @@
 	purple_signal_connect(blist_handle, "blist-node-aliased",
 						gtkchat, PURPLE_CALLBACK(blist_node_aliased_cb), conv);
 
-#if GTK_CHECK_VERSION(2,6,0)
 	gtk_tree_view_column_set_expand(col, TRUE);
 	g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 
 	gtk_tree_view_append_column(GTK_TREE_VIEW(list), col);
 
@@ -4929,6 +4824,121 @@
 	return FALSE;
 }
 
+/* Close button {{{ */
+static gboolean
+close_button_left_cb(GtkWidget *widget, GdkEventCrossing *event, GtkLabel *label)
+{
+	static GdkCursor *ptr = NULL;
+	if (ptr == NULL) {
+		ptr = gdk_cursor_new(GDK_LEFT_PTR);
+	}
+
+	gtk_label_set_markup(label, "×");
+	gdk_window_set_cursor(event->window, ptr);
+	return FALSE;
+}
+
+static gboolean
+close_button_entered_cb(GtkWidget *widget, GdkEventCrossing *event, GtkLabel *label)
+{
+	static GdkCursor *hand = NULL;
+	if (hand == NULL) {
+		hand = gdk_cursor_new(GDK_HAND2);
+	}
+
+	gtk_label_set_markup(label, "<u>×</u>");
+	gdk_window_set_cursor(event->window, hand);
+	return FALSE;
+}
+
+static GtkWidget *
+create_close_button(void)
+{
+	GtkWidget *ebox = gtk_event_box_new();
+	GtkWidget *close_image;
+
+	gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
+	gtk_widget_set_events(ebox, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+	close_image = gtk_label_new("×");
+	g_signal_connect(G_OBJECT(ebox), "enter-notify-event", G_CALLBACK(close_button_entered_cb), close_image);
+	g_signal_connect(G_OBJECT(ebox), "leave-notify-event", G_CALLBACK(close_button_left_cb), close_image);
+	gtk_widget_show(close_image);
+	gtk_container_add(GTK_CONTAINER(ebox), close_image);
+
+	return ebox;
+}
+
+/* }}} */
+
+/* Quick Find {{{ */
+static gboolean
+pidgin_conv_end_quickfind(PidginConversation *gtkconv)
+{
+	gtk_widget_modify_base(gtkconv->quickfind.entry, GTK_STATE_NORMAL, NULL);
+
+	gtk_imhtml_search_clear(GTK_IMHTML(gtkconv->imhtml));
+	gtk_widget_hide_all(gtkconv->quickfind.container);
+
+	gtk_widget_grab_focus(gtkconv->entry);
+	return TRUE;
+}
+
+static gboolean
+quickfind_process_input(GtkWidget *entry, GdkEventKey *event, PidginConversation *gtkconv)
+{
+	switch (event->keyval) {
+		case GDK_Return:
+		case GDK_KP_Enter:
+			if (gtk_imhtml_search_find(GTK_IMHTML(gtkconv->imhtml), gtk_entry_get_text(GTK_ENTRY(entry)))) {
+				gtk_widget_modify_base(gtkconv->quickfind.entry, GTK_STATE_NORMAL, NULL);
+			} else {
+				GdkColor col;
+				col.red = 0xffff;
+				col.green = 0xafff;
+				col.blue = 0xafff;
+				gtk_widget_modify_base(gtkconv->quickfind.entry, GTK_STATE_NORMAL, &col);
+			}
+			break;
+		case GDK_Escape:
+			pidgin_conv_end_quickfind(gtkconv);
+			break;
+		default:
+			return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+pidgin_conv_setup_quickfind(PidginConversation *gtkconv, GtkWidget *container)
+{
+	GtkWidget *widget = gtk_hbox_new(FALSE, 0);
+	GtkWidget *label, *entry, *close;
+
+	gtk_box_pack_start(GTK_BOX(container), widget, FALSE, FALSE, 0);
+
+	close = create_close_button();
+	gtk_box_pack_start(GTK_BOX(widget), close, FALSE, FALSE, 0);
+	gtk_tooltips_set_tip(gtkconv->tooltips, close,
+	                     _("Close Find bar"), NULL);
+
+	label = gtk_label_new(_("Find:"));
+	gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 10);
+
+	entry = gtk_entry_new();
+	gtk_box_pack_start(GTK_BOX(widget), entry, TRUE, TRUE, 0);
+
+	gtkconv->quickfind.entry = entry;
+	gtkconv->quickfind.container = widget;
+
+	/* Hook to signals and stuff */
+	g_signal_connect(G_OBJECT(entry), "key_press_event",
+			G_CALLBACK(quickfind_process_input), gtkconv);
+	g_signal_connect_swapped(G_OBJECT(close), "button-press-event",
+			G_CALLBACK(pidgin_conv_end_quickfind), gtkconv);
+}
+
+/* }}} */
+
 static GtkWidget *
 setup_common_pane(PidginConversation *gtkconv)
 {
@@ -4947,9 +4957,7 @@
 
 	/* Setup the info pane */
 	event_box = gtk_event_box_new();
-#if GTK_CHECK_VERSION(2,4,0)
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), FALSE);
-#endif
 	gtk_widget_show(event_box);
 	gtkconv->infopane_hbox = gtk_hbox_new(FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(vbox), event_box, FALSE, FALSE, 0);
@@ -5019,9 +5027,7 @@
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "markup", CONV_TEXT_COLUMN, NULL);
 	g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
 
-#if GTK_CHECK_VERSION(2, 6, 0)
 	g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 
 	rend = gtk_cell_renderer_pixbuf_new();
 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE);
@@ -5071,6 +5077,8 @@
 	g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event",
 	                 G_CALLBACK(refocus_entry_cb), gtkconv);
 
+	pidgin_conv_setup_quickfind(gtkconv, vbox);
+
 	gtkconv->lower_hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 	gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, FALSE, FALSE, 0);
 	gtk_widget_show(gtkconv->lower_hbox);
@@ -6164,10 +6172,8 @@
 
 	ls = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)));
 
-#if GTK_CHECK_VERSION(2,6,0)
 	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls),  GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
 										 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID);
-#endif
 
 	l = cbuddies;
 	while (l != NULL) {
@@ -6269,12 +6275,7 @@
 							   CHAT_USERS_NAME_COLUMN, &val, -1);
 
 			if (!purple_utf8_strcasecmp((char *)l->data, val)) {
-#if GTK_CHECK_VERSION(2,2,0)
 				f = gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
-#else
-				gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
-				f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
-#endif
 			}
 			else
 				f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
@@ -6532,6 +6533,7 @@
 		/* Deal with menu items */
 		gtk_widget_show(win->menu.view_log);
 		gtk_widget_show(win->menu.send_file);
+		gtk_widget_show(g_object_get_data(G_OBJECT(win->window), "get_attention"));
 		gtk_widget_show(win->menu.add_pounce);
 		gtk_widget_show(win->menu.get_info);
 		gtk_widget_hide(win->menu.invite);
@@ -6560,6 +6562,7 @@
 		/* Deal with menu items */
 		gtk_widget_show(win->menu.view_log);
 		gtk_widget_hide(win->menu.send_file);
+		gtk_widget_hide(g_object_get_data(G_OBJECT(win->window), "get_attention"));
 		gtk_widget_hide(win->menu.add_pounce);
 		gtk_widget_hide(win->menu.get_info);
 		gtk_widget_show(win->menu.invite);
@@ -6637,6 +6640,7 @@
 			gtk_widget_set_sensitive(win->menu.send_file,
 									 (prpl_info->send_file != NULL && (!prpl_info->can_receive_file ||
 									  prpl_info->can_receive_file(gc, purple_conversation_get_name(conv)))));
+			gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(win->window), "get_attention"), (prpl_info->send_attention != NULL));
 			gtk_widget_set_sensitive(win->menu.alias,
 									 (account != NULL) &&
 									 (purple_find_buddy(account, purple_conversation_get_name(conv)) != NULL));
@@ -6657,6 +6661,8 @@
 		/* Then deal with menu items */
 		gtk_widget_set_sensitive(win->menu.view_log, TRUE);
 		gtk_widget_set_sensitive(win->menu.send_file, FALSE);
+		gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(win->window),
+			"get_attention"), FALSE);
 		gtk_widget_set_sensitive(win->menu.add_pounce, TRUE);
 		gtk_widget_set_sensitive(win->menu.get_info, FALSE);
 		gtk_widget_set_sensitive(win->menu.invite, FALSE);
@@ -6790,31 +6796,7 @@
 				? gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text))
 				: NULL;
 			char *esc = NULL, *tmp;
-#if GTK_CHECK_VERSION(2,6,0)
 			esc = topic ? g_markup_escape_text(topic, -1) : NULL;
-#else
-			/* GTK < 2.6 doesn't have auto ellipsization, so we do a crude
-			 * trucation to prevent forcing the window to be as wide as the topic */
-			int len = 0;
-			char *c;
-
-			if (topic != NULL) {
-				tmp = g_strdup(topic);
-				c = tmp;
-				while(*c && len < 72) {
-					c = g_utf8_next_char(c);
-					len++;
-				}
-				if (len == 72) {
-					*c = '\0';
-					c = g_strdup_printf("%s...", tmp);
-					g_free(tmp);
-					tmp = c;
-				}
-				esc = g_markup_escape_text(tmp, -1);
-				g_free(tmp);
-			}
-#endif
 			tmp = g_markup_escape_text(purple_conversation_get_title(conv), -1);
 			markup = g_strdup_printf("%s%s<span color='%s' size='smaller'>%s</span>",
 						tmp, esc  && *esc ? "\n" : "",
@@ -7165,9 +7147,7 @@
 
 	event = gtk_event_box_new();
 	gtk_container_add(GTK_CONTAINER(gtkconv->u.im->icon_container), event);
-#if GTK_CHECK_VERSION(2,4,0)
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE);
-#endif
 	gtk_widget_add_events(event,
                               GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
 	g_signal_connect(G_OBJECT(event), "button-press-event",
@@ -7240,12 +7220,7 @@
 	horiz = (gtk_notebook_get_tab_pos(notebook) == GTK_POS_TOP ||
 			gtk_notebook_get_tab_pos(notebook) == GTK_POS_BOTTOM);
 
-#if GTK_CHECK_VERSION(2,2,0)
 	count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
-#else
-	/* this is hacky, but it's only for Gtk 2.0.0... */
-	count = g_list_length(GTK_NOTEBOOK(notebook)->children);
-#endif
 
 	for (i = 0; i < count; i++) {
 
@@ -8249,9 +8224,7 @@
 		}
 		gtk_rc_parse_string(str->str);
 		g_string_free(str, TRUE);
-#if GTK_CHECK_VERSION(2,4,0)
 		gtk_rc_reset_styles(settings);
-#endif
 	}
 }
 
@@ -8498,16 +8471,6 @@
 	return FALSE;
 }
 
-#if !GTK_CHECK_VERSION(2,6,0)
-/* Courtesy of Galeon! */
-static void
-tab_close_button_state_changed_cb(GtkWidget *widget, GtkStateType prev_state)
-{
-	if (GTK_WIDGET_STATE(widget) == GTK_STATE_ACTIVE)
-		gtk_widget_set_state(widget, GTK_STATE_NORMAL);
-}
-#endif
-
 static void
 notebook_init_grab(PidginWindow *gtkwin, GtkWidget *widget)
 {
@@ -9149,9 +9112,7 @@
 	entry = gtk_entry_new();
 	gtk_entry_set_has_frame(GTK_ENTRY(entry), FALSE);
 	gtk_entry_set_width_chars(GTK_ENTRY(entry), 10);
-#if GTK_CHECK_VERSION(2,4,0)
 	gtk_entry_set_alignment(GTK_ENTRY(entry), 0.5);
-#endif
 
 	gtk_box_pack_start(GTK_BOX(gtkconv->infopane_hbox), entry, TRUE, TRUE, 0);
 	/* after the tab label */
@@ -9350,9 +9311,7 @@
 	GtkPositionType pos;
 	GtkWidget *testidea;
 	GtkWidget *menubar;
-#if GTK_CHECK_VERSION(2,6,0)
 	GdkModifierType state;
-#endif
 
 	win = g_malloc0(sizeof(PidginWindow));
 
@@ -9360,10 +9319,9 @@
 
 	/* Create the window. */
 	win->window = pidgin_create_window(NULL, 0, "conversation", TRUE);
-#if GTK_CHECK_VERSION(2,6,0)
 	if (!gtk_get_current_event_state(&state))
 		gtk_window_set_focus_on_map(GTK_WINDOW(win->window), FALSE);
-#endif
+
 	/* Etan: I really think this entire function call should happen only
 	 * when we are on Windows but I was informed that back before we used
 	 * to save the window position we stored the window size, so I'm
@@ -9506,32 +9464,6 @@
 }
 
 static gboolean
-close_button_left_cb(GtkWidget *widget, GdkEventCrossing *event, GtkLabel *label)
-{
-	static GdkCursor *ptr = NULL;
-	if (ptr == NULL) {
-		ptr = gdk_cursor_new(GDK_LEFT_PTR);
-	}
-
-	gtk_label_set_markup(label, "×");
-	gdk_window_set_cursor(event->window, ptr);
-	return FALSE;
-}
-
-static gboolean
-close_button_entered_cb(GtkWidget *widget, GdkEventCrossing *event, GtkLabel *label)
-{
-	static GdkCursor *hand = NULL;
-	if (hand == NULL) {
-		hand = gdk_cursor_new(GDK_HAND2);
-	}
-
-	gtk_label_set_markup(label, "<u>×</u>");
-	gdk_window_set_cursor(event->window, hand);
-	return FALSE;
-}
-
-static gboolean
 gtkconv_tab_set_tip(GtkWidget *widget, GdkEventCrossing *event, PidginConversation *gtkconv)
 {
 #if GTK_CHECK_VERSION(2, 12, 0)
@@ -9561,7 +9493,6 @@
 	PurpleConversation *conv = gtkconv->active_conv;
 	PidginConversation *focus_gtkconv;
 	GtkWidget *tab_cont = gtkconv->tab_cont;
-	GtkWidget *close_image;
 	PurpleConversationType conv_type;
 	const gchar *tmp_lab;
 
@@ -9575,32 +9506,13 @@
 
 
 	/* Close button. */
-	gtkconv->close = gtk_event_box_new();
-#if GTK_CHECK_VERSION(2,4,0)
-	gtk_event_box_set_visible_window(GTK_EVENT_BOX(gtkconv->close), FALSE);
-#endif
-	gtk_widget_set_events(gtkconv->close, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
-	close_image = gtk_label_new("×");
-	g_signal_connect(G_OBJECT(gtkconv->close), "enter-notify-event", G_CALLBACK(close_button_entered_cb), close_image);
-	g_signal_connect(G_OBJECT(gtkconv->close), "leave-notify-event", G_CALLBACK(close_button_left_cb), close_image);
-	gtk_widget_show(close_image);
-	gtk_container_add(GTK_CONTAINER(gtkconv->close), close_image);
+	gtkconv->close = create_close_button();
 	gtk_tooltips_set_tip(gtkconv->tooltips, gtkconv->close,
 	                     _("Close conversation"), NULL);
 
 	g_signal_connect(G_OBJECT(gtkconv->close), "button-press-event",
 			 G_CALLBACK(close_conv_cb), gtkconv);
 
-#if !GTK_CHECK_VERSION(2,6,0)
-	/*
-	* I love Galeon. They have a fix for that stupid annoying visible
-	* border bug. I love you guys! -- ChipX86
-	*/
-	/* This is fixed properly in some version of Gtk before 2.6.0  */
-	g_signal_connect(G_OBJECT(gtkconv->close), "state_changed",
-	                 G_CALLBACK(tab_close_button_state_changed_cb), NULL);
-#endif
-
 	/* Status icon. */
 	gtkconv->icon = gtk_image_new();
 	gtkconv->menu_icon = gtk_image_new();
@@ -9669,7 +9581,6 @@
 	else if (purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == (GTK_POS_RIGHT|8))
 		angle = 270;
 
-#if GTK_CHECK_VERSION(2,6,0)
 	if (!angle) {
 		g_object_set(G_OBJECT(gtkconv->tab_label), "ellipsize", PANGO_ELLIPSIZE_END,  NULL);
 		gtk_label_set_width_chars(GTK_LABEL(gtkconv->tab_label), 4);
@@ -9686,7 +9597,6 @@
 	}
 
 	gtk_label_set_angle(GTK_LABEL(gtkconv->tab_label), angle);
-#endif
 
 #if 0
 	gtk_misc_set_alignment(GTK_MISC(gtkconv->tab_label), 0.00, 0.5);
@@ -9709,9 +9619,7 @@
 	}
 
 	ebox = gtk_event_box_new();
-#if GTK_CHECK_VERSION(2,4,0)
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
-#endif
 	gtk_container_add(GTK_CONTAINER(ebox), gtkconv->tabby);
 	g_signal_connect(G_OBJECT(ebox), "enter-notify-event",
 			G_CALLBACK(gtkconv_tab_set_tip), gtkconv);
--- a/pidgin/gtkconv.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkconv.h	Mon Mar 08 22:53:02 2010 +0000
@@ -177,6 +177,12 @@
 		int timer;
 		GList *current;
 	} attach;
+
+	/* Quick Find (since 2.7.0) */
+	struct {
+		GtkWidget *entry;
+		GtkWidget *container;
+	} quickfind;
 };
 
 /*@}*/
--- a/pidgin/gtkdebug.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkdebug.c	Mon Mar 08 22:53:02 2010 +0000
@@ -250,11 +250,11 @@
 static void
 pause_cb(GtkWidget *w, DebugWindow *win)
 {
-	win->paused = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
+	win->paused = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(w));
 
 #ifdef HAVE_REGEX_H
 	if(!win->paused) {
-		if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
+		if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
 			regex_filter_all(win);
 		else
 			regex_show_all(win);
@@ -445,7 +445,7 @@
 	/* we check if the filter is on in case it was only of the options that
 	 * got changed, and not the expression.
 	 */
-	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
+	if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
 		regex_filter_all(win);
 }
 
@@ -459,9 +459,9 @@
 	if(!win || !win->window)
 		return;
 
-	current = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter));
+	current = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter));
 	if(active != current)
-		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter), active);
+		gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), active);
 }
 
 static void
@@ -483,7 +483,7 @@
 
 	win->invert = active;
 
-	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
+	if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
 		regex_filter_all(win);
 }
 
@@ -496,7 +496,7 @@
 
 	win->highlight = active;
 
-	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
+	if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
 		regex_filter_all(win);
 }
 
@@ -522,7 +522,7 @@
 	gtk_tree_model_get(model, iter, 0, &text, 1, &level, -1);
 
 	if (level >= purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel")) {
-		if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) {
+		if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) {
 			regex_match(win, text);
 		} else {
 			gtk_imhtml_append_text(GTK_IMHTML(win->text), text, 0);
@@ -546,8 +546,8 @@
 
 static void
 regex_changed_cb(GtkWidget *w, DebugWindow *win) {
-	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) {
-		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter),
+	if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) {
+		gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter),
 									 FALSE);
 	}
 
@@ -561,9 +561,9 @@
 regex_key_release_cb(GtkWidget *w, GdkEventKey *e, DebugWindow *win) {
 	if(e->keyval == GDK_Return &&
 	   GTK_WIDGET_IS_SENSITIVE(win->filter) &&
-	   !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
+	   !gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
 	{
-		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter), TRUE);
+		gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), TRUE);
 	}
 }
 
@@ -588,10 +588,10 @@
 }
 
 static void
-regex_filter_toggled_cb(GtkToggleButton *button, DebugWindow *win) {
+regex_filter_toggled_cb(GtkToggleToolButton *button, DebugWindow *win) {
 	gboolean active;
 
-	active = gtk_toggle_button_get_active(button);
+	active = gtk_toggle_tool_button_get_active(button);
 
 	purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/filter", active);
 
@@ -611,7 +611,7 @@
 
 	if (GPOINTER_TO_INT(value) != gtk_combo_box_get_active(GTK_COMBO_BOX(win->filterlevel)))
 		gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel), GPOINTER_TO_INT(value));
-	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)))
+	if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)))
 		regex_filter_all(win);
 	else
 		regex_show_all(win);
@@ -677,9 +677,12 @@
 	GtkWidget *vbox;
 	GtkWidget *toolbar;
 	GtkWidget *frame;
-	GtkWidget *image;
 	gint width, height;
 	void *handle;
+	GtkToolItem *item;
+#if !GTK_CHECK_VERSION(2,12,0)
+	GtkTooltips *tooltips;
+#endif
 
 	win = g_new0(DebugWindow, 1);
 
@@ -720,10 +723,13 @@
 	if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/toolbar")) {
 		/* Setup our top button bar thingie. */
 		toolbar = gtk_toolbar_new();
+#if !GTK_CHECK_VERSION(2,12,0)
+		tooltips = gtk_tooltips_new();
+#endif
+#if !GTK_CHECK_VERSION(2,14,0)
 		gtk_toolbar_set_tooltips(GTK_TOOLBAR(toolbar), TRUE);
-#if GTK_CHECK_VERSION(2,4,0)
+#endif
 		gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), TRUE);
-#endif
 		g_signal_connect(G_OBJECT(toolbar), "button-press-event", G_CALLBACK(toolbar_context), win);
 
 		gtk_toolbar_set_style(GTK_TOOLBAR(toolbar),
@@ -737,61 +743,93 @@
 
 #ifndef HAVE_REGEX_H
 		/* Find button */
-		gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_FIND,
-		                         _("Find"), NULL, G_CALLBACK(find_cb),
-		                         win, -1);
+		item = gtk_tool_button_new_from_stock(GTK_STOCK_FIND);
+		gtk_tool_item_set_is_important(item, TRUE);
+#if GTK_CHECK_VERSION(2,12,0)
+		gtk_tool_item_set_tooltip_text(item, _("Find"));
+#else
+		gtk_tool_item_set_tooltip(item, tooltips, _("Find"), NULL);
+#endif
+		g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(find_cb), win);
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
 #endif /* HAVE_REGEX_H */
 
 		/* Save */
-		gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_SAVE,
-		                         _("Save"), NULL, G_CALLBACK(save_cb),
-		                         win, -1);
+		item = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
+		gtk_tool_item_set_is_important(item, TRUE);
+#if GTK_CHECK_VERSION(2,12,0)
+		gtk_tool_item_set_tooltip_text(item, _("Save"));
+#else
+		gtk_tool_item_set_tooltip(item, tooltips, _("Save"), NULL);
+#endif
+		g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(save_cb), win);
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
 
 		/* Clear button */
-		gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_CLEAR,
-		                         _("Clear"), NULL, G_CALLBACK(clear_cb),
-		                         win, -1);
+		item = gtk_tool_button_new_from_stock(GTK_STOCK_CLEAR);
+		gtk_tool_item_set_is_important(item, TRUE);
+#if GTK_CHECK_VERSION(2,12,0)
+		gtk_tool_item_set_tooltip_text(item, _("Clear"));
+#else
+		gtk_tool_item_set_tooltip(item, tooltips, _("Clear"), NULL);
+#endif
+		g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(clear_cb), win);
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
 
-		gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1);
+		item = gtk_separator_tool_item_new();
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
 
 		/* Pause */
-		image = gtk_image_new_from_stock(PIDGIN_STOCK_PAUSE, GTK_ICON_SIZE_MENU);
-		gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
-		                                    GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
-		                                    NULL, _("Pause"), _("Pause"),
-		                                    NULL, image,
-		                                    G_CALLBACK(pause_cb), win);
+		item = gtk_toggle_tool_button_new_from_stock(PIDGIN_STOCK_PAUSE);
+		gtk_tool_item_set_is_important(item, TRUE);
+#if GTK_CHECK_VERSION(2,12,0)
+		gtk_tool_item_set_tooltip_text(item, _("Pause"));
+#else
+		gtk_tool_item_set_tooltip(item, tooltips, _("Pause"), NULL);
+#endif
+		g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(pause_cb), win);
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
 
 #ifdef HAVE_REGEX_H
 		/* regex stuff */
-		gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1);
+		item = gtk_separator_tool_item_new();
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
 
 		/* regex toggle button */
-		image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
-		win->filter =
-			gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
-									   GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
-									   NULL, _("Filter"), _("Filter"),
-									   NULL, image,
-									   G_CALLBACK(regex_filter_toggled_cb),
-									   win);
+		item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_FIND);
+		gtk_tool_item_set_is_important(item, TRUE);
+		win->filter = GTK_WIDGET(item);
+		gtk_tool_button_set_label(GTK_TOOL_BUTTON(win->filter), _("Filter"));
+#if GTK_CHECK_VERSION(2,12,0)
+		gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(win->filter), _("Filter"));
+#else
+		gtk_tooltips_set_tip(tooltips, win->filter, _("Filter"), NULL);
+#endif
+		g_signal_connect(G_OBJECT(win->filter), "clicked", G_CALLBACK(regex_filter_toggled_cb), win);
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(win->filter));
+
 		/* we purposely disable the toggle button here in case
 		 * /purple/gtk/debug/expression has an empty string.  If it does not have
 		 * an empty string, the change signal will get called and make the
 		 * toggle button sensitive.
 		 */
 		gtk_widget_set_sensitive(win->filter, FALSE);
-		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter),
+		gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter),
 									 purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/filter"));
 		purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/filter",
 									regex_pref_filter_cb, win);
 
 		/* regex entry */
 		win->expression = gtk_entry_new();
-		gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
-								   GTK_TOOLBAR_CHILD_WIDGET, win->expression,
-								   NULL, _("Right click for more options."),
-								   NULL, NULL, NULL, NULL);
+		item = gtk_tool_item_new();
+#if GTK_CHECK_VERSION(2,12,0)
+		gtk_widget_set_tooltip_text(win->expression, _("Right click for more options."));
+#else
+		gtk_tooltips_set_tip(tooltips, win->expression, _("Right click for more options."), NULL);
+#endif
+		gtk_container_add(GTK_CONTAINER(item), GTK_WIDGET(win->expression));
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
+
 		/* this needs to be before the text is set from the pref if we want it
 		 * to colorize a stored expression.
 		 */
@@ -817,18 +855,23 @@
 
 #endif /* HAVE_REGEX_H */
 
-		gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1);
+		item = gtk_separator_tool_item_new();
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
 
-		gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
-		                           GTK_TOOLBAR_CHILD_WIDGET, gtk_label_new(_("Level ")),
-		                           NULL, _("Select the debug filter level."),
-		                           NULL, NULL, NULL, NULL);
+		item = gtk_tool_item_new();
+		gtk_container_add(GTK_CONTAINER(item), gtk_label_new(_("Level ")));
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
 
 		win->filterlevel = gtk_combo_box_new_text();
-		gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
-		                           GTK_TOOLBAR_CHILD_WIDGET, win->filterlevel,
-		                           NULL, _("Select the debug filter level."),
-		                           NULL, NULL, NULL, NULL);
+		item = gtk_tool_item_new();
+#if GTK_CHECK_VERSION(2,12,0)
+		gtk_widget_set_tooltip_text(win->filterlevel, _("Select the debug filter level."));
+#else
+		gtk_tooltips_set_tip(tooltips, win->filterlevel, _("Select the debug filter level."), NULL);
+#endif
+		gtk_container_add(GTK_CONTAINER(item), win->filterlevel);
+		gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item));
+
 		gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("All"));
 		gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("Misc"));
 		gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("Info"));
--- a/pidgin/gtkdialogs.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkdialogs.c	Mon Mar 08 22:53:02 2010 +0000
@@ -46,8 +46,6 @@
 
 static GList *dialogwindows = NULL;
 
-static GtkWidget *about = NULL;
-
 struct _PidginGroupMergeObject {
 	PurpleGroup* parent;
 	char *new_name;
@@ -357,11 +355,9 @@
 	}
 }
 
-static void destroy_about(void)
+static void destroy_win(GtkWidget *button, GtkWidget *win)
 {
-	if (about != NULL)
-		gtk_widget_destroy(about);
-	about = NULL;
+	gtk_widget_destroy(win);
 }
 
 #if 0
@@ -404,130 +400,129 @@
 }
 #endif
 
-void pidgin_dialogs_about()
+/* Note: Frees 'string' */
+static GtkWidget *
+pidgin_build_help_dialog(const char *title, const char *role, GString *string)
 {
-	GtkWidget *vbox;
-	GtkWidget *logo;
-	GtkWidget *frame;
-	GtkWidget *text;
-	GtkWidget *button;
-	GtkTextIter iter;
-	GString *str;
-	AtkObject *obj;
-	char* filename, *tmp;
+	GtkWidget *win, *vbox, *frame, *logo, *imhtml, *button;
 	GdkPixbuf *pixbuf;
-	PidginBuddyList *buddylist;
-
-	if (about != NULL) {
-		gtk_window_present(GTK_WINDOW(about));
-		return;
-	}
+	GtkTextIter iter;
+	AtkObject *obj;
+	char *filename, *tmp;
 
-	tmp = g_strdup_printf(_("About %s"), PIDGIN_NAME);
-	about = pidgin_create_dialog(tmp, PIDGIN_HIG_BORDER, "about", TRUE);
-	g_free(tmp);
-	gtk_window_set_default_size(GTK_WINDOW(about), 340, 450);
-
-	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(about), FALSE, PIDGIN_HIG_BORDER);
+	win = pidgin_create_dialog(title, PIDGIN_HIG_BORDER, role, TRUE);
+	vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
+	gtk_window_set_default_size(GTK_WINDOW(win), 450, 450);
 
 	/* Generate a logo with a version number */
-	logo = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-	gtk_widget_realize(logo);
 	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "logo.png", NULL);
 	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
 	g_free(filename);
+
 #if 0  /* Don't versionize the logo when the logo has the version in it */
 	pidgin_logo_versionize(&pixbuf, logo);
 #endif
-	gtk_widget_destroy(logo);
+
+	/* Insert the logo */
 	logo = gtk_image_new_from_pixbuf(pixbuf);
 	g_object_unref(G_OBJECT(pixbuf));
-	/* Insert the logo */
 	obj = gtk_widget_get_accessible(logo);
 	tmp = g_strconcat(PIDGIN_NAME, " " DISPLAY_VERSION, NULL);
 	atk_object_set_description(obj, tmp);
 	g_free(tmp);
 	gtk_box_pack_start(GTK_BOX(vbox), logo, FALSE, FALSE, 0);
 
-	frame = pidgin_create_imhtml(FALSE, &text, NULL, NULL);
-	gtk_imhtml_set_format_functions(GTK_IMHTML(text), GTK_IMHTML_ALL ^ GTK_IMHTML_SMILEY);
+	frame = pidgin_create_imhtml(FALSE, &imhtml, NULL, NULL);
+	gtk_imhtml_set_format_functions(GTK_IMHTML(imhtml), GTK_IMHTML_ALL ^ GTK_IMHTML_SMILEY);
 	gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
 
+	gtk_imhtml_append_text(GTK_IMHTML(imhtml), string->str, GTK_IMHTML_NO_SCROLL);
+	gtk_text_buffer_get_start_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)), &iter);
+	gtk_text_buffer_place_cursor(gtk_text_view_get_buffer(GTK_TEXT_VIEW(imhtml)), &iter);
+
+	button = pidgin_dialog_add_button(GTK_DIALOG(win), GTK_STOCK_CLOSE,
+	                G_CALLBACK(destroy_win), win);
+
+	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+	gtk_widget_grab_default(button);
+
+	gtk_widget_show_all(win);
+	gtk_window_present(GTK_WINDOW(win));
+
+	g_string_free(string, TRUE);
+
+	return win;
+}
+
+void pidgin_dialogs_about(void)
+{
+	GString *str;
+	char *tmp;
+	static GtkWidget *about = NULL;
+
+	if (about != NULL) {
+		gtk_window_present(GTK_WINDOW(about));
+		return;
+	}
+
 	str = g_string_sized_new(4096);
 
 	g_string_append_printf(str,
-		"<CENTER><FONT SIZE=\"4\"><B>%s %s</B></FONT></CENTER><BR>(libpurple %s)<BR>%s<BR><BR>", PIDGIN_NAME, DISPLAY_VERSION, purple_core_get_version(), REVISION);
+		"<CENTER><FONT SIZE=\"4\"><B>%s %s</B></FONT></CENTER> (libpurple %s)"
+		"<BR>%s<BR><BR>", PIDGIN_NAME, DISPLAY_VERSION,
+		purple_core_get_version(), REVISION);
+
+	g_string_append_printf(str,
+		_("%s is a messaging client based on libpurple which is capable of "
+		  "connecting to multiple messaging services at once.  %s is written "
+		  "in C using GTK+.  %s is released, and may be modified and "
+		  "redistributed,  under the terms of the GPL version 2 (or later).  "
+		  "A copy of the GPL is distributed with %s.  %s is copyrighted by "
+		  "its contributors, a list of whom is also distributed with %s.  "
+		  "There is no warranty for %s.<BR><BR>"), PIDGIN_NAME, PIDGIN_NAME,
+		PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME);
+
+	g_string_append_printf(str,
+			_("<FONT SIZE=\"4\"><B>Helpful Resources</B></FONT><BR>\t<A "
+			  "HREF=\"%s\">Website</A><BR>\t<A HREF=\"%s\">Frequently Asked "
+			  "Questions</A><BR>\tIRC Channel: #pidgin on irc.freenode.net<BR>"
+			  "\tXMPP MUC: devel@conference.pidgin.im<BR><BR>"), PURPLE_WEBSITE,
+			"http://developer.pidgin.im/wiki/FAQ");
 
 	g_string_append_printf(str,
-		_("%s is a graphical modular messaging client based on "
-		  "libpurple which is capable of connecting to "
-		  "AIM, MSN, Yahoo!, XMPP, ICQ, IRC, SILC, SIP/SIMPLE, "
-		  "Novell GroupWise, Lotus Sametime, Bonjour, Zephyr, "
-		  "MySpaceIM, Gadu-Gadu, and QQ all at once.  "
-		  "It is written using GTK+.<BR><BR>"
-		  "You may modify and redistribute the program under "
-		  "the terms of the GPL (version 2 or later).  A copy of the GPL is "
-		  "contained in the 'COPYING' file distributed with %s.  "
-		  "%s is copyrighted by its contributors.  See the 'COPYRIGHT' "
-		  "file for the complete list of contributors.  We provide no "
-		  "warranty for this program.<BR><BR>"), PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME);
+			_("<font size=\"4\"><b>Help from other Pidgin users</b></font> is "
+			  "available by e-mailing <a "
+			  "href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/>"
+			  "This is a <b>public</b> mailing list! "
+			  "(<a href=\"http://pidgin.im/pipermail/support/\">archive</a>)<br/>"
+			  "We can't help with third-party protocols or plugins!<br/>"
+			  "This list's primary language is <b>English</b>.  You are "
+			  "welcome to post in another language, but the responses may "
+			  "be less helpful.<br/>"));
 
-	g_string_append(str, "<FONT SIZE=\"4\">URL:</FONT> <A HREF=\""
-				PURPLE_WEBSITE "\">" PURPLE_WEBSITE "</A><BR/><BR/>");
-	g_string_append_printf(str, _("<FONT SIZE=\"4\">FAQ:</FONT> <A HREF=\""
-				"http://developer.pidgin.im/wiki/FAQ\">"
-				"http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>"));
-	g_string_append_printf(str,
-			_("<font size=\"4\">Help from other Pidgin users:</font> "
-			"<a href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/>"
-			"This is a <b>public</b> mailing list! "
-			"(<a href=\"http://pidgin.im/pipermail/support/\">archive</a>)<br/>"
-			"We can't help with 3rd party protocols or plugins!<br/>"
-			"This list's primary language is <b>English</b>.  You are "
-			"welcome to post in another language, but the responses may "
-			"be less helpful.<br/><br/>"));
-	g_string_append_printf(str, _("<FONT SIZE=\"4\">IRC Channel:</FONT> "
-				"#pidgin on irc.freenode.net<BR><BR>"));
-	g_string_append_printf(str, _("<FONT SIZE=\"4\">XMPP MUC:</FONT> "
-				"devel@conference.pidgin.im<BR><BR>"));
+	tmp = g_strdup_printf(_("About %s"), PIDGIN_NAME);
+	about = pidgin_build_help_dialog(tmp, "about", str);
+	g_signal_connect(G_OBJECT(about), "destroy", G_CALLBACK(gtk_widget_destroyed), &about);
+	g_free(tmp);
+}
 
-	/* Current Developers */
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
-						   _("Current Developers"));
-	add_developers(str, developers);
-	g_string_append(str, "<BR/>");
-
-	/* Crazy Patch Writers */
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
-						   _("Crazy Patch Writers"));
-	add_developers(str, patch_writers);
-	g_string_append(str, "<BR/>");
-
-	/* Retired Developers */
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
-						   _("Retired Developers"));
-	add_developers(str, retired_developers);
-	g_string_append(str, "<BR/>");
+void pidgin_dialogs_buildinfo(void)
+{
+	GString *str;
+	char *tmp;
+	static GtkWidget *buildinfo = NULL;
 
-	/* Retired Crazy Patch Writers */
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
-						   _("Retired Crazy Patch Writers"));
-	add_developers(str, retired_patch_writers);
-	g_string_append(str, "<BR/>");
+	if (buildinfo != NULL) {
+		gtk_window_present(GTK_WINDOW(buildinfo));
+		return;
+	}
 
-	/* Current Translators */
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
-						   _("Current Translators"));
-	add_translators(str, translators);
-	g_string_append(str, "<BR/>");
+	str = g_string_sized_new(4096);
 
-	/* Past Translators */
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
-						   _("Past Translators"));
-	add_translators(str, past_translators);
-	g_string_append(str, "<BR/>");
+	g_string_append_printf(str,
+		"<FONT SIZE=\"4\"><B>%s %s</B></FONT> (libpurple %s)<BR>%s<BR><BR>", PIDGIN_NAME, DISPLAY_VERSION, purple_core_get_version(), REVISION);
 
-	g_string_append_printf(str, "<FONT SIZE=\"4\">%s</FONT><br/>", _("Debugging Information"));
+	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s</B></FONT><br/>", _("Build Information"));
 
 	/* The following primarly intented for user/developer interaction and thus
 	   ought not be translated */
@@ -704,31 +699,82 @@
 
 	/* End of not to be translated section */
 
-	gtk_imhtml_append_text(GTK_IMHTML(text), str->str, GTK_IMHTML_NO_SCROLL);
-	g_string_free(str, TRUE);
+	tmp = g_strdup_printf(_("%s Build Information"), PIDGIN_NAME);
+	buildinfo = pidgin_build_help_dialog(tmp, "buildinfo", str);
+	g_signal_connect(G_OBJECT(buildinfo), "destroy", G_CALLBACK(gtk_widget_destroyed), &buildinfo);
+	g_free(tmp);
+}
 
-	gtk_text_buffer_get_start_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)), &iter);
-	gtk_text_buffer_place_cursor(gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)), &iter);
+void pidgin_dialogs_developers(void)
+{
+	GString *str;
+	char *tmp;
+	static GtkWidget *developer_info = NULL;
+
+	if (developer_info != NULL) {
+		gtk_window_present(GTK_WINDOW(developer_info));
+		return;
+	}
 
-	/* Close Button */
-	button = pidgin_dialog_add_button(GTK_DIALOG(about), GTK_STOCK_CLOSE,
-	                G_CALLBACK(destroy_about), about);
+	str = g_string_sized_new(4096);
+
+	/* Current Developers */
+	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+						   _("Current Developers"));
+	add_developers(str, developers);
+	g_string_append(str, "<BR/>");
 
-	g_signal_connect(G_OBJECT(about), "destroy",
-					 G_CALLBACK(destroy_about), G_OBJECT(about));
+	/* Crazy Patch Writers */
+	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+						   _("Crazy Patch Writers"));
+	add_developers(str, patch_writers);
+	g_string_append(str, "<BR/>");
+
+	/* Retired Developers */
+	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+						   _("Retired Developers"));
+	add_developers(str, retired_developers);
+	g_string_append(str, "<BR/>");
 
-	/* this makes the sizes not work? */
-	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
-	gtk_widget_grab_default(button);
+	/* Retired Crazy Patch Writers */
+	g_string_append_printf(str, "<FONT SIZE=\"4\"><B>%s:</B></FONT><BR/>",
+						   _("Retired Crazy Patch Writers"));
+	add_developers(str, retired_patch_writers);
+
+	tmp = g_strdup_printf(_("%s Developer Information"), PIDGIN_NAME);
+	developer_info = pidgin_build_help_dialog(tmp, "developer_info", str);
+	g_signal_connect(G_OBJECT(developer_info), "destroy", G_CALLBACK(gtk_widget_destroyed), &developer_info);
+	g_free(tmp);
+}
+
+void pidgin_dialogs_translators(void)
+{
+	GString *str;
+	char *tmp;
+	static GtkWidget *translator_info = NULL;
 
-	/* Let's give'em something to talk about -- woah woah woah */
-	buddylist = pidgin_blist_get_default_gtk_blist();
-	if (buddylist)
-		gtk_window_set_transient_for(GTK_WINDOW(about),
-				GTK_WINDOW(buddylist->window));
+	if (translator_info != NULL) {
+		gtk_window_present(GTK_WINDOW(translator_info));
+		return;
+	}
+
+	str = g_string_sized_new(4096);
 
-	gtk_widget_show_all(about);
-	gtk_window_present(GTK_WINDOW(about));
+	/* Current Translators */
+	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
+						   _("Current Translators"));
+	add_translators(str, translators);
+	g_string_append(str, "<BR/>");
+
+	/* Past Translators */
+	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
+						   _("Past Translators"));
+	add_translators(str, past_translators);
+
+	tmp = g_strdup_printf(_("%s Translator Information"), PIDGIN_NAME);
+	translator_info = pidgin_build_help_dialog(tmp, "translator_info", str);
+	g_signal_connect(G_OBJECT(translator_info), "destroy", G_CALLBACK(gtk_widget_destroyed), &translator_info);
+	g_free(tmp);
 }
 
 static void
--- a/pidgin/gtkdialogs.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkdialogs.h	Mon Mar 08 22:53:02 2010 +0000
@@ -33,6 +33,9 @@
 /* Functions in gtkdialogs.c (these should actually stay in this file) */
 void pidgin_dialogs_destroy_all(void);
 void pidgin_dialogs_about(void);
+void pidgin_dialogs_buildinfo(void);
+void pidgin_dialogs_developers(void);
+void pidgin_dialogs_translators(void);
 void pidgin_dialogs_im(void);
 void pidgin_dialogs_im_with_user(PurpleAccount *, const char *);
 void pidgin_dialogs_info(void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/gtkdocklet-gtk.c	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,155 @@
+/*
+ * System tray icon (aka docklet) plugin for Purple
+ *
+ * Copyright (C) 2007 Anders Hasselqvist
+ * 
+ * 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "internal.h"
+#include "pidgin.h"
+#include "debug.h"
+#include "prefs.h"
+#include "pidginstock.h"
+#include "gtkdocklet.h"
+
+/* globals */
+GtkStatusIcon *docklet = NULL;
+
+static void
+docklet_gtk_status_activated_cb(GtkStatusIcon *status_icon, gpointer user_data)
+{
+	pidgin_docklet_clicked(1); 
+}
+
+static void
+docklet_gtk_status_clicked_cb(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
+{
+	pidgin_docklet_clicked(button); 
+}
+
+static void
+docklet_gtk_status_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending)
+{
+	const gchar *icon_name = NULL;
+
+	switch (status) {
+		case PURPLE_STATUS_OFFLINE:
+			icon_name = PIDGIN_STOCK_TRAY_OFFLINE;
+			break;
+		case PURPLE_STATUS_AWAY:
+			icon_name = PIDGIN_STOCK_TRAY_AWAY;
+			break;
+		case PURPLE_STATUS_UNAVAILABLE:
+			icon_name = PIDGIN_STOCK_TRAY_BUSY;
+			break;
+		case PURPLE_STATUS_EXTENDED_AWAY:
+			icon_name = PIDGIN_STOCK_TRAY_XA;
+			break;
+		case PURPLE_STATUS_INVISIBLE:
+			icon_name = PIDGIN_STOCK_TRAY_INVISIBLE;
+			break;
+		default:
+			icon_name = PIDGIN_STOCK_TRAY_AVAILABLE;
+			break;
+	}
+
+	if (pending)
+		icon_name = PIDGIN_STOCK_TRAY_PENDING;
+	if (connecting)
+		icon_name = PIDGIN_STOCK_TRAY_CONNECT;
+
+	if (icon_name) {
+		gtk_status_icon_set_from_icon_name(docklet, icon_name);
+	}
+}
+
+static void
+docklet_gtk_status_set_tooltip(gchar *tooltip)
+{
+	if (tooltip) {
+		gtk_status_icon_set_tooltip(docklet, tooltip);
+	} else {
+		gtk_status_icon_set_tooltip(docklet, NULL);
+	}
+}
+
+static void
+docklet_gtk_status_position_menu(GtkMenu *menu,
+                                 int *x, int *y, gboolean *push_in,
+                                 gpointer user_data)
+{
+	gtk_status_icon_position_menu(menu, x, y, push_in, docklet);
+}
+
+static void
+docklet_gtk_status_destroy(void)
+{
+	g_return_if_fail(docklet != NULL);
+
+	pidgin_docklet_remove();
+	
+	g_object_unref(G_OBJECT(docklet));
+	docklet = NULL;
+
+	purple_debug_info("docklet", "GTK+ destroyed\n");
+}
+
+static void
+docklet_gtk_status_create(gboolean recreate)
+{
+	if (docklet) {
+		/* if this is being called when a tray icon exists, it's because
+		   something messed up. try destroying it before we proceed,
+		   although docklet_refcount may be all hosed. hopefully won't happen. */
+		purple_debug_warning("docklet", "trying to create icon but it already exists?\n");
+		docklet_gtk_status_destroy();
+	}
+
+	docklet = gtk_status_icon_new();
+	g_return_if_fail(docklet != NULL);
+
+	g_signal_connect(G_OBJECT(docklet), "activate", G_CALLBACK(docklet_gtk_status_activated_cb), NULL);
+	g_signal_connect(G_OBJECT(docklet), "popup-menu", G_CALLBACK(docklet_gtk_status_clicked_cb), NULL);
+
+	pidgin_docklet_embedded();
+	gtk_status_icon_set_visible(docklet, TRUE);
+	purple_debug_info("docklet", "GTK+ created\n");
+}
+
+static void
+docklet_gtk_status_create_ui_op(void)
+{
+	docklet_gtk_status_create(FALSE);
+}
+
+static struct docklet_ui_ops ui_ops =
+{
+	docklet_gtk_status_create_ui_op,
+	docklet_gtk_status_destroy,
+	docklet_gtk_status_update_icon,
+	NULL,
+	docklet_gtk_status_set_tooltip,
+	docklet_gtk_status_position_menu
+};
+
+void
+docklet_ui_init(void)
+{
+	pidgin_docklet_set_ui_ops(&ui_ops);
+	gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(),
+		DATADIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S "pidgin" G_DIR_SEPARATOR_S "tray");
+}
--- a/pidgin/gtkdocklet-x11.c	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,360 +0,0 @@
-/*
- * System tray icon (aka docklet) plugin for Purple
- *
- * Copyright (C) 2002-3 Robert McQueen <robot101@debian.org>
- * Copyright (C) 2003 Herman Bloggs <hermanator12002@yahoo.com>
- * Inspired by a similar plugin by:
- *  John (J5) Palmieri <johnp@martianrock.com>
- *
- * 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 "pidgin.h"
-#include "debug.h"
-#include "prefs.h"
-#include "pidginstock.h"
-
-#include "gtkdialogs.h"
-
-#include "eggtrayicon.h"
-#include "gtkdocklet.h"
-#include <gdk/gdkkeysyms.h>
-
-#define SHORT_EMBED_TIMEOUT 5000
-#define LONG_EMBED_TIMEOUT 15000
-
-/* globals */
-static EggTrayIcon *docklet = NULL;
-static GtkWidget *image = NULL;
-static GtkTooltips *tooltips = NULL;
-static GdkPixbuf *blank_icon = NULL;
-static int embed_timeout = 0;
-static int docklet_height = 0;
-
-/* protos */
-static void docklet_x11_create(gboolean);
-
-static gboolean
-docklet_x11_recreate_cb(gpointer data)
-{
-	docklet_x11_create(TRUE);
-
-	return FALSE; /* for when we're called by the glib idle handler */
-}
-
-static void
-docklet_x11_embedded_cb(GtkWidget *widget, void *data)
-{
-	purple_debug(PURPLE_DEBUG_INFO, "docklet", "embedded\n");
-
-	g_source_remove(embed_timeout);
-	embed_timeout = 0;
-	pidgin_docklet_embedded();
-	purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded", FALSE);
-}
-
-static void
-docklet_x11_destroyed_cb(GtkWidget *widget, void *data)
-{
-	purple_debug(PURPLE_DEBUG_INFO, "docklet", "destroyed\n");
-
-	pidgin_docklet_remove();
-
-	g_object_unref(G_OBJECT(docklet));
-	docklet = NULL;
-
-	g_idle_add(docklet_x11_recreate_cb, NULL);
-}
-
-static gboolean
-docklet_x11_clicked_cb(GtkWidget *button, GdkEventButton *event, void *data)
-{
-	if (event->type != GDK_BUTTON_PRESS)
-		return FALSE;
-
-	pidgin_docklet_clicked(event->button);
-	return TRUE;
-}
-
-static gboolean
-docklet_x11_pressed_cb(GtkWidget *button, GdkEventKey *event)
-{
-	guint state, keyval;
-
-	state = event->state & gtk_accelerator_get_default_mod_mask();
-	keyval = event->keyval;
-	if (state == 0 &&
-	    (keyval == GDK_Return ||
-	     keyval == GDK_KP_Enter ||
-	     keyval == GDK_ISO_Enter ||
-	     keyval == GDK_space ||
-	     keyval == GDK_KP_Space))
-	{
-		pidgin_docklet_clicked(1);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static void
-docklet_x11_popup_cb(GtkWidget *button)
-{
-	pidgin_docklet_clicked(3);
-}
-
-static void
-docklet_x11_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending)
-{
-	const gchar *icon_name = NULL;
-
-	g_return_if_fail(image != NULL);
-
-	switch (status) {
-		case PURPLE_STATUS_OFFLINE:
-			icon_name = PIDGIN_STOCK_TRAY_OFFLINE;
-			break;
-		case PURPLE_STATUS_AWAY:
-			icon_name = PIDGIN_STOCK_TRAY_AWAY;
-			break;
-		case PURPLE_STATUS_UNAVAILABLE:
-			icon_name = PIDGIN_STOCK_TRAY_BUSY;
-			break;
-		case PURPLE_STATUS_EXTENDED_AWAY:
-			icon_name = PIDGIN_STOCK_TRAY_XA;
-			break;
-		case PURPLE_STATUS_INVISIBLE:
-			icon_name = PIDGIN_STOCK_TRAY_INVISIBLE;
-			break;
-		default:
-			icon_name = PIDGIN_STOCK_TRAY_AVAILABLE;
-			break;
-	}
-
-	if (pending)
-		icon_name = PIDGIN_STOCK_TRAY_PENDING;
-	if (connecting)
-		icon_name = PIDGIN_STOCK_TRAY_CONNECT;
-
-	if(icon_name) {
-		int icon_size;
-		if (docklet_height < 22)
-			icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
-		else if (docklet_height < 32)
-			icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL);
-		else if (docklet_height < 48)
-			icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MEDIUM);
-		else
-			icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE);
-
-		gtk_image_set_from_stock(GTK_IMAGE(image), icon_name, icon_size);
-	}
-}
-
-static void
-docklet_x11_resize_icon(GtkWidget *widget)
-{
-	if (docklet_height == MIN(widget->allocation.height, widget->allocation.width))
-		return;
-	docklet_height = MIN(widget->allocation.height, widget->allocation.width);
-	pidgin_docklet_update_icon();
-}
-
-static void
-docklet_x11_blank_icon(void)
-{
-	if (!blank_icon) {
-		GtkIconSize size = GTK_ICON_SIZE_LARGE_TOOLBAR;
-		gint width, height;
-		g_object_get(G_OBJECT(image), "icon-size", &size, NULL);
-		gtk_icon_size_lookup(size, &width, &height);
-		blank_icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
-		gdk_pixbuf_fill(blank_icon, 0);
-	}
-
-	gtk_image_set_from_pixbuf(GTK_IMAGE(image), blank_icon);
-}
-
-static void
-docklet_x11_set_tooltip(gchar *tooltip)
-{
-	if (!tooltips)
-		tooltips = gtk_tooltips_new();
-
-	/* image->parent is a GtkEventBox */
-	if (tooltip) {
-		gtk_tooltips_enable(tooltips);
-		gtk_tooltips_set_tip(tooltips, image->parent, tooltip, NULL);
-	} else {
-		gtk_tooltips_set_tip(tooltips, image->parent, "", NULL);
-		gtk_tooltips_disable(tooltips);
-	}
-}
-
-#if GTK_CHECK_VERSION(2,2,0)
-static void
-docklet_x11_position_menu(GtkMenu *menu, int *x, int *y, gboolean *push_in,
-						  gpointer user_data)
-{
-	GtkWidget *widget = GTK_WIDGET(docklet);
-	GtkRequisition req;
-	gint menu_xpos, menu_ypos;
-
-	gtk_widget_size_request(GTK_WIDGET(menu), &req);
-	gdk_window_get_origin(widget->window, &menu_xpos, &menu_ypos);
-
-	menu_xpos += widget->allocation.x;
-	menu_ypos += widget->allocation.y;
-
-	if (menu_ypos > gdk_screen_get_height(gtk_widget_get_screen(widget)) / 2)
-		menu_ypos -= req.height;
-	else
-		menu_ypos += widget->allocation.height;
-
-	*x = menu_xpos;
-	*y = menu_ypos;
-
-	*push_in = TRUE;
-}
-#endif
-
-static void
-docklet_x11_destroy(void)
-{
-	g_return_if_fail(docklet != NULL);
-
-	if (embed_timeout)
-		g_source_remove(embed_timeout);
-
-	pidgin_docklet_remove();
-
-	g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_x11_destroyed_cb), NULL);
-	gtk_widget_destroy(GTK_WIDGET(docklet));
-
-	g_object_unref(G_OBJECT(docklet));
-	docklet = NULL;
-
-	if (blank_icon)
-		g_object_unref(G_OBJECT(blank_icon));
-	blank_icon = NULL;
-
-	image = NULL;
-
-	purple_debug(PURPLE_DEBUG_INFO, "docklet", "destroyed\n");
-}
-
-static gboolean
-docklet_x11_embed_timeout_cb(gpointer data)
-{
-	/* The docklet was not embedded within the timeout.
-	 * Remove it as a visibility manager, but leave the plugin
-	 * loaded so that it can embed automatically if/when a notification
-	 * area becomes available.
-	 */
-	purple_debug_info("docklet", "failed to embed within timeout\n");
-	pidgin_docklet_remove();
-
-	return FALSE;
-}
-
-static void
-docklet_x11_create(gboolean recreate)
-{
-	GtkWidget *box;
-
-	if (docklet) {
-		/* if this is being called when a tray icon exists, it's because
-		   something messed up. try destroying it before we proceed,
-		   although docklet_refcount may be all hosed. hopefully won't happen. */
-		purple_debug(PURPLE_DEBUG_WARNING, "docklet", "trying to create icon but it already exists?\n");
-		docklet_x11_destroy();
-	}
-
-	docklet = egg_tray_icon_new(PIDGIN_NAME);
-	box = gtk_event_box_new();
-	image = gtk_image_new();
-	GTK_WIDGET_SET_FLAGS (image, GTK_CAN_FOCUS);
-
-	g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_x11_embedded_cb), NULL);
-	g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_x11_destroyed_cb), NULL);
-	g_signal_connect(G_OBJECT(docklet), "size-allocate", G_CALLBACK(docklet_x11_resize_icon), NULL);
-	g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_x11_clicked_cb), NULL);
-	g_signal_connect(G_OBJECT(box), "key-press-event", G_CALLBACK(docklet_x11_pressed_cb), NULL);
-	g_signal_connect(G_OBJECT(box), "popup-menu", G_CALLBACK(docklet_x11_popup_cb), NULL);
-	gtk_container_add(GTK_CONTAINER(box), image);
-	gtk_container_add(GTK_CONTAINER(docklet), box);
-
-	if (!gtk_check_version(2,4,0))
-		g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL);
-
-	gtk_widget_show_all(GTK_WIDGET(docklet));
-
-	/* ref the docklet before we bandy it about the place */
-	g_object_ref(G_OBJECT(docklet));
-
-	/* This is a hack to avoid a race condition between the docklet getting
-	 * embedded in the notification area and the gtkblist restoring its
-	 * previous visibility state.  If the docklet does not get embedded within
-	 * the timeout, it will be removed as a visibility manager until it does
-	 * get embedded.  Ideally, we would only call docklet_embedded() when the
-	 * icon was actually embedded. This only happens when the docklet is first
-	 * created, not when being recreated.
-	 *
-	 * The x11 docklet tracks whether it successfully embedded in a pref and
-	 * allows for a longer timeout period if it successfully embedded the last
-	 * time it was run. This should hopefully solve problems with the buddy
-	 * list not properly starting hidden when Pidgin is started on login.
-	 */
-	if(!recreate) {
-		pidgin_docklet_embedded();
-		if(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) {
-			embed_timeout = g_timeout_add(LONG_EMBED_TIMEOUT, docklet_x11_embed_timeout_cb, NULL);
-		} else {
-			embed_timeout = g_timeout_add(SHORT_EMBED_TIMEOUT, docklet_x11_embed_timeout_cb, NULL);
-		}
-	}
-
-	purple_debug(PURPLE_DEBUG_INFO, "docklet", "created\n");
-}
-
-static void
-docklet_x11_create_ui_op(void)
-{
-	docklet_x11_create(FALSE);
-}
-
-static struct docklet_ui_ops ui_ops =
-{
-	docklet_x11_create_ui_op,
-	docklet_x11_destroy,
-	docklet_x11_update_icon,
-	docklet_x11_blank_icon,
-	docklet_x11_set_tooltip,
-#if GTK_CHECK_VERSION(2,2,0)
-	docklet_x11_position_menu
-#else
-	NULL
-#endif
-};
-
-void
-docklet_ui_init()
-{
-	pidgin_docklet_set_ui_ops(&ui_ops);
-	purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/x11");
-	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded", FALSE);
-}
--- a/pidgin/gtkdocklet.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkdocklet.h	Mon Mar 08 22:53:02 2010 +0000
@@ -49,7 +49,7 @@
 void pidgin_docklet_uninit(void);
 void*pidgin_docklet_get_handle(void);
 
-/* function in gtkdocklet-{x11,win32}.c */
+/* function in gtkdocklet-{gtk,x11,win32}.c */
 void docklet_ui_init(void);
 
 #endif /* _GTKDOCKLET_H_ */
--- a/pidgin/gtkexpander.c	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1659 +0,0 @@
-/* GTK - The GIMP Toolkit
- *
- * Copyright (C) 2003 Sun Microsystems, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- *
- * Authors:
- *	Mark McLoughlin <mark@skynet.ie>
- */
-
-/*
-#include <config.h>
-*/
-
-#include <gtk/gtkversion.h>
-#if !GTK_CHECK_VERSION(2,4,0)
-#include "gtkexpander.h"
-
-#include <gtk/gtklabel.h>
-#include <gtk/gtkcontainer.h>
-#include <gtk/gtkmarshal.h>
-#include <gtk/gtkmain.h>
-#include <gtk/gtkprivate.h>
-#include <gdk/gdkkeysyms.h>
-
-#define P_(x) (x)
-
-#define DEFAULT_EXPANDER_SIZE 10
-#define DEFAULT_EXPANDER_SPACING 2
-
-enum
-{
-  PROP_0,
-  PROP_EXPANDED,
-  PROP_LABEL,
-  PROP_USE_UNDERLINE,
-  PROP_USE_MARKUP,
-  PROP_SPACING,
-  PROP_LABEL_WIDGET
-};
-
-struct _GtkExpanderPrivate
-{
-  GtkWidget        *label_widget;
-  GdkWindow        *event_window;
-  gint              spacing;
-
-  GtkExpanderStyle  expander_style;
-  guint             animation_timeout;
-
-  guint             expanded : 1;
-  guint             use_underline : 1;
-  guint             use_markup : 1;
-  guint             button_down : 1;
-  guint             prelight : 1;
-};
-
-static void gtk_expander_class_init (GtkExpanderClass *klass);
-static void gtk_expander_init       (GtkExpander      *expander);
-
-static void gtk_expander_set_property (GObject          *object,
-				       guint             prop_id,
-				       const GValue     *value,
-				       GParamSpec       *pspec);
-static void gtk_expander_get_property (GObject          *object,
-				       guint             prop_id,
-				       GValue           *value,
-				       GParamSpec       *pspec);
-
-static void gtk_expander_finalize (GObject *object);
-
-static void gtk_expander_destroy (GtkObject *object);
-
-static void     gtk_expander_realize        (GtkWidget        *widget);
-static void     gtk_expander_unrealize      (GtkWidget        *widget);
-static void     gtk_expander_size_request   (GtkWidget        *widget,
-					     GtkRequisition   *requisition);
-static void     gtk_expander_size_allocate  (GtkWidget        *widget,
-					     GtkAllocation    *allocation);
-static void     gtk_expander_map            (GtkWidget        *widget);
-static void     gtk_expander_unmap          (GtkWidget        *widget);
-static gboolean gtk_expander_expose         (GtkWidget        *widget,
-					     GdkEventExpose   *event);
-static gboolean gtk_expander_button_press   (GtkWidget        *widget,
-					     GdkEventButton   *event);
-static gboolean gtk_expander_button_release (GtkWidget        *widget,
-					     GdkEventButton   *event);
-static gboolean gtk_expander_enter_notify   (GtkWidget        *widget,
-					     GdkEventCrossing *event);
-static gboolean gtk_expander_leave_notify   (GtkWidget        *widget,
-					     GdkEventCrossing *event);
-static gboolean gtk_expander_focus          (GtkWidget        *widget,
-					     GtkDirectionType  direction);
-static void     gtk_expander_grab_notify    (GtkWidget        *widget,
-					     gboolean          was_grabbed);
-static void     gtk_expander_state_changed  (GtkWidget        *widget,
-					     GtkStateType      previous_state);
-
-static void gtk_expander_add    (GtkContainer *container,
-				 GtkWidget    *widget);
-static void gtk_expander_remove (GtkContainer *container,
-				 GtkWidget    *widget);
-static void gtk_expander_forall (GtkContainer *container,
-				 gboolean        include_internals,
-				 GtkCallback     callback,
-				 gpointer        callback_data);
-
-static void gtk_expander_activate (GtkExpander *expander);
-
-static void get_expander_bounds (GtkExpander  *expander,
-				 GdkRectangle *rect);
-
-static GtkBinClass *parent_class = NULL;
-
-GType
-gtk_expander_get_type (void)
-{
-  static GType expander_type = 0;
-
-  if (!expander_type)
-    {
-      static const GTypeInfo expander_info =
-      {
-	sizeof (GtkExpanderClass),
-	NULL,		/* base_init */
-	NULL,		/* base_finalize */
-	(GClassInitFunc) gtk_expander_class_init,
-	NULL,		/* class_finalize */
-	NULL,		/* class_data */
-	sizeof (GtkExpander),
-	0,		/* n_preallocs */
-	(GInstanceInitFunc) gtk_expander_init,
-      };
-
-      expander_type = g_type_register_static (GTK_TYPE_BIN,
-					      "GtkExpander",
-					      &expander_info, 0);
-    }
-
-  return expander_type;
-}
-
-static void
-gtk_expander_class_init (GtkExpanderClass *klass)
-{
-  GObjectClass *gobject_class;
-  GtkObjectClass *object_class;
-  GtkWidgetClass *widget_class;
-  GtkContainerClass *container_class;
-
-  parent_class = g_type_class_peek_parent (klass);
-
-  gobject_class   = (GObjectClass *) klass;
-  object_class    = (GtkObjectClass *) klass;
-  widget_class    = (GtkWidgetClass *) klass;
-  container_class = (GtkContainerClass *) klass;
-
-  gobject_class->set_property = gtk_expander_set_property;
-  gobject_class->get_property = gtk_expander_get_property;
-  gobject_class->finalize     = gtk_expander_finalize;
-
-  object_class->destroy = gtk_expander_destroy;
-
-  widget_class->realize              = gtk_expander_realize;
-  widget_class->unrealize            = gtk_expander_unrealize;
-  widget_class->size_request         = gtk_expander_size_request;
-  widget_class->size_allocate        = gtk_expander_size_allocate;
-  widget_class->map                  = gtk_expander_map;
-  widget_class->unmap                = gtk_expander_unmap;
-  widget_class->expose_event         = gtk_expander_expose;
-  widget_class->button_press_event   = gtk_expander_button_press;
-  widget_class->button_release_event = gtk_expander_button_release;
-  widget_class->enter_notify_event   = gtk_expander_enter_notify;
-  widget_class->leave_notify_event   = gtk_expander_leave_notify;
-  widget_class->focus                = gtk_expander_focus;
-  widget_class->grab_notify          = gtk_expander_grab_notify;
-  widget_class->state_changed        = gtk_expander_state_changed;
-
-  container_class->add    = gtk_expander_add;
-  container_class->remove = gtk_expander_remove;
-  container_class->forall = gtk_expander_forall;
-
-  klass->activate = gtk_expander_activate;
-
-  g_object_class_install_property (gobject_class,
-				   PROP_EXPANDED,
-				   g_param_spec_boolean ("expanded",
-							 P_("Expanded"),
-							 P_("Whether the expander has been opened to reveal the child widget"),
-							 FALSE,
-							 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-  g_object_class_install_property (gobject_class,
-				   PROP_LABEL,
-				   g_param_spec_string ("label",
-							P_("Label"),
-							P_("Text of the expander's label"),
-							NULL,
-							G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-  g_object_class_install_property (gobject_class,
-				   PROP_USE_UNDERLINE,
-				   g_param_spec_boolean ("use_underline",
-							 P_("Use underline"),
-							 P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
-							 FALSE,
-							 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-  g_object_class_install_property (gobject_class,
-				   PROP_USE_MARKUP,
-				   g_param_spec_boolean ("use_markup",
-							 P_("Use markup"),
-							 P_("The text of the label includes XML markup. See pango_parse_markup()"),
-							 FALSE,
-							 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
-  g_object_class_install_property (gobject_class,
-				   PROP_SPACING,
-				   g_param_spec_int ("spacing",
-						     P_("Spacing"),
-						     P_("Space to put between the label and the child"),
-						     0,
-						     G_MAXINT,
-						     0,
-						     G_PARAM_READWRITE));
-
-  g_object_class_install_property (gobject_class,
-				   PROP_LABEL_WIDGET,
-				   g_param_spec_object ("label_widget",
-							P_("Label widget"),
-							P_("A widget to display in place of the usual expander label"),
-							GTK_TYPE_WIDGET,
-							G_PARAM_READWRITE));
-
-  gtk_widget_class_install_style_property (widget_class,
-					   g_param_spec_int ("expander-size",
-							     P_("Expander Size"),
-							     P_("Size of the expander arrow"),
-							     0,
-							     G_MAXINT,
-							     DEFAULT_EXPANDER_SIZE,
-							     G_PARAM_READABLE));
-
-  gtk_widget_class_install_style_property (widget_class,
-					   g_param_spec_int ("expander-spacing",
-							     P_("Indicator Spacing"),
-							     P_("Spacing around expander arrow"),
-							     0,
-							     G_MAXINT,
-							     DEFAULT_EXPANDER_SPACING,
-							     G_PARAM_READABLE));
-
-  widget_class->activate_signal =
-    g_signal_new ("activate",
-		  G_TYPE_FROM_CLASS (gobject_class),
-		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-		  G_STRUCT_OFFSET (GtkExpanderClass, activate),
-		  NULL, NULL,
-		  gtk_marshal_VOID__VOID,
-		  G_TYPE_NONE, 0);
-}
-
-static void
-gtk_expander_finalize (GObject *obj)
-{
-	GtkExpander *self = (GtkExpander *)obj;
-
-	g_free(self->priv);
-
-	G_OBJECT_CLASS(parent_class)->finalize (obj);
-}
-
-static void
-gtk_expander_init (GtkExpander *expander)
-{
-  GtkExpanderPrivate *priv;
-
-  expander->priv = priv = g_new0(GtkExpanderPrivate, 1);
-
-  GTK_WIDGET_SET_FLAGS (expander, GTK_CAN_FOCUS);
-  GTK_WIDGET_SET_FLAGS (expander, GTK_NO_WINDOW);
-
-  priv->label_widget = NULL;
-  priv->event_window = NULL;
-  priv->spacing = 0;
-
-  priv->expander_style = GTK_EXPANDER_COLLAPSED;
-  priv->animation_timeout = 0;
-
-  priv->expanded = FALSE;
-  priv->use_underline = FALSE;
-  priv->use_markup = FALSE;
-  priv->button_down = FALSE;
-  priv->prelight = FALSE;
-}
-
-static void
-gtk_expander_set_property (GObject      *object,
-			   guint         prop_id,
-			   const GValue *value,
-			   GParamSpec   *pspec)
-{
-  GtkExpander *expander = GTK_EXPANDER (object);
-
-  switch (prop_id)
-    {
-    case PROP_EXPANDED:
-      gtk_expander_set_expanded (expander, g_value_get_boolean (value));
-      break;
-    case PROP_LABEL:
-      gtk_expander_set_label (expander, g_value_get_string (value));
-      break;
-    case PROP_USE_UNDERLINE:
-      gtk_expander_set_use_underline (expander, g_value_get_boolean (value));
-      break;
-    case PROP_USE_MARKUP:
-      gtk_expander_set_use_markup (expander, g_value_get_boolean (value));
-      break;
-    case PROP_SPACING:
-      gtk_expander_set_spacing (expander, g_value_get_int (value));
-      break;
-    case PROP_LABEL_WIDGET:
-      gtk_expander_set_label_widget (expander, g_value_get_object (value));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-gtk_expander_get_property (GObject    *object,
-			   guint       prop_id,
-			   GValue     *value,
-			   GParamSpec *pspec)
-{
-  GtkExpander *expander = GTK_EXPANDER (object);
-  GtkExpanderPrivate *priv = expander->priv;
-
-  switch (prop_id)
-    {
-    case PROP_EXPANDED:
-      g_value_set_boolean (value, priv->expanded);
-      break;
-    case PROP_LABEL:
-      g_value_set_string (value, gtk_expander_get_label (expander));
-      break;
-    case PROP_USE_UNDERLINE:
-      g_value_set_boolean (value, priv->use_underline);
-      break;
-    case PROP_USE_MARKUP:
-      g_value_set_boolean (value, priv->use_markup);
-      break;
-    case PROP_SPACING:
-      g_value_set_int (value, priv->spacing);
-      break;
-    case PROP_LABEL_WIDGET:
-      g_value_set_object (value,
-			  priv->label_widget ?
-			  G_OBJECT (priv->label_widget) : NULL);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-gtk_expander_destroy (GtkObject *object)
-{
-  GtkExpanderPrivate *priv = GTK_EXPANDER (object)->priv;
-
-  if (priv->animation_timeout)
-    {
-      g_source_remove (priv->animation_timeout);
-      priv->animation_timeout = 0;
-    }
-
-  GTK_OBJECT_CLASS (parent_class)->destroy (object);
-}
-
-static void
-gtk_expander_realize (GtkWidget *widget)
-{
-  GtkExpanderPrivate *priv;
-  GdkWindowAttr attributes;
-  gint attributes_mask;
-  gint border_width;
-  GdkRectangle expander_rect;
-
-  priv = GTK_EXPANDER (widget)->priv;
-  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
-
-  border_width = GTK_CONTAINER (widget)->border_width;
-
-  get_expander_bounds (GTK_EXPANDER (widget), &expander_rect);
-
-  attributes.window_type = GDK_WINDOW_CHILD;
-  attributes.x = widget->allocation.x + border_width;
-  attributes.y = expander_rect.y;
-  attributes.width = MAX (widget->allocation.width - 2 * border_width, 1);
-  attributes.height = expander_rect.width;
-  attributes.wclass = GDK_INPUT_ONLY;
-  attributes.event_mask = gtk_widget_get_events (widget)     |
-				GDK_BUTTON_PRESS_MASK        |
-				GDK_BUTTON_RELEASE_MASK      |
-				GDK_ENTER_NOTIFY_MASK        |
-				GDK_LEAVE_NOTIFY_MASK;
-
-  attributes_mask = GDK_WA_X | GDK_WA_Y;
-
-  widget->window = gtk_widget_get_parent_window (widget);
-  g_object_ref (widget->window);
-
-  priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
-				       &attributes, attributes_mask);
-  gdk_window_set_user_data (priv->event_window, widget);
-
-  widget->style = gtk_style_attach (widget->style, widget->window);
-  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
-}
-
-static void
-gtk_expander_unrealize (GtkWidget *widget)
-{
-  GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
-
-  if (priv->event_window)
-    {
-      gdk_window_set_user_data (priv->event_window, NULL);
-      gdk_window_destroy (priv->event_window);
-      priv->event_window = NULL;
-    }
-
-  GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
-}
-
-static void
-gtk_expander_size_request (GtkWidget      *widget,
-			   GtkRequisition *requisition)
-{
-  GtkExpander *expander;
-  GtkBin *bin;
-  GtkExpanderPrivate *priv;
-  gint border_width;
-  gint expander_size;
-  gint expander_spacing;
-  gboolean interior_focus;
-  gint focus_width;
-  gint focus_pad;
-
-  bin = GTK_BIN (widget);
-  expander = GTK_EXPANDER (widget);
-  priv = expander->priv;
-
-  border_width = GTK_CONTAINER (widget)->border_width;
-
-  gtk_widget_style_get (widget,
-			"interior-focus", &interior_focus,
-			"focus-line-width", &focus_width,
-			"focus-padding", &focus_pad,
-			"expander-size", &expander_size,
-			"expander-spacing", &expander_spacing,
-			NULL);
-
-  requisition->width = expander_size + 2 * expander_spacing +
-		       2 * focus_width + 2 * focus_pad;
-  requisition->height = interior_focus ? (2 * focus_width + 2 * focus_pad) : 0;
-
-  if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
-    {
-      GtkRequisition label_requisition;
-
-      gtk_widget_size_request (priv->label_widget, &label_requisition);
-
-      requisition->width  += label_requisition.width;
-      requisition->height += label_requisition.height;
-    }
-
-  requisition->height = MAX (expander_size + 2 * expander_spacing, requisition->height);
-
-  if (!interior_focus)
-    requisition->height += 2 * focus_width + 2 * focus_pad;
-
-  if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
-    {
-      GtkRequisition child_requisition;
-
-      gtk_widget_size_request (bin->child, &child_requisition);
-
-      requisition->width = MAX (requisition->width, child_requisition.width);
-      requisition->height += child_requisition.height + priv->spacing;
-    }
-
-  requisition->width  += 2 * border_width;
-  requisition->height += 2 * border_width;
-}
-
-static void
-get_expander_bounds (GtkExpander  *expander,
-		     GdkRectangle *rect)
-{
-  GtkWidget *widget;
-  GtkBin *bin;
-  GtkExpanderPrivate *priv;
-  gint border_width;
-  gint expander_size;
-  gint expander_spacing;
-  gboolean interior_focus;
-  gint focus_width;
-  gint focus_pad;
-  gboolean ltr;
-
-  widget = GTK_WIDGET (expander);
-  bin = GTK_BIN (expander);
-  priv = expander->priv;
-
-  border_width = GTK_CONTAINER (expander)->border_width;
-
-  gtk_widget_style_get (widget,
-			"interior-focus", &interior_focus,
-			"focus-line-width", &focus_width,
-			"focus-padding", &focus_pad,
-			"expander-size", &expander_size,
-			"expander-spacing", &expander_spacing,
-			NULL);
-
-  ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-
-  rect->x = widget->allocation.x + border_width;
-  rect->y = widget->allocation.y + border_width;
-
-  if (ltr)
-    rect->x += expander_spacing;
-  else
-    rect->x += widget->allocation.width - 2 * border_width -
-               expander_spacing - expander_size;
-
-  if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
-    {
-      GtkAllocation label_allocation;
-
-      label_allocation = priv->label_widget->allocation;
-
-      if (expander_size < label_allocation.height)
-	rect->y += focus_width + focus_pad + (label_allocation.height - expander_size) / 2;
-      else
-	rect->y += expander_spacing;
-    }
-  else
-    {
-      rect->y += expander_spacing;
-    }
-
-  if (!interior_focus)
-    {
-      if (ltr)
-	rect->x += focus_width + focus_pad;
-      else
-	rect->x -= focus_width + focus_pad;
-      rect->y += focus_width + focus_pad;
-    }
-
-  rect->width = rect->height = expander_size;
-}
-
-static void
-gtk_expander_size_allocate (GtkWidget     *widget,
-			    GtkAllocation *allocation)
-{
-  GtkExpander *expander;
-  GtkBin *bin;
-  GtkExpanderPrivate *priv;
-  GtkRequisition child_requisition;
-  gboolean child_visible = FALSE;
-  gint border_width;
-  gint expander_size;
-  gint expander_spacing;
-  gboolean interior_focus;
-  gint focus_width;
-  gint focus_pad;
-  gint label_height;
-
-  expander = GTK_EXPANDER (widget);
-  bin = GTK_BIN (widget);
-  priv = expander->priv;
-
-  border_width = GTK_CONTAINER (widget)->border_width;
-
-  gtk_widget_style_get (widget,
-			"interior-focus", &interior_focus,
-			"focus-line-width", &focus_width,
-			"focus-padding", &focus_pad,
-			"expander-size", &expander_size,
-			"expander-spacing", &expander_spacing,
-			NULL);
-
-  child_requisition.width = 0;
-  child_requisition.height = 0;
-  if (bin->child && GTK_WIDGET_CHILD_VISIBLE (bin->child))
-    {
-      child_visible = TRUE;
-      gtk_widget_get_child_requisition (bin->child, &child_requisition);
-    }
-
-  widget->allocation = *allocation;
-
-  if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
-    {
-      GtkAllocation label_allocation;
-      GtkRequisition label_requisition;
-      gboolean ltr;
-
-      gtk_widget_get_child_requisition (priv->label_widget, &label_requisition);
-
-      ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-
-      if (ltr)
-	label_allocation.x = (widget->allocation.x +
-                              border_width + focus_width + focus_pad +
-                              expander_size + 2 * expander_spacing);
-      else
-        label_allocation.x = (widget->allocation.x + widget->allocation.width -
-                              (label_requisition.width +
-                               border_width + focus_width + focus_pad +
-                               expander_size + 2 * expander_spacing));
-
-      label_allocation.y = widget->allocation.y + border_width + focus_width + focus_pad;
-
-      label_allocation.width = MIN (label_requisition.width,
-				    allocation->width - 2 * border_width -
-				    expander_size - 2 * expander_spacing -
-				    2 * focus_width - 2 * focus_pad);
-      label_allocation.width = MAX (label_allocation.width, 1);
-
-      label_allocation.height = MIN (label_requisition.height,
-				     allocation->height - 2 * border_width -
-				     2 * focus_width - 2 * focus_pad -
-				     (child_visible ? priv->spacing : 0));
-      label_allocation.height = MAX (label_allocation.height, 1);
-
-      gtk_widget_size_allocate (priv->label_widget, &label_allocation);
-
-      label_height = label_allocation.height;
-    }
-  else
-    {
-      label_height = 0;
-    }
-
-  if (GTK_WIDGET_REALIZED (widget))
-    {
-      GdkRectangle rect;
-
-      get_expander_bounds (expander, &rect);
-
-      gdk_window_move_resize (priv->event_window,
-			      allocation->x + border_width, rect.y,
-			      MAX (allocation->width - 2 * border_width, 1), rect.width);
-    }
-
-  if (child_visible)
-    {
-      GtkAllocation child_allocation;
-      gint top_height;
-
-      top_height = MAX (2 * expander_spacing + expander_size,
-			label_height +
-			(interior_focus ? 2 * focus_width + 2 * focus_pad : 0));
-
-      child_allocation.x = widget->allocation.x + border_width;
-      child_allocation.y = widget->allocation.y + border_width + top_height + priv->spacing;
-
-      if (!interior_focus)
-	child_allocation.y += 2 * focus_width + 2 * focus_pad;
-
-      child_allocation.width = MAX (allocation->width - 2 * border_width, 1);
-
-      child_allocation.height = allocation->height - top_height -
-				2 * border_width - priv->spacing -
-				(!interior_focus ? 2 * focus_width + 2 * focus_pad : 0);
-      child_allocation.height = MAX (child_allocation.height, 1);
-
-      gtk_widget_size_allocate (bin->child, &child_allocation);
-    }
-}
-
-static void
-gtk_expander_map (GtkWidget *widget)
-{
-  GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
-
-  if (priv->label_widget)
-    gtk_widget_map (priv->label_widget);
-
-  GTK_WIDGET_CLASS (parent_class)->map (widget);
-
-  if (priv->event_window)
-    gdk_window_show (priv->event_window);
-}
-
-static void
-gtk_expander_unmap (GtkWidget *widget)
-{
-  GtkExpanderPrivate *priv = GTK_EXPANDER (widget)->priv;
-
-  if (priv->event_window)
-    gdk_window_hide (priv->event_window);
-
-  GTK_WIDGET_CLASS (parent_class)->unmap (widget);
-
-  if (priv->label_widget)
-    gtk_widget_unmap (priv->label_widget);
-}
-
-static void
-gtk_expander_paint_prelight (GtkExpander *expander)
-{
-  GtkWidget *widget;
-  GtkContainer *container;
-  GtkExpanderPrivate *priv;
-  GdkRectangle area;
-  gboolean interior_focus;
-  int focus_width;
-  int focus_pad;
-  int expander_size;
-  int expander_spacing;
-
-  priv = expander->priv;
-  widget = GTK_WIDGET (expander);
-  container = GTK_CONTAINER (expander);
-
-  gtk_widget_style_get (widget,
-			"interior-focus", &interior_focus,
-			"focus-line-width", &focus_width,
-			"focus-padding", &focus_pad,
-			"expander-size", &expander_size,
-			"expander-spacing", &expander_spacing,
-			NULL);
-
-  area.x = widget->allocation.x + container->border_width;
-  area.y = widget->allocation.y + container->border_width;
-  area.width = widget->allocation.width - (2 * container->border_width);
-
-  if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
-    area.height = priv->label_widget->allocation.height;
-  else
-    area.height = 0;
-
-  area.height += interior_focus ? (focus_width + focus_pad) * 2 : 0;
-  area.height = MAX (area.height, expander_size + 2 * expander_spacing);
-  area.height += !interior_focus ? (focus_width + focus_pad) * 2 : 0;
-
-  gtk_paint_flat_box (widget->style, widget->window,
-		      GTK_STATE_PRELIGHT,
-		      GTK_SHADOW_ETCHED_OUT,
-		      &area, widget, "expander",
-		      area.x, area.y,
-		      area.width, area.height);
-}
-
-static void
-gtk_expander_paint (GtkExpander *expander)
-{
-  GtkWidget *widget;
-  GdkRectangle clip;
-  GtkStateType state;
-
-  widget = GTK_WIDGET (expander);
-
-  get_expander_bounds (expander, &clip);
-
-  state = widget->state;
-  if (expander->priv->prelight)
-    {
-      state = GTK_STATE_PRELIGHT;
-
-      gtk_expander_paint_prelight (expander);
-    }
-
-  gtk_paint_expander (widget->style,
-		      widget->window,
-		      state,
-		      &clip,
-		      widget,
-		      "expander",
-		      clip.x + clip.width / 2,
-		      clip.y + clip.height / 2,
-		      expander->priv->expander_style);
-}
-
-static void
-gtk_expander_paint_focus (GtkExpander  *expander,
-			  GdkRectangle *area)
-{
-  GtkWidget *widget;
-  GtkExpanderPrivate *priv;
-  gint x, y, width, height;
-  gboolean interior_focus;
-  gint border_width;
-  gint focus_width;
-  gint focus_pad;
-  gint expander_size;
-  gint expander_spacing;
-  gboolean ltr;
-
-  widget = GTK_WIDGET (expander);
-  priv = expander->priv;
-
-  border_width = GTK_CONTAINER (widget)->border_width;
-
-  gtk_widget_style_get (widget,
-			"interior-focus", &interior_focus,
-			"focus-line-width", &focus_width,
-			"focus-padding", &focus_pad,
-			"expander-size", &expander_size,
-			"expander-spacing", &expander_spacing,
-			NULL);
-
-  ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
-
-  x = widget->allocation.x + border_width;
-  y = widget->allocation.y + border_width;
-
-  if (ltr && interior_focus)
-    x += expander_spacing * 2 + expander_size;
-
-  width = height = 0;
-
-  if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
-    {
-      GtkAllocation label_allocation = priv->label_widget->allocation;
-
-      width  = label_allocation.width;
-      height = label_allocation.height;
-    }
-
-  if (!interior_focus)
-    {
-      width += expander_size + 2 * expander_spacing;
-      height = MAX (height, expander_size + 2 * expander_spacing);
-    }
-
-  width  += 2 * focus_pad + 2 * focus_width;
-  height += 2 * focus_pad + 2 * focus_width;
-
-  gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
-		   area, widget, "expander",
-		   x, y, width, height);
-}
-
-static gboolean
-gtk_expander_expose (GtkWidget      *widget,
-		     GdkEventExpose *event)
-{
-  if (GTK_WIDGET_DRAWABLE (widget))
-    {
-      GtkExpander *expander = GTK_EXPANDER (widget);
-
-      gtk_expander_paint (expander);
-
-      if (GTK_WIDGET_HAS_FOCUS (expander))
-	gtk_expander_paint_focus (expander, &event->area);
-
-      GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
-    }
-
-  return FALSE;
-}
-
-static gboolean
-gtk_expander_button_press (GtkWidget      *widget,
-			   GdkEventButton *event)
-{
-  GtkExpander *expander = GTK_EXPANDER (widget);
-
-  if (event->button == 1 && event->window == expander->priv->event_window)
-    {
-      expander->priv->button_down = TRUE;
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-static gboolean
-gtk_expander_button_release (GtkWidget      *widget,
-			     GdkEventButton *event)
-{
-  GtkExpander *expander = GTK_EXPANDER (widget);
-
-  if (event->button == 1 && expander->priv->button_down)
-    {
-      gtk_widget_activate (widget);
-      expander->priv->button_down = FALSE;
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-static void
-gtk_expander_grab_notify (GtkWidget *widget,
-			  gboolean   was_grabbed)
-{
-  if (!was_grabbed)
-    GTK_EXPANDER (widget)->priv->button_down = FALSE;
-}
-
-static void
-gtk_expander_state_changed (GtkWidget    *widget,
-			    GtkStateType  previous_state)
-{
-  if (!GTK_WIDGET_IS_SENSITIVE (widget))
-    GTK_EXPANDER (widget)->priv->button_down = FALSE;
-}
-
-static void
-gtk_expander_redraw_expander (GtkExpander *expander)
-{
-  GtkWidget *widget;
-
-  widget = GTK_WIDGET (expander);
-
-  if (GTK_WIDGET_REALIZED (widget))
-    gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
-}
-
-static gboolean
-gtk_expander_enter_notify (GtkWidget        *widget,
-			   GdkEventCrossing *event)
-{
-  GtkExpander *expander = GTK_EXPANDER (widget);
-  GtkWidget *event_widget;
-
-  event_widget = gtk_get_event_widget ((GdkEvent *) event);
-
-  if (event_widget == widget &&
-      event->detail != GDK_NOTIFY_INFERIOR)
-    {
-      expander->priv->prelight = TRUE;
-
-      if (expander->priv->label_widget)
-	gtk_widget_set_state (expander->priv->label_widget, GTK_STATE_PRELIGHT);
-
-      gtk_expander_redraw_expander (expander);
-    }
-
-  return FALSE;
-}
-
-static gboolean
-gtk_expander_leave_notify (GtkWidget        *widget,
-			   GdkEventCrossing *event)
-{
-  GtkExpander *expander = GTK_EXPANDER (widget);
-  GtkWidget *event_widget;
-
-  event_widget = gtk_get_event_widget ((GdkEvent *) event);
-
-  if (event_widget == widget &&
-      event->detail != GDK_NOTIFY_INFERIOR)
-    {
-      expander->priv->prelight = FALSE;
-
-      if (expander->priv->label_widget)
-	gtk_widget_set_state (expander->priv->label_widget, GTK_STATE_NORMAL);
-
-      gtk_expander_redraw_expander (expander);
-    }
-
-  return FALSE;
-}
-
-typedef enum
-{
-  FOCUS_NONE,
-  FOCUS_WIDGET,
-  FOCUS_LABEL,
-  FOCUS_CHILD
-} FocusSite;
-
-static gboolean
-focus_current_site (GtkExpander      *expander,
-		    GtkDirectionType  direction)
-{
-  GtkWidget *current_focus;
-
-  current_focus = GTK_CONTAINER (expander)->focus_child;
-
-  if (!current_focus)
-    return FALSE;
-
-  return gtk_widget_child_focus (current_focus, direction);
-}
-
-static gboolean
-focus_in_site (GtkExpander      *expander,
-	       FocusSite         site,
-	       GtkDirectionType  direction)
-{
-  switch (site)
-    {
-    case FOCUS_WIDGET:
-      gtk_widget_grab_focus (GTK_WIDGET (expander));
-      return TRUE;
-    case FOCUS_LABEL:
-      if (expander->priv->label_widget)
-	return gtk_widget_child_focus (expander->priv->label_widget, direction);
-      else
-	return FALSE;
-    case FOCUS_CHILD:
-      {
-	GtkWidget *child = gtk_bin_get_child (GTK_BIN (expander));
-
-	if (child && GTK_WIDGET_CHILD_VISIBLE (child))
-	  return gtk_widget_child_focus (child, direction);
-	else
-	  return FALSE;
-      }
-    case FOCUS_NONE:
-      break;
-    }
-
-  g_assert_not_reached ();
-  return FALSE;
-}
-
-static FocusSite
-get_next_site (GtkExpander      *expander,
-	       FocusSite         site,
-	       GtkDirectionType  direction)
-{
-  gboolean ltr;
-
-  ltr = gtk_widget_get_direction (GTK_WIDGET (expander)) != GTK_TEXT_DIR_RTL;
-
-  switch (site)
-    {
-    case FOCUS_NONE:
-      switch (direction)
-	{
-	case GTK_DIR_TAB_BACKWARD:
-	case GTK_DIR_LEFT:
-	case GTK_DIR_UP:
-	  return FOCUS_CHILD;
-	case GTK_DIR_TAB_FORWARD:
-	case GTK_DIR_DOWN:
-	case GTK_DIR_RIGHT:
-	  return FOCUS_WIDGET;
-	}
-    case FOCUS_WIDGET:
-      switch (direction)
-	{
-	case GTK_DIR_TAB_BACKWARD:
-	case GTK_DIR_UP:
-	  return FOCUS_NONE;
-	case GTK_DIR_LEFT:
-	  return ltr ? FOCUS_NONE : FOCUS_LABEL;
-	case GTK_DIR_TAB_FORWARD:
-	case GTK_DIR_DOWN:
-	  return FOCUS_LABEL;
-	case GTK_DIR_RIGHT:
-	  return ltr ? FOCUS_LABEL : FOCUS_NONE;
-	  break;
-	}
-    case FOCUS_LABEL:
-      switch (direction)
-	{
-	case GTK_DIR_TAB_BACKWARD:
-	case GTK_DIR_UP:
-	  return FOCUS_WIDGET;
-	case GTK_DIR_LEFT:
-	  return ltr ? FOCUS_WIDGET : FOCUS_CHILD;
-	case GTK_DIR_TAB_FORWARD:
-	case GTK_DIR_DOWN:
-	  return FOCUS_CHILD;
-	case GTK_DIR_RIGHT:
-	  return ltr ? FOCUS_CHILD : FOCUS_WIDGET;
-	  break;
-	}
-    case FOCUS_CHILD:
-      switch (direction)
-	{
-	case GTK_DIR_TAB_BACKWARD:
-	case GTK_DIR_LEFT:
-	case GTK_DIR_UP:
-	  return FOCUS_LABEL;
-	case GTK_DIR_TAB_FORWARD:
-	case GTK_DIR_DOWN:
-	case GTK_DIR_RIGHT:
-	  return FOCUS_NONE;
-	}
-    }
-
-  g_assert_not_reached ();
-  return FOCUS_NONE;
-}
-
-static gboolean
-gtk_expander_focus (GtkWidget        *widget,
-		    GtkDirectionType  direction)
-{
-  GtkExpander *expander = GTK_EXPANDER (widget);
-
-  if (!focus_current_site (expander, direction))
-    {
-      GtkWidget *old_focus_child;
-      gboolean widget_is_focus;
-      FocusSite site = FOCUS_NONE;
-
-      widget_is_focus = gtk_widget_is_focus (widget);
-      old_focus_child = GTK_CONTAINER (widget)->focus_child;
-
-      if (old_focus_child && old_focus_child == expander->priv->label_widget)
-	site = FOCUS_LABEL;
-      else if (old_focus_child)
-	site = FOCUS_CHILD;
-      else if (widget_is_focus)
-	site = FOCUS_WIDGET;
-
-      while ((site = get_next_site (expander, site, direction)) != FOCUS_NONE)
-	{
-	  if (focus_in_site (expander, site, direction))
-	    return TRUE;
-	}
-
-      return FALSE;
-    }
-
-  return TRUE;
-}
-
-static void
-gtk_expander_add (GtkContainer *container,
-		  GtkWidget    *widget)
-{
-  GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
-
-  gtk_widget_set_child_visible (widget, GTK_EXPANDER (container)->priv->expanded);
-  gtk_widget_queue_resize (GTK_WIDGET (container));
-}
-
-static void
-gtk_expander_remove (GtkContainer *container,
-		     GtkWidget    *widget)
-{
-  GtkExpander *expander = GTK_EXPANDER (container);
-
-  if (GTK_EXPANDER (expander)->priv->label_widget == widget)
-    gtk_expander_set_label_widget (expander, NULL);
-  else
-    GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
-}
-
-static void
-gtk_expander_forall (GtkContainer *container,
-		     gboolean      include_internals,
-		     GtkCallback   callback,
-		     gpointer      callback_data)
-{
-  GtkBin *bin = GTK_BIN (container);
-  GtkExpanderPrivate *priv = GTK_EXPANDER (container)->priv;
-
-  if (bin->child)
-    (* callback) (bin->child, callback_data);
-
-  if (priv->label_widget)
-    (* callback) (priv->label_widget, callback_data);
-}
-
-static void
-gtk_expander_activate (GtkExpander *expander)
-{
-  gtk_expander_set_expanded (expander, !expander->priv->expanded);
-}
-
-
-/**
- * gtk_expander_new:
- * @label: the text of the label
- *
- * Creates a new expander using @label as the text of the label.
- *
- * Return value: a new #GtkExpander widget.
- *
- * Since: 2.4
- **/
-GtkWidget *
-gtk_expander_new (const gchar *label)
-{
-  return g_object_new (GTK_TYPE_EXPANDER, "label", label, NULL);
-}
-
-/**
- * gtk_expander_new_with_mnemonic:
- * @label: the text of the label with an underscore in front of the
- *         mnemonic character
- *
- * Creates a new expander using @label as the text of the label.
- * If characters in @label are preceded by an underscore, they are underlined.
- * If you need a literal underscore character in a label, use '__' (two
- * underscores). The first underlined character represents a keyboard
- * accelerator called a mnemonic.
- * Pressing Alt and that key activates the button.
- *
- * Return value: a new #GtkExpander widget.
- *
- * Since: 2.4
- **/
-GtkWidget *
-gtk_expander_new_with_mnemonic (const gchar *label)
-{
-  return g_object_new (GTK_TYPE_EXPANDER,
-		       "label", label,
-		       "use_underline", TRUE,
-		       NULL);
-}
-
-static gboolean
-gtk_expander_animation_timeout (GtkExpander *expander)
-{
-  GtkExpanderPrivate *priv = expander->priv;
-  GdkRectangle area;
-  gboolean finish = FALSE;
-
-  GDK_THREADS_ENTER();
-
-  if (GTK_WIDGET_REALIZED (expander))
-    {
-      get_expander_bounds (expander, &area);
-      gdk_window_invalidate_rect (GTK_WIDGET (expander)->window, &area, TRUE);
-    }
-
-  if (priv->expanded)
-    {
-      if (priv->expander_style == GTK_EXPANDER_COLLAPSED)
-	{
-	  priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
-	}
-      else
-	{
-	  priv->expander_style = GTK_EXPANDER_EXPANDED;
-	  finish = TRUE;
-	}
-    }
-  else
-    {
-      if (priv->expander_style == GTK_EXPANDER_EXPANDED)
-	{
-	  priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
-	}
-      else
-	{
-	  priv->expander_style = GTK_EXPANDER_COLLAPSED;
-	  finish = TRUE;
-	}
-    }
-
-  if (finish)
-    {
-      priv->animation_timeout = 0;
-      if (GTK_BIN (expander)->child)
-	gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
-      gtk_widget_queue_resize (GTK_WIDGET (expander));
-    }
-
-  GDK_THREADS_LEAVE();
-
-  return !finish;
-}
-
-static void
-gtk_expander_start_animation (GtkExpander *expander)
-{
-  GtkExpanderPrivate *priv = expander->priv;
-
-  if (priv->animation_timeout)
-    g_source_remove (priv->animation_timeout);
-
-  priv->animation_timeout =
-		g_timeout_add (50,
-			       (GSourceFunc) gtk_expander_animation_timeout,
-			       expander);
-}
-
-/**
- * gtk_expander_set_expanded:
- * @expander: a #GtkExpander
- * @expanded: whether the child widget is revealed
- *
- * Sets the state of the expander. Set to %TRUE, if you want
- * the child widget to be revealed, and %FALSE if you want the
- * child widget to be hidden.
- *
- * Since: 2.4
- **/
-void
-gtk_expander_set_expanded (GtkExpander *expander,
-			   gboolean     expanded)
-{
-  GtkExpanderPrivate *priv;
-
-  g_return_if_fail (GTK_IS_EXPANDER (expander));
-
-  priv = expander->priv;
-
-  expanded = expanded != FALSE;
-
-  if (priv->expanded != expanded)
-    {
-      priv->expanded = expanded;
-
-      if (GTK_WIDGET_REALIZED (expander))
-	{
-	  gtk_expander_start_animation (expander);
-	}
-      else
-	{
-	  priv->expander_style = expanded ? GTK_EXPANDER_EXPANDED :
-					    GTK_EXPANDER_COLLAPSED;
-
-	  if (GTK_BIN (expander)->child)
-	    {
-	      gtk_widget_set_child_visible (GTK_BIN (expander)->child, priv->expanded);
-	      gtk_widget_queue_resize (GTK_WIDGET (expander));
-	    }
-	}
-
-      g_object_notify (G_OBJECT (expander), "expanded");
-    }
-}
-
-/**
- * gtk_expander_get_expanded:
- * @expander:a #GtkExpander
- *
- * Queries a #GtkExpander and returns its current state. Returns %TRUE
- * if the child widget is revealed.
- *
- * See gtk_expander_set_expanded().
- *
- * Return value: the current state of the expander.
- *
- * Since: 2.4
- **/
-gboolean
-gtk_expander_get_expanded (GtkExpander *expander)
-{
-  g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
-
-  return expander->priv->expanded;
-}
-
-/**
- * gtk_expander_set_spacing:
- * @expander: a #GtkExpander
- * @spacing: distance between the expander and child in pixels.
- *
- * Sets the spacing field of @expander, which is the number of pixels to
- * place between expander and the child.
- *
- * Since: 2.4
- **/
-void
-gtk_expander_set_spacing (GtkExpander *expander,
-			  gint         spacing)
-{
-  g_return_if_fail (GTK_IS_EXPANDER (expander));
-  g_return_if_fail (spacing >= 0);
-
-  if (expander->priv->spacing != spacing)
-    {
-      expander->priv->spacing = spacing;
-
-      gtk_widget_queue_resize (GTK_WIDGET (expander));
-
-      g_object_notify (G_OBJECT (expander), "spacing");
-    }
-}
-
-/**
- * gtk_expander_get_spacing:
- * @expander: a #GtkExpander
- *
- * Gets the value set by gtk_expander_set_spacing().
- *
- * Return value: spacing between the expander and child.
- *
- * Since: 2.4
- **/
-gint
-gtk_expander_get_spacing (GtkExpander *expander)
-{
-  g_return_val_if_fail (GTK_IS_EXPANDER (expander), 0);
-
-  return expander->priv->spacing;
-}
-
-/**
- * gtk_expander_set_label:
- * @expander: a #GtkExpander
- * @label: a string
- *
- * Sets the text of the label of the expander to @label.
- *
- * This will also clear any previously set labels.
- *
- * Since: 2.4
- **/
-void
-gtk_expander_set_label (GtkExpander *expander,
-			const gchar *label)
-{
-  g_return_if_fail (GTK_IS_EXPANDER (expander));
-
-  if (!label)
-    {
-      gtk_expander_set_label_widget (expander, NULL);
-    }
-  else
-    {
-      GtkWidget *child;
-
-      child = gtk_label_new (label);
-      gtk_label_set_use_underline (GTK_LABEL (child), expander->priv->use_underline);
-      gtk_label_set_use_markup (GTK_LABEL (child), expander->priv->use_markup);
-      gtk_widget_show (child);
-
-      gtk_expander_set_label_widget (expander, child);
-    }
-
-  g_object_notify (G_OBJECT (expander), "label");
-}
-
-/**
- * gtk_expander_get_label:
- * @expander: a #GtkExpander
- *
- * Fetches the text from the label of the expander, as set by
- * gtk_expander_set_label(). If the label text has not
- * been set the return value will be %NULL. This will be the
- * case if you create an empty button with gtk_button_new() to
- * use as a container.
- *
- * Return value: The text of the label widget. This string is owned
- * by the widget and must not be modified or freed.
- *
- * Since: 2.4
- **/
-G_CONST_RETURN char *
-gtk_expander_get_label (GtkExpander *expander)
-{
-  GtkExpanderPrivate *priv;
-
-  g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
-
-  priv = expander->priv;
-
-  if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
-    return gtk_label_get_text (GTK_LABEL (priv->label_widget));
-  else
-    return NULL;
-}
-
-/**
- * gtk_expander_set_use_underline:
- * @expander: a #GtkExpander
- * @use_underline: %TRUE if underlines in the text indicate mnemonics
- *
- * If true, an underline in the text of the expander label indicates
- * the next character should be used for the mnemonic accelerator key.
- *
- * Since: 2.4
- **/
-void
-gtk_expander_set_use_underline (GtkExpander *expander,
-				gboolean     use_underline)
-{
-  GtkExpanderPrivate *priv;
-
-  g_return_if_fail (GTK_IS_EXPANDER (expander));
-
-  priv = expander->priv;
-
-  use_underline = use_underline != FALSE;
-
-  if (priv->use_underline != use_underline)
-    {
-      priv->use_underline = use_underline;
-
-      if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
-	gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), use_underline);
-
-      g_object_notify (G_OBJECT (expander), "use-underline");
-    }
-}
-
-/**
- * gtk_expander_get_use_underline:
- * @expander: a #GtkExpander
- *
- * Returns whether an embedded underline in the expander label indicates a
- * mnemonic. See gtk_expander_set_use_underline().
- *
- * Return value: %TRUE if an embedded underline in the expander label
- *               indicates the mnemonic accelerator keys.
- *
- * Since: 2.4
- **/
-gboolean
-gtk_expander_get_use_underline (GtkExpander *expander)
-{
-  g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
-
-  return expander->priv->use_underline;
-}
-
-/**
- * gtk_expander_set_use_markup:
- * @expander: a #GtkExpander
- * @use_markup: %TRUE if the label's text should be parsed for markup
- *
- * Sets whether the text of the label contains markup in <link
- * linkend="PangoMarkupFormat">Pango's text markup
- * language</link>. See gtk_label_set_markup().
- *
- * Since: 2.4
- **/
-void
-gtk_expander_set_use_markup (GtkExpander *expander,
-			     gboolean     use_markup)
-{
-  GtkExpanderPrivate *priv;
-
-  g_return_if_fail (GTK_IS_EXPANDER (expander));
-
-  priv = expander->priv;
-
-  use_markup = use_markup != FALSE;
-
-  if (priv->use_markup != use_markup)
-    {
-      priv->use_markup = use_markup;
-
-      if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
-	gtk_label_set_use_markup (GTK_LABEL (priv->label_widget), use_markup);
-
-      g_object_notify (G_OBJECT (expander), "use-markup");
-    }
-}
-
-/**
- * gtk_expander_get_use_markup:
- * @expander: a #GtkExpander
- *
- * Returns whether the label's text is interpreted as marked up with
- * the <link linkend="PangoMarkupFormat">Pango text markup
- * language</link>. See gtk_expander_set_use_markup ().
- *
- * Return value: %TRUE if the label's text will be parsed for markup
- *
- * Since: 2.4
- **/
-gboolean
-gtk_expander_get_use_markup (GtkExpander *expander)
-{
-  g_return_val_if_fail (GTK_IS_EXPANDER (expander), FALSE);
-
-  return expander->priv->use_markup;
-}
-
-/**
- * gtk_expander_set_label_widget:
- * @expander: a #GtkExpander
- * @label_widget: the new label widget
- *
- * Set the label widget for the expander. This is the widget
- * that will appear embedded alongside the expander arrow.
- *
- * Since: 2.4
- **/
-void
-gtk_expander_set_label_widget (GtkExpander *expander,
-			       GtkWidget   *label_widget)
-{
-  GtkExpanderPrivate *priv;
-
-  g_return_if_fail (GTK_IS_EXPANDER (expander));
-  g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
-  g_return_if_fail (label_widget == NULL || label_widget->parent == NULL);
-
-  priv = expander->priv;
-
-  if (priv->label_widget == label_widget)
-    return;
-
-  if (priv->label_widget)
-    {
-      gtk_widget_set_state (priv->label_widget, GTK_STATE_NORMAL);
-      gtk_widget_unparent (priv->label_widget);
-    }
-
-  priv->label_widget = label_widget;
-
-  if (label_widget)
-    {
-      priv->label_widget = label_widget;
-
-      gtk_widget_set_parent (label_widget, GTK_WIDGET (expander));
-
-      if (priv->prelight)
-	gtk_widget_set_state (label_widget, GTK_STATE_PRELIGHT);
-    }
-
-  if (GTK_WIDGET_VISIBLE (expander))
-    gtk_widget_queue_resize (GTK_WIDGET (expander));
-
-  g_object_freeze_notify (G_OBJECT (expander));
-  g_object_notify (G_OBJECT (expander), "label-widget");
-  g_object_notify (G_OBJECT (expander), "label");
-  g_object_thaw_notify (G_OBJECT (expander));
-}
-
-/**
- * gtk_expander_get_label_widget:
- * @expander: a #GtkExpander
- *
- * Retrieves the label widget for the frame. See
- * gtk_expander_set_label_widget().
- *
- * Return value: the label widget, or %NULL if there is none.
- *
- * Since: 2.4
- **/
-GtkWidget *
-gtk_expander_get_label_widget (GtkExpander *expander)
-{
-  g_return_val_if_fail (GTK_IS_EXPANDER (expander), NULL);
-
-  return expander->priv->label_widget;
-}
-
-#define __GTK_EXPANDER_C__
-
-#endif /* Gtk 2.4 */
--- a/pidgin/gtkexpander.h	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/* GTK - The GIMP Toolkit
- *
- * Copyright (C) 2003 Sun Microsystems, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- *
- * Authors:
- *	Mark McLoughlin <mark@skynet.ie>
- */
-
-#ifndef __GTK_EXPANDER_H__
-#define __GTK_EXPANDER_H__
-
-#include <gtk/gtkbin.h>
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_EXPANDER            (gtk_expander_get_type ())
-#define GTK_EXPANDER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_EXPANDER, GtkExpander))
-#define GTK_EXPANDER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_EXPANDER, GtkExpanderClass))
-#define GTK_IS_EXPANDER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_EXPANDER))
-#define GTK_IS_EXPANDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_EXPANDER))
-#define GTK_EXPANDER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_EXPANDER, GtkExpanderClass))
-
-typedef struct _GtkExpander        GtkExpander;
-typedef struct _GtkExpanderClass   GtkExpanderClass;
-typedef struct _GtkExpanderPrivate GtkExpanderPrivate;
-
-struct _GtkExpander
-{
-  GtkBin              bin;
-
-  GtkExpanderPrivate *priv;
-};
-
-struct _GtkExpanderClass
-{
-  GtkBinClass    parent_class;
-
-  /* Key binding signal; to get notification on the expansion
-   * state connect to notify:expanded.
-   */
-  void        (* activate) (GtkExpander *expander);
-};
-
-GType                 gtk_expander_get_type          (void) G_GNUC_CONST;
-
-GtkWidget            *gtk_expander_new               (const gchar *label);
-GtkWidget            *gtk_expander_new_with_mnemonic (const gchar *label);
-
-void                  gtk_expander_set_expanded      (GtkExpander *expander,
-						      gboolean     expanded);
-gboolean              gtk_expander_get_expanded      (GtkExpander *expander);
-
-/* Spacing between the expander/label and the child */
-void                  gtk_expander_set_spacing       (GtkExpander *expander,
-						      gint         spacing);
-gint                  gtk_expander_get_spacing       (GtkExpander *expander);
-
-void                  gtk_expander_set_label         (GtkExpander *expander,
-						      const gchar *label);
-G_CONST_RETURN gchar *gtk_expander_get_label         (GtkExpander *expander);
-
-void                  gtk_expander_set_use_underline (GtkExpander *expander,
-						      gboolean     use_underline);
-gboolean              gtk_expander_get_use_underline (GtkExpander *expander);
-
-void                  gtk_expander_set_use_markup    (GtkExpander *expander,
-						      gboolean    use_markup);
-gboolean              gtk_expander_get_use_markup    (GtkExpander *expander);
-
-void                  gtk_expander_set_label_widget  (GtkExpander *expander,
-						      GtkWidget   *label_widget);
-GtkWidget            *gtk_expander_get_label_widget  (GtkExpander *expander);
-
-G_END_DECLS
-
-#endif /* __GTK_EXPANDER_H__ */
--- a/pidgin/gtkft.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkft.c	Mon Mar 08 22:53:02 2010 +0000
@@ -32,10 +32,8 @@
 #include "prpl.h"
 #include "util.h"
 
-#include "gtkcellrendererprogress.h"
 #include "gtkft.h"
 #include "prefs.h"
-#include "gtkexpander.h"
 #include "pidginstock.h"
 #include "gtkutils.h"
 
@@ -460,27 +458,15 @@
 #ifdef _WIN32
 	/* If using Win32... */
 	int code;
-	if (G_WIN32_HAVE_WIDECHAR_API ()) {
-		wchar_t *wc_filename = g_utf8_to_utf16(
-				purple_xfer_get_local_filename(
-					dialog->selected_xfer),
-				-1, NULL, NULL, NULL);
-
-		code = (int) ShellExecuteW(NULL, NULL, wc_filename, NULL, NULL,
-				SW_SHOW);
+	wchar_t *wc_filename = g_utf8_to_utf16(
+			purple_xfer_get_local_filename(
+				dialog->selected_xfer),
+			-1, NULL, NULL, NULL);
 
-		g_free(wc_filename);
-	} else {
-		char *l_filename = g_locale_from_utf8(
-				purple_xfer_get_local_filename(
-					dialog->selected_xfer),
-				-1, NULL, NULL, NULL);
+	code = (int) ShellExecuteW(NULL, NULL, wc_filename, NULL, NULL,
+			SW_SHOW);
 
-		code = (int) ShellExecuteA(NULL, NULL, l_filename, NULL, NULL,
-				SW_SHOW);
-
-		g_free(l_filename);
-	}
+	g_free(wc_filename);
 
 	if (code == SE_ERR_ASSOCINCOMPLETE || code == SE_ERR_NOASSOC)
 	{
@@ -589,7 +575,7 @@
 
 	/* Build the tree model */
 	/* Transfer type, Progress Bar, Filename, Size, Remaining */
-	model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_DOUBLE,
+	model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_INT,
 							   G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
 							   G_TYPE_POINTER);
 	dialog->model = model;
@@ -621,9 +607,9 @@
 	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
 
 	/* Progress bar column */
-	renderer = pidgin_cell_renderer_progress_new();
+	renderer = gtk_cell_renderer_progress_new();
 	column = gtk_tree_view_column_new_with_attributes(_("Progress"), renderer,
-				"percentage", COLUMN_PROGRESS, NULL);
+				"value", COLUMN_PROGRESS, NULL);
 	gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
 	gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
 
@@ -920,7 +906,7 @@
 	lfilename = utf8;
 	gtk_list_store_set(dialog->model, &data->iter,
 					   COLUMN_STATUS, pixbuf,
-					   COLUMN_PROGRESS, 0.0,
+					   COLUMN_PROGRESS, 0,
 					   COLUMN_FILENAME, (type == PURPLE_XFER_RECEIVE)
 					                     ? purple_xfer_get_filename(xfer)
 							     : lfilename,
@@ -1053,7 +1039,7 @@
 	remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer));
 
 	gtk_list_store_set(xfer_dialog->model, &data->iter,
-					   COLUMN_PROGRESS, purple_xfer_get_progress(xfer),
+					   COLUMN_PROGRESS, (gint)(purple_xfer_get_progress(xfer) * 100),
 					   COLUMN_SIZE, size_str,
 					   COLUMN_REMAINING, remaining_str,
 					   -1);
--- a/pidgin/gtkimhtml.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkimhtml.c	Mon Mar 08 22:53:02 2010 +0000
@@ -63,18 +63,8 @@
 
 #include <pango/pango-font.h>
 
-/* GTK+ < 2.4.x hack, see pidgin.h for details. */
-#if (!GTK_CHECK_VERSION(2,4,0))
-#define GTK_WRAP_WORD_CHAR GTK_WRAP_WORD
-#endif
-
 #define TOOLTIP_TIMEOUT 500
 
-/* GTK+ 2.0 hack */
-#if (!GTK_CHECK_VERSION(2,2,0))
-#define gtk_widget_get_clipboard(x, y) gtk_clipboard_get(y)
-#endif
-
 static GtkTextViewClass *parent_class = NULL;
 
 struct scalable_data {
@@ -552,10 +542,8 @@
 	gtk_window_set_title(GTK_WINDOW(imhtml->tip_window), "GtkIMHtml");
 	gtk_window_set_resizable (GTK_WINDOW (imhtml->tip_window), FALSE);
 	gtk_widget_set_name (imhtml->tip_window, "gtk-tooltips");
-#if GTK_CHECK_VERSION(2,10,0)
 	gtk_window_set_type_hint (GTK_WINDOW (imhtml->tip_window),
 		GDK_WINDOW_TYPE_HINT_TOOLTIP);
-#endif
 	g_signal_connect_swapped (G_OBJECT (imhtml->tip_window), "expose_event",
 							  G_CALLBACK (gtk_imhtml_tip_paint), imhtml);
 
@@ -760,32 +748,6 @@
 	return FALSE;
 }
 
-#if (!GTK_CHECK_VERSION(2,2,0))
-/*
- * XXX - This should be removed eventually.
- *
- * This function exists to work around a gross bug in GtkTextView.
- * Basically, we short circuit ctrl+a and ctrl+end because they make
- * el program go boom.
- *
- * It's supposed to be fixed in gtk2.2.  You can view the bug report at
- * http://bugzilla.gnome.org/show_bug.cgi?id=107939
- */
-static gboolean
-gtk_key_pressed_cb(GtkIMHtml *imhtml, GdkEventKey *event, gpointer data)
-{
-	if (event->state & GDK_CONTROL_MASK) {
-		switch (event->keyval) {
-			case 'a':
-			case GDK_Home:
-			case GDK_End:
-				return TRUE;
-		}
-	}
-	return FALSE;
-}
-#endif /* !(GTK+ >= 2.2.0) */
-
 static gint
 gtk_imhtml_expose_event (GtkWidget      *widget,
 			 GdkEventExpose *event)
@@ -1700,10 +1662,6 @@
 	g_signal_connect(G_OBJECT(imhtml), "motion-notify-event", G_CALLBACK(gtk_motion_event_notify), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "leave-notify-event", G_CALLBACK(gtk_leave_event_notify), NULL);
 	g_signal_connect(G_OBJECT(imhtml), "enter-notify-event", G_CALLBACK(gtk_enter_event_notify), NULL);
-#if (!GTK_CHECK_VERSION(2,2,0))
-	/* See the comment for gtk_key_pressed_cb */
-	g_signal_connect(G_OBJECT(imhtml), "key_press_event", G_CALLBACK(gtk_key_pressed_cb), NULL);
-#endif
 	g_signal_connect(G_OBJECT(imhtml), "button_press_event", G_CALLBACK(gtk_imhtml_button_press_event), NULL);
 	g_signal_connect(G_OBJECT(imhtml->text_buffer), "insert-text", G_CALLBACK(preinsert_cb), imhtml);
 	g_signal_connect(G_OBJECT(imhtml->text_buffer), "delete_range", G_CALLBACK(delete_cb), imhtml);
@@ -2173,50 +2131,56 @@
 	return (*len > 0);
 }
 
-GtkIMHtmlSmiley *
-gtk_imhtml_smiley_get(GtkIMHtml *imhtml,
-	const gchar *sml,
-	const gchar *text)
-{
-	GtkSmileyTree *t;
+static GtkIMHtmlSmiley *gtk_imhtml_smiley_get_from_tree(GtkSmileyTree *t, const gchar *text)
+{
 	const gchar *x = text;
-	if (sml == NULL)
-		t = imhtml->default_smilies;
-	else
-		t = g_hash_table_lookup(imhtml->smiley_data, sml);
-
+	gchar *pos;
 
 	if (t == NULL)
-		return sml ? gtk_imhtml_smiley_get(imhtml, NULL, text) : NULL;
+		return NULL;
 
 	while (*x) {
-		gchar *pos;
-
-		if (!t->values) {
-			return sml ? gtk_imhtml_smiley_get(imhtml, NULL, text) : NULL;
-		}
-
-		pos = strchr (t->values->str, *x);
-		if (pos) {
-			t = t->children [GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str)];
-		} else {
-			return sml ? gtk_imhtml_smiley_get(imhtml, NULL, text) : NULL;
-		}
+		if (!t->values)
+			return NULL;
+
+		pos = strchr(t->values->str, *x);
+		if (!pos)
+			return NULL;
+
+		t = t->children[GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str)];
 		x++;
 	}
 
 	return t->image;
 }
 
+GtkIMHtmlSmiley *
+gtk_imhtml_smiley_get(GtkIMHtml *imhtml, const gchar *sml, const gchar *text)
+{
+	GtkIMHtmlSmiley *ret;
+
+	/* Look for custom smileys first */
+	if (sml != NULL) {
+		ret = gtk_imhtml_smiley_get_from_tree(g_hash_table_lookup(imhtml->smiley_data, sml), text);
+		if (ret != NULL)
+			return ret;
+	}
+
+	/* Fall back to check for default smileys */
+	return gtk_imhtml_smiley_get_from_tree(imhtml->default_smilies, text);
+}
+
 static GdkPixbufAnimation *
 gtk_smiley_get_image(GtkIMHtmlSmiley *smiley)
 {
-	if (!smiley->icon && smiley->file) {
-		smiley->icon = gdk_pixbuf_animation_new_from_file(smiley->file, NULL);
-	} else if (!smiley->icon && smiley->loader) {
-		smiley->icon = gdk_pixbuf_loader_get_animation(smiley->loader);
-		if (smiley->icon)
-			g_object_ref(G_OBJECT(smiley->icon));
+	if (!smiley->icon) {
+		if (smiley->file) {
+			smiley->icon = gdk_pixbuf_animation_new_from_file(smiley->file, NULL);
+		} else if (smiley->loader) {
+			smiley->icon = gdk_pixbuf_loader_get_animation(smiley->loader);
+			if (smiley->icon)
+				g_object_ref(G_OBJECT(smiley->icon));
+		}
 	}
 
 	return smiley->icon;
@@ -3692,22 +3656,12 @@
 	image->filesel = NULL;
 
 	if (save->data && save->datasize) {
-#if GLIB_CHECK_VERSION(2,8,0)
 		g_file_set_contents(filename, save->data, save->datasize, &error);
-#else
-		purple_util_write_data_to_file_absolute(filename, save->data, save->datasize);
-#endif
 	} else {
 		gchar *type = NULL;
-#if GTK_CHECK_VERSION(2,2,0)
 		GSList *formats = gdk_pixbuf_get_formats();
-#else
-		char *basename = g_path_get_basename(filename);
-		char *ext = strrchr(basename, '.');
-#endif
 		char *newfilename;
 
-#if GTK_CHECK_VERSION(2,2,0)
 		while (formats) {
 			GdkPixbufFormat *format = formats->data;
 			gchar **extensions = gdk_pixbuf_format_get_extensions(format);
@@ -3734,31 +3688,14 @@
 		}
 
 		g_slist_free(formats);
-#else
-		/* this is really ugly code, but I think it will work */
-		if (ext) {
-			ext++;
-			if (!g_ascii_strcasecmp(ext, "jpeg") || !g_ascii_strcasecmp(ext, "jpg"))
-				type = g_strdup("jpeg");
-			else if (!g_ascii_strcasecmp(ext, "png"))
-				type = g_strdup("png");
-		}
-
-		g_free(basename);
-#endif
 
 		/* If I can't find a valid type, I will just tell the user about it and then assume
 		   it's a png */
 		if (!type){
 			char *basename, *tmp;
 			char *dirname;
-#if GTK_CHECK_VERSION(2,4,0)
 			GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
 							_("<span size='larger' weight='bold'>Unrecognized file type</span>\n\nDefaulting to PNG."));
-#else
-			GtkWidget *dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
-							_("Unrecognized file type\n\nDefaulting to PNG."));
-#endif
 
 			g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
 			gtk_widget_show(dialog);
@@ -3787,20 +3724,14 @@
 	}
 
 	if (error){
-#if GTK_CHECK_VERSION(2,4,0)
 		GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
 				_("<span size='larger' weight='bold'>Error saving image</span>\n\n%s"), error->message);
-#else
-		GtkWidget *dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
-				_("Error saving image\n\n%s"), error->message);
-#endif
 		g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
 		gtk_widget_show(dialog);
 		g_error_free(error);
 	}
 }
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 static void
 image_save_check_if_exists_cb(GtkWidget *widget, gint response, GtkIMHtmlImageSave *save)
 {
@@ -3814,32 +3745,6 @@
 	}
 
 	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
-#else /* FILECHOOSER */
-static void
-image_save_check_if_exists_cb(GtkWidget *button, GtkIMHtmlImageSave *save)
-{
-	gchar *filename;
-	GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
-	filename = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(image->filesel)));
-
-	if (g_file_test(filename, G_FILE_TEST_IS_DIR)) {
-		gchar *dirname;
-		/* append a / is needed */
-		if (filename[strlen(filename) - 1] != G_DIR_SEPARATOR) {
-			dirname = g_strconcat(filename, G_DIR_SEPARATOR_S, NULL);
-		} else {
-			dirname = g_strdup(filename);
-		}
-		gtk_file_selection_set_filename(GTK_FILE_SELECTION(image->filesel), dirname);
-		g_free(dirname);
-		g_free(filename);
-		return;
-	}
-#endif /* FILECHOOSER */
-#if 0 /* mismatched curly braces */
-	}
-#endif
 
 	/*
 	 * XXX - We should probably prompt the user to determine if they really
@@ -3857,15 +3762,6 @@
 	g_free(filename);
 }
 
-#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-static void
-image_save_cancel_cb(GtkIMHtmlImage *image)
-{
-	gtk_widget_destroy(image->filesel);
-	image->filesel = NULL;
-}
-#endif /* FILECHOOSER */
-
 static void
 gtk_imhtml_image_save(GtkWidget *w, GtkIMHtmlImageSave *save)
 {
@@ -3876,7 +3772,6 @@
 		return;
 	}
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 	image->filesel = gtk_file_chooser_dialog_new(_("Save Image"),
 						NULL,
 						GTK_FILE_CHOOSER_ACTION_SAVE,
@@ -3888,17 +3783,6 @@
 		gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(image->filesel), image->filename);
 	g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(image->filesel)), "response",
 					 G_CALLBACK(image_save_check_if_exists_cb), save);
-#else /* FILECHOOSER */
-	image->filesel = gtk_file_selection_new(_("Save Image"));
-	if (image->filename != NULL)
-		gtk_file_selection_set_filename(GTK_FILE_SELECTION(image->filesel), image->filename);
-	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(image->filesel)), "delete_event",
-							 G_CALLBACK(image_save_cancel_cb), image);
-	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(image->filesel)->cancel_button),
-							 "clicked", G_CALLBACK(image_save_cancel_cb), image);
-	g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(image->filesel)->ok_button), "clicked",
-					 G_CALLBACK(image_save_check_if_exists_cb), save);
-#endif /* FILECHOOSER */
 
 	gtk_widget_show(image->filesel);
 }
@@ -5030,9 +4914,7 @@
 
 	if (imhtml_smiley && imhtml_smiley->flags & GTK_IMHTML_SMILEY_CUSTOM) {
 		ebox = gtk_event_box_new();
-#if GTK_CHECK_VERSION(2,4,0)
 		gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
-#endif
 		gtk_widget_show(ebox);
 	}
 
--- a/pidgin/gtkimhtmltoolbar.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Mon Mar 08 22:53:02 2010 +0000
@@ -33,6 +33,7 @@
 #include "request.h"
 #include "pidginstock.h"
 #include "util.h"
+#include "debug.h"
 
 #include "gtkdialogs.h"
 #include "gtkimhtmltoolbar.h"
@@ -478,35 +479,19 @@
 	GtkTextIter iter;
 	GtkTextMark *ins;
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 	if (response != GTK_RESPONSE_ACCEPT)
-#else /* FILECHOOSER */
-	if (response != GTK_RESPONSE_OK)
-#endif /* FILECHOOSER */
 	{
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
 		return;
 	}
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
-#else /* FILECHOOSER */
-	filename = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(widget)));
-#endif /* FILECHOOSER */
 
 	if (filename == NULL) {
 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
 		return;
 	}
 
-#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-	if (pidgin_check_if_dir(filename, GTK_FILE_SELECTION(widget))) {
-		g_free(filename);
-		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
-		return;
-	}
-#endif /* FILECHOOSER */
-
 	/* The following triggers a callback that closes the widget */
 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
 
@@ -549,7 +534,6 @@
 	GtkWidget *window;
 
 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image))) {
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 		window = gtk_file_chooser_dialog_new(_("Insert Image"),
 						NULL,
 						GTK_FILE_CHOOSER_ACTION_OPEN,
@@ -559,12 +543,6 @@
 		gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_ACCEPT);
 		g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(window)),
 				"response", G_CALLBACK(do_insert_image_cb), toolbar);
-#else /* FILECHOOSER */
-		window = gtk_file_selection_new(_("Insert Image"));
-		gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_OK);
-		g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(window)),
-				"response", G_CALLBACK(do_insert_image_cb), toolbar);
-#endif /* FILECHOOSER */
 
 		gtk_widget_show(window);
 		toolbar->image_dialog = window;
@@ -921,6 +899,17 @@
 	gtk_widget_grab_focus(toolbar->imhtml);
 }
 
+static void send_attention_cb(GtkWidget *attention, GtkIMHtmlToolbar *toolbar)
+{
+	PurpleConversation *conv =
+		g_object_get_data(G_OBJECT(toolbar), "active_conv");
+	const gchar *who = purple_conversation_get_name(conv);
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+
+	toggle_button_set_active_block(GTK_TOGGLE_BUTTON(attention), FALSE, toolbar);
+	purple_prpl_send_attention(gc, who, 0);
+}
+
 static void update_buttons_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, GtkIMHtmlToolbar *toolbar)
 {
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bold), buttons & GTK_IMHTML_BOLD);
@@ -1241,6 +1230,8 @@
 		{PIDGIN_STOCK_TOOLBAR_INSERT_LINK, insert_link_cb, &toolbar->link, _("Insert Link")},
 		{"", NULL, NULL, NULL},
 		{PIDGIN_STOCK_TOOLBAR_SMILEY, insert_smiley_cb, &toolbar->smiley, _("Insert Smiley")},
+		/*{PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, send_attention_cb, 
+			g_object_get_data(G_OBJECT(toolbar->imhtml), "attention"), _("Send Attention")},*/
 		{NULL, NULL, NULL, NULL}
 	};
 	int iter;
@@ -1258,6 +1249,13 @@
 			button = gtk_vseparator_new();
 		gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
 	}
+	/* create the attention button (this is a bit hacky to not break ABI) */
+	button = pidgin_pixbuf_toolbar_button_from_stock(PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION);
+	g_signal_connect(G_OBJECT(button), "clicked",
+		G_CALLBACK(send_attention_cb), toolbar);
+	g_object_set_data(G_OBJECT(toolbar), "attention", button);
+	gtk_tooltips_set_tip(toolbar->tooltips, button, _("Send Attention"), NULL);
+	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
 
 	gtk_box_pack_start(GTK_BOX(toolbar), hbox, FALSE, FALSE, 0);
 	g_object_set_data(G_OBJECT(toolbar), "wide-view", hbox);
@@ -1306,6 +1304,7 @@
 	GtkWidget *insert_button;
 	GtkWidget *font_button;
 	GtkWidget *smiley_button;
+	GtkWidget *attention_button;
 	GtkWidget *font_menu;
 	GtkWidget *insert_menu;
 	GtkWidget *menuitem;
@@ -1447,6 +1446,35 @@
 	g_signal_connect_swapped(G_OBJECT(smiley_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->smiley);
 	gtk_widget_show_all(smiley_button);
 
+	/* Sep */
+	sep = gtk_vseparator_new();
+	gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
+	gtk_widget_show_all(sep);
+
+	/* Attention */
+	attention_button = gtk_button_new();
+	gtk_button_set_relief(GTK_BUTTON(attention_button), GTK_RELIEF_NONE);
+	bbox = gtk_hbox_new(FALSE, 3);
+	gtk_container_add(GTK_CONTAINER(attention_button), bbox);
+	image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, 
+		gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
+	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
+	label = gtk_label_new_with_mnemonic(_("_Attention!"));
+	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(box), attention_button, FALSE, FALSE, 0);
+	g_signal_connect_swapped(G_OBJECT(attention_button), "clicked", 
+		G_CALLBACK(gtk_button_clicked),
+	    g_object_get_data(G_OBJECT(toolbar), "attention"));
+	gtk_widget_show_all(attention_button);
+	
+	g_signal_connect(G_OBJECT(g_object_get_data(G_OBJECT(toolbar), "attention")),
+	        "notify::sensitive",
+			G_CALLBACK(button_sensitiveness_changed), attention_button);
+
+	/* set attention button to be greyed out until we get a conversation */
+	gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(toolbar), "attention"),
+		FALSE);
+	
 	gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0);
 	g_object_set_data(G_OBJECT(hbox), "lean-view", box);
 	gtk_widget_show(box);
@@ -1457,9 +1485,7 @@
 			G_CALLBACK(purple_prefs_trigger_callback), PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
 			NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
 
-#if GTK_CHECK_VERSION(2,4,0)
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE);
-#endif
 
 	gtk_widget_add_events(event, GDK_BUTTON_PRESS_MASK);
 	gtk_box_pack_start(GTK_BOX(hbox), event, TRUE, TRUE, 0);
@@ -1524,3 +1550,21 @@
 	g_free(toolbar->sml);
 	toolbar->sml = g_strdup(proto_id);
 }
+
+void gtk_imhtmltoolbar_switch_active_conversation(GtkIMHtmlToolbar *toolbar,
+	PurpleConversation *conv)
+{
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	PurplePlugin *prpl = purple_connection_get_prpl(gc);
+	GtkWidget *attention =
+		g_object_get_data(G_OBJECT(toolbar), "attention");
+
+	g_object_set_data(G_OBJECT(toolbar), "active_conv", conv);
+	
+	/* gray out attention button on protocols that don't support it
+	 for the time being it is always disabled for chats */
+	gtk_widget_set_sensitive(attention,
+		conv && prpl && purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM && 
+		PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention != NULL);
+}
+
--- a/pidgin/gtkimhtmltoolbar.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkimhtmltoolbar.h	Mon Mar 08 22:53:02 2010 +0000
@@ -90,6 +90,11 @@
 void gtk_imhtmltoolbar_attach    (GtkIMHtmlToolbar *toolbar, GtkWidget *imhtml);
 void gtk_imhtmltoolbar_associate_smileys (GtkIMHtmlToolbar *toolbar, const char *proto_id);
 
+/**
+ * @since 2.7.0
+ */
+void gtk_imhtmltoolbar_switch_active_conversation(GtkIMHtmlToolbar *toolbar,
+	PurpleConversation *conv);
 
 #ifdef __cplusplus
 }
--- a/pidgin/gtklog.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtklog.c	Mon Mar 08 22:53:02 2010 +0000
@@ -252,7 +252,6 @@
 		GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(treestore), iter);
 		gboolean first = !gtk_tree_path_prev(path);
 
-#if GTK_CHECK_VERSION(2,2,0)
 		if (!gtk_tree_store_remove(treestore, iter) && first)
 		{
 			/* iter was the last child at its level */
@@ -263,9 +262,7 @@
 				gtk_tree_store_remove(treestore, iter);
 			}
 		}
-#else
-		gtk_tree_store_remove(treestore, iter);
-#endif
+
 		gtk_tree_path_free(path);
 	}
 
--- a/pidgin/gtkmain.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkmain.c	Mon Mar 08 22:53:02 2010 +0000
@@ -805,9 +805,7 @@
 		return 1;
 	}
 
-#if GLIB_CHECK_VERSION(2,2,0)
 	g_set_application_name(PIDGIN_NAME);
-#endif /* glib-2.0 >= 2.2.0 */
 
 #ifdef _WIN32
 	winpidgin_init(hint);
--- a/pidgin/gtkmenutray.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkmenutray.c	Mon Mar 08 22:53:02 2010 +0000
@@ -138,9 +138,7 @@
 static void
 pidgin_menu_tray_init(PidginMenuTray *menu_tray) {
 	GtkWidget *widget = GTK_WIDGET(menu_tray);
-#if GTK_CHECK_VERSION(2,2,0)
 	GtkSettings *settings;
-#endif
 	gint height = -1;
 
 	gtk_menu_item_set_right_justified(GTK_MENU_ITEM(menu_tray), TRUE);
@@ -148,15 +146,11 @@
 	if(!GTK_IS_WIDGET(menu_tray->tray))
 		menu_tray->tray = gtk_hbox_new(FALSE, 0);
 
-#if GTK_CHECK_VERSION(2,2,0)
 	settings =
 		gtk_settings_get_for_screen(gtk_widget_get_screen(widget));
 
 	if(gtk_icon_size_lookup_for_settings(settings, GTK_ICON_SIZE_MENU,
 										 NULL, &height))
-#else
-	if(gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, NULL, &height))
-#endif
 	{
 		gtk_widget_set_size_request(widget, -1, height);
 	}
--- a/pidgin/gtknotify.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtknotify.c	Mon Mar 08 22:53:02 2010 +0000
@@ -627,12 +627,7 @@
 						PIDGIN_MAIL_DATA, &data, -1);
 				if (data && data->account == account) {
 					if (clear) {
-#if GTK_CHECK_VERSION(2,2,0)
 						advanced = gtk_tree_store_remove(treemodel, &iter);
-#else
-						gtk_tree_store_remove(treemodel, &iter);
-						advanced = (iter.stamp == 0) ? FALSE : TRUE;
-#endif
 						mail_dialog->total_count -= data->count;
 
 						if (data->purple_has_handle)
--- a/pidgin/gtkplugin.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkplugin.c	Mon Mar 08 22:53:02 2010 +0000
@@ -670,15 +670,11 @@
 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
 
 	website_button = gtk_event_box_new();
-#if GTK_CHECK_VERSION(2,4,0)
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(website_button), FALSE);
-#endif
 
 	plugin_website = GTK_LABEL(gtk_label_new(NULL));
-#if GTK_CHECK_VERSION(2,6,0)
 	g_object_set(G_OBJECT(plugin_website),
 		"ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
-#endif
 	gtk_misc_set_alignment(GTK_MISC(plugin_website), 0, 0);
 	gtk_container_add(GTK_CONTAINER(website_button),
 		GTK_WIDGET(plugin_website));
@@ -778,10 +774,8 @@
 							"markup", 1,
 							"foreground-set", 3,
 							NULL);
-#if GTK_CHECK_VERSION(2,6,0)
 	gtk_tree_view_column_set_expand (col, TRUE);
 	g_object_set(rendt, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 	gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
 	gtk_tree_view_column_set_sort_column_id(col, 1);
 	g_object_unref(G_OBJECT(ls));
--- a/pidgin/gtkpounce.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkpounce.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1063,15 +1063,6 @@
 	return FALSE;
 }
 
-#if !GTK_CHECK_VERSION(2,2,0)
-static void
-count_selected_helper(GtkTreeModel *model, GtkTreePath *path,
-					GtkTreeIter *iter, gpointer user_data)
-{
-	(*(gint *)user_data)++;
-}
-#endif
-
 static void
 pounces_manager_connection_cb(PurpleConnection *gc, GtkWidget *add_button)
 {
@@ -1163,11 +1154,7 @@
 	PouncesManager *dialog = user_data;
 	int num_selected = 0;
 
-#if GTK_CHECK_VERSION(2,2,0)
 	num_selected = gtk_tree_selection_count_selected_rows(sel);
-#else
-	gtk_tree_selection_selected_foreach(sel, count_selected_helper, &num_selected);
-#endif
 
 	gtk_widget_set_sensitive(dialog->modify_button, (num_selected > 0));
 	gtk_widget_set_sensitive(dialog->delete_button, (num_selected > 0));
--- a/pidgin/gtkprefs.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkprefs.c	Mon Mar 08 22:53:02 2010 +0000
@@ -223,16 +223,8 @@
 
 	g_return_val_if_fail(menuitems != NULL, NULL);
 
-#if 0 /* GTK_CHECK_VERSION(2,4,0) */
-	if(type == PURPLE_PREF_INT)
-		model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
-	else if(type == PURPLE_PREF_STRING)
-		model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
-	dropdown = gtk_combo_box_new_with_model(model);
-#else
 	dropdown = gtk_option_menu_new();
 	menu = gtk_menu_new();
-#endif
 
 	if (type == PURPLE_PREF_INT)
 		stored_int = purple_prefs_get_int(key);
@@ -908,11 +900,9 @@
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "pixbuf", 0, NULL);
 
 	cell_rend = gtk_cell_renderer_text_new();
-	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, TRUE);
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "markup", 1, NULL);
-/*#if GTK_CHECK_VERSION(2,6,0)
-			g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif*/
+	g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 
 	gtk_drag_dest_set(combo_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te,
 					sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE);
@@ -1090,7 +1080,7 @@
 	g_signal_connect(G_OBJECT(prefs_blist_themes_combo_box), "changed",
 						(GCallback)prefs_set_blist_theme_cb, NULL);
 	gtk_size_group_add_widget(combo_sg, prefs_blist_themes_combo_box);
-	gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_blist_themes_combo_box, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_blist_themes_combo_box, TRUE, TRUE, 0);
 
 	gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
 
@@ -1108,7 +1098,7 @@
 	g_signal_connect(G_OBJECT(prefs_status_themes_combo_box), "changed",
 						(GCallback)prefs_set_status_icon_theme_cb, NULL);
 	gtk_size_group_add_widget(combo_sg, prefs_status_themes_combo_box);
-	gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_status_themes_combo_box, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_status_themes_combo_box, TRUE, TRUE, 0);
 
 	gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
 
@@ -1126,7 +1116,7 @@
 	g_signal_connect(G_OBJECT(prefs_sound_themes_combo_box), "changed",
 						(GCallback)prefs_set_sound_theme_cb, NULL);
 	gtk_size_group_add_widget(combo_sg, prefs_sound_themes_combo_box);
-	gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_sound_themes_combo_box, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_sound_themes_combo_box, TRUE, TRUE, 0);
 
 	gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
 
@@ -1144,7 +1134,7 @@
 	g_signal_connect(G_OBJECT(prefs_smiley_themes_combo_box), "changed",
 						(GCallback)prefs_set_smiley_theme_cb, NULL);
 	gtk_size_group_add_widget(combo_sg, prefs_smiley_themes_combo_box);
-	gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_smiley_themes_combo_box, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(themesel_hbox), prefs_smiley_themes_combo_box, TRUE, TRUE, 0);
 
 	gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
 
@@ -1419,10 +1409,8 @@
 					_("Bottom"), GTK_POS_BOTTOM,
 					_("Left"), GTK_POS_LEFT,
 					_("Right"), GTK_POS_RIGHT,
-#if GTK_CHECK_VERSION(2,6,0)
 					_("Left Vertical"), GTK_POS_LEFT|8,
 					_("Right Vertical"), GTK_POS_RIGHT|8,
-#endif
 					NULL);
 	gtk_size_group_add_widget(sg, label);
 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
@@ -1517,7 +1505,7 @@
 		PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines",
 		1, 8, NULL);
 
-#if GTK_CHECK_VERSION(2,4,0) && defined _WIN32
+#ifdef _WIN32
 	{
 	GtkWidget *fontpref, *font_button, *hbox;
 	const char *font_name;
@@ -2729,12 +2717,7 @@
 static int
 prefs_notebook_add_page(const char *text, GtkWidget *page, int ind)
 {
-#if GTK_CHECK_VERSION(2,4,0)
 	return gtk_notebook_append_page(GTK_NOTEBOOK(prefsnotebook), page, gtk_label_new(text));
-#else
-	gtk_notebook_append_page(GTK_NOTEBOOK(prefsnotebook), page, gtk_label_new(text));
-	return gtk_notebook_page_num(GTK_NOTEBOOK(prefsnotebook), page);
-#endif
 }
 
 static void
--- a/pidgin/gtkprivacy.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkprivacy.c	Mon Mar 08 22:53:02 2010 +0000
@@ -220,7 +220,7 @@
 
 	for (i = 0; i < menu_entry_count; i++) {
 		if (menu_entries[i].num == account->perm_deny) {
-			gtk_option_menu_set_history(GTK_OPTION_MENU(dialog->type_menu), i);
+			gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->type_menu), i);
 			break;
 		}
 	}
@@ -234,9 +234,9 @@
  *       Even better: the privacy API needs to not suck.
  */
 static void
-type_changed_cb(GtkOptionMenu *optmenu, PidginPrivacyDialog *dialog)
+type_changed_cb(GtkComboBox *combo, PidginPrivacyDialog *dialog)
 {
-	int new_type = menu_entries[gtk_option_menu_get_history(optmenu)].num;
+	int new_type = menu_entries[gtk_combo_box_get_active(combo)].num;
 
 	dialog->account->perm_deny = new_type;
 	serv_set_permit_deny(purple_account_get_connection(dialog->account));
@@ -343,8 +343,7 @@
 	GtkWidget *button;
 	GtkWidget *dropdown;
 	GtkWidget *label;
-	GtkWidget *menu;
-	int selected = 0;
+	int selected = -1;
 	int i;
 
 	dialog = g_new0(PidginPrivacyDialog, 1);
@@ -372,22 +371,19 @@
 	dialog->account = pidgin_account_option_menu_get_selected(dropdown);
 
 	/* Add the drop-down list with the allow/block types. */
-	dialog->type_menu = gtk_option_menu_new();
+	dialog->type_menu = gtk_combo_box_new_text();
 	gtk_box_pack_start(GTK_BOX(vbox), dialog->type_menu, FALSE, FALSE, 0);
 	gtk_widget_show(dialog->type_menu);
 
-	/* Build the menu for that. */
-	menu = gtk_menu_new();
-
 	for (i = 0; i < menu_entry_count; i++) {
-		pidgin_new_item(menu, _(menu_entries[i].text));
+		gtk_combo_box_append_text(GTK_COMBO_BOX(dialog->type_menu),
+		                          _(menu_entries[i].text));
 
 		if (menu_entries[i].num == dialog->account->perm_deny)
 			selected = i;
 	}
 
-	gtk_option_menu_set_menu(GTK_OPTION_MENU(dialog->type_menu), menu);
-	gtk_option_menu_set_history(GTK_OPTION_MENU(dialog->type_menu), selected);
+	gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->type_menu), selected);
 
 	g_signal_connect(G_OBJECT(dialog->type_menu), "changed",
 					 G_CALLBACK(type_changed_cb), dialog);
@@ -421,7 +417,7 @@
 	button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLOSE, G_CALLBACK(close_cb), dialog);
 	dialog->close_button = button;
 
-	type_changed_cb(GTK_OPTION_MENU(dialog->type_menu), dialog);
+	type_changed_cb(GTK_COMBO_BOX(dialog->type_menu), dialog);
 #if 0
 	if (dialog->account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) {
 		gtk_widget_show(dialog->allow_widget);
--- a/pidgin/gtkrequest.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkrequest.c	Mon Mar 08 22:53:02 2010 +0000
@@ -230,10 +230,10 @@
 }
 
 static void
-field_choice_menu_cb(GtkOptionMenu *menu, PurpleRequestField *field)
+field_choice_menu_cb(GtkComboBox *menu, PurpleRequestField *field)
 {
 	purple_request_field_choice_set_value(field,
-			gtk_option_menu_get_history(menu));
+			gtk_combo_box_get_active(menu));
 }
 
 static void
@@ -626,9 +626,7 @@
 	/* Create the dialog. */
 	data->dialog = dialog = gtk_dialog_new();
 
-#if GTK_CHECK_VERSION(2,10,0)
 	gtk_window_set_deletable(GTK_WINDOW(data->dialog), FALSE);
-#endif
 
 	if (title != NULL)
 		gtk_window_set_title(GTK_WINDOW(dialog), title);
@@ -930,26 +928,15 @@
 
 	if (num_labels > 5)
 	{
-		GtkWidget *menu;
-		GtkWidget *item;
-
-		widget = gtk_option_menu_new();
-
-		menu = gtk_menu_new();
+		widget = gtk_combo_box_new_text();
 
 		for (l = labels; l != NULL; l = l->next)
 		{
 			const char *text = l->data;
-
-			item = gtk_menu_item_new_with_label(text);
-			gtk_widget_show(item);
-
-			gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+			gtk_combo_box_append_text(GTK_COMBO_BOX(widget), text);
 		}
 
-		gtk_widget_show(menu);
-		gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu);
-		gtk_option_menu_set_history(GTK_OPTION_MENU(widget),
+		gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
 						purple_request_field_choice_get_default_value(field));
 
 		g_signal_connect(G_OBJECT(widget), "changed",
@@ -1066,6 +1053,9 @@
 	GtkTreeViewColumn *column;
 	GtkTreeIter iter;
 	GList *l;
+	GList *icons = NULL;
+
+	icons = purple_request_field_list_get_icons(field);
 
 	/* Create the scrolled window */
 	sw = gtk_scrolled_window_new(NULL, NULL);
@@ -1077,7 +1067,10 @@
 	gtk_widget_show(sw);
 
 	/* Create the list store */
-	store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING);
+	if (icons)
+		store = gtk_list_store_new(3, G_TYPE_POINTER, G_TYPE_STRING, GDK_TYPE_PIXBUF);
+	else
+		store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING);
 
 	/* Create the tree view */
 	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
@@ -1096,13 +1089,38 @@
 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(column, renderer, "text", 1);
 
+	if (icons)
+	{
+		renderer = gtk_cell_renderer_pixbuf_new();
+		gtk_tree_view_column_pack_start(column, renderer, TRUE);
+		gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", 2);
+
+		gtk_widget_set_size_request(treeview, 200, 400);
+	}
+
 	for (l = purple_request_field_list_get_items(field); l != NULL; l = l->next)
 	{
 		const char *text = (const char *)l->data;
 
 		gtk_list_store_append(store, &iter);
 
-		gtk_list_store_set(store, &iter,
+		if (icons)
+		{
+			const char *icon_path = (const char *)icons->data;
+			GdkPixbuf* pixbuf = NULL;
+
+			if (icon_path)
+				pixbuf = gdk_pixbuf_new_from_file(icon_path, NULL);
+
+			gtk_list_store_set(store, &iter,
+						   0, purple_request_field_list_get_data(field, text),
+						   1, text,
+						   2, pixbuf,
+						   -1);
+			icons = icons->next;
+		}
+		else
+			gtk_list_store_set(store, &iter,
 						   0, purple_request_field_list_get_data(field, text),
 						   1, text,
 						   -1);
@@ -1503,7 +1521,6 @@
 	}
 }
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 static void
 file_ok_check_if_exists_cb(GtkWidget *widget, gint response, PidginRequestData *data)
 {
@@ -1528,51 +1545,6 @@
 		}
 		g_free(current_folder);
 	}
-
-#else /* FILECHOOSER */
-
-static void
-file_ok_check_if_exists_cb(GtkWidget *button, PidginRequestData *data)
-{
-	const gchar *name;
-	gchar *current_folder;
-
-	generic_response_start(data);
-
-	name = gtk_file_selection_get_filename(GTK_FILE_SELECTION(data->dialog));
-
-	/* If name is a directory then change directories */
-	if (data->type == PURPLE_REQUEST_FILE) {
-		if (pidgin_check_if_dir(name, GTK_FILE_SELECTION(data->dialog)))
-			return;
-	}
-
-	current_folder = g_path_get_dirname(name);
-
-	g_free(data->u.file.name);
-	if (data->type == PURPLE_REQUEST_FILE)
-		data->u.file.name = g_strdup(name);
-	else
-	{
-		if (g_file_test(name, G_FILE_TEST_IS_DIR))
-			data->u.file.name = g_strdup(name);
-		else
-			data->u.file.name = g_strdup(current_folder);
-	}
-
-	if (current_folder != NULL) {
-		if (data->u.file.savedialog) {
-			purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder", current_folder);
-		} else {
-			purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder", current_folder);
-		}
-		g_free(current_folder);
-	}
-
-#endif /* FILECHOOSER */
-#if 0 /* mismatched curly braces */
-	}
-#endif
 	if ((data->u.file.savedialog == TRUE) &&
 		(g_file_test(data->u.file.name, G_FILE_TEST_EXISTS))) {
 		purple_request_action(data, NULL, _("That file already exists"),
@@ -1585,20 +1557,6 @@
 		file_yes_no_cb(data, 1);
 }
 
-#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-static gboolean
-file_cancel_cb(PidginRequestData *data)
-{
-	generic_response_start(data);
-
-	if (data->cbs[0] != NULL)
-		((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL);
-
-	purple_request_close(data->type, data);
-	return FALSE;
-}
-#endif /* FILECHOOSER */
-
 static void *
 pidgin_request_file(const char *title, const char *filename,
 					  gboolean savedialog,
@@ -1609,9 +1567,7 @@
 	PidginRequestData *data;
 	GtkWidget *filesel;
 	const gchar *current_folder;
-#if GTK_CHECK_VERSION(2,4,0)
 	gboolean folder_set = FALSE;
-#endif
 
 	data = g_new0(PidginRequestData, 1);
 	data->type = PURPLE_REQUEST_FILE;
@@ -1622,7 +1578,6 @@
 	data->cbs[1] = ok_cb;
 	data->u.file.savedialog = savedialog;
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 	filesel = gtk_file_chooser_dialog_new(
 						title ? title : (savedialog ? _("Save File...")
 													: _("Open File...")),
@@ -1668,30 +1623,6 @@
 #endif
 	g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(filesel)), "response",
 					 G_CALLBACK(file_ok_check_if_exists_cb), data);
-#else /* FILECHOOSER */
-	filesel = gtk_file_selection_new(
-			title ? title : (savedialog ? _("Save File...")
-				: _("Open File...")));
-	if (savedialog) {
-		current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_save_folder");
-	} else {
-		current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_open_folder");
-	}
-	if (current_folder != NULL) {
-		gchar *path = g_strdup_printf("%s%s", current_folder, G_DIR_SEPARATOR_S);
-		gtk_file_selection_set_filename(GTK_FILE_SELECTION(filesel), path);
-		g_free(path);
-	}
-	if (filename != NULL)
-		gtk_file_selection_set_filename(GTK_FILE_SELECTION(filesel), filename);
-
-	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(filesel)), "delete_event",
-							 G_CALLBACK(file_cancel_cb), data);
-	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button),
-					 "clicked", G_CALLBACK(file_cancel_cb), data);
-	g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
-					 G_CALLBACK(file_ok_check_if_exists_cb), data);
-#endif /* FILECHOOSER */
 
 	pidgin_auto_parent_window(filesel);
 
@@ -1719,7 +1650,6 @@
 	data->cbs[1] = ok_cb;
 	data->u.file.savedialog = FALSE;
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 	dirsel = gtk_file_chooser_dialog_new(
 						title ? title : _("Select Folder..."),
 						NULL,
@@ -1734,16 +1664,6 @@
 
 	g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(dirsel)), "response",
 						G_CALLBACK(file_ok_check_if_exists_cb), data);
-#else
-	dirsel = gtk_file_selection_new(title ? title : _("Select Folder..."));
-
-	g_signal_connect_swapped(G_OBJECT(dirsel), "delete_event",
-							 G_CALLBACK(file_cancel_cb), data);
-	g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(dirsel)->cancel_button),
-					 "clicked", G_CALLBACK(file_cancel_cb), data);
-	g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(dirsel)->ok_button), "clicked",
-					 G_CALLBACK(file_ok_check_if_exists_cb), data);
-#endif
 
 	data->dialog = dirsel;
 	pidgin_auto_parent_window(dirsel);
--- a/pidgin/gtksavedstatuses.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtksavedstatuses.c	Mon Mar 08 22:53:02 2010 +0000
@@ -34,7 +34,6 @@
 #include "util.h"
 
 #include "gtkblist.h"
-#include "gtkexpander.h"
 #include "pidgin.h"
 #include "gtkimhtml.h"
 #include "gtkimhtmltoolbar.h"
@@ -86,17 +85,16 @@
 };
 
 /**
- * These are used in the GtkComboBox to select the specific
- * PurpleStatusType when setting a substatus for a particular saved
- * status.
+ * These are used in the GtkComboBox to select the specific PurpleStatusType
+ * when setting a (sub)status for a particular saved status.
  */
 enum
 {
-	SUBSTATUS_COLUMN_ICON,
+	STATUS_COLUMN_ICON,
 	/** A hidden column containing the ID of this PurpleStatusType. */
-	SUBSTATUS_COLUMN_STATUS_ID,
-	SUBSTATUS_COLUMN_STATUS_NAME,
-	SUBSTATUS_NUM_COLUMNS
+	STATUS_COLUMN_STATUS_ID,
+	STATUS_COLUMN_STATUS_NAME,
+	STATUS_NUM_COLUMNS
 };
 
 typedef struct
@@ -119,7 +117,7 @@
 
 	gchar *original_title;
 	GtkEntry *title;
-	GtkOptionMenu *type;
+	GtkComboBox *type;
 	GtkIMHtml *message;
 } StatusEditor;
 
@@ -180,23 +178,6 @@
 	return FALSE;
 }
 
-#if !GTK_CHECK_VERSION(2,2,0)
-static void
-count_selected_helper(GtkTreeModel *model, GtkTreePath *path,
-					GtkTreeIter *iter, gpointer user_data)
-{
-	(*(gint *)user_data)++;
-}
-
-static void
-list_selected_helper(GtkTreeModel *model, GtkTreePath *path,
-					GtkTreeIter *iter, gpointer user_data)
-{
-	GList **list = (GList **)user_data;
-	*list = g_list_append(*list, gtk_tree_path_copy(path));
-}
-#endif
-
 static void
 status_window_use_cb(GtkButton *button, StatusWindow *dialog)
 {
@@ -207,11 +188,7 @@
 
 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
 
-#if GTK_CHECK_VERSION(2,2,0)
 	num_selected = gtk_tree_selection_count_selected_rows(selection);
-#else
-	gtk_tree_selection_selected_foreach(selection, count_selected_helper, &num_selected);
-#endif
 	if (num_selected != 1)
 		/*
 		 * This shouldn't happen because the "Use" button should have
@@ -219,11 +196,7 @@
 		 */
 		return;
 
-#if GTK_CHECK_VERSION(2,2,0)
 	list = gtk_tree_selection_get_selected_rows(selection, NULL);
-#else
-	gtk_tree_selection_selected_foreach(selection, list_selected_helper, &list);
-#endif
 
 	if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model),
 								&iter, list->data))
@@ -311,11 +284,7 @@
 	gpointer handle;
 
 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
-#if GTK_CHECK_VERSION(2,2,0)
 	sel_paths = gtk_tree_selection_get_selected_rows(selection, NULL);
-#else
-	gtk_tree_selection_selected_foreach(selection, list_selected_helper, &sel_paths);
-#endif
 
 	/* This is ugly because we're not allowed to modify the model from within
 	 * gtk_tree_selection_selected_foreach() and the GtkTreePaths can become invalid
@@ -365,11 +334,7 @@
 	int num_selected;
 	GtkTreeModel *model = GTK_TREE_MODEL(dialog->model);
 
-#if GTK_CHECK_VERSION(2,2,0)
 	sel_paths = gtk_tree_selection_get_selected_rows(sel, NULL);
-#else
-	gtk_tree_selection_selected_foreach(sel, list_selected_helper, &sel_paths);
-#endif
 
 	for (tmp = sel_paths, num_selected = 0; tmp; tmp = tmp->next, num_selected++) {
 		GtkTreeIter iter;
@@ -521,9 +486,7 @@
 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(column, renderer, "text",
 									   STATUS_WINDOW_COLUMN_TITLE);
-#if GTK_CHECK_VERSION(2,6,0)
 	g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 
 	column = gtk_tree_view_column_new();
 	gtk_tree_view_column_set_title(column, _("Type"));
@@ -550,9 +513,7 @@
 	gtk_tree_view_column_pack_start(column, renderer, TRUE);
 	gtk_tree_view_column_add_attribute(column, renderer, "text",
 									   STATUS_WINDOW_COLUMN_MESSAGE);
-#if GTK_CHECK_VERSION(2,6,0)
 	g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 
 	/* Enable CTRL+F searching */
 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), STATUS_WINDOW_COLUMN_TITLE);
@@ -780,7 +741,7 @@
 		return;
 	}
 
-	type = gtk_option_menu_get_history(dialog->type) + (PURPLE_STATUS_UNSET + 1);
+	type = gtk_combo_box_get_active(dialog->type) + (PURPLE_STATUS_UNSET + 1);
 	message = gtk_imhtml_get_markup(dialog->message);
 	unformatted = purple_markup_strip_html(message);
 
@@ -875,53 +836,56 @@
 }
 
 static GtkWidget *
-create_stock_item(const gchar *str, const gchar *icon)
-{
-	GtkWidget *menuitem = gtk_menu_item_new();
-	GtkWidget *label = gtk_label_new_with_mnemonic(str);
-	GtkWidget *hbox = gtk_hbox_new(FALSE, 4);
-	GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
-	GtkWidget *image = gtk_image_new_from_stock(icon, icon_size);;
-
-	gtk_widget_show(label);
-	gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
-	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
-	gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
-	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
-
-	gtk_container_add(GTK_CONTAINER(menuitem), hbox);
-
-	return menuitem;
-}
-
-static GtkWidget *
 create_status_type_menu(PurpleStatusPrimitive type)
 {
 	int i;
 	GtkWidget *dropdown;
-	GtkWidget *menu;
-	GtkWidget *item;
+	GtkListStore *store;
+	GtkTreeIter iter;
+	GtkCellRenderer *renderer;
 
-	dropdown = gtk_option_menu_new();
-	menu = gtk_menu_new();
+	store = gtk_list_store_new(STATUS_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
 
 	for (i = PURPLE_STATUS_UNSET + 1; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
 	{
-		if (i == PURPLE_STATUS_MOBILE || i == PURPLE_STATUS_TUNE)
+		/* Someone should fix this for 3.0.0.  The independent boolean
+		 * should probably be set on the status type, not the status.
+		 * I guess that would prevent third party plugins from creating
+		 * independent statuses?
+		 */
+		if (i == PURPLE_STATUS_MOBILE ||
+		    i == PURPLE_STATUS_MOOD ||
+		    i == PURPLE_STATUS_TUNE)
 			/*
 			 * Special-case these.  They're intended to be independent
 			 * status types, so don't show them in the list.
 			 */
 			continue;
 
-		item = create_stock_item(purple_primitive_get_name_from_type(i),
-					get_stock_icon_from_primitive(i));
-		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+		gtk_list_store_append(store, &iter);
+		gtk_list_store_set(store, &iter,
+		                   STATUS_COLUMN_ICON, get_stock_icon_from_primitive(i),
+		                   STATUS_COLUMN_STATUS_ID, purple_primitive_get_id_from_type(i),
+		                   STATUS_COLUMN_STATUS_NAME, purple_primitive_get_name_from_type(i),
+		                   -1);
 	}
 
-	gtk_menu_set_active(GTK_MENU(menu), type - (PURPLE_STATUS_UNSET + 1));
-	gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu);
-	gtk_widget_show_all(menu);
+	dropdown = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
+
+	renderer = gtk_cell_renderer_pixbuf_new();
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown), renderer, FALSE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown), renderer,
+	                               "stock-id", STATUS_COLUMN_ICON,
+	                               NULL);
+
+	renderer = gtk_cell_renderer_text_new();
+	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown), renderer, TRUE);
+	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown), renderer,
+	                               "text", STATUS_COLUMN_STATUS_NAME,
+	                               NULL);
+
+	gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown),
+	                         type - (PURPLE_STATUS_UNSET + 1));
 
 	return dropdown;
 }
@@ -1191,7 +1155,7 @@
 		dropdown = create_status_type_menu(purple_savedstatus_get_type(saved_status));
 	else
 		dropdown = create_status_type_menu(PURPLE_STATUS_AWAY);
-	dialog->type = GTK_OPTION_MENU(dropdown);
+	dialog->type = GTK_COMBO_BOX(dropdown);
 	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Status:"), sg, dropdown, TRUE, NULL);
 
 	/* Status message */
@@ -1306,7 +1270,7 @@
 	if (!gtk_combo_box_get_active_iter(box, &iter))
 		return;
 	gtk_tree_model_get(GTK_TREE_MODEL(select->model), &iter,
-					   SUBSTATUS_COLUMN_STATUS_ID, &id,
+					   STATUS_COLUMN_STATUS_ID, &id,
 					   -1);
 	type = purple_account_get_status_type(select->account, id);
 	g_free(id);
@@ -1397,7 +1361,7 @@
 	}
 
 	gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter,
-					   SUBSTATUS_COLUMN_STATUS_ID, &id,
+					   STATUS_COLUMN_STATUS_ID, &id,
 					   -1);
 	type = purple_account_get_status_type(dialog->account, id);
 	if (purple_status_type_get_attr(type, "message") != NULL)
@@ -1487,7 +1451,7 @@
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
 	gtk_size_group_add_widget(sg, label);
 
-	dialog->model = gtk_list_store_new(SUBSTATUS_NUM_COLUMNS,
+	dialog->model = gtk_list_store_new(STATUS_NUM_COLUMNS,
 									   G_TYPE_STRING,
 									   G_TYPE_STRING,
 									   G_TYPE_STRING);
@@ -1500,12 +1464,12 @@
 			NULL);
 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, FALSE);
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), rend,
-						"stock-id", SUBSTATUS_COLUMN_ICON, NULL);
+						"stock-id", STATUS_COLUMN_ICON, NULL);
 
 	rend = GTK_CELL_RENDERER(gtk_cell_renderer_text_new());
 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, TRUE);
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), rend,
-						"text", SUBSTATUS_COLUMN_STATUS_NAME, NULL);
+						"text", STATUS_COLUMN_STATUS_NAME, NULL);
 
 	g_signal_connect(G_OBJECT(combo), "changed",
 					 G_CALLBACK(substatus_selection_changed_cb), dialog);
@@ -1581,9 +1545,9 @@
 
 		gtk_list_store_append(dialog->model, &iter);
 		gtk_list_store_set(dialog->model, &iter,
-						   SUBSTATUS_COLUMN_ICON, pidgin_stock_id_from_status_primitive(prim),
-						   SUBSTATUS_COLUMN_STATUS_ID, id,
-						   SUBSTATUS_COLUMN_STATUS_NAME, name,
+						   STATUS_COLUMN_ICON, pidgin_stock_id_from_status_primitive(prim),
+						   STATUS_COLUMN_STATUS_ID, id,
+						   STATUS_COLUMN_STATUS_NAME, name,
 						   -1);
 		if ((status_id != NULL) && !strcmp(status_id, id))
 		{
@@ -1862,9 +1826,7 @@
 	g_object_set(G_OBJECT(icon_rend),
 			"stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL),
 			NULL);
-#if GTK_CHECK_VERSION(2,6,0)
 	g_object_set(text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 
 	gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), index);
 	g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(status_menu_cb), callback);
--- a/pidgin/gtkscrollbook.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkscrollbook.c	Mon Mar 08 22:53:02 2010 +0000
@@ -80,11 +80,7 @@
 {
 	int index, count;
 	index = gtk_notebook_get_current_page(GTK_NOTEBOOK(scroll_book->notebook));
-#if GTK_CHECK_VERSION(2,2,0)
 	count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(scroll_book->notebook));
-#else
-	count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children);
-#endif
 
 	if (index + 1 < count)
 		gtk_notebook_set_current_page(GTK_NOTEBOOK(scroll_book->notebook), index + 1);
@@ -130,11 +126,7 @@
 {
 	int count;
 	int index = gtk_notebook_get_current_page(GTK_NOTEBOOK(scroll_book->notebook));
-#if GTK_CHECK_VERSION(2,2,0)
 	count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(scroll_book->notebook));
-#else
-	count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children);
-#endif
 	refresh_scroll_box(scroll_book, index, count);
 }
 
@@ -149,11 +141,7 @@
 switch_page_cb(GtkNotebook *notebook, GtkNotebookPage *page, guint page_num, PidginScrollBook *scroll_book)
 {
 	int count;
-#if GTK_CHECK_VERSION(2,2,0)
 	count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(scroll_book->notebook));
-#else
-	count = g_list_length(GTK_NOTEBOOK(scroll_book->notebook)->children);
-#endif
 	refresh_scroll_box(scroll_book, page_num, count);
 }
 
@@ -268,9 +256,7 @@
 	/* Close */
 	eb = gtk_event_box_new();
 	gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0);
-#if GTK_CHECK_VERSION(2,4,0)
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(eb), FALSE);
-#endif
 	gtk_widget_set_events(eb, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
 	close_button = gtk_label_new("×");
 	g_signal_connect(G_OBJECT(eb), "enter-notify-event", G_CALLBACK(close_button_entered_cb), close_button);
--- a/pidgin/gtksession.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtksession.c	Mon Mar 08 22:53:02 2010 +0000
@@ -163,10 +163,8 @@
 		ret[j++] = g_strdup(config_dir);
 	}
 
-#if GTK_CHECK_VERSION(2,2,0)
 	ret[j++] = g_strdup("--display");
 	ret[j++] = g_strdup((gchar *)gdk_display_get_name(gdk_display_get_default()));
-#endif
 
 	ret[j++] = NULL;
 
--- a/pidgin/gtksound.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtksound.c	Mon Mar 08 22:53:02 2010 +0000
@@ -72,7 +72,8 @@
 	{N_("Others talk in chat"), "chat_msg_recv", "receive.wav"},
 	/* this isn't a terminator, it's the buddy pounce default sound event ;-) */
 	{NULL, "pounce_default", "alert.wav"},
-	{N_("Someone says your username in chat"), "nick_said", "alert.wav"}
+	{N_("Someone says your username in chat"), "nick_said", "alert.wav"},
+	{N_("Attention received"), "got_attention", "alert.wav"}
 };
 
 static gboolean
@@ -144,7 +145,7 @@
 				   char *message, PurpleConversation *conv,
 				   PurpleMessageFlags flags, PurpleSoundEventID event)
 {
-	if (flags & PURPLE_MESSAGE_DELAYED)
+	if (flags & PURPLE_MESSAGE_DELAYED || flags & PURPLE_MESSAGE_NOTIFY)
 		return;
 
 	if (conv==NULL)
@@ -199,7 +200,7 @@
 {
 	PurpleConvChat *chat;
 
-	if (flags & PURPLE_MESSAGE_DELAYED)
+	if (flags & PURPLE_MESSAGE_DELAYED || flags & PURPLE_MESSAGE_NOTIFY)
 		return;
 
 	chat = purple_conversation_get_chat_data(conv);
@@ -219,6 +220,13 @@
 		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
@@ -299,6 +307,10 @@
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/pounce_default", TRUE);
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/pounce_default", "");
 	purple_prefs_add_string(PIDGIN_PREFS_ROOT "/sound/theme", "");
+	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/sent_attention", TRUE);
+	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/sent_attention", "");
+	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/enabled/got_attention", TRUE);
+	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/file/got_attention", "");
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/conv_focus", TRUE);
 	purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/sound/mute", FALSE);
 	purple_prefs_add_path(PIDGIN_PREFS_ROOT "/sound/command", "");
@@ -345,6 +357,12 @@
 	purple_signal_connect(conv_handle, "received-chat-msg",
 						gtk_sound_handle, PURPLE_CALLBACK(chat_msg_received_cb),
 						GINT_TO_POINTER(PURPLE_SOUND_CHAT_SAY));
+	purple_signal_connect(conv_handle, "got-attention", gtk_sound_handle,
+						PURPLE_CALLBACK(got_attention_cb),
+						  GINT_TO_POINTER(PURPLE_SOUND_GOT_ATTENTION));
+	/* for the time being, don't handle sent-attention here, since playing a
+	 sound would result induplicate sounds. And fixing that would require changing the
+	 conversation signal for msg-recv */	
 }
 
 static void
@@ -539,18 +557,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 */
 
--- a/pidgin/gtkstatusbox.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkstatusbox.c	Mon Mar 08 22:53:02 2010 +0000
@@ -1180,7 +1180,6 @@
 	return FALSE;
 }
 
-#if GTK_CHECK_VERSION(2,6,0)
 static gboolean
 dropdown_store_row_separator_func(GtkTreeModel *model,
 								  GtkTreeIter *iter, gpointer data)
@@ -1194,7 +1193,6 @@
 
 	return FALSE;
 }
-#endif
 
 static void
 cache_pixbufs(PidginStatusBox *status_box)
@@ -1305,11 +1303,9 @@
 static void
 pidgin_status_box_list_position (PidginStatusBox *status_box, int *x, int *y, int *width, int *height)
 {
-#if GTK_CHECK_VERSION(2,2,0)
   GdkScreen *screen;
   gint monitor_num;
   GdkRectangle monitor;
-#endif
   GtkRequisition popup_req;
   GtkPolicyType hpolicy, vpolicy;
 
@@ -1335,7 +1331,6 @@
 
   *height = popup_req.height;
 
-#if GTK_CHECK_VERSION(2,2,0)
   screen = gtk_widget_get_screen (GTK_WIDGET (status_box));
   monitor_num = gdk_screen_get_monitor_at_window (screen,
 						  GTK_WIDGET (status_box)->window);
@@ -1368,7 +1363,6 @@
       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (status_box->scrolled_window),
 				      hpolicy, vpolicy);
     }
-#endif
 }
 
 static gboolean
@@ -1376,29 +1370,20 @@
 		      guint32    activate_time,
 		      gboolean   grab_keyboard)
 {
-  if ((gdk_pointer_grab (window, TRUE,
+	if ((gdk_pointer_grab (window, TRUE,
 			 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
 			 GDK_POINTER_MOTION_MASK,
 			 NULL, NULL, activate_time) == 0))
-    {
-      if (!grab_keyboard ||
-	  gdk_keyboard_grab (window, TRUE,
-			     activate_time) == 0)
-	return TRUE;
-      else
 	{
-#if GTK_CHECK_VERSION(2,2,0)
-	  gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
-				      activate_time);
-#else
-	  gdk_pointer_ungrab(activate_time);
-	  gdk_keyboard_ungrab(activate_time);
-#endif
-	  return FALSE;
+		if (!grab_keyboard || gdk_keyboard_grab (window, TRUE, activate_time) == 0)
+			return TRUE;
+		else {
+			gdk_display_pointer_ungrab (gdk_drawable_get_display (window), activate_time);
+			return FALSE;
+		}
 	}
-    }
-
-  return FALSE;
+
+	return FALSE;
 }
 
 
@@ -1793,9 +1778,7 @@
 	gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->vsep, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->arrow, FALSE, FALSE, 0);
 	gtk_widget_show_all(status_box->toggle_button);
-#if GTK_CHECK_VERSION(2,4,0)
 	gtk_button_set_focus_on_click(GTK_BUTTON(status_box->toggle_button), FALSE);
-#endif
 
 	text_rend = gtk_cell_renderer_text_new();
 	icon_rend = gtk_cell_renderer_pixbuf_new();
@@ -1809,14 +1792,10 @@
 	}
 
 	gtk_window_set_resizable (GTK_WINDOW (status_box->popup_window), FALSE);
-#if GTK_CHECK_VERSION(2,10,0)
 	gtk_window_set_type_hint (GTK_WINDOW (status_box->popup_window),
 			GDK_WINDOW_TYPE_HINT_POPUP_MENU);
-#endif
-#if GTK_CHECK_VERSION(2,2,0)
 	gtk_window_set_screen (GTK_WINDOW (status_box->popup_window),
 			gtk_widget_get_screen (GTK_WIDGET (status_box)));
-#endif
 	status_box->popup_frame = gtk_frame_new (NULL);
 	gtk_frame_set_shadow_type (GTK_FRAME (status_box->popup_frame),
 			GTK_SHADOW_ETCHED_IN);
@@ -1843,10 +1822,8 @@
 	gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (status_box->tree_view),
 			FALSE);
-#if GTK_CHECK_VERSION(2,6,0)
 	gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (status_box->tree_view),
 			TRUE);
-#endif
 	gtk_tree_view_set_model (GTK_TREE_VIEW (status_box->tree_view),
 			GTK_TREE_MODEL(status_box->dropdown_store));
 	status_box->column = gtk_tree_view_column_new ();
@@ -1864,9 +1841,7 @@
 	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(status_box->tree_view),
 				pidgin_tree_view_search_equal_func, NULL, NULL);
 
-#if GTK_CHECK_VERSION(2, 6, 0)
 	g_object_set(text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 
 	status_box->icon_rend = gtk_cell_renderer_pixbuf_new();
 	status_box->text_rend = gtk_cell_renderer_text_new();
@@ -1877,9 +1852,7 @@
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), status_box->icon_rend, "stock-id", ICON_STOCK_COLUMN, NULL);
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), status_box->text_rend, "markup", TEXT_COLUMN, NULL);
 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), emblem_rend, "pixbuf", EMBLEM_COLUMN, "visible", EMBLEM_VISIBLE_COLUMN, NULL);
-#if GTK_CHECK_VERSION(2, 6, 0)
 	g_object_set(status_box->text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-#endif
 
 	status_box->vbox = gtk_vbox_new(0, FALSE);
 	status_box->sw = pidgin_create_imhtml(FALSE, &status_box->imhtml, NULL, NULL);
@@ -1924,9 +1897,7 @@
 	g_signal_connect(G_OBJECT(status_box->tree_view), "cursor-changed",
 					 G_CALLBACK(treeview_cursor_changed_cb), status_box->dropdown_store);
 
-#if GTK_CHECK_VERSION(2,6,0)
 	gtk_tree_view_set_row_separator_func(GTK_TREE_VIEW(status_box->tree_view), dropdown_store_row_separator_func, NULL, NULL);
-#endif
 
 	status_box->token_status_account = check_active_accounts_for_identical_statuses();
 
@@ -2210,14 +2181,12 @@
 {
 	/* Don't do anything unless GTK actually supports
 	 * gtk_combo_box_set_row_separator_func */
-#if GTK_CHECK_VERSION(2,6,0)
 	GtkTreeIter iter;
 
 	gtk_list_store_append(status_box->dropdown_store, &iter);
 	gtk_list_store_set(status_box->dropdown_store, &iter,
 			   TYPE_COLUMN, PIDGIN_STATUS_BOX_TYPE_SEPARATOR,
 			   -1);
-#endif
 }
 
 void
@@ -2241,7 +2210,6 @@
 static void
 pixbuf_size_prepared_cb(GdkPixbufLoader *loader, int width, int height, gpointer data)
 {
-#if GTK_CHECK_VERSION(2,2,0)
 	int w, h;
 	GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MEDIUM);
 	gtk_icon_size_lookup(icon_size, &w, &h);
@@ -2250,7 +2218,6 @@
 	else if (width > height)
 		h = height * w / width;
 	gdk_pixbuf_loader_set_size(loader, w, h);
-#endif
 }
 
 static void
--- a/pidgin/gtkutils.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkutils.c	Mon Mar 08 22:53:02 2010 +0000
@@ -406,13 +406,13 @@
 	gtk_container_add(GTK_CONTAINER(button), bbox);
 
 	if (icon) {
-		gtk_box_pack_start_defaults(GTK_BOX(bbox), ibox);
+		gtk_box_pack_start(GTK_BOX(bbox), ibox, TRUE, TRUE, 0);
 		image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_BUTTON);
 		gtk_box_pack_end(GTK_BOX(ibox), image, FALSE, TRUE, 0);
 	}
 
 	if (text) {
-		gtk_box_pack_start_defaults(GTK_BOX(bbox), lbox);
+		gtk_box_pack_start(GTK_BOX(bbox), lbox, TRUE, TRUE, 0);
 		label = gtk_label_new(NULL);
 		gtk_label_set_text_with_mnemonic(GTK_LABEL(label), text);
 		gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
@@ -1247,7 +1247,6 @@
 							gboolean *push_in,
 							gpointer data)
 {
-#if GTK_CHECK_VERSION(2,2,0)
 	GtkWidget *widget;
 	GtkRequisition requisition;
 	GdkScreen *screen;
@@ -1388,7 +1387,6 @@
 	{
 		*y = monitor.y;
 	}
-#endif
 }
 
 
@@ -1630,7 +1628,6 @@
 			char key[64];
 			const char *itemname = NULL;
 
-#if GTK_CHECK_VERSION(2,6,0)
 			const char * const *langs;
 			int i;
 			langs = g_get_language_names();
@@ -1639,15 +1636,7 @@
 				itemname = purple_desktop_item_get_string(item, key);
 				break;
 			}
-#else
-			const char *lang = g_getenv("LANG");
-			char *dot;
-			dot = strchr(lang, '.');
-			if (dot)
-				*dot = '\0';
-			g_snprintf(key, sizeof(key), "Name[%s]", lang);
-			itemname = purple_desktop_item_get_string(item, key);
-#endif
+
 			if (!itemname)
 				itemname = purple_desktop_item_get_string(item, "Name");
 
@@ -1664,10 +1653,13 @@
 						       purple_desktop_item_get_string(item, "URL"), itemname);
 				break;
 			default:
-				/* I don't know if we really want to do anything here.  Most of the desktop item types are crap like
-				 * "MIME Type" (I have no clue how that would be a desktop item) and "Comment"... nothing we can really
-				 * send.  The only logical one is "Application," but do we really want to send a binary and nothing else?
-				 * Probably not.  I'll just give an error and return. */
+				/* I don't know if we really want to do anything here.  Most of
+				 * the desktop item types are crap like "MIME Type" (I have no
+				 * clue how that would be a desktop item) and "Comment"...
+				 * nothing we can really send.  The only logical one is
+				 * "Application," but do we really want to send a binary and
+				 * nothing else? Probably not.  I'll just give an error and
+				 * return. */
 				/* The original patch sent the icon used by the launcher.  That's probably wrong */
 				purple_notify_error(NULL, NULL, _("Cannot send launcher"),
 				                    _("You dragged a desktop launcher. Most "
@@ -1862,10 +1854,6 @@
 	return menuitem;
 }
 
-#if GTK_CHECK_VERSION(2,3,0)
-# define NEW_STYLE_COMPLETION
-#endif
-
 typedef struct
 {
 	GtkWidget *entry;
@@ -1874,97 +1862,9 @@
 	PidginFilterBuddyCompletionEntryFunc filter_func;
 	gpointer filter_func_user_data;
 
-#ifdef NEW_STYLE_COMPLETION
 	GtkListStore *store;
-#else
-	GCompletion *completion;
-	gboolean completion_started;
-	GList *log_items;
-#endif /* NEW_STYLE_COMPLETION */
 } PidginCompletionData;
 
-#ifndef NEW_STYLE_COMPLETION
-static gboolean
-completion_entry_event(GtkEditable *entry, GdkEventKey *event,
-					   PidginCompletionData *data)
-{
-	int pos, end_pos;
-
-	if (event->type == GDK_KEY_PRESS && event->keyval == GDK_Tab)
-	{
-		gtk_editable_get_selection_bounds(entry, &pos, &end_pos);
-
-		if (data->completion_started &&
-			pos != end_pos && pos > 1 &&
-			end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry))))
-		{
-			gtk_editable_select_region(entry, 0, 0);
-			gtk_editable_set_position(entry, -1);
-
-			return TRUE;
-		}
-	}
-	else if (event->type == GDK_KEY_PRESS && event->length > 0)
-	{
-		char *prefix, *nprefix;
-
-		gtk_editable_get_selection_bounds(entry, &pos, &end_pos);
-
-		if (data->completion_started &&
-			pos != end_pos && pos > 1 &&
-			end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry))))
-		{
-			char *temp;
-
-			temp = gtk_editable_get_chars(entry, 0, pos);
-			prefix = g_strconcat(temp, event->string, NULL);
-			g_free(temp);
-		}
-		else if (pos == end_pos && pos > 1 &&
-				 end_pos == strlen(gtk_entry_get_text(GTK_ENTRY(entry))))
-		{
-			prefix = g_strconcat(gtk_entry_get_text(GTK_ENTRY(entry)),
-								 event->string, NULL);
-		}
-		else
-			return FALSE;
-
-		pos = strlen(prefix);
-		nprefix = NULL;
-
-		g_completion_complete(data->completion, prefix, &nprefix);
-
-		if (nprefix != NULL)
-		{
-			gtk_entry_set_text(GTK_ENTRY(entry), nprefix);
-			gtk_editable_set_position(entry, pos);
-			gtk_editable_select_region(entry, pos, -1);
-
-			data->completion_started = TRUE;
-
-			g_free(nprefix);
-			g_free(prefix);
-
-			return TRUE;
-		}
-
-		g_free(prefix);
-	}
-
-	return FALSE;
-}
-
-static void
-destroy_completion_data(GtkWidget *w, PidginCompletionData *data)
-{
-	g_list_foreach(data->completion->items, (GFunc)g_free, NULL);
-	g_completion_free(data->completion);
-
-	g_free(data);
-}
-#endif /* !NEW_STYLE_COMPLETION */
-
-#ifdef NEW_STYLE_COMPLETION
 static gboolean buddyname_completion_match_func(GtkEntryCompletion *completion,
 		const gchar *key, GtkTreeIter *iter, gpointer user_data)
 {
@@ -2098,7 +1998,6 @@
 
 	g_free(normalized_buddyname);
 }
-#endif /* NEW_STYLE_COMPLETION */
 
 static void get_log_set_name(PurpleLogSet *set, gpointer value, PidginCompletionData *data)
 {
@@ -2113,14 +2012,8 @@
 		entry.entry.logged_buddy = set;
 
 		if (filter_func(&entry, user_data)) {
-#ifdef NEW_STYLE_COMPLETION
 			add_buddyname_autocomplete_entry(data->store,
 												NULL, NULL, set->account, set->name);
-#else
-			/* Steal the name for the GCompletion. */
-			data->log_items = g_list_append(data->log_items, set->name);
-			set->name = set->normalized_name = NULL;
-#endif /* NEW_STYLE_COMPLETION */
 		}
 	}
 }
@@ -2133,14 +2026,7 @@
 	gpointer user_data = data->filter_func_user_data;
 	GHashTable *sets;
 
-#ifdef NEW_STYLE_COMPLETION
 	gtk_list_store_clear(data->store);
-#else
-	GList *item = g_list_append(NULL, NULL);
-
-	g_list_foreach(data->completion->items, (GFunc)g_free, NULL);
-	g_completion_clear_items(data->completion);
-#endif /* NEW_STYLE_COMPLETION */
 
 	for (gnode = purple_get_blist()->root; gnode != NULL; gnode = gnode->next)
 	{
@@ -2159,35 +2045,21 @@
 				entry.entry.buddy = (PurpleBuddy *) bnode;
 
 				if (filter_func(&entry, user_data)) {
-#ifdef NEW_STYLE_COMPLETION
 					add_buddyname_autocomplete_entry(data->store,
 														((PurpleContact *)cnode)->alias,
 														purple_buddy_get_contact_alias(entry.entry.buddy),
 														entry.entry.buddy->account,
 														entry.entry.buddy->name
 													 );
-#else
-					item->data = g_strdup(entry.entry.buddy->name);
-					g_completion_add_items(data->completion, item);
-#endif /* NEW_STYLE_COMPLETION */
 				}
 			}
 		}
 	}
 
-#ifndef NEW_STYLE_COMPLETION
-	g_list_free(item);
-	data->log_items = NULL;
-#endif /* NEW_STYLE_COMPLETION */
-
 	sets = purple_log_get_log_sets();
 	g_hash_table_foreach(sets, (GHFunc)get_log_set_name, data);
 	g_hash_table_destroy(sets);
 
-#ifndef NEW_STYLE_COMPLETION
-	g_completion_add_items(data->completion, data->log_items);
-	g_list_free(data->log_items);
-#endif /* NEW_STYLE_COMPLETION */
 }
 
 static void
@@ -2208,7 +2080,6 @@
 {
 	PidginCompletionData *data;
 
-#ifdef NEW_STYLE_COMPLETION
 	/*
 	 * Store the displayed completion value, the buddy name, the UTF-8
 	 * normalized & casefolded buddy name, the UTF-8 normalized &
@@ -2252,33 +2123,6 @@
 
 	gtk_entry_completion_set_text_column(completion, 0);
 
-#else /* !NEW_STYLE_COMPLETION */
-
-	data = g_new0(PidginCompletionData, 1);
-
-	data->entry = entry;
-	data->accountopt = accountopt;
-	if (filter_func == NULL) {
-		data->filter_func = pidgin_screenname_autocomplete_default_filter;
-		data->filter_func_user_data = NULL;
-	} else {
-		data->filter_func = filter_func;
-		data->filter_func_user_data = user_data;
-	}
-	data->completion = g_completion_new(NULL);
-	data->completion_started = FALSE;
-
-	add_completion_list(data);
-
-	g_completion_set_compare(data->completion, g_ascii_strncasecmp);
-
-	g_signal_connect(G_OBJECT(entry), "event",
-					 G_CALLBACK(completion_entry_event), data);
-	g_signal_connect(G_OBJECT(entry), "destroy",
-					 G_CALLBACK(destroy_completion_data), data);
-
-#endif /* !NEW_STYLE_COMPLETION */
-
 	purple_signal_connect(purple_connections_get_handle(), "signed-on", entry,
 						PURPLE_CALLBACK(repopulate_autocomplete), data);
 	purple_signal_connect(purple_connections_get_handle(), "signed-off", entry,
@@ -2322,11 +2166,7 @@
 	gdk_window_set_cursor(widget->window, cursor);
 	gdk_cursor_unref(cursor);
 
-#if GTK_CHECK_VERSION(2,4,0)
 	gdk_display_flush(gdk_drawable_get_display(GDK_DRAWABLE(widget->window)));
-#else
-	gdk_flush();
-#endif
 }
 
 void pidgin_clear_cursor(GtkWidget *widget)
@@ -2347,23 +2187,6 @@
 	gpointer data;
 };
 
-#if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
-static void
-icon_filesel_delete_cb(GtkWidget *w, struct _icon_chooser *dialog)
-{
-	if (dialog->icon_filesel != NULL)
-		gtk_widget_destroy(dialog->icon_filesel);
-
-	if (dialog->callback)
-		dialog->callback(NULL, dialog->data);
-
-	g_free(dialog);
-}
-#endif /* FILECHOOSER */
-
-
-
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 static void
 icon_filesel_choose_cb(GtkWidget *widget, gint response, struct _icon_chooser *dialog)
 {
@@ -2387,33 +2210,7 @@
 		g_free(current_folder);
 	}
 
-#else /* FILECHOOSER */
-static void
-icon_filesel_choose_cb(GtkWidget *w, struct _icon_chooser *dialog)
-{
-	char *filename, *current_folder;
-
-	filename = g_strdup(gtk_file_selection_get_filename(
-				GTK_FILE_SELECTION(dialog->icon_filesel)));
-
-	/* If they typed in a directory, change there */
-	if (pidgin_check_if_dir(filename,
-				GTK_FILE_SELECTION(dialog->icon_filesel)))
-	{
-		g_free(filename);
-		return;
-	}
-
-	current_folder = g_path_get_dirname(filename);
-	if (current_folder != NULL) {
-		purple_prefs_set_path(PIDGIN_PREFS_ROOT "/filelocations/last_icon_folder", current_folder);
-		g_free(current_folder);
-	}
-
-#endif /* FILECHOOSER */
-#if 0 /* mismatched curly braces */
-	}
-#endif
+
 	if (dialog->callback)
 		dialog->callback(filename, dialog->data);
 	gtk_widget_destroy(dialog->icon_filesel);
@@ -2423,11 +2220,7 @@
 
 
 static void
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 icon_preview_change_cb(GtkFileChooser *widget, struct _icon_chooser *dialog)
-#else /* FILECHOOSER */
-icon_preview_change_cb(GtkTreeSelection *sel, struct _icon_chooser *dialog)
-#endif /* FILECHOOSER */
 {
 	GdkPixbuf *pixbuf, *scale;
 	int height, width;
@@ -2435,13 +2228,8 @@
 	struct stat st;
 	char *filename;
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 	filename = gtk_file_chooser_get_preview_filename(
 					GTK_FILE_CHOOSER(dialog->icon_filesel));
-#else /* FILECHOOSER */
-	filename = g_strdup(gtk_file_selection_get_filename(
-		GTK_FILE_SELECTION(dialog->icon_filesel)));
-#endif /* FILECHOOSER */
 
 	if (!filename || g_stat(filename, &st) || !(pixbuf = gdk_pixbuf_new_from_file(filename, NULL)))
 	{
@@ -2477,20 +2265,13 @@
 GtkWidget *pidgin_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char *, gpointer), gpointer data) {
 	struct _icon_chooser *dialog = g_new0(struct _icon_chooser, 1);
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 	GtkWidget *vbox;
-#else
-	GtkWidget *hbox;
-	GtkWidget *tv;
-	GtkTreeSelection *sel;
-#endif /* FILECHOOSER */
 	const char *current_folder;
 
 	dialog->callback = callback;
 	dialog->data = data;
 
 	current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_icon_folder");
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 
 	dialog->icon_filesel = gtk_file_chooser_dialog_new(_("Buddy Icon"),
 							   parent,
@@ -2521,39 +2302,6 @@
 	g_signal_connect(G_OBJECT(dialog->icon_filesel), "response",
 					 G_CALLBACK(icon_filesel_choose_cb), dialog);
 	icon_preview_change_cb(NULL, dialog);
-#else /* FILECHOOSER */
-	dialog->icon_filesel = gtk_file_selection_new(_("Buddy Icon"));
-	dialog->icon_preview = gtk_image_new();
-	dialog->icon_text = gtk_label_new(NULL);
-	if ((current_folder != NULL) && (*current_folder != '\0'))
-		gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog->icon_filesel),
-										current_folder);
-
-	gtk_widget_set_size_request(GTK_WIDGET(dialog->icon_preview),-1, 50);
-	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_box_pack_start(
-		GTK_BOX(GTK_FILE_SELECTION(dialog->icon_filesel)->main_vbox),
-		hbox, FALSE, FALSE, 0);
-	gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_preview,
-			 FALSE, FALSE, 0);
-	gtk_box_pack_end(GTK_BOX(hbox), dialog->icon_text, FALSE, FALSE, 0);
-
-	tv = GTK_FILE_SELECTION(dialog->icon_filesel)->file_list;
-	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv));
-
-	g_signal_connect(G_OBJECT(sel), "changed",
-					 G_CALLBACK(icon_preview_change_cb), dialog);
-	g_signal_connect(
-		G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->ok_button),
-		"clicked",
-		G_CALLBACK(icon_filesel_choose_cb), dialog);
-	g_signal_connect(
-		G_OBJECT(GTK_FILE_SELECTION(dialog->icon_filesel)->cancel_button),
-		"clicked",
-		G_CALLBACK(icon_filesel_delete_cb), dialog);
-	g_signal_connect(G_OBJECT(dialog->icon_filesel), "destroy",
-					 G_CALLBACK(icon_filesel_delete_cb), dialog);
-#endif /* FILECHOOSER */
 
 #ifdef _WIN32
 	g_signal_connect(G_OBJECT(dialog->icon_filesel), "show",
@@ -2564,7 +2312,6 @@
 }
 
 
-#if GTK_CHECK_VERSION(2,2,0)
 static gboolean
 str_array_match(char **a, char **b)
 {
@@ -2578,22 +2325,16 @@
 				return TRUE;
 	return FALSE;
 }
-#endif
 
 gpointer
 pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len)
 {
 	PurplePluginProtocolInfo *prpl_info;
-#if GTK_CHECK_VERSION(2,2,0)
 	char **prpl_formats;
 	int width, height;
 	char **pixbuf_formats = NULL;
 	GdkPixbufFormat *format;
 	GdkPixbuf *pixbuf;
-#if !GTK_CHECK_VERSION(2,4,0)
-	GdkPixbufLoader *loader;
-#endif
-#endif
 	gchar *contents;
 	gsize length;
 
@@ -2602,22 +2343,8 @@
 	g_return_val_if_fail(prpl_info->icon_spec.format != NULL, NULL);
 
 
-#if GTK_CHECK_VERSION(2,2,0)
-#if GTK_CHECK_VERSION(2,4,0)
 	format = gdk_pixbuf_get_file_info(path, &width, &height);
-#else
-	loader = gdk_pixbuf_loader_new();
-	if (g_file_get_contents(path, &contents, &length, NULL)) {
-		gdk_pixbuf_loader_write(loader, contents, length, NULL);
-		g_free(contents);
-	}
-	gdk_pixbuf_loader_close(loader, NULL);
-	pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
-	width = gdk_pixbuf_get_width(pixbuf);
-	height = gdk_pixbuf_get_height(pixbuf);
-	format = gdk_pixbuf_loader_get_format(loader);
-	g_object_unref(G_OBJECT(loader));
-#endif
+
 	if (format == NULL)
 		return NULL;
 
@@ -2629,25 +2356,18 @@
 		   prpl_info->icon_spec.max_width >= width &&
 		   prpl_info->icon_spec.min_height <= height &&
 		   prpl_info->icon_spec.max_height >= height)))                   /* The icon is the correct size */
-#endif
 	{
-#if GTK_CHECK_VERSION(2,2,0)
 		g_strfreev(prpl_formats);
 		g_strfreev(pixbuf_formats);
-#endif
+
 		/* We don't need to scale the image. */
-
 		contents = NULL;
 		if (!g_file_get_contents(path, &contents, &length, NULL))
 		{
 			g_free(contents);
-#if GTK_CHECK_VERSION(2,2,0) && !GTK_CHECK_VERSION(2,4,0)
-		g_object_unref(G_OBJECT(pixbuf));
-#endif
 			return NULL;
 		}
 	}
-#if GTK_CHECK_VERSION(2,2,0)
 	else
 	{
 		int i;
@@ -2769,156 +2489,8 @@
 	if (len)
 		*len = length;
 	return contents;
-#else
-	/*
-	 * The chosen icon wasn't the right size, and we're using
-	 * GTK+ 2.0 so we can't scale it.
-	 */
-	return NULL;
-#endif
 }
 
-#if !GTK_CHECK_VERSION(2,6,0)
-static void
-_gdk_file_scale_size_prepared_cb (GdkPixbufLoader *loader,
-		  int              width,
-		  int              height,
-		  gpointer         data)
-{
-	struct {
-		gint width;
-		gint height;
-		gboolean preserve_aspect_ratio;
-	} *info = data;
-
-	g_return_if_fail (width > 0 && height > 0);
-
-	if (info->preserve_aspect_ratio &&
-		(info->width > 0 || info->height > 0)) {
-		if (info->width < 0)
-		{
-			width = width * (double)info->height/(double)height;
-			height = info->height;
-		}
-		else if (info->height < 0)
-		{
-			height = height * (double)info->width/(double)width;
-			width = info->width;
-		}
-		else if ((double)height * (double)info->width >
-				 (double)width * (double)info->height) {
-			width = 0.5 + (double)width * (double)info->height / (double)height;
-			height = info->height;
-		} else {
-			height = 0.5 + (double)height * (double)info->width / (double)width;
-			width = info->width;
-		}
-	} else {
-			if (info->width > 0)
-				width = info->width;
-			if (info->height > 0)
-				height = info->height;
-	}
-
-#if GTK_CHECK_VERSION(2,2,0) /* 2.0 users are going to have very strangely sized things */
-	gdk_pixbuf_loader_set_size (loader, width, height);
-#else
-#warning  nosnilmot could not be bothered to fix this properly for you
-#warning  ... good luck ... your images may end up strange sizes
-#endif
-}
-
-GdkPixbuf *
-gdk_pixbuf_new_from_file_at_scale(const char *filename, int width, int height,
-				  				  gboolean preserve_aspect_ratio,
-								  GError **error)
-{
-	GdkPixbufLoader *loader;
-	GdkPixbuf       *pixbuf;
-	guchar buffer [4096];
-	int length;
-	FILE *f;
-	struct {
-		gint width;
-		gint height;
-		gboolean preserve_aspect_ratio;
-	} info;
-	GdkPixbufAnimation *animation;
-	GdkPixbufAnimationIter *iter;
-	gboolean has_frame;
-
-	g_return_val_if_fail (filename != NULL, NULL);
-	g_return_val_if_fail (width > 0 || width == -1, NULL);
-	g_return_val_if_fail (height > 0 || height == -1, NULL);
-
-	f = g_fopen (filename, "rb");
-	if (!f) {
-		gint save_errno = errno;
-		gchar *display_name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
-		g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (save_errno),
-					 _("Failed to open file '%s': %s"),
-					 display_name ? display_name : "(unknown)",
-					 g_strerror (save_errno));
-		g_free (display_name);
-		return NULL;
-	}
-
-	loader = gdk_pixbuf_loader_new ();
-
-	info.width = width;
-	info.height = height;
-	info.preserve_aspect_ratio = preserve_aspect_ratio;
-
-	g_signal_connect (loader, "size-prepared", G_CALLBACK (_gdk_file_scale_size_prepared_cb), &info);
-
-	has_frame = FALSE;
-	while (!has_frame && !feof (f) && !ferror (f)) {
-		length = fread (buffer, 1, sizeof (buffer), f);
-		if (length > 0)
-			if (!gdk_pixbuf_loader_write (loader, buffer, length, error)) {
-				gdk_pixbuf_loader_close (loader, NULL);
-				fclose (f);
-				g_object_unref (loader);
-				return NULL;
-			}
-
-		animation = gdk_pixbuf_loader_get_animation (loader);
-		if (animation) {
-			iter = gdk_pixbuf_animation_get_iter (animation, 0);
-			if (!gdk_pixbuf_animation_iter_on_currently_loading_frame (iter)) {
-				has_frame = TRUE;
-			}
-			g_object_unref (iter);
-		}
-	}
-
-	fclose (f);
-
-	if (!gdk_pixbuf_loader_close (loader, error) && !has_frame) {
-		g_object_unref (loader);
-		return NULL;
-	}
-
-	pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
-
-	if (!pixbuf) {
-		gchar *display_name = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
-		g_object_unref (loader);
-		g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
-					 _("Failed to load image '%s': reason not known, probably a corrupt image file"),
-					 display_name ? display_name : "(unknown)");
-		g_free (display_name);
-		return NULL;
-	}
-
-	g_object_ref (pixbuf);
-
-	g_object_unref (loader);
-
-	return pixbuf;
-}
-#endif /* ! Gtk 2.6.0 */
-
 void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename)
 {
 	PurpleBuddy *buddy;
@@ -2951,34 +2523,10 @@
 
 void pidgin_set_urgent(GtkWindow *window, gboolean urgent)
 {
-#if GTK_CHECK_VERSION(2,8,0)
-	gtk_window_set_urgency_hint(window, urgent);
-#elif defined _WIN32
+#if defined _WIN32
 	winpidgin_window_flash(window, urgent);
-#elif defined GDK_WINDOWING_X11
-	GdkWindow *gdkwin;
-	XWMHints *hints;
-
-	g_return_if_fail(window != NULL);
-
-	gdkwin = GTK_WIDGET(window)->window;
-
-	g_return_if_fail(gdkwin != NULL);
-
-	hints = XGetWMHints(GDK_WINDOW_XDISPLAY(gdkwin),
-	                    GDK_WINDOW_XWINDOW(gdkwin));
-	if(!hints)
-		hints = XAllocWMHints();
-
-	if (urgent)
-		hints->flags |= XUrgencyHint;
-	else
-		hints->flags &= ~XUrgencyHint;
-	XSetWMHints(GDK_WINDOW_XDISPLAY(gdkwin),
-	            GDK_WINDOW_XWINDOW(gdkwin), hints);
-	XFree(hints);
 #else
-	/* do something else? */
+	gtk_window_set_urgency_hint(window, urgent);
 #endif
 }
 
@@ -3287,31 +2835,6 @@
 	return dim_grey_string;
 }
 
-#if !GTK_CHECK_VERSION(2,2,0)
-GtkTreePath *
-gtk_tree_path_new_from_indices (gint first_index, ...)
-{
-	int arg;
-	va_list args;
-	GtkTreePath *path;
-
-	path = gtk_tree_path_new ();
-
-	va_start (args, first_index);
-	arg = first_index;
-
-	while (arg != -1)
-	{
-		gtk_tree_path_append_index (path, arg);
-		arg = va_arg (args, gint);
-	}
-
-	va_end (args);
-
-	return path;
-}
-#endif
-
 static void
 combo_box_changed_cb(GtkComboBox *combo_box, GtkEntry *entry)
 {
@@ -3457,7 +2980,6 @@
 	return FALSE;
 #endif
 #else
-#if GTK_CHECK_VERSION(2,4,0)
 	/* This finds the currently active window and makes that the parent window. */
 	GList *windows = NULL;
 	GtkWidget *parent = NULL;
@@ -3500,7 +3022,6 @@
 		gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(parent));
 		return TRUE;
 	}
-#endif
 	return FALSE;
 #endif
 }
@@ -3584,23 +3105,13 @@
 #ifdef _WIN32
 	/* If using Win32... */
 	int code;
-	if (G_WIN32_HAVE_WIDECHAR_API()) {
-		wchar_t *wc_filename = g_utf8_to_utf16(
-				uri, -1, NULL, NULL, NULL);
-
-		code = (int)ShellExecuteW(NULL, NULL, wc_filename, NULL, NULL,
-				SW_SHOW);
-
-		g_free(wc_filename);
-	} else {
-		char *l_filename = g_locale_from_utf8(
-				uri, -1, NULL, NULL, NULL);
-
-		code = (int)ShellExecuteA(NULL, NULL, l_filename, NULL, NULL,
-				SW_SHOW);
-
-		g_free(l_filename);
-	}
+	wchar_t *wc_filename = g_utf8_to_utf16(
+			uri, -1, NULL, NULL, NULL);
+
+	code = (int)ShellExecuteW(NULL, NULL, wc_filename, NULL, NULL,
+			SW_SHOW);
+
+	g_free(wc_filename);
 
 	if (code == SE_ERR_ASSOCINCOMPLETE || code == SE_ERR_NOASSOC)
 	{
@@ -3697,13 +3208,10 @@
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
 
 	/* Open Containing Directory */
-#if GTK_CHECK_VERSION(2,6,0)
 	img = gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU);
 	item = gtk_image_menu_item_new_with_mnemonic(_("Open _Containing Directory"));
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
-#else
-	item = gtk_menu_item_new_with_mnemonic(_("Open _Containing Directory"));
-#endif
+
 	g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(open_containing_cb), (gpointer)url);
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
 
@@ -3769,13 +3277,10 @@
 	url = gtk_imhtml_link_get_url(link);
 
 	/* Play Sound */
-#if GTK_CHECK_VERSION(2,6,0)
 	img = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU);
 	item = gtk_image_menu_item_new_with_mnemonic(_("_Play Sound"));
 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
-#else
-	item = gtk_menu_item_new_with_mnemonic(_("_Play Sound"));
-#endif
+
 	g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_link_activate), link);
 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
 
@@ -3897,17 +3402,18 @@
 
 	do {
 		DWORD nameSize = 256;
-		char start[256];
-		/* I don't think we need to worry about non-ASCII protocol names */
-		ret = RegEnumKeyExA(HKEY_CLASSES_ROOT, idx++, start, &nameSize,
+		wchar_t start[256];
+		ret = RegEnumKeyExW(HKEY_CLASSES_ROOT, idx++, start, &nameSize,
 							NULL, NULL, NULL, NULL);
 		if (ret == ERROR_SUCCESS) {
 			HKEY reg_key = NULL;
-			ret = RegOpenKeyExA(HKEY_CLASSES_ROOT, start, 0, KEY_READ, &reg_key);
+			ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, start, 0, KEY_READ, &reg_key);
 			if (ret == ERROR_SUCCESS) {
-				ret = RegQueryValueExA(reg_key, "URL Protocol", NULL, NULL, NULL, NULL);
+				ret = RegQueryValueExW(reg_key, L"URL Protocol", NULL, NULL, NULL, NULL);
 				if (ret == ERROR_SUCCESS) {
-					gchar *protocol = g_strdup_printf("%s:", start);
+					gchar *utf8 = g_utf16_to_utf8(start, -1, NULL, NULL, NULL);
+					gchar *protocol = g_strdup_printf("%s:", utf8);
+					g_free(utf8);
 					registered_url_handlers = g_slist_prepend(registered_url_handlers, protocol);
 					/* We still pass everything to the "http" "open" handler for security reasons */
 					gtk_imhtml_class_register_protocol(protocol, url_clicked_cb, link_context_menu);
--- a/pidgin/gtkutils.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkutils.h	Mon Mar 08 22:53:02 2010 +0000
@@ -648,17 +648,6 @@
  */
 gpointer pidgin_convert_buddy_icon(PurplePlugin *plugin, const char *path, size_t *len);
 
-#if !GTK_CHECK_VERSION(2,6,0)
-/**
- * Creates a new pixbuf by loading an image from a file. The image will
- * be scaled to fit in the requested size, optionally preserving the image's
- * aspect ratio.
- */
-GdkPixbuf *gdk_pixbuf_new_from_file_at_scale(const char *filename, int width, int height,
-											 gboolean preserve_aspect_ratio,
-											 GError **error);
-#endif
-
 #if !(defined PIDGIN_DISABLE_DEPRECATED) || (defined _PIDGIN_GTKUTILS_C_)
 /**
  * Set or unset a custom buddyicon for a user.
@@ -760,20 +749,6 @@
  */
 const char *pidgin_get_dim_grey_string(GtkWidget *widget);
 
-#if !GTK_CHECK_VERSION(2,2,0)
-/**
- * This is copied from Gtk to support Gtk 2.0
- *
- * Creates a new path with @a first_index and the varargs as indices.
- *
- * @param first_index    first integer
- * @param ...            list of integers terminated by -1
- *
- * @return               A newly created GtkTreePath.
- */
-GtkTreePath *gtk_tree_path_new_from_indices (gint first_index, ...);
-#endif
-
 /**
  * Create a simple text GtkComboBoxEntry equivalent
  *
--- a/pidgin/gtkwhiteboard.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/gtkwhiteboard.c	Mon Mar 08 22:53:02 2010 +0000
@@ -767,7 +767,6 @@
 
 	int result;
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 	dialog = gtk_file_chooser_dialog_new (_("Save File"),
 										  GTK_WINDOW(gtkwb->window),
 										  GTK_FILE_CHOOSER_ACTION_SAVE,
@@ -786,21 +785,15 @@
 	else
 		gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), filename_for_existing_document);
 	*/
-#else
-	dialog = gtk_file_selection_new(_("Save File"));
-	gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog), "whiteboard.jpg");
-#endif
+
 	result = gtk_dialog_run(GTK_DIALOG(dialog));
 
 	if(result == GTK_RESPONSE_ACCEPT)
 	{
 		char *filename;
 
-#if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */
 		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
-#else
-		filename = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(dialog)));
-#endif
+
 		gtk_widget_destroy(dialog);
 
 		/* Makes an icon from the whiteboard's canvas 'image' */
--- a/pidgin/minidialog.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/minidialog.c	Mon Mar 08 22:53:02 2010 +0000
@@ -339,26 +339,20 @@
 
 	param_spec = g_param_spec_string("title", "title",
 		"String specifying the mini-dialog's title", NULL,
-#if GTK_CHECK_VERSION(2,8,0)
 		G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
-#endif
 		G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_TITLE, param_spec);
 
 	param_spec = g_param_spec_string("description", "description",
 		"Description text for the mini-dialog, if desired", NULL,
-#if GTK_CHECK_VERSION(2,8,0)
 		G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
-#endif
 		G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_DESCRIPTION, param_spec);
 
 	param_spec = g_param_spec_string("icon-name", "icon-name",
 		"String specifying the Gtk stock name of the dialog's icon",
 		NULL,
-#if GTK_CHECK_VERSION(2,8,0)
 		G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB |
-#endif
 		G_PARAM_READWRITE);
 	g_object_class_install_property (object_class, PROP_ICON_NAME, param_spec);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgin-2-uninstalled.pc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,21 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+datarootdir=@datarootdir@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+
+abs_srcdir=@abs_srcdir@
+abs_builddir=@abs_builddir@
+
+abs_top_srcdir=@abs_top_srcdir@
+abs_top_builddir=@abs_top_builddir@
+
+plugindir=${libdir}/pidgin
+
+Name: Pidgin
+Description: Pidgin is a GTK2-based instant messenger application.
+Version: @VERSION@
+Requires: gtk+-2.0 purple
+Cflags: -I${abs_top_srcdir}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgin-2.pc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,15 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+datarootdir=@datarootdir@
+datadir=@datadir@
+sysconfdir=@sysconfdir@
+
+plugindir=${libdir}/pidgin
+
+Name: Pidgin
+Description: Pidgin is a GTK2-based instant messenger application.
+Version: @VERSION@
+Requires: gtk+-2.0 purple
+Cflags: -I${includedir}
--- a/pidgin/pidgin-uninstalled.pc.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pidgin-uninstalled.pc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -9,6 +9,8 @@
 abs_srcdir=@abs_srcdir@
 abs_builddir=@abs_builddir@
 
+plugindir=${libdir}/pidgin
+
 Name: Pidgin
 Description: Pidgin is a GTK2-based instant messenger application.
 Version: @VERSION@
--- a/pidgin/pidgin.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pidgin.h	Mon Mar 08 22:53:02 2010 +0000
@@ -57,33 +57,6 @@
 #endif
 
 /*
- * This is backwards-compatibility code for older versions of GTK+ (< 2.4.x)
- * It defines the new wrap behavior (unknown in earlier versions)
- * as the old (slightly buggy) wrap behavior.
- * It also includes our back-ported GtkExpander
- */
-/** @cond */
-#if (!GTK_CHECK_VERSION(2,4,0))
-# define GTK_WRAP_WORD_CHAR GTK_WRAP_WORD
-# include "gtkexpander.h"
-#endif
-/** @endcond */
-
-/*
- * We include the sources for GtkComboBox and GtkCellView because
- * they don't exist in older versions of GTK+, and we use them
- * in a few places.
- */
-#if !GTK_CHECK_VERSION(2,6,0)
-#   include "gtkcellview.h"
-#   include "gtkcellviewmenuitem.h"
-#   include "pidgincombobox.h"
-#   if !GTK_CHECK_VERSION(2,4,0)
-#       include "gtkcelllayout.h"
-#   endif /* Less than GTK+ 2.4 */
-#endif /* Less than GTK+ 2.6 */
-
-/*
  * Spacings between components, as defined by the
  * GNOME Human Interface Guidelines.
  */
--- a/pidgin/pidgin.pc.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pidgin.pc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -6,6 +6,8 @@
 datadir=@datadir@
 sysconfdir=@sysconfdir@
 
+plugindir=${libdir}/pidgin
+
 Name: Pidgin
 Description: Pidgin is a GTK2-based instant messenger application.
 Version: @VERSION@
--- a/pidgin/pidgincombobox.c	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3749 +0,0 @@
-/* pidgincombobox.c
- * Copyright (C) 2002, 2003  Kristian Rietveld <kris@gtk.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- */
-
-/*
-#include <config.h>
-*/
-#include <gtk/gtkversion.h>
-#if !GTK_CHECK_VERSION(2,6,0)
-#include "pidgincombobox.h"
-
-#if !GTK_CHECK_VERSION(2,4,0)
-#include <gtk/gtkarrow.h>
-#include <gtk/gtkbindings.h>
-#include "gtkcelllayout.h"
-#include <gtk/gtkcellrenderertext.h>
-#include "gtkcellview.h"
-#include "gtkcellviewmenuitem.h"
-#include <gtk/gtkeventbox.h>
-#include <gtk/gtkframe.h>
-#include <gtk/gtkhbox.h>
-#include <gtk/gtkliststore.h>
-#include <gtk/gtkmain.h>
-#include <gtk/gtkmenu.h>
-#include <gtk/gtktogglebutton.h>
-#include <gtk/gtktreeselection.h>
-/*
-#include <gtk/gtktreeprivate.h>
-*/
-#include <gtk/gtkvseparator.h>
-#include <gtk/gtkwindow.h>
-#include <gtk/gtkversion.h>
-
-#include <gdk/gdkkeysyms.h>
-
-#include <gobject/gvaluecollector.h>
-
-#include <string.h>
-#include <stdarg.h>
-
-#define P_(x) (x)
-
-/* WELCOME, to THE house of evil code */
-
-typedef struct _ComboCellInfo ComboCellInfo;
-struct _ComboCellInfo
-{
-  GtkCellRenderer *cell;
-  GSList *attributes;
-
-  GtkCellLayoutDataFunc func;
-  gpointer func_data;
-  GDestroyNotify destroy;
-
-  guint expand : 1;
-  guint pack : 1;
-};
-
-struct _GtkComboBoxPrivate
-{
-  GtkTreeModel *model;
-
-  gint col_column;
-  gint row_column;
-
-  gint wrap_width;
-
-  gint active_item;
-
-  GtkWidget *tree_view;
-  GtkTreeViewColumn *column;
-
-  GtkWidget *cell_view;
-  GtkWidget *cell_view_frame;
-
-  GtkWidget *button;
-  GtkWidget *box;
-  GtkWidget *arrow;
-  GtkWidget *separator;
-
-  GtkWidget *popup_widget;
-  GtkWidget *popup_window;
-  GtkWidget *popup_frame;
-
-  guint inserted_id;
-  guint deleted_id;
-  guint reordered_id;
-  guint changed_id;
-
-  gint width;
-  GSList *cells;
-
-  guint popup_in_progress : 1;
-  guint destroying : 1;
-};
-
-/* While debugging this evil code, I have learned that
- * there are actually 4 modes to this widget, which can
- * be characterized as follows
- *
- * 1) menu mode, no child added
- *
- * tree_view -> NULL
- * cell_view -> GtkCellView, regular child
- * cell_view_frame -> NULL
- * button -> GtkToggleButton set_parent to combo
- * box -> child of button
- * arrow -> child of box
- * separator -> child of box
- * popup_widget -> GtkMenu
- * popup_window -> NULL
- * popup_frame -> NULL
- *
- * 2) menu mode, child added
- *
- * tree_view -> NULL
- * cell_view -> NULL
- * cell_view_frame -> NULL
- * button -> GtkToggleButton set_parent to combo
- * box -> NULL
- * arrow -> GtkArrow, child of button
- * separator -> NULL
- * popup_widget -> GtkMenu
- * popup_window -> NULL
- * popup_frame -> NULL
- *
- * 3) list mode, no child added
- *
- * tree_view -> GtkTreeView, child of popup_frame
- * cell_view -> GtkCellView, regular child
- * cell_view_frame -> GtkFrame, set parent to combo
- * button -> GtkToggleButton, set_parent to combo
- * box -> NULL
- * arrow -> GtkArrow, child of button
- * separator -> NULL
- * popup_widget -> tree_view
- * popup_window -> GtkWindow
- * popup_frame -> GtkFrame, child of popup_window
- *
- * 4) list mode, child added
- *
- * tree_view -> GtkTreeView, child of popup_frame
- * cell_view -> NULL
- * cell_view_frame -> NULL
- * button -> GtkToggleButton, set_parent to combo
- * box -> NULL
- * arrow -> GtkArrow, child of button
- * separator -> NULL
- * popup_widget -> tree_view
- * popup_window -> GtkWindow
- * popup_frame -> GtkFrame, child of popup_window
- *
- */
-
-enum {
-  CHANGED,
-  LAST_SIGNAL
-};
-
-enum {
-  PROP_0,
-  PROP_MODEL,
-  PROP_WRAP_WIDTH,
-  PROP_ROW_SPAN_COLUMN,
-  PROP_COLUMN_SPAN_COLUMN,
-  PROP_ACTIVE
-};
-
-static GtkBinClass *parent_class = NULL;
-static guint combo_box_signals[LAST_SIGNAL] = {0,};
-
-#define BONUS_PADDING 4
-
-
-/* common */
-static void     gtk_combo_box_class_init           (GtkComboBoxClass *klass);
-static void     gtk_combo_box_cell_layout_init     (GtkCellLayoutIface *iface);
-static void     gtk_combo_box_init                 (GtkComboBox      *combo_box);
-static void     gtk_combo_box_finalize             (GObject          *object);
-static void     gtk_combo_box_destroy              (GtkObject        *object);
-
-static void     gtk_combo_box_set_property         (GObject         *object,
-                                                    guint            prop_id,
-                                                    const GValue    *value,
-                                                    GParamSpec      *spec);
-static void     gtk_combo_box_get_property         (GObject         *object,
-                                                    guint            prop_id,
-                                                    GValue          *value,
-                                                    GParamSpec      *spec);
-
-static void     gtk_combo_box_state_changed        (GtkWidget        *widget,
-			                            GtkStateType      previous);
-static void     gtk_combo_box_style_set            (GtkWidget       *widget,
-                                                    GtkStyle        *previous);
-static void     gtk_combo_box_button_toggled       (GtkWidget       *widget,
-                                                    gpointer         data);
-static void     gtk_combo_box_add                  (GtkContainer    *container,
-                                                    GtkWidget       *widget);
-static void     gtk_combo_box_remove               (GtkContainer    *container,
-                                                    GtkWidget       *widget);
-
-static ComboCellInfo *gtk_combo_box_get_cell_info  (GtkComboBox      *combo_box,
-                                                    GtkCellRenderer  *cell);
-
-static void     gtk_combo_box_menu_show            (GtkWidget        *menu,
-                                                    gpointer          user_data);
-static void     gtk_combo_box_menu_hide            (GtkWidget        *menu,
-                                                    gpointer          user_data);
-
-static void     gtk_combo_box_set_popup_widget     (GtkComboBox      *combo_box,
-                                                    GtkWidget        *popup);
-#if GTK_CHECK_VERSION(2,2,0)
-static void     gtk_combo_box_menu_position_below  (GtkMenu          *menu,
-                                                    gint             *x,
-                                                    gint             *y,
-                                                    gint             *push_in,
-                                                    gpointer          user_data);
-static void     gtk_combo_box_menu_position_over   (GtkMenu          *menu,
-                                                    gint             *x,
-                                                    gint             *y,
-                                                    gint             *push_in,
-                                                    gpointer          user_data);
-static void     gtk_combo_box_menu_position        (GtkMenu          *menu,
-                                                    gint             *x,
-                                                    gint             *y,
-                                                    gint             *push_in,
-                                                    gpointer          user_data);
-#endif
-
-static gint     gtk_combo_box_calc_requested_width (GtkComboBox      *combo_box,
-                                                    GtkTreePath      *path);
-static void     gtk_combo_box_remeasure            (GtkComboBox      *combo_box);
-
-static void     gtk_combo_box_unset_model          (GtkComboBox      *combo_box);
-
-static void     gtk_combo_box_size_request         (GtkWidget        *widget,
-                                                    GtkRequisition   *requisition);
-static void     gtk_combo_box_size_allocate        (GtkWidget        *widget,
-                                                    GtkAllocation    *allocation);
-static void     gtk_combo_box_forall               (GtkContainer     *container,
-                                                    gboolean          include_internals,
-                                                    GtkCallback       callback,
-                                                    gpointer          callback_data);
-static gboolean gtk_combo_box_expose_event         (GtkWidget        *widget,
-                                                    GdkEventExpose   *event);
-static gboolean gtk_combo_box_scroll_event         (GtkWidget        *widget,
-                                                    GdkEventScroll   *event);
-static void     gtk_combo_box_set_active_internal  (GtkComboBox      *combo_box,
-						    gint              index);
-static gboolean gtk_combo_box_key_press            (GtkWidget        *widget,
-						    GdkEventKey      *event,
-						    gpointer          data);
-
-/* listening to the model */
-static void     gtk_combo_box_model_row_inserted   (GtkTreeModel     *model,
-						    GtkTreePath      *path,
-						    GtkTreeIter      *iter,
-						    gpointer          user_data);
-static void     gtk_combo_box_model_row_deleted    (GtkTreeModel     *model,
-						    GtkTreePath      *path,
-						    gpointer          user_data);
-static void     gtk_combo_box_model_rows_reordered (GtkTreeModel     *model,
-						    GtkTreePath      *path,
-						    GtkTreeIter      *iter,
-						    gint             *new_order,
-						    gpointer          user_data);
-static void     gtk_combo_box_model_row_changed    (GtkTreeModel     *model,
-						    GtkTreePath      *path,
-						    GtkTreeIter      *iter,
-						    gpointer          data);
-
-/* list */
-static void     gtk_combo_box_list_position        (GtkComboBox      *combo_box,
-						    gint             *x,
-						    gint             *y,
-						    gint             *width,
-						    gint             *height);
-
-static void     gtk_combo_box_list_setup           (GtkComboBox      *combo_box);
-static void     gtk_combo_box_list_destroy         (GtkComboBox      *combo_box);
-
-static void     gtk_combo_box_list_remove_grabs    (GtkComboBox      *combo_box);
-
-static gboolean gtk_combo_box_list_button_released (GtkWidget        *widget,
-                                                    GdkEventButton   *event,
-                                                    gpointer          data);
-static gboolean gtk_combo_box_list_key_press       (GtkWidget        *widget,
-                                                    GdkEventKey      *event,
-                                                    gpointer          data);
-static gboolean gtk_combo_box_list_button_pressed  (GtkWidget        *widget,
-                                                    GdkEventButton   *event,
-                                                    gpointer          data);
-
-static void     gtk_combo_box_list_row_changed     (GtkTreeModel     *model,
-                                                    GtkTreePath      *path,
-                                                    GtkTreeIter      *iter,
-                                                    gpointer          data);
-
-/* menu */
-static void     gtk_combo_box_menu_setup           (GtkComboBox      *combo_box,
-                                                    gboolean          add_children);
-static void     gtk_combo_box_menu_fill            (GtkComboBox      *combo_box);
-static void     gtk_combo_box_menu_destroy         (GtkComboBox      *combo_box);
-
-static void     gtk_combo_box_item_get_size        (GtkComboBox      *combo_box,
-                                                    gint              index,
-                                                    gint             *cols,
-                                                    gint             *rows);
-static void     gtk_combo_box_relayout_item        (GtkComboBox      *combo_box,
-                                                    gint              index);
-static void     gtk_combo_box_relayout             (GtkComboBox      *combo_box);
-
-static gboolean gtk_combo_box_menu_button_press    (GtkWidget        *widget,
-                                                    GdkEventButton   *event,
-                                                    gpointer          user_data);
-static void     gtk_combo_box_menu_item_activate   (GtkWidget        *item,
-                                                    gpointer          user_data);
-static void     gtk_combo_box_menu_row_inserted    (GtkTreeModel     *model,
-                                                    GtkTreePath      *path,
-                                                    GtkTreeIter      *iter,
-                                                    gpointer          user_data);
-static void     gtk_combo_box_menu_row_deleted     (GtkTreeModel     *model,
-                                                    GtkTreePath      *path,
-                                                    gpointer          user_data);
-static void     gtk_combo_box_menu_rows_reordered  (GtkTreeModel     *model,
-						    GtkTreePath      *path,
-						    GtkTreeIter      *iter,
-						    gint             *new_order,
-						    gpointer          user_data);
-static void     gtk_combo_box_menu_row_changed     (GtkTreeModel     *model,
-                                                    GtkTreePath      *path,
-                                                    GtkTreeIter      *iter,
-                                                    gpointer          data);
-static gboolean gtk_combo_box_menu_key_press       (GtkWidget        *widget,
-						    GdkEventKey      *event,
-						    gpointer          data);
-
-/* cell layout */
-static void     gtk_combo_box_cell_layout_pack_start         (GtkCellLayout         *layout,
-                                                              GtkCellRenderer       *cell,
-                                                              gboolean               expand);
-static void     gtk_combo_box_cell_layout_pack_end           (GtkCellLayout         *layout,
-                                                              GtkCellRenderer       *cell,
-                                                              gboolean               expand);
-static void     gtk_combo_box_cell_layout_clear              (GtkCellLayout         *layout);
-static void     gtk_combo_box_cell_layout_add_attribute      (GtkCellLayout         *layout,
-                                                              GtkCellRenderer       *cell,
-                                                              const gchar           *attribute,
-                                                              gint                   column);
-static void     gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
-                                                              GtkCellRenderer       *cell,
-                                                              GtkCellLayoutDataFunc  func,
-                                                              gpointer               func_data,
-                                                              GDestroyNotify         destroy);
-static void     gtk_combo_box_cell_layout_clear_attributes   (GtkCellLayout         *layout,
-                                                              GtkCellRenderer       *cell);
-static void     gtk_combo_box_cell_layout_reorder            (GtkCellLayout         *layout,
-                                                              GtkCellRenderer       *cell,
-                                                              gint                   position);
-static gboolean gtk_combo_box_mnemonic_activate              (GtkWidget    *widget,
-							      gboolean      group_cycling);
-
-static void     cell_view_sync_cells (GtkComboBox *combo_box,
-                                      GtkCellView *cell_view);
-
-#if !GTK_CHECK_VERSION(2,4,0)
-static void     gtk_menu_attach (GtkMenu   *menu,
-                                 GtkWidget *child,
-                                 guint      left_attach,
-                                 guint      right_attach,
-                                 guint      top_attach,
-                                 guint      bottom_attach);
-#endif
-
-GType
-gtk_combo_box_get_type (void)
-{
-  static GType combo_box_type = 0;
-
-  if (!combo_box_type)
-    {
-      static const GTypeInfo combo_box_info =
-        {
-          sizeof (GtkComboBoxClass),
-          NULL, /* base_init */
-          NULL, /* base_finalize */
-          (GClassInitFunc) gtk_combo_box_class_init,
-          NULL, /* class_finalize */
-          NULL, /* class_data */
-          sizeof (GtkComboBox),
-          0,
-          (GInstanceInitFunc) gtk_combo_box_init
-        };
-
-      static const GInterfaceInfo cell_layout_info =
-        {
-          (GInterfaceInitFunc) gtk_combo_box_cell_layout_init,
-          NULL,
-          NULL
-        };
-
-      combo_box_type = g_type_register_static (GTK_TYPE_BIN,
-                                               "PidginComboBox",
-                                               &combo_box_info,
-                                               0);
-
-      g_type_add_interface_static (combo_box_type,
-                                   GTK_TYPE_CELL_LAYOUT,
-                                   &cell_layout_info);
-    }
-
-  return combo_box_type;
-}
-
-/* common */
-static void
-gtk_combo_box_class_init (GtkComboBoxClass *klass)
-{
-  GObjectClass *object_class;
-  GtkBindingSet *binding_set;
-  GtkObjectClass *gtk_object_class;
-  GtkContainerClass *container_class;
-  GtkWidgetClass *widget_class;
-
-  binding_set = gtk_binding_set_by_class (klass);
-
-  container_class = (GtkContainerClass *)klass;
-  container_class->forall = gtk_combo_box_forall;
-  container_class->add = gtk_combo_box_add;
-  container_class->remove = gtk_combo_box_remove;
-
-  widget_class = (GtkWidgetClass *)klass;
-  widget_class->size_allocate = gtk_combo_box_size_allocate;
-  widget_class->size_request = gtk_combo_box_size_request;
-  widget_class->expose_event = gtk_combo_box_expose_event;
-  widget_class->scroll_event = gtk_combo_box_scroll_event;
-  widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
-  widget_class->style_set = gtk_combo_box_style_set;
-  widget_class->state_changed = gtk_combo_box_state_changed;
-
-  gtk_object_class = (GtkObjectClass *)klass;
-  gtk_object_class->destroy = gtk_combo_box_destroy;
-
-  object_class = (GObjectClass *)klass;
-  object_class->finalize = gtk_combo_box_finalize;
-  object_class->set_property = gtk_combo_box_set_property;
-  object_class->get_property = gtk_combo_box_get_property;
-
-  parent_class = g_type_class_peek_parent (klass);
-
-  /* signals */
-  combo_box_signals[CHANGED] =
-    g_signal_new ("changed",
-                  G_OBJECT_CLASS_TYPE (klass),
-                  G_SIGNAL_RUN_LAST,
-                  G_STRUCT_OFFSET (GtkComboBoxClass, changed),
-                  NULL, NULL,
-                  g_cclosure_marshal_VOID__VOID,
-                  G_TYPE_NONE, 0);
-
-  /* properties */
-  g_object_class_install_property (object_class,
-                                   PROP_MODEL,
-                                   g_param_spec_object ("model",
-                                                        P_("ComboBox model"),
-                                                        P_("The model for the combo box"),
-                                                        GTK_TYPE_TREE_MODEL,
-                                                        G_PARAM_READWRITE));
-
-  g_object_class_install_property (object_class,
-                                   PROP_WRAP_WIDTH,
-                                   g_param_spec_int ("wrap_width",
-                                                     P_("Wrap width"),
-                                                     P_("Wrap width for layouting the items in a grid"),
-                                                     0,
-                                                     G_MAXINT,
-                                                     0,
-                                                     G_PARAM_READWRITE));
-
-  g_object_class_install_property (object_class,
-                                   PROP_ROW_SPAN_COLUMN,
-                                   g_param_spec_int ("row_span_column",
-                                                     P_("Row span column"),
-                                                     P_("TreeModel column containing the row span values"),
-                                                     0,
-                                                     G_MAXINT,
-                                                     0,
-                                                     G_PARAM_READWRITE));
-
-  g_object_class_install_property (object_class,
-                                   PROP_COLUMN_SPAN_COLUMN,
-                                   g_param_spec_int ("column_span_column",
-                                                     P_("Column span column"),
-
-                                                     P_("TreeModel column containing the column span values"),
-                                                     0,
-                                                     G_MAXINT,
-                                                     0,
-                                                     G_PARAM_READWRITE));
-
-  g_object_class_install_property (object_class,
-                                   PROP_ACTIVE,
-                                   g_param_spec_int ("active",
-                                                     P_("Active item"),
-                                                     P_("The item which is currently active"),
-                                                     -1,
-                                                     G_MAXINT,
-                                                     -1,
-                                                     G_PARAM_READWRITE));
-
-  gtk_widget_class_install_style_property (widget_class,
-                                           g_param_spec_boolean ("appears-as-list",
-                                                                 P_("Appears as list"),
-                                                                 P_("Whether combobox dropdowns should look like lists rather than menus"),
-                                                                 FALSE,
-                                                                 G_PARAM_READABLE));
-}
-
-static void
-gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
-{
-  iface->pack_start = gtk_combo_box_cell_layout_pack_start;
-  iface->pack_end = gtk_combo_box_cell_layout_pack_end;
-  iface->clear = gtk_combo_box_cell_layout_clear;
-  iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
-  iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
-  iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
-  iface->reorder = gtk_combo_box_cell_layout_reorder;
-}
-
-static void
-gtk_combo_box_init (GtkComboBox *combo_box)
-{
-  combo_box->priv = g_new0(GtkComboBoxPrivate,1);
-
-  combo_box->priv->cell_view = gtk_cell_view_new ();
-  gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (combo_box));
-  GTK_BIN (combo_box)->child = combo_box->priv->cell_view;
-  gtk_widget_show (combo_box->priv->cell_view);
-
-  combo_box->priv->width = 0;
-  combo_box->priv->wrap_width = 0;
-
-  combo_box->priv->active_item = -1;
-  combo_box->priv->col_column = -1;
-  combo_box->priv->row_column = -1;
-}
-
-static void
-gtk_combo_box_set_property (GObject      *object,
-                            guint         prop_id,
-                            const GValue *value,
-                            GParamSpec   *pspec)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (object);
-
-  switch (prop_id)
-    {
-      case PROP_MODEL:
-        gtk_combo_box_set_model (combo_box, g_value_get_object (value));
-        break;
-
-      case PROP_WRAP_WIDTH:
-        gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
-        break;
-
-      case PROP_ROW_SPAN_COLUMN:
-        gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
-        break;
-
-      case PROP_COLUMN_SPAN_COLUMN:
-        gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
-        break;
-
-      case PROP_ACTIVE:
-        gtk_combo_box_set_active (combo_box, g_value_get_int (value));
-        break;
-
-      default:
-        break;
-    }
-}
-
-static void
-gtk_combo_box_get_property (GObject    *object,
-                            guint       prop_id,
-                            GValue     *value,
-                            GParamSpec *pspec)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (object);
-
-  switch (prop_id)
-    {
-      case PROP_MODEL:
-        g_value_set_object (value, combo_box->priv->model);
-        break;
-
-      case PROP_WRAP_WIDTH:
-        g_value_set_int (value, combo_box->priv->wrap_width);
-        break;
-
-      case PROP_ROW_SPAN_COLUMN:
-        g_value_set_int (value, combo_box->priv->row_column);
-        break;
-
-      case PROP_COLUMN_SPAN_COLUMN:
-        g_value_set_int (value, combo_box->priv->col_column);
-        break;
-
-      case PROP_ACTIVE:
-        g_value_set_int (value, gtk_combo_box_get_active (combo_box));
-        break;
-
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-        break;
-    }
-}
-
-static void
-gtk_combo_box_state_changed (GtkWidget    *widget,
-			     GtkStateType  previous)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
-
-  if (GTK_WIDGET_REALIZED (widget))
-    {
-      if (combo_box->priv->tree_view && combo_box->priv->cell_view)
-	gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
-					    &widget->style->base[GTK_WIDGET_STATE (widget)]);
-    }
-
-  gtk_widget_queue_draw (widget);
-}
-
-static void
-gtk_combo_box_check_appearance (GtkComboBox *combo_box)
-{
-  gboolean appears_as_list;
-
-  /* if wrap_width > 0, then we are in grid-mode and forced to use
-   * unix style
-   */
-  if (combo_box->priv->wrap_width)
-    appears_as_list = FALSE;
-  else
-    gtk_widget_style_get (GTK_WIDGET (combo_box),
-			  "appears-as-list", &appears_as_list,
-			  NULL);
-
-  if (appears_as_list)
-    {
-      /* Destroy all the menu mode widgets, if they exist. */
-      if (GTK_IS_MENU (combo_box->priv->popup_widget))
-	gtk_combo_box_menu_destroy (combo_box);
-
-      /* Create the list mode widgets, if they don't already exist. */
-      if (!GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
-	gtk_combo_box_list_setup (combo_box);
-    }
-  else
-    {
-      /* Destroy all the list mode widgets, if they exist. */
-      if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
-	gtk_combo_box_list_destroy (combo_box);
-
-      /* Create the menu mode widgets, if they don't already exist. */
-      if (!GTK_IS_MENU (combo_box->priv->popup_widget))
-	gtk_combo_box_menu_setup (combo_box, TRUE);
-    }
-}
-
-static void
-gtk_combo_box_style_set (GtkWidget *widget,
-                         GtkStyle  *previous)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
-
-  gtk_combo_box_check_appearance (combo_box);
-
-  if (combo_box->priv->tree_view && combo_box->priv->cell_view)
-    gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
-					&widget->style->base[GTK_WIDGET_STATE (widget)]);
-}
-
-static void
-gtk_combo_box_button_toggled (GtkWidget *widget,
-                              gpointer   data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (data);
-
-  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
-    {
-      if (!combo_box->priv->popup_in_progress)
-        gtk_combo_box_popup (combo_box);
-    }
-  else
-    gtk_combo_box_popdown (combo_box);
-}
-
-static void
-gtk_combo_box_add (GtkContainer *container,
-                   GtkWidget    *widget)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (container);
-
-  if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent)
-    {
-      gtk_widget_unparent (combo_box->priv->cell_view);
-      GTK_BIN (container)->child = NULL;
-      gtk_widget_queue_resize (GTK_WIDGET (container));
-    }
-
-  gtk_widget_set_parent (widget, GTK_WIDGET (container));
-  GTK_BIN (container)->child = widget;
-
-  if (combo_box->priv->cell_view &&
-      widget != combo_box->priv->cell_view)
-    {
-      /* since the cell_view was unparented, it's gone now */
-      combo_box->priv->cell_view = NULL;
-
-      if (!combo_box->priv->tree_view && combo_box->priv->separator)
-        {
-	  gtk_container_remove (GTK_CONTAINER (combo_box->priv->separator->parent),
-				combo_box->priv->separator);
-	  combo_box->priv->separator = NULL;
-
-          gtk_widget_queue_resize (GTK_WIDGET (container));
-        }
-      else if (combo_box->priv->cell_view_frame)
-        {
-          gtk_widget_unparent (combo_box->priv->cell_view_frame);
-          combo_box->priv->cell_view_frame = NULL;
-        }
-    }
-}
-
-static void
-gtk_combo_box_remove (GtkContainer *container,
-		      GtkWidget    *widget)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (container);
-  gboolean appears_as_list;
-
-  gtk_widget_unparent (widget);
-  GTK_BIN (container)->child = NULL;
-
-  if (combo_box->priv->destroying)
-    return;
-
-  gtk_widget_queue_resize (GTK_WIDGET (container));
-
-  if (!combo_box->priv->tree_view)
-    appears_as_list = FALSE;
-  else
-    appears_as_list = TRUE;
-
-  if (appears_as_list)
-    gtk_combo_box_list_destroy (combo_box);
-  else if (GTK_IS_MENU (combo_box->priv->popup_widget))
-    {
-      gtk_combo_box_menu_destroy (combo_box);
-      gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
-      combo_box->priv->popup_widget = NULL;
-    }
-
-  if (!combo_box->priv->cell_view)
-    {
-      combo_box->priv->cell_view = gtk_cell_view_new ();
-      gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (container));
-      GTK_BIN (container)->child = combo_box->priv->cell_view;
-
-      gtk_widget_show (combo_box->priv->cell_view);
-      gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
-			       combo_box->priv->model);
-      cell_view_sync_cells (combo_box, GTK_CELL_VIEW (combo_box->priv->cell_view));
-    }
-
-
-  if (appears_as_list)
-    gtk_combo_box_list_setup (combo_box);
-  else
-    gtk_combo_box_menu_setup (combo_box, TRUE);
-
-  gtk_combo_box_set_active_internal (combo_box, combo_box->priv->active_item);
-}
-
-static ComboCellInfo *
-gtk_combo_box_get_cell_info (GtkComboBox     *combo_box,
-                             GtkCellRenderer *cell)
-{
-  GSList *i;
-
-  for (i = combo_box->priv->cells; i; i = i->next)
-    {
-      ComboCellInfo *info = (ComboCellInfo *)i->data;
-
-      if (info && info->cell == cell)
-        return info;
-    }
-
-  return NULL;
-}
-
-static void
-gtk_combo_box_menu_show (GtkWidget *menu,
-                         gpointer   user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
-                                TRUE);
-  combo_box->priv->popup_in_progress = FALSE;
-}
-
-static void
-gtk_combo_box_menu_hide (GtkWidget *menu,
-                         gpointer   user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
-                                FALSE);
-}
-
-static void
-gtk_combo_box_detacher (GtkWidget *widget,
-			GtkMenu	  *menu)
-{
-  GtkComboBox *combo_box;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (widget));
-
-  combo_box = GTK_COMBO_BOX (widget);
-  g_return_if_fail (combo_box->priv->popup_widget == (GtkWidget*) menu);
-
-  g_signal_handlers_disconnect_by_func (menu,
-					gtk_combo_box_menu_show,
-					combo_box);
-  g_signal_handlers_disconnect_by_func (menu,
-					gtk_combo_box_menu_hide,
-					combo_box);
-
-  combo_box->priv->popup_widget = NULL;
-}
-
-static void
-gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
-                                GtkWidget   *popup)
-{
-  if (GTK_IS_MENU (combo_box->priv->popup_widget))
-    {
-      gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
-      combo_box->priv->popup_widget = NULL;
-    }
-  else if (combo_box->priv->popup_widget)
-    {
-      gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame),
-                            combo_box->priv->popup_widget);
-      g_object_unref (G_OBJECT (combo_box->priv->popup_widget));
-      combo_box->priv->popup_widget = NULL;
-    }
-
-  if (GTK_IS_MENU (popup))
-    {
-      if (combo_box->priv->popup_window)
-        {
-          gtk_widget_destroy (combo_box->priv->popup_window);
-          combo_box->priv->popup_window = NULL;
-	  combo_box->priv->popup_frame = NULL;
-        }
-
-      combo_box->priv->popup_widget = popup;
-
-      g_signal_connect (popup, "show",
-                        G_CALLBACK (gtk_combo_box_menu_show), combo_box);
-      g_signal_connect (popup, "hide",
-                        G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
-
-      gtk_menu_attach_to_widget (GTK_MENU (popup),
-				 GTK_WIDGET (combo_box),
-				 gtk_combo_box_detacher);
-    }
-  else
-    {
-      if (!combo_box->priv->popup_window)
-        {
-          combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
-	  gtk_window_set_resizable (GTK_WINDOW (combo_box->priv->popup_window), FALSE);
-#if GTK_CHECK_VERSION(2,2,0)
-          gtk_window_set_screen (GTK_WINDOW (combo_box->priv->popup_window),
-                                 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
-#endif
-
-          combo_box->priv->popup_frame = gtk_frame_new (NULL);
-          gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame),
-                                     GTK_SHADOW_ETCHED_IN);
-          gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window),
-                             combo_box->priv->popup_frame);
-
-          gtk_widget_show (combo_box->priv->popup_frame);
-        }
-
-      gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
-                         popup);
-      gtk_widget_show (popup);
-      g_object_ref (G_OBJECT (popup));
-      combo_box->priv->popup_widget = popup;
-    }
-}
-
-#if GTK_CHECK_VERSION(2,2,0)
-static void
-gtk_combo_box_menu_position_below (GtkMenu  *menu,
-				   gint     *x,
-				   gint     *y,
-				   gint     *push_in,
-				   gpointer  user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-  gint sx, sy;
-  GtkWidget *child;
-  GtkRequisition req;
-  GdkScreen *screen;
-  gint monitor_num;
-  GdkRectangle monitor;
-
-  /* FIXME: is using the size request here broken? */
-   child = GTK_BIN (combo_box)->child;
-
-   gdk_window_get_origin (child->window, &sx, &sy);
-
-   if (GTK_WIDGET_NO_WINDOW (child))
-      {
-	sx += child->allocation.x;
-	sy += child->allocation.y;
-      }
-
-   gtk_widget_size_request (GTK_WIDGET (menu), &req);
-
-   if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
-     *x = sx;
-   else
-     *x = sx + child->allocation.width - req.width;
-   *y = sy;
-
-  screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
-  monitor_num = gdk_screen_get_monitor_at_window (screen,
-						  GTK_WIDGET (combo_box)->window);
-  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
-
-  if (*x < monitor.x)
-    *x = monitor.x;
-  else if (*x + req.width > monitor.x + monitor.width)
-    *x = monitor.x + monitor.width - req.width;
-
-  if (monitor.y + monitor.height - *y - child->allocation.height >= req.height)
-    *y += child->allocation.height;
-  else if (*y - monitor.y >= req.height)
-    *y -= req.height;
-  else if (monitor.y + monitor.height - *y - child->allocation.height > *y - monitor.y)
-    *y += child->allocation.height;
-  else
-    *y -= req.height;
-
-   *push_in = FALSE;
-}
-
-static void
-gtk_combo_box_menu_position_over (GtkMenu  *menu,
-				  gint     *x,
-				  gint     *y,
-				  gboolean *push_in,
-				  gpointer  user_data)
-{
-  GtkComboBox *combo_box;
-  GtkWidget *active;
-  GtkWidget *child;
-  GtkWidget *widget;
-  GtkRequisition requisition;
-  GList *children;
-  gint screen_width;
-  gint menu_xpos;
-  gint menu_ypos;
-  gint menu_width;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (user_data));
-
-  combo_box = GTK_COMBO_BOX (user_data);
-  widget = GTK_WIDGET (combo_box);
-
-  gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
-  menu_width = requisition.width;
-
-  active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
-  gdk_window_get_origin (widget->window, &menu_xpos, &menu_ypos);
-
-  menu_xpos += widget->allocation.x;
-  menu_ypos += widget->allocation.y + widget->allocation.height / 2 - 2;
-
-  if (active != NULL)
-    {
-      gtk_widget_get_child_requisition (active, &requisition);
-      menu_ypos -= requisition.height / 2;
-    }
-
-  children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
-  while (children)
-    {
-      child = children->data;
-
-      if (active == child)
-	break;
-
-      if (GTK_WIDGET_VISIBLE (child))
-	{
-	  gtk_widget_get_child_requisition (child, &requisition);
-	  menu_ypos -= requisition.height;
-	}
-
-      children = children->next;
-    }
-
-  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
-    menu_xpos = menu_xpos + widget->allocation.width - menu_width;
-
-  /* Clamp the position on screen */
-  screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
-
-  if (menu_xpos < 0)
-    menu_xpos = 0;
-  else if ((menu_xpos + menu_width) > screen_width)
-    menu_xpos -= ((menu_xpos + menu_width) - screen_width);
-
-  *x = menu_xpos;
-  *y = menu_ypos;
-
-  *push_in = TRUE;
-}
-
-static void
-gtk_combo_box_menu_position (GtkMenu  *menu,
-			     gint     *x,
-			     gint     *y,
-			     gint     *push_in,
-			     gpointer  user_data)
-{
-  GtkComboBox *combo_box;
-  GtkWidget *menu_item;
-
-  combo_box = GTK_COMBO_BOX (user_data);
-
-  if (combo_box->priv->wrap_width > 0 || combo_box->priv->cell_view == NULL)
-    gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
-  else
-    {
-      menu_item = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
-      if (menu_item)
-	gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget),
-				    menu_item);
-
-      gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
-    }
-
-}
-#endif /* Gtk 2.2 */
-
-static void
-gtk_combo_box_list_position (GtkComboBox *combo_box,
-			     gint        *x,
-			     gint        *y,
-			     gint        *width,
-			     gint        *height)
-{
-  GtkWidget *sample;
-  GtkRequisition popup_req;
-#if GTK_CHECK_VERSION(2,2,0)
-  GdkScreen *screen;
-  gint monitor_num;
-  GdkRectangle monitor;
-#endif
-
-  sample = GTK_BIN (combo_box)->child;
-
-  *width = sample->allocation.width;
-  gtk_widget_size_request (combo_box->priv->popup_window, &popup_req);
-  *height = popup_req.height;
-
-  gdk_window_get_origin (sample->window, x, y);
-
-  if (combo_box->priv->cell_view_frame)
-    {
-       *x -= GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
-	     GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
-       *width += 2 * (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
-            GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
-    }
-
-  if (GTK_WIDGET_NO_WINDOW (sample))
-    {
-      *x += sample->allocation.x;
-      *y += sample->allocation.y;
-    }
-
-#if GTK_CHECK_VERSION(2,2,0)
-  screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
-  monitor_num = gdk_screen_get_monitor_at_window (screen,
-						  GTK_WIDGET (combo_box)->window);
-  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
-
-  if (*x < monitor.x)
-    *x = monitor.x;
-  else if (*x + *width > monitor.x + monitor.width)
-    *x = monitor.x + monitor.width - *width;
-
-  if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
-    *y += sample->allocation.height;
-  else
-    *y -= *height;
-#endif /* Gtk 2.2 */
-}
-
-/**
- * gtk_combo_box_popup:
- * @combo_box: a #GtkComboBox
- *
- * Pops up the menu or dropdown list of @combo_box.
- *
- * This function is mostly intended for use by accessibility technologies;
- * applications should have little use for it.
- *
- * Since: 2.4
- **/
-void
-gtk_combo_box_popup (GtkComboBox *combo_box)
-{
-  gint x, y, width, height;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-
-  if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
-    return;
-
-  if (GTK_IS_MENU (combo_box->priv->popup_widget))
-    {
-      gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget),
-			   combo_box->priv->active_item);
-
-      if (combo_box->priv->wrap_width == 0)
-	{
-          GtkRequisition requisition;
-
-	  width = GTK_WIDGET (combo_box)->allocation.width;
-          gtk_widget_size_request (combo_box->priv->popup_widget, &requisition);
-
-	  gtk_widget_set_size_request (combo_box->priv->popup_widget,
-                                       MAX (width, requisition.width), -1);
-	}
-
-      gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
-		      NULL, NULL,
-#if GTK_CHECK_VERSION(2,2,0)
-		      gtk_combo_box_menu_position,
-#else
-		      NULL,
-#endif
-		      combo_box, 0, 0);
-      return;
-    }
-
-  gtk_widget_show_all (combo_box->priv->popup_frame);
-  gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
-
-  gtk_widget_set_size_request (combo_box->priv->popup_window, width, -1);
-  gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
-
-  /* popup */
-  gtk_widget_show (combo_box->priv->popup_window);
-
-  gtk_widget_grab_focus (combo_box->priv->popup_window);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
-                                TRUE);
-
-  if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view))
-    {
-      gdk_keyboard_grab (combo_box->priv->popup_window->window,
-                         FALSE, GDK_CURRENT_TIME);
-      gtk_widget_grab_focus (combo_box->priv->tree_view);
-    }
-
-  gtk_grab_add (combo_box->priv->popup_window);
-  gdk_pointer_grab (combo_box->priv->popup_window->window, TRUE,
-                    GDK_BUTTON_PRESS_MASK |
-                    GDK_BUTTON_RELEASE_MASK |
-                    GDK_POINTER_MOTION_MASK,
-                    NULL, NULL, GDK_CURRENT_TIME);
-
-  gtk_grab_add (combo_box->priv->tree_view);
-}
-
-/**
- * gtk_combo_box_popdown:
- * @combo_box: a #GtkComboBox
- *
- * Hides the menu or dropdown list of @combo_box.
- *
- * This function is mostly intended for use by accessibility technologies;
- * applications should have little use for it.
- *
- * Since: 2.4
- **/
-void
-gtk_combo_box_popdown (GtkComboBox *combo_box)
-{
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-
-  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (combo_box)))
-    return;
-
-  if (GTK_IS_MENU (combo_box->priv->popup_widget))
-    {
-      gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget));
-      return;
-    }
-
-  gtk_combo_box_list_remove_grabs (combo_box);
-  gtk_widget_hide_all (combo_box->priv->popup_window);
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
-                                FALSE);
-}
-
-static gint
-gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
-                                    GtkTreePath *path)
-{
-  gint padding;
-  GtkRequisition req;
-
-  if (combo_box->priv->cell_view)
-    gtk_widget_style_get (combo_box->priv->cell_view,
-                          "focus-line-width", &padding,
-                          NULL);
-  else
-    padding = 0;
-
-  /* add some pixels for good measure */
-  padding += BONUS_PADDING;
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
-                                   path, &req);
-  else
-    req.width = 0;
-
-  return req.width + padding;
-}
-
-static void
-gtk_combo_box_remeasure (GtkComboBox *combo_box)
-{
-  GtkTreeIter iter;
-  GtkTreePath *path;
-  gint padding = 0;
-
-  if (!combo_box->priv->model ||
-      !gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
-    return;
-
-  combo_box->priv->width = 0;
-
-#if GTK_CHECK_VERSION(2,2,0)
-  path = gtk_tree_path_new_from_indices (0, -1);
-#else
-  path = gtk_tree_path_new_first();
-#endif
-
-  if (combo_box->priv->cell_view)
-    gtk_widget_style_get (combo_box->priv->cell_view,
-                          "focus-line-width", &padding,
-                          NULL);
-  else
-    padding = 0;
-
-  /* add some pixels for good measure */
-  padding += BONUS_PADDING;
-
-  do
-    {
-      GtkRequisition req;
-
-      if (combo_box->priv->cell_view)
-	gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
-                                       path, &req);
-      else
-        req.width = 0;
-
-      combo_box->priv->width = MAX (combo_box->priv->width,
-                                    req.width + padding);
-
-      gtk_tree_path_next (path);
-    }
-  while (gtk_tree_model_iter_next (combo_box->priv->model, &iter));
-
-  gtk_tree_path_free (path);
-}
-
-static void
-gtk_combo_box_size_request (GtkWidget      *widget,
-                            GtkRequisition *requisition)
-{
-  gint width, height;
-  GtkRequisition bin_req;
-
-  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
-
-  /* common */
-  gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
-  gtk_combo_box_remeasure (combo_box);
-  bin_req.width = MAX (bin_req.width, combo_box->priv->width);
-
-  gtk_combo_box_check_appearance (combo_box);
-
-  if (!combo_box->priv->tree_view)
-    {
-      /* menu mode */
-
-      if (combo_box->priv->cell_view)
-        {
-          GtkRequisition button_req, sep_req, arrow_req;
-          gint border_width, xthickness, ythickness;
-
-          gtk_widget_size_request (combo_box->priv->button, &button_req);
-          border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
-          xthickness = combo_box->priv->button->style->xthickness;
-          ythickness = combo_box->priv->button->style->ythickness;
-
-          bin_req.width = MAX (bin_req.width, combo_box->priv->width);
-
-          gtk_widget_size_request (combo_box->priv->separator, &sep_req);
-          gtk_widget_size_request (combo_box->priv->arrow, &arrow_req);
-
-          height = MAX (sep_req.height, arrow_req.height);
-          height = MAX (height, bin_req.height);
-
-          width = bin_req.width + sep_req.width + arrow_req.width;
-
-          height += border_width + 1 + ythickness * 2 + 4;
-          width += border_width + 1 + xthickness * 2 + 4;
-
-          requisition->width = width;
-          requisition->height = height;
-        }
-      else
-        {
-          GtkRequisition but_req;
-
-          gtk_widget_size_request (combo_box->priv->button, &but_req);
-
-          requisition->width = bin_req.width + but_req.width;
-          requisition->height = MAX (bin_req.height, but_req.height);
-        }
-    }
-  else
-    {
-      /* list mode */
-      GtkRequisition button_req, frame_req;
-
-      /* sample + frame */
-      *requisition = bin_req;
-
-      if (combo_box->priv->cell_view_frame)
-        {
-	  gtk_widget_size_request (combo_box->priv->cell_view_frame, &frame_req);
-          requisition->width += 2 *
-            (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
-             GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
-          requisition->height += 2 *
-            (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
-             GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
-        }
-
-      /* the button */
-      gtk_widget_size_request (combo_box->priv->button, &button_req);
-
-      requisition->height = MAX (requisition->height, button_req.height);
-      requisition->width += button_req.width;
-    }
-}
-
-static void
-gtk_combo_box_size_allocate (GtkWidget     *widget,
-                             GtkAllocation *allocation)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
-  GtkAllocation child;
-  GtkRequisition req;
-  gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
-
-  widget->allocation = *allocation;
-
-  gtk_combo_box_check_appearance (combo_box);
-
-  if (!combo_box->priv->tree_view)
-    {
-      if (combo_box->priv->cell_view)
-        {
-          gint border_width, xthickness, ythickness;
-          gint width;
-
-          /* menu mode */
-          gtk_widget_size_allocate (combo_box->priv->button, allocation);
-
-          /* set some things ready */
-          border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
-          xthickness = combo_box->priv->button->style->xthickness;
-          ythickness = combo_box->priv->button->style->ythickness;
-
-          child.x = allocation->x + border_width + 1 + xthickness + 2;
-          child.y = allocation->y + border_width + 1 + ythickness + 2;
-
-          width = MAX(1, allocation->width - (border_width + 1 + xthickness * 2 + 4));
-
-          /* handle the children */
-          gtk_widget_size_request (combo_box->priv->arrow, &req);
-          child.width = req.width;
-          child.height = MAX(1, allocation->height - 2 * (child.y - allocation->y));
-          if (!is_rtl)
-            child.x += width - req.width;
-          gtk_widget_size_allocate (combo_box->priv->arrow, &child);
-          if (is_rtl)
-            child.x += req.width;
-          gtk_widget_size_request (combo_box->priv->separator, &req);
-          child.width = req.width;
-          if (!is_rtl)
-            child.x -= req.width;
-          gtk_widget_size_allocate (combo_box->priv->separator, &child);
-
-          if (is_rtl)
-            {
-              child.x += req.width;
-              child.width = MAX(1, allocation->x + allocation->width
-                - (border_width + 1 + xthickness + 2) - child.x);
-            }
-          else
-            {
-              child.width = child.x;
-              child.x = allocation->x + border_width + 1 + xthickness + 2;
-              child.width = MAX(1, child.width - child.x);
-            }
-
-          gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
-        }
-      else
-        {
-          gtk_widget_size_request (combo_box->priv->button, &req);
-          if (is_rtl)
-            child.x = allocation->x;
-          else
-            child.x = allocation->x + allocation->width - req.width;
-          child.y = allocation->y;
-          child.width = req.width;
-          child.height = allocation->height;
-          gtk_widget_size_allocate (combo_box->priv->button, &child);
-
-          if (is_rtl)
-            child.x = allocation->x + req.width;
-          else
-            child.x = allocation->x;
-          child.y = allocation->y;
-          child.width = MAX(1, allocation->width - req.width);
-          gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
-        }
-    }
-  else
-    {
-      /* list mode */
-
-      /* button */
-      gtk_widget_size_request (combo_box->priv->button, &req);
-      if (is_rtl)
-        child.x = allocation->x;
-      else
-        child.x = allocation->x + allocation->width - req.width;
-      child.y = allocation->y;
-      child.width = req.width;
-      child.height = allocation->height;
-      gtk_widget_size_allocate (combo_box->priv->button, &child);
-
-      /* frame */
-      if (is_rtl)
-        child.x = allocation->x + req.width;
-      else
-        child.x = allocation->x;
-      child.y = allocation->y;
-      child.width = MAX (1, allocation->width - req.width);
-      child.height = allocation->height;
-
-      if (combo_box->priv->cell_view_frame)
-        {
-          gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
-
-          /* the sample */
-          child.x +=
-            GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
-            GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
-          child.y +=
-            GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
-            GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
-          child.width -= 2 * (
-            GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
-            GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
-	  child.width = MAX(1,child.width);
-          child.height -= 2 * (
-            GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
-            GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
-	  child.height = MAX(1,child.height);
-        }
-
-      gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
-    }
-}
-
-static void
-gtk_combo_box_unset_model (GtkComboBox *combo_box)
-{
-  if (combo_box->priv->model)
-    {
-      g_signal_handler_disconnect (combo_box->priv->model,
-				   combo_box->priv->inserted_id);
-      g_signal_handler_disconnect (combo_box->priv->model,
-				   combo_box->priv->deleted_id);
-      g_signal_handler_disconnect (combo_box->priv->model,
-				   combo_box->priv->reordered_id);
-      g_signal_handler_disconnect (combo_box->priv->model,
-				   combo_box->priv->changed_id);
-    }
-
-  /* menu mode */
-  if (!combo_box->priv->tree_view)
-    {
-      if (combo_box->priv->popup_widget)
-        gtk_container_foreach (GTK_CONTAINER (combo_box->priv->popup_widget),
-                               (GtkCallback)gtk_widget_destroy, NULL);
-    }
-
-  if (combo_box->priv->model)
-    {
-      g_object_unref (G_OBJECT (combo_box->priv->model));
-      combo_box->priv->model = NULL;
-    }
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
-}
-
-static void
-gtk_combo_box_forall (GtkContainer *container,
-                      gboolean      include_internals,
-                      GtkCallback   callback,
-                      gpointer      callback_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (container);
-
-  if (include_internals)
-    {
-      if (combo_box->priv->button)
-	(* callback) (combo_box->priv->button, callback_data);
-      if (combo_box->priv->cell_view_frame)
-	(* callback) (combo_box->priv->cell_view_frame, callback_data);
-    }
-
-  if (GTK_BIN (container)->child)
-    (* callback) (GTK_BIN (container)->child, callback_data);
-}
-
-static gboolean
-gtk_combo_box_expose_event (GtkWidget      *widget,
-                            GdkEventExpose *event)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
-
-  if (!combo_box->priv->tree_view)
-    {
-      gtk_container_propagate_expose (GTK_CONTAINER (widget),
-                                      combo_box->priv->button, event);
-    }
-  else
-    {
-      gtk_container_propagate_expose (GTK_CONTAINER (widget),
-                                      combo_box->priv->button, event);
-
-      if (combo_box->priv->cell_view_frame)
-        gtk_container_propagate_expose (GTK_CONTAINER (widget),
-                                        combo_box->priv->cell_view_frame, event);
-    }
-
-  gtk_container_propagate_expose (GTK_CONTAINER (widget),
-                                  GTK_BIN (widget)->child, event);
-
-  return FALSE;
-}
-
-static gboolean
-gtk_combo_box_scroll_event (GtkWidget          *widget,
-                            GdkEventScroll     *event)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
-  gint index;
-  gint items;
-
-  index = gtk_combo_box_get_active (combo_box);
-
-  if (index != -1)
-    {
-      items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
-
-      if (event->direction == GDK_SCROLL_UP)
-        index--;
-      else
-        index++;
-
-      gtk_combo_box_set_active (combo_box, CLAMP (index, 0, items - 1));
-    }
-
-  return TRUE;
-}
-
-/*
- * menu style
- */
-
-static void
-cell_view_sync_cells (GtkComboBox *combo_box,
-                      GtkCellView *cell_view)
-{
-  GSList *k;
-
-  for (k = combo_box->priv->cells; k; k = k->next)
-    {
-      GSList *j;
-      ComboCellInfo *info = (ComboCellInfo *)k->data;
-
-      if (info->pack == GTK_PACK_START)
-        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view),
-                                    info->cell, info->expand);
-      else if (info->pack == GTK_PACK_END)
-        gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (cell_view),
-                                  info->cell, info->expand);
-
-      gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
-                                          info->cell,
-                                          info->func, info->func_data, NULL);
-
-      for (j = info->attributes; j; j = j->next->next)
-        {
-          gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cell_view),
-                                         info->cell,
-                                         j->data,
-                                         GPOINTER_TO_INT (j->next->data));
-        }
-    }
-}
-
-static void
-gtk_combo_box_menu_setup (GtkComboBox *combo_box,
-                          gboolean     add_children)
-{
-  GtkWidget *menu;
-
-  if (combo_box->priv->cell_view)
-    {
-      combo_box->priv->button = gtk_toggle_button_new ();
-      g_signal_connect (combo_box->priv->button, "toggled",
-                        G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
-      g_signal_connect_after (combo_box->priv->button, "key_press_event",
-			      G_CALLBACK (gtk_combo_box_key_press), combo_box);
-      gtk_widget_set_parent (combo_box->priv->button,
-                             GTK_BIN (combo_box)->child->parent);
-
-      combo_box->priv->box = gtk_hbox_new (FALSE, 0);
-      gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
-			 combo_box->priv->box);
-
-      combo_box->priv->separator = gtk_vseparator_new ();
-      gtk_container_add (GTK_CONTAINER (combo_box->priv->box),
-			 combo_box->priv->separator);
-
-      combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
-      gtk_container_add (GTK_CONTAINER (combo_box->priv->box),
-			 combo_box->priv->arrow);
-
-      gtk_widget_show_all (combo_box->priv->button);
-    }
-  else
-    {
-      combo_box->priv->button = gtk_toggle_button_new ();
-      g_signal_connect (combo_box->priv->button, "toggled",
-                        G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
-      g_signal_connect_after (combo_box, "key_press_event",
-			      G_CALLBACK (gtk_combo_box_key_press), combo_box);
-      gtk_widget_set_parent (combo_box->priv->button,
-                             GTK_BIN (combo_box)->child->parent);
-
-      combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
-      gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
-                         combo_box->priv->arrow);
-      gtk_widget_show_all (combo_box->priv->button);
-    }
-
-  g_signal_connect (combo_box->priv->button, "button_press_event",
-                    G_CALLBACK (gtk_combo_box_menu_button_press),
-                    combo_box);
-
-  /* create our funky menu */
-  menu = gtk_menu_new ();
-  g_signal_connect (menu, "key_press_event",
-		    G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
-  gtk_combo_box_set_popup_widget (combo_box, menu);
-
-  /* add items */
-  if (add_children)
-    gtk_combo_box_menu_fill (combo_box);
-
-}
-
-static void
-gtk_combo_box_menu_fill (GtkComboBox *combo_box)
-{
-  gint i, items;
-  GtkWidget *menu;
-  GtkWidget *tmp;
-
-  if (!combo_box->priv->model)
-    return;
-
-  items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
-  menu = combo_box->priv->popup_widget;
-
-  for (i = 0; i < items; i++)
-    {
-      GtkTreePath *path;
-#if GTK_CHECK_VERSION(2,2,0)
-      path = gtk_tree_path_new_from_indices (i, -1);
-#else
-      char buf[32];
-      g_snprintf(buf, sizeof(buf), "%d", i);
-      path = gtk_tree_path_new_from_string(buf);
-#endif
-      tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
-                                                    path);
-      g_signal_connect (tmp, "activate",
-                        G_CALLBACK (gtk_combo_box_menu_item_activate),
-                        combo_box);
-
-      cell_view_sync_cells (combo_box,
-                            GTK_CELL_VIEW (GTK_BIN (tmp)->child));
-
-      gtk_menu_shell_append (GTK_MENU_SHELL (menu), tmp);
-
-      if (combo_box->priv->wrap_width)
-        gtk_combo_box_relayout_item (combo_box, i);
-
-      gtk_widget_show (tmp);
-
-      gtk_tree_path_free (path);
-    }
-}
-
-static void
-gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
-{
-  g_signal_handlers_disconnect_matched (combo_box->priv->button,
-                                        G_SIGNAL_MATCH_DATA,
-                                        0, 0, NULL,
-                                        gtk_combo_box_menu_button_press, NULL);
-
-  /* unparent will remove our latest ref */
-  gtk_widget_unparent (combo_box->priv->button);
-
-  combo_box->priv->box = NULL;
-  combo_box->priv->button = NULL;
-  combo_box->priv->arrow = NULL;
-  combo_box->priv->separator = NULL;
-
-  /* changing the popup window will unref the menu and the children */
-}
-
-/*
- * grid
- */
-
-static void
-gtk_combo_box_item_get_size (GtkComboBox *combo_box,
-                             gint         index_,
-                             gint        *cols,
-                             gint        *rows)
-{
-  GtkTreeIter iter;
-
-  gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter, NULL, index_);
-
-  if (cols)
-    {
-      if (combo_box->priv->col_column == -1)
-        *cols = 1;
-      else
-        gtk_tree_model_get (combo_box->priv->model, &iter,
-                            combo_box->priv->col_column, cols,
-                            -1);
-    }
-
-  if (rows)
-    {
-      if (combo_box->priv->row_column == -1)
-        *rows = 1;
-      else
-        gtk_tree_model_get (combo_box->priv->model, &iter,
-                            combo_box->priv->row_column, rows,
-                            -1);
-    }
-}
-
-static gboolean
-menu_occupied (GtkMenu *menu,
-               guint    left_attach,
-               guint    right_attach,
-               guint    top_attach,
-               guint    bottom_attach)
-{
-  GList *i;
-
-  g_return_val_if_fail (GTK_IS_MENU (menu), TRUE);
-  g_return_val_if_fail (left_attach < right_attach, TRUE);
-  g_return_val_if_fail (top_attach < bottom_attach, TRUE);
-
-  for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
-    {
-      guint l, r, b, t;
-      gboolean h_intersect = FALSE;
-      gboolean v_intersect = FALSE;
-
-      gtk_container_child_get (GTK_CONTAINER (menu), i->data,
-                               "left_attach", &l,
-                               "right_attach", &r,
-                               "bottom_attach", &b,
-                               "top_attach", &t,
-                               NULL);
-
-      /* look if this item intersects with the given coordinates */
-      h_intersect  = left_attach <= l && l <= right_attach;
-      h_intersect &= left_attach <= r && r <= right_attach;
-
-      v_intersect  = top_attach <= t && t <= bottom_attach;
-      v_intersect &= top_attach <= b && b <= bottom_attach;
-
-      if (h_intersect && v_intersect)
-        return TRUE;
-    }
-
-  return FALSE;
-}
-
-static void
-gtk_combo_box_relayout_item (GtkComboBox *combo_box,
-                             gint         index)
-{
-  gint current_col = 0, current_row = 0;
-  gint rows, cols;
-  GList *list, *nth;
-  GtkWidget *item, *last;
-  GtkWidget *menu;
-
-  menu = combo_box->priv->popup_widget;
-  if (!GTK_IS_MENU_SHELL (menu))
-    return;
-
-  list = gtk_container_get_children (GTK_CONTAINER (menu));
-  nth = g_list_nth (list, index);
-  item = nth->data;
-  if (nth->prev)
-    last = nth->prev->data;
-  else
-    last = NULL;
-  g_list_free (list);
-
-  gtk_combo_box_item_get_size (combo_box, index, &cols, &rows);
-
-   if (combo_box->priv->col_column == -1 &&
-      combo_box->priv->row_column == -1 &&
-      last)
-    {
-      gtk_container_child_get (GTK_CONTAINER (menu),
-			       last,
-			       "right_attach", &current_col,
-			       "top_attach", &current_row,
-			       NULL);
-      if (current_col + cols > combo_box->priv->wrap_width)
-	{
-	  current_col = 0;
-	  current_row++;
-	}
-    }
-  else
-    {
-      /* look for a good spot */
-      while (1)
-	{
-	  if (current_col + cols > combo_box->priv->wrap_width)
-	    {
-	      current_col = 0;
-	      current_row++;
-	    }
-
-	  if (!menu_occupied (GTK_MENU (menu),
-			      current_col, current_col + cols,
-			      current_row, current_row + rows))
-	    break;
-
-	  current_col++;
-	}
-    }
-
-  /* set attach props */
-  gtk_menu_attach (GTK_MENU (menu), item,
-                   current_col, current_col + cols,
-                   current_row, current_row + rows);
-}
-
-static void
-gtk_combo_box_relayout (GtkComboBox *combo_box)
-{
-  GList *list, *j;
-  GtkWidget *menu;
-
-  menu = combo_box->priv->popup_widget;
-
-  /* do nothing unless we are in menu style and realized */
-  if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
-    return;
-
-  /* get rid of all children */
-  list = gtk_container_get_children (GTK_CONTAINER (menu));
-
-  for (j = g_list_last (list); j; j = j->prev)
-    gtk_container_remove (GTK_CONTAINER (menu), j->data);
-
-  g_list_free (list);
-
-  /* and relayout */
-  gtk_combo_box_menu_fill (combo_box);
-}
-
-/* callbacks */
-static gboolean
-gtk_combo_box_menu_button_press (GtkWidget      *widget,
-                                 GdkEventButton *event,
-                                 gpointer        user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-
-  if (! GTK_IS_MENU (combo_box->priv->popup_widget))
-    return FALSE;
-
-  if (event->type == GDK_BUTTON_PRESS && event->button == 1)
-    {
-      combo_box->priv->popup_in_progress = TRUE;
-
-      gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget),
-			   combo_box->priv->active_item);
-
-      if (combo_box->priv->wrap_width == 0)
-	{
-          GtkRequisition requisition;
-          gint width;
-
-	  width = GTK_WIDGET (combo_box)->allocation.width;
-          gtk_widget_size_request (combo_box->priv->popup_widget, &requisition);
-
-	  gtk_widget_set_size_request (combo_box->priv->popup_widget,
-                                       MAX (width, requisition.width), -1);
-	}
-
-      gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
-                      NULL, NULL,
-#if GTK_CHECK_VERSION(2,2,0)
-                      gtk_combo_box_menu_position,
-#else
-		      NULL,
-#endif
-		      combo_box, event->button, event->time);
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-static void
-gtk_combo_box_menu_item_activate (GtkWidget *item,
-                                  gpointer   user_data)
-{
-  gint index;
-  GtkWidget *menu;
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-
-  menu = combo_box->priv->popup_widget;
-  g_return_if_fail (GTK_IS_MENU (menu));
-
-  index = g_list_index (GTK_MENU_SHELL (menu)->children, item);
-
-  gtk_combo_box_set_active (combo_box, index);
-}
-
-static void
-gtk_combo_box_model_row_inserted (GtkTreeModel     *model,
-				  GtkTreePath      *path,
-				  GtkTreeIter      *iter,
-				  gpointer          user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-  gint index = gtk_tree_path_get_indices (path)[0];
-
-  if (combo_box->priv->active_item >= index)
-    combo_box->priv->active_item++;
-
-  if (!combo_box->priv->tree_view)
-    gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
-}
-
-static void
-gtk_combo_box_model_row_deleted (GtkTreeModel     *model,
-				 GtkTreePath      *path,
-				 gpointer          user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-  gint index = gtk_tree_path_get_indices (path)[0];
-
-  if (!combo_box->priv->tree_view)
-    gtk_combo_box_menu_row_deleted (model, path, user_data);
-
-  if (index == combo_box->priv->active_item)
-    {
-      gint items = gtk_tree_model_iter_n_children (model, NULL);
-
-      if (items == 0)
-	gtk_combo_box_set_active_internal (combo_box, -1);
-      else if (index == items)
-	gtk_combo_box_set_active_internal (combo_box, index - 1);
-      else
-	gtk_combo_box_set_active_internal (combo_box, index);
-    }
-  else if (combo_box->priv->active_item > index)
-    combo_box->priv->active_item--;
-}
-
-static void
-gtk_combo_box_model_rows_reordered (GtkTreeModel    *model,
-				    GtkTreePath     *path,
-				    GtkTreeIter     *iter,
-				    gint            *new_order,
-				    gpointer         user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-  gint items = gtk_tree_model_iter_n_children (model, NULL);
-  gint i;
-
-  for (i = 0; i < items; i++)
-    if (new_order[i] == combo_box->priv->active_item)
-      {
-	combo_box->priv->active_item = i;
-	break;
-      }
-
-  if (!combo_box->priv->tree_view)
-    gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
-}
-
-static void
-gtk_combo_box_model_row_changed (GtkTreeModel     *model,
-				 GtkTreePath      *path,
-				 GtkTreeIter      *iter,
-				 gpointer          user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-  gint index = gtk_tree_path_get_indices (path)[0];
-
-  if (index == combo_box->priv->active_item &&
-      combo_box->priv->cell_view)
-    gtk_widget_queue_resize (GTK_WIDGET (combo_box->priv->cell_view));
-
-  if (combo_box->priv->tree_view)
-    gtk_combo_box_list_row_changed (model, path, iter, user_data);
-  else
-    gtk_combo_box_menu_row_changed (model, path, iter, user_data);
-}
-
-
-static void
-gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
-                                 GtkTreePath  *path,
-                                 GtkTreeIter  *iter,
-                                 gpointer      user_data)
-{
-  GtkWidget *menu;
-  GtkWidget *item;
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-
-  if (!combo_box->priv->popup_widget)
-    return;
-
-  menu = combo_box->priv->popup_widget;
-  g_return_if_fail (GTK_IS_MENU (menu));
-
-  item = gtk_cell_view_menu_item_new_from_model (model, path);
-  g_signal_connect (item, "activate",
-                    G_CALLBACK (gtk_combo_box_menu_item_activate),
-                    combo_box);
-
-  cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (item)->child));
-
-  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item,
-                         gtk_tree_path_get_indices (path)[0]);
-  gtk_widget_show (item);
-}
-
-static void
-gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
-                                GtkTreePath  *path,
-                                gpointer      user_data)
-{
-  gint index;
-  GtkWidget *menu;
-  GtkWidget *item;
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-
-  if (!combo_box->priv->popup_widget)
-    return;
-
-  index = gtk_tree_path_get_indices (path)[0];
-
-  menu = combo_box->priv->popup_widget;
-  g_return_if_fail (GTK_IS_MENU (menu));
-
-  item = g_list_nth_data (GTK_MENU_SHELL (menu)->children, index);
-  g_return_if_fail (GTK_IS_MENU_ITEM (item));
-
-  gtk_container_remove (GTK_CONTAINER (menu), item);
-}
-
-static void
-gtk_combo_box_menu_rows_reordered  (GtkTreeModel     *model,
-				    GtkTreePath      *path,
-				    GtkTreeIter      *iter,
-				    gint             *new_order,
-				    gpointer          user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-
-  gtk_combo_box_relayout (combo_box);
-}
-
-static void
-gtk_combo_box_menu_row_changed (GtkTreeModel *model,
-                                GtkTreePath  *path,
-                                GtkTreeIter  *iter,
-                                gpointer      user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-  gint width;
-
-  if (!combo_box->priv->popup_widget)
-    return;
-
-  if (combo_box->priv->wrap_width)
-    gtk_combo_box_relayout_item (combo_box,
-                                 gtk_tree_path_get_indices (path)[0]);
-
-  width = gtk_combo_box_calc_requested_width (combo_box, path);
-
-  if (width > combo_box->priv->width)
-    {
-      if (combo_box->priv->cell_view)
-	{
-	  gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
-	  gtk_widget_queue_resize (combo_box->priv->cell_view);
-	}
-      combo_box->priv->width = width;
-    }
-}
-
-/*
- * list style
- */
-
-static void
-gtk_combo_box_list_setup (GtkComboBox *combo_box)
-{
-  GSList *i;
-  GtkTreeSelection *sel;
-
-  combo_box->priv->button = gtk_toggle_button_new ();
-  gtk_widget_set_parent (combo_box->priv->button,
-                         GTK_BIN (combo_box)->child->parent);
-  g_signal_connect (combo_box->priv->button, "button_press_event",
-                    G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
-  g_signal_connect (combo_box->priv->button, "toggled",
-                    G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
-  g_signal_connect_after (combo_box, "key_press_event",
-			  G_CALLBACK (gtk_combo_box_key_press), combo_box);
-
-  combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
-  gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
-                     combo_box->priv->arrow);
-  combo_box->priv->separator = NULL;
-  gtk_widget_show_all (combo_box->priv->button);
-
-  if (combo_box->priv->cell_view)
-    {
-      combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
-      gtk_widget_set_parent (combo_box->priv->cell_view_frame,
-                             GTK_BIN (combo_box)->child->parent);
-      gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
-                                 GTK_SHADOW_IN);
-
-      gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
-					  &GTK_WIDGET (combo_box)->style->base[GTK_WIDGET_STATE (combo_box)]);
-
-      combo_box->priv->box = gtk_event_box_new ();
-      /*
-      gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->box),
-					FALSE);
-      */
-
-      gtk_container_add (GTK_CONTAINER (combo_box->priv->cell_view_frame),
-			 combo_box->priv->box);
-
-      gtk_widget_show_all (combo_box->priv->cell_view_frame);
-
-      g_signal_connect (combo_box->priv->box, "button_press_event",
-			G_CALLBACK (gtk_combo_box_list_button_pressed),
-			combo_box);
-    }
-
-  combo_box->priv->tree_view = gtk_tree_view_new ();
-  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
-  gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
-  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view),
-                                     FALSE);
-  /*
-  _gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (combo_box->priv->tree_view),
-				      TRUE);
-  */
-  if (combo_box->priv->model)
-    gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
-			     combo_box->priv->model);
-
-  g_signal_connect (combo_box->priv->tree_view, "button_press_event",
-                    G_CALLBACK (gtk_combo_box_list_button_pressed),
-                    combo_box);
-  g_signal_connect (combo_box->priv->tree_view, "button_release_event",
-                    G_CALLBACK (gtk_combo_box_list_button_released),
-                    combo_box);
-  g_signal_connect (combo_box->priv->tree_view, "key_press_event",
-                    G_CALLBACK (gtk_combo_box_list_key_press),
-                    combo_box);
-
-  combo_box->priv->column = gtk_tree_view_column_new ();
-  gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
-                               combo_box->priv->column);
-
-  /* sync up */
-  for (i = combo_box->priv->cells; i; i = i->next)
-    {
-      GSList *j;
-      ComboCellInfo *info = (ComboCellInfo *)i->data;
-
-      if (info->pack == GTK_PACK_START)
-        gtk_tree_view_column_pack_start (combo_box->priv->column,
-                                         info->cell, info->expand);
-      else if (info->pack == GTK_PACK_END)
-        gtk_tree_view_column_pack_end (combo_box->priv->column,
-                                       info->cell, info->expand);
-
-      for (j = info->attributes; j; j = j->next->next)
-        {
-          gtk_tree_view_column_add_attribute (combo_box->priv->column,
-                                              info->cell,
-                                              j->data,
-                                              GPOINTER_TO_INT (j->next->data));
-        }
-    }
-
-  if (combo_box->priv->active_item != -1)
-    {
-      GtkTreePath *path;
-
-#if GTK_CHECK_VERSION(2,2,0)
-      path = gtk_tree_path_new_from_indices (combo_box->priv->active_item, -1);
-#else
-      char buf[32];
-      g_snprintf(buf, sizeof(buf), "%d", combo_box->priv->active_item);
-      path = gtk_tree_path_new_from_string(buf);
-#endif
-      if (path)
-        {
-          gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
-                                    path, NULL, FALSE);
-          gtk_tree_path_free (path);
-        }
-    }
-
-  /* set sample/popup widgets */
-  gtk_combo_box_set_popup_widget (combo_box, combo_box->priv->tree_view);
-
-  gtk_widget_show (combo_box->priv->tree_view);
-}
-
-static void
-gtk_combo_box_list_destroy (GtkComboBox *combo_box)
-{
-  /* disconnect signals */
-  g_signal_handlers_disconnect_matched (combo_box->priv->tree_view,
-                                        G_SIGNAL_MATCH_DATA,
-                                        0, 0, NULL, NULL, combo_box);
-  g_signal_handlers_disconnect_matched (combo_box->priv->button,
-                                        G_SIGNAL_MATCH_DATA,
-                                        0, 0, NULL,
-                                        gtk_combo_box_list_button_pressed,
-                                        NULL);
-  if (combo_box->priv->box)
-    g_signal_handlers_disconnect_matched (combo_box->priv->box,
-					  G_SIGNAL_MATCH_DATA,
-					  0, 0, NULL,
-					  gtk_combo_box_list_button_pressed,
-					  NULL);
-
-  /* destroy things (unparent will kill the latest ref from us)
-   * last unref on button will destroy the arrow
-   */
-  gtk_widget_unparent (combo_box->priv->button);
-  combo_box->priv->button = NULL;
-  combo_box->priv->arrow = NULL;
-
-  if (combo_box->priv->cell_view)
-    {
-      g_object_set (G_OBJECT (combo_box->priv->cell_view),
-                    "background_set", FALSE,
-                    NULL);
-    }
-
-  if (combo_box->priv->cell_view_frame)
-    {
-      gtk_widget_unparent (combo_box->priv->cell_view_frame);
-      combo_box->priv->cell_view_frame = NULL;
-      combo_box->priv->box = NULL;
-    }
-
-  gtk_widget_destroy (combo_box->priv->tree_view);
-
-  combo_box->priv->tree_view = NULL;
-  combo_box->priv->popup_widget = NULL;
-}
-
-/* callbacks */
-static void
-gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box)
-{
-  if (combo_box->priv->tree_view &&
-      GTK_WIDGET_HAS_GRAB (combo_box->priv->tree_view))
-    {
-      gtk_grab_remove (combo_box->priv->tree_view);
-    }
-
-  if (combo_box->priv->popup_window &&
-      GTK_WIDGET_HAS_GRAB (combo_box->priv->popup_window))
-    {
-      gtk_grab_remove (combo_box->priv->popup_window);
-      gdk_keyboard_ungrab (GDK_CURRENT_TIME);
-      gdk_pointer_ungrab (GDK_CURRENT_TIME);
-    }
-}
-
-static gboolean
-gtk_combo_box_list_button_pressed (GtkWidget      *widget,
-                                   GdkEventButton *event,
-                                   gpointer        data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (data);
-
-  GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
-
-  if (ewidget == combo_box->priv->tree_view)
-    return TRUE;
-
-  if ((ewidget != combo_box->priv->button && ewidget != combo_box->priv->box) ||
-      gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
-    return FALSE;
-
-  gtk_combo_box_popup (combo_box);
-
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
-                                TRUE);
-
-  combo_box->priv->popup_in_progress = TRUE;
-
-  return TRUE;
-}
-
-static gboolean
-gtk_combo_box_list_button_released (GtkWidget      *widget,
-                                    GdkEventButton *event,
-                                    gpointer        data)
-{
-  gboolean ret;
-  GtkTreePath *path = NULL;
-
-  GtkComboBox *combo_box = GTK_COMBO_BOX (data);
-
-  gboolean popup_in_progress = FALSE;
-
-  GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
-
-  if (combo_box->priv->popup_in_progress)
-    {
-      popup_in_progress = TRUE;
-      combo_box->priv->popup_in_progress = FALSE;
-    }
-
-  if (ewidget != combo_box->priv->tree_view)
-    {
-      if (ewidget == combo_box->priv->button &&
-          !popup_in_progress &&
-          gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
-        {
-          gtk_combo_box_popdown (combo_box);
-          return TRUE;
-        }
-
-      /* released outside treeview */
-      if (ewidget != combo_box->priv->button)
-        {
-          gtk_combo_box_popdown (combo_box);
-
-          return TRUE;
-        }
-
-      return FALSE;
-    }
-
-  /* select something cool */
-  ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
-                                       event->x, event->y,
-                                       &path,
-                                       NULL, NULL, NULL);
-
-  if (!ret)
-    return TRUE; /* clicked outside window? */
-
-  gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
-  gtk_combo_box_popdown (combo_box);
-
-  gtk_tree_path_free (path);
-
-  return TRUE;
-}
-
-static gboolean
-gtk_combo_box_key_press (GtkWidget   *widget,
-			 GdkEventKey *event,
-			 gpointer     data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (data);
-  guint state = event->state & gtk_accelerator_get_default_mod_mask ();
-  gint items = 0;
-  gint index = gtk_combo_box_get_active (combo_box);
-  gint new_index;
-
-  if (combo_box->priv->model)
-    items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
-
-  if ((event->keyval == GDK_Down || event->keyval == GDK_KP_Down) &&
-      state == GDK_MOD1_MASK)
-    {
-      gtk_combo_box_popup (combo_box);
-
-      return TRUE;
-    }
-
-  switch (event->keyval)
-    {
-    case GDK_Down:
-    case GDK_KP_Down:
-      new_index = index + 1;
-      break;
-    case GDK_Up:
-    case GDK_KP_Up:
-      new_index = index - 1;
-      break;
-    case GDK_Page_Up:
-    case GDK_KP_Page_Up:
-    case GDK_Home:
-    case GDK_KP_Home:
-      new_index = 0;
-      break;
-    case GDK_Page_Down:
-    case GDK_KP_Page_Down:
-    case GDK_End:
-    case GDK_KP_End:
-      new_index = items - 1;
-      break;
-    default:
-      return FALSE;
-    }
-
-  if (items > 0)
-    gtk_combo_box_set_active (combo_box, CLAMP (new_index, 0, items - 1));
-
-  return TRUE;
-}
-
-static gboolean
-gtk_combo_box_menu_key_press (GtkWidget   *widget,
-			      GdkEventKey *event,
-			      gpointer     data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (data);
-  guint state = event->state & gtk_accelerator_get_default_mod_mask ();
-
-  if ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) &&
-      state == GDK_MOD1_MASK)
-    {
-      gtk_combo_box_popdown (combo_box);
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-static gboolean
-gtk_combo_box_list_key_press (GtkWidget   *widget,
-                              GdkEventKey *event,
-                              gpointer     data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (data);
-  guint state = event->state & gtk_accelerator_get_default_mod_mask ();
-
-  if (event->keyval == GDK_Escape ||
-      ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) &&
-       state == GDK_MOD1_MASK))
-    {
-      /* reset active item -- this is incredibly lame and ugly */
-      gtk_combo_box_set_active (combo_box,
-				gtk_combo_box_get_active (combo_box));
-
-      gtk_combo_box_popdown (combo_box);
-
-      return TRUE;
-    }
-
-  if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter ||
-      event->keyval == GDK_space || event->keyval == GDK_KP_Space)
-  {
-    gboolean ret = FALSE;
-    GtkTreeIter iter;
-    GtkTreeModel *model = NULL;
-
-    if (combo_box->priv->model)
-      {
-	GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
-
-	ret = gtk_tree_selection_get_selected (sel, &model, &iter);
-      }
-    if (ret)
-      {
-	GtkTreePath *path;
-
-	path = gtk_tree_model_get_path (model, &iter);
-	if (path)
-	  {
-	    gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
-	    gtk_tree_path_free (path);
-	  }
-      }
-
-    gtk_combo_box_popdown (combo_box);
-
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
-static void
-gtk_combo_box_list_row_changed (GtkTreeModel *model,
-                                GtkTreePath  *path,
-                                GtkTreeIter  *iter,
-                                gpointer      data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (data);
-  gint width;
-
-  width = gtk_combo_box_calc_requested_width (combo_box, path);
-
-  if (width > combo_box->priv->width)
-    {
-      if (combo_box->priv->cell_view)
-	{
-	  gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
-	  gtk_widget_queue_resize (combo_box->priv->cell_view);
-	}
-      combo_box->priv->width = width;
-    }
-}
-
-/*
- * GtkCellLayout implementation
- */
-static void
-gtk_combo_box_cell_layout_pack_start (GtkCellLayout   *layout,
-                                      GtkCellRenderer *cell,
-                                      gboolean         expand)
-{
-  ComboCellInfo *info;
-  GtkComboBox *combo_box;
-  GtkWidget *menu;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  combo_box = GTK_COMBO_BOX (layout);
-
-  g_object_ref (G_OBJECT (cell));
-  gtk_object_sink (GTK_OBJECT (cell));
-
-  info = g_new0 (ComboCellInfo, 1);
-  info->cell = cell;
-  info->expand = expand;
-  info->pack = GTK_PACK_START;
-
-  combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
-                                cell, expand);
-
-  if (combo_box->priv->column)
-    gtk_tree_view_column_pack_start (combo_box->priv->column, cell, expand);
-
-  menu = combo_box->priv->popup_widget;
-  if (GTK_IS_MENU (menu))
-    {
-      GList *i, *list;
-
-      list = gtk_container_get_children (GTK_CONTAINER (menu));
-      for (i = list; i; i = i->next)
-        {
-          GtkCellView *view;
-
-          if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
-            view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
-          else
-            view = GTK_CELL_VIEW (i->data);
-
-          gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), cell, expand);
-        }
-      g_list_free (list);
-    }
-}
-
-static void
-gtk_combo_box_cell_layout_pack_end (GtkCellLayout   *layout,
-                                    GtkCellRenderer *cell,
-                                    gboolean         expand)
-{
-  ComboCellInfo *info;
-  GtkComboBox *combo_box;
-  GtkWidget *menu;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  combo_box = GTK_COMBO_BOX (layout);
-
-  g_object_ref (G_OBJECT (cell));
-  gtk_object_sink (GTK_OBJECT (cell));
-
-  info = g_new0 (ComboCellInfo, 1);
-  info->cell = cell;
-  info->expand = expand;
-  info->pack = GTK_PACK_END;
-
-  combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
-                              cell, expand);
-
-  if (combo_box->priv->column)
-    gtk_tree_view_column_pack_end (combo_box->priv->column, cell, expand);
-
-  menu = combo_box->priv->popup_widget;
-  if (GTK_IS_MENU (menu))
-    {
-      GList *i, *list;
-
-      list = gtk_container_get_children (GTK_CONTAINER (menu));
-      for (i = list; i; i = i->next)
-        {
-          GtkCellView *view;
-
-          if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
-            view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
-          else
-            view = GTK_CELL_VIEW (i->data);
-
-          gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (view), cell, expand);
-        }
-      g_list_free (list);
-    }
-}
-
-static void
-gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
-{
-  GtkWidget *menu;
-  GtkComboBox *combo_box;
-  GSList *i;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (layout));
-
-  combo_box = GTK_COMBO_BOX (layout);
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->cell_view));
-
-  if (combo_box->priv->column)
-    gtk_tree_view_column_clear (combo_box->priv->column);
-
-  for (i = combo_box->priv->cells; i; i = i->next)
-    {
-     ComboCellInfo *info = (ComboCellInfo *)i->data;
-
-      gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
-      g_object_unref (G_OBJECT (info->cell));
-      g_free (info);
-      i->data = NULL;
-    }
-  g_slist_free (combo_box->priv->cells);
-  combo_box->priv->cells = NULL;
-
-  menu = combo_box->priv->popup_widget;
-  if (GTK_IS_MENU (menu))
-    {
-      GList *i, *list;
-
-      list = gtk_container_get_children (GTK_CONTAINER (menu));
-      for (i = list; i; i = i->next)
-        {
-          GtkCellView *view;
-
-          if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
-            view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
-          else
-            view = GTK_CELL_VIEW (i->data);
-
-          gtk_cell_layout_clear (GTK_CELL_LAYOUT (view));
-        }
-      g_list_free (list);
-    }
-}
-
-static void
-gtk_combo_box_cell_layout_add_attribute (GtkCellLayout   *layout,
-                                         GtkCellRenderer *cell,
-                                         const gchar     *attribute,
-                                         gint             column)
-{
-  ComboCellInfo *info;
-  GtkComboBox *combo_box;
-  GtkWidget *menu;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  combo_box = GTK_COMBO_BOX (layout);
-
-  info = gtk_combo_box_get_cell_info (combo_box, cell);
-
-  info->attributes = g_slist_prepend (info->attributes,
-                                      GINT_TO_POINTER (column));
-  info->attributes = g_slist_prepend (info->attributes,
-                                      g_strdup (attribute));
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
-                                   cell, attribute, column);
-
-  if (combo_box->priv->column)
-    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
-                                   cell, attribute, column);
-
-  menu = combo_box->priv->popup_widget;
-  if (GTK_IS_MENU (menu))
-    {
-      GList *i, *list;
-
-      list = gtk_container_get_children (GTK_CONTAINER (menu));
-      for (i = list; i; i = i->next)
-        {
-          GtkCellView *view;
-
-          if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
-            view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
-          else
-            view = GTK_CELL_VIEW (i->data);
-
-          gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), cell,
-                                         attribute, column);
-        }
-      g_list_free (list);
-    }
-
-  gtk_widget_queue_resize (GTK_WIDGET (combo_box));
-}
-
-static void
-gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
-                                              GtkCellRenderer       *cell,
-                                              GtkCellLayoutDataFunc  func,
-                                              gpointer               func_data,
-                                              GDestroyNotify         destroy)
-{
-  ComboCellInfo *info;
-  GtkComboBox *combo_box;
-  GtkWidget *menu;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (layout));
-
-  combo_box = GTK_COMBO_BOX (layout);
-
-  info = gtk_combo_box_get_cell_info (combo_box, cell);
-  g_return_if_fail (info != NULL);
-
-  if (info->destroy)
-    {
-      GDestroyNotify d = info->destroy;
-
-      info->destroy = NULL;
-      d (info->func_data);
-    }
-
-  info->func = func;
-  info->func_data = func_data;
-  info->destroy = destroy;
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell, func, func_data, NULL);
-
-  if (combo_box->priv->column)
-    gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->column), cell, func, func_data, NULL);
-
-  menu = combo_box->priv->popup_widget;
-  if (GTK_IS_MENU (menu))
-    {
-      GList *i, *list;
-
-      list = gtk_container_get_children (GTK_CONTAINER (menu));
-      for (i = list; i; i = i->next)
-        {
-          GtkCellView *view;
-
-          if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
-            view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
-          else
-            view = GTK_CELL_VIEW (i->data);
-
-          gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (view), cell,
-                                              func, func_data, NULL);
-        }
-      g_list_free (list);
-    }
-
-  gtk_widget_queue_resize (GTK_WIDGET (combo_box));
-}
-
-static void
-gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout   *layout,
-                                            GtkCellRenderer *cell)
-{
-  ComboCellInfo *info;
-  GtkComboBox *combo_box;
-  GtkWidget *menu;
-  GSList *list;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  combo_box = GTK_COMBO_BOX (layout);
-
-  info = gtk_combo_box_get_cell_info (combo_box, cell);
-  if (info)
-    {
-      list = info->attributes;
-      while (list && list->next)
-	{
-	  g_free (list->data);
-	  list = list->next->next;
-	}
-      g_slist_free (info->attributes);
-      info->attributes = NULL;
-    }
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell);
-
-  if (combo_box->priv->column)
-    gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->column), cell);
-
-  menu = combo_box->priv->popup_widget;
-  if (GTK_IS_MENU (menu))
-    {
-      GList *i, *list;
-
-      list = gtk_container_get_children (GTK_CONTAINER (menu));
-      for (i = list; i; i = i->next)
-        {
-          GtkCellView *view;
-
-          if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
-            view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
-          else
-            view = GTK_CELL_VIEW (i->data);
-
-          gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (view), cell);
-        }
-      g_list_free (list);
-    }
-
-  gtk_widget_queue_resize (GTK_WIDGET (combo_box));
-}
-
-static void
-gtk_combo_box_cell_layout_reorder (GtkCellLayout   *layout,
-                                   GtkCellRenderer *cell,
-                                   gint             position)
-{
-  ComboCellInfo *info;
-  GtkComboBox *combo_box;
-  GtkWidget *menu;
-  GSList *link;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (layout));
-  g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
-
-  combo_box = GTK_COMBO_BOX (layout);
-
-  info = gtk_combo_box_get_cell_info (combo_box, cell);
-
-  g_return_if_fail (info != NULL);
-  g_return_if_fail (position >= 0);
-
-  link = g_slist_find (combo_box->priv->cells, info);
-
-  g_return_if_fail (link != NULL);
-
-  combo_box->priv->cells = g_slist_delete_link (combo_box->priv->cells, link);
-  combo_box->priv->cells = g_slist_insert (combo_box->priv->cells, info,
-                                           position);
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
-                             cell, position);
-
-  if (combo_box->priv->column)
-    gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->column),
-                             cell, position);
-
-  menu = combo_box->priv->popup_widget;
-  if (GTK_IS_MENU (menu))
-    {
-      GList *i, *list;
-
-      list = gtk_container_get_children (GTK_CONTAINER (menu));
-      for (i = list; i; i = i->next)
-        {
-          GtkCellView *view;
-
-          if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
-            view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
-          else
-            view = GTK_CELL_VIEW (i->data);
-
-          gtk_cell_layout_reorder (GTK_CELL_LAYOUT (view), cell, position);
-        }
-      g_list_free (list);
-    }
-
-  gtk_widget_queue_draw (GTK_WIDGET (combo_box));
-}
-
-/*
- * public API
- */
-
-/**
- * gtk_combo_box_new:
- *
- * Creates a new empty #GtkComboBox.
- *
- * Return value: A new #GtkComboBox.
- *
- * Since: 2.4
- */
-GtkWidget *
-gtk_combo_box_new (void)
-{
-  return GTK_WIDGET (g_object_new (GTK_TYPE_COMBO_BOX, NULL));
-}
-
-/**
- * gtk_combo_box_new_with_model:
- * @model: A #GtkTreeModel.
- *
- * Creates a new #GtkComboBox with the model initialized to @model.
- *
- * Return value: A new #GtkComboBox.
- *
- * Since: 2.4
- */
-GtkWidget *
-gtk_combo_box_new_with_model (GtkTreeModel *model)
-{
-  GtkComboBox *combo_box;
-
-  g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
-
-  combo_box = GTK_COMBO_BOX (g_object_new (GTK_TYPE_COMBO_BOX,
-                                           "model", model,
-                                           NULL));
-
-  return GTK_WIDGET (combo_box);
-}
-
-/**
- * gtk_combo_box_set_wrap_width:
- * @combo_box: A #GtkComboBox.
- * @width: Preferred number of columns.
- *
- * Sets the wrap width of @combo_box to be @width. The wrap width is basically
- * the preferred number of columns when you want to the popup to be layed out
- * in a table.
- *
- * Since: 2.4
- */
-void
-gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
-                              gint         width)
-{
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-  g_return_if_fail (width >= 0);
-
-  if (width != combo_box->priv->wrap_width)
-    {
-      combo_box->priv->wrap_width = width;
-
-      gtk_combo_box_check_appearance (combo_box);
-      gtk_combo_box_relayout (combo_box);
-
-      g_object_notify (G_OBJECT (combo_box), "wrap_width");
-    }
-}
-
-/**
- * gtk_combo_box_set_row_span_column:
- * @combo_box: A #GtkComboBox.
- * @row_span: A column in the model passed during construction.
- *
- * Sets the column with row span information for @combo_box to be @row_span.
- * The row span column contains integers which indicate how many rows
- * an item should span.
- *
- * Since: 2.4
- */
-void
-gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
-                                   gint         row_span)
-{
-  gint col;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-
-  col = gtk_tree_model_get_n_columns (combo_box->priv->model);
-  g_return_if_fail (row_span >= 0 && row_span < col);
-
-  if (row_span != combo_box->priv->row_column)
-    {
-      combo_box->priv->row_column = row_span;
-
-      gtk_combo_box_relayout (combo_box);
-
-      g_object_notify (G_OBJECT (combo_box), "row_span_column");
-    }
-}
-
-/**
- * gtk_combo_box_set_column_span_column:
- * @combo_box: A #GtkComboBox.
- * @column_span: A column in the model passed during construction.
- *
- * Sets the column with column span information for @combo_box to be
- * @column_span. The column span column contains integers which indicate
- * how many columns an item should span.
- *
- * Since: 2.4
- */
-void
-gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
-                                      gint         column_span)
-{
-  gint col;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-
-  col = gtk_tree_model_get_n_columns (combo_box->priv->model);
-  g_return_if_fail (column_span >= 0 && column_span < col);
-
-  if (column_span != combo_box->priv->col_column)
-    {
-      combo_box->priv->col_column = column_span;
-
-      gtk_combo_box_relayout (combo_box);
-
-      g_object_notify (G_OBJECT (combo_box), "column_span_column");
-    }
-}
-
-/**
- * gtk_combo_box_get_active:
- * @combo_box: A #GtkComboBox.
- *
- * Returns the index of the currently active item, or -1 if there's no
- * active item.
- *
- * Return value: An integer which is the index of the currently active item, or
- * -1 if there's no active item.
- *
- * Since: 2.4
- */
-gint
-gtk_combo_box_get_active (GtkComboBox *combo_box)
-{
-  g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
-
-  return combo_box->priv->active_item;
-}
-
-/**
- * gtk_combo_box_set_active:
- * @combo_box: A #GtkComboBox.
- * @index_: An index in the model passed during construction, or -1 to have
- * no active item.
- *
- * Sets the active item of @combo_box to be the item at @index.
- *
- * Since: 2.4
- */
-void
-gtk_combo_box_set_active (GtkComboBox *combo_box,
-                          gint         index_)
-{
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-  /* -1 means "no item selected" */
-  g_return_if_fail (index_ >= -1);
-
-  if (combo_box->priv->active_item == index_)
-    return;
-
-  gtk_combo_box_set_active_internal (combo_box, index_);
-}
-
-static void
-gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
-				   gint         index)
-{
-  GtkTreePath *path;
-
-  combo_box->priv->active_item = index;
-
-  if (index == -1)
-    {
-      if (combo_box->priv->tree_view)
-        gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)));
-      else
-        {
-          GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
-
-          if (GTK_IS_MENU (menu))
-            gtk_menu_set_active (menu, -1);
-        }
-
-      if (combo_box->priv->cell_view)
-        gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
-    }
-  else
-    {
-#if GTK_CHECK_VERSION(2,2,0)
-      path = gtk_tree_path_new_from_indices (index, -1);
-#else
-      char buf[32];
-      g_snprintf(buf, sizeof(buf), "%d", index);
-      path = gtk_tree_path_new_from_string(buf);
-#endif
-
-      if (combo_box->priv->tree_view)
-	gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view), path, NULL, FALSE);
-      else
-        {
-          GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
-
-          if (GTK_IS_MENU (menu))
-            gtk_menu_set_active (GTK_MENU (menu), index);
-        }
-
-      if (combo_box->priv->cell_view)
-	gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), path);
-
-      gtk_tree_path_free (path);
-    }
-
-  g_signal_emit_by_name (combo_box, "changed", NULL, NULL);
-}
-
-
-/**
- * gtk_combo_box_get_active_iter:
- * @combo_box: A #GtkComboBox
- * @iter: The uninitialized #GtkTreeIter.
- *
- * Sets @iter to point to the current active item, if it exists.
- *
- * Return value: %TRUE, if @iter was set
- *
- * Since: 2.4
- **/
-gboolean
-gtk_combo_box_get_active_iter (GtkComboBox     *combo_box,
-                               GtkTreeIter     *iter)
-{
-  GtkTreePath *path;
-  gint active;
-  gboolean retval;
-#if !GTK_CHECK_VERSION(2,2,0)
-  char buf[32];
-#endif
-
-  g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
-
-  active = gtk_combo_box_get_active (combo_box);
-  if (active < 0)
-    return FALSE;
-
-#if GTK_CHECK_VERSION(2,2,0)
-  path = gtk_tree_path_new_from_indices (active, -1);
-#else
-  g_snprintf(buf, sizeof(buf), "%d", active);
-  path = gtk_tree_path_new_from_string(buf);
-#endif
-  retval = gtk_tree_model_get_iter (gtk_combo_box_get_model (combo_box),
-                                    iter, path);
-  gtk_tree_path_free (path);
-
-  return retval;
-}
-
-/**
- * gtk_combo_box_set_active_iter:
- * @combo_box: A #GtkComboBox
- * @iter: The #GtkTreeIter.
- *
- * Sets the current active item to be the one referenced by @iter.
- * @iter must correspond to a path of depth one.
- *
- * Since: 2.4
- **/
-void
-gtk_combo_box_set_active_iter (GtkComboBox     *combo_box,
-                               GtkTreeIter     *iter)
-{
-  GtkTreePath *path;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-
-  path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
-  g_return_if_fail (path != NULL);
-  g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
-
-  gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
-  gtk_tree_path_free (path);
-}
-
-/**
- * gtk_combo_box_set_model:
- * @combo_box: A #GtkComboBox.
- * @model: A #GtkTreeModel.
- *
- * Sets the model used by @combo_box to be @model. Will unset a previously set
- * model (if applicable). If @model is %NULL, then it will unset the model.
- *
- * Note that this function does not clear the cell renderers, you have to
- * call gtk_combo_box_cell_layout_clear() yourself if you need to set up
- * different cell renderers for the new model.
- *
- * Since: 2.4
- */
-void
-gtk_combo_box_set_model (GtkComboBox  *combo_box,
-                         GtkTreeModel *model)
-{
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-
-  if (!model)
-    {
-      gtk_combo_box_unset_model (combo_box);
-      return;
-    }
-
-  g_return_if_fail (GTK_IS_TREE_MODEL (model));
-
-  if (model == combo_box->priv->model)
-    return;
-
-  if (combo_box->priv->model)
-    gtk_combo_box_unset_model (combo_box);
-
-  combo_box->priv->model = model;
-  g_object_ref (G_OBJECT (combo_box->priv->model));
-
-  combo_box->priv->inserted_id =
-    g_signal_connect (combo_box->priv->model, "row_inserted",
-		      G_CALLBACK (gtk_combo_box_model_row_inserted),
-		      combo_box);
-  combo_box->priv->deleted_id =
-    g_signal_connect (combo_box->priv->model, "row_deleted",
-		      G_CALLBACK (gtk_combo_box_model_row_deleted),
-		      combo_box);
-  combo_box->priv->reordered_id =
-    g_signal_connect (combo_box->priv->model, "rows_reordered",
-		      G_CALLBACK (gtk_combo_box_model_rows_reordered),
-		      combo_box);
-  combo_box->priv->changed_id =
-    g_signal_connect (combo_box->priv->model, "row_changed",
-		      G_CALLBACK (gtk_combo_box_model_row_changed),
-		      combo_box);
-
-  if (combo_box->priv->tree_view)
-    {
-      /* list mode */
-      gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
-                               combo_box->priv->model);
-    }
-  else
-    {
-      /* menu mode */
-      if (combo_box->priv->popup_widget)
-	gtk_combo_box_menu_fill (combo_box);
-
-    }
-
-  if (combo_box->priv->cell_view)
-    gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
-                             combo_box->priv->model);
-}
-
-/**
- * gtk_combo_box_get_model
- * @combo_box: A #GtkComboBox.
- *
- * Returns the #GtkTreeModel which is acting as data source for @combo_box.
- *
- * Return value: A #GtkTreeModel which was passed during construction.
- *
- * Since: 2.4
- */
-GtkTreeModel *
-gtk_combo_box_get_model (GtkComboBox *combo_box)
-{
-  g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
-
-  return combo_box->priv->model;
-}
-
-
-/* convenience API for simple text combos */
-
-/**
- * gtk_combo_box_new_text:
- *
- * Convenience function which constructs a new text combo box, which is a
- * #GtkComboBox just displaying strings. If you use this function to create
- * a text combo box, you should only manipulate its data source with the
- * following convenience functions: gtk_combo_box_append_text(),
- * gtk_combo_box_insert_text(), gtk_combo_box_prepend_text() and
- * gtk_combo_box_remove_text().
- *
- * Return value: A new text combo box.
- *
- * Since: 2.4
- */
-GtkWidget *
-gtk_combo_box_new_text (void)
-{
-  GtkWidget *combo_box;
-  GtkCellRenderer *cell;
-  GtkListStore *store;
-
-  store = gtk_list_store_new (1, G_TYPE_STRING);
-  combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
-  g_object_unref (store);
-
-  cell = gtk_cell_renderer_text_new ();
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
-  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
-                                  "text", 0,
-                                  NULL);
-
-  return combo_box;
-}
-
-/**
- * gtk_combo_box_append_text:
- * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
- * @text: A string.
- *
- * Appends @string to the list of strings stored in @combo_box. Note that
- * you can only use this function with combo boxes constructed with
- * gtk_combo_box_new_text().
- *
- * Since: 2.4
- */
-void
-gtk_combo_box_append_text (GtkComboBox *combo_box,
-                           const gchar *text)
-{
-  GtkTreeIter iter;
-  GtkListStore *store;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-  g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
-  g_return_if_fail (text != NULL);
-
-  store = GTK_LIST_STORE (combo_box->priv->model);
-
-  gtk_list_store_append (store, &iter);
-  gtk_list_store_set (store, &iter, 0, text, -1);
-}
-
-/**
- * gtk_combo_box_insert_text:
- * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
- * @position: An index to insert @text.
- * @text: A string.
- *
- * Inserts @string at @position in the list of strings stored in @combo_box.
- * Note that you can only use this function with combo boxes constructed
- * with gtk_combo_box_new_text().
- *
- * Since: 2.4
- */
-void
-gtk_combo_box_insert_text (GtkComboBox *combo_box,
-                           gint         position,
-                           const gchar *text)
-{
-  GtkTreeIter iter;
-  GtkListStore *store;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-  g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
-  g_return_if_fail (position >= 0);
-  g_return_if_fail (text != NULL);
-
-  store = GTK_LIST_STORE (combo_box->priv->model);
-
-  gtk_list_store_insert (store, &iter, position);
-  gtk_list_store_set (store, &iter, 0, text, -1);
-}
-
-/**
- * gtk_combo_box_prepend_text:
- * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
- * @text: A string.
- *
- * Prepends @string to the list of strings stored in @combo_box. Note that
- * you can only use this function with combo boxes constructed with
- * gtk_combo_box_new_text().
- *
- * Since: 2.4
- */
-void
-gtk_combo_box_prepend_text (GtkComboBox *combo_box,
-                            const gchar *text)
-{
-  GtkTreeIter iter;
-  GtkListStore *store;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-  g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
-  g_return_if_fail (text != NULL);
-
-  store = GTK_LIST_STORE (combo_box->priv->model);
-
-  gtk_list_store_prepend (store, &iter);
-  gtk_list_store_set (store, &iter, 0, text, -1);
-}
-
-/**
- * gtk_combo_box_remove_text:
- * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
- * @position: Index of the item to remove.
- *
- * Removes the string at @position from @combo_box. Note that you can only use
- * this function with combo boxes constructed with gtk_combo_box_new_text().
- *
- * Since: 2.4
- */
-void
-gtk_combo_box_remove_text (GtkComboBox *combo_box,
-                           gint         position)
-{
-  GtkTreeIter iter;
-  GtkListStore *store;
-
-  g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
-  g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
-  g_return_if_fail (position >= 0);
-
-  store = GTK_LIST_STORE (combo_box->priv->model);
-
-  if (gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter,
-                                     NULL, position))
-    gtk_list_store_remove (store, &iter);
-}
-
-static gboolean
-gtk_combo_box_mnemonic_activate (GtkWidget *widget,
-				 gboolean   group_cycling)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
-
-  gtk_widget_grab_focus (combo_box->priv->button);
-
-  return TRUE;
-}
-
-static void
-gtk_combo_box_destroy (GtkObject *object)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (object);
-
-  gtk_combo_box_popdown (combo_box);
-
-  combo_box->priv->destroying = 1;
-
-  GTK_OBJECT_CLASS (parent_class)->destroy (object);
-  combo_box->priv->cell_view = NULL;
-
-  combo_box->priv->destroying = 0;
-}
-
-static void
-gtk_combo_box_finalize (GObject *object)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (object);
-  GSList *i;
-
-  if (GTK_IS_MENU (combo_box->priv->popup_widget))
-    {
-      gtk_combo_box_menu_destroy (combo_box);
-      gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
-      combo_box->priv->popup_widget = NULL;
-    }
-
-  if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
-    gtk_combo_box_list_destroy (combo_box);
-
-  if (combo_box->priv->popup_window)
-    gtk_widget_destroy (combo_box->priv->popup_window);
-
-  gtk_combo_box_unset_model (combo_box);
-
-  for (i = combo_box->priv->cells; i; i = i->next)
-    {
-      ComboCellInfo *info = (ComboCellInfo *)i->data;
-      GSList *list = info->attributes;
-
-      if (info->destroy)
-	info->destroy (info->func_data);
-
-      while (list && list->next)
-	{
-	  g_free (list->data);
-	  list = list->next->next;
-	}
-      g_slist_free (info->attributes);
-
-      g_object_unref (G_OBJECT (info->cell));
-      g_free (info);
-    }
-   g_slist_free (combo_box->priv->cells);
-
-   g_free (combo_box->priv);
-
-   G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-
-/**
- * Code below this point has been pulled in from gtkmenu.c in 2.4.14
- * and is needed to provide gtk_menu_attach()
- */
-
-typedef struct
-{
-  gint left_attach;
-  gint right_attach;
-  gint top_attach;
-  gint bottom_attach;
-  gint effective_left_attach;
-  gint effective_right_attach;
-  gint effective_top_attach;
-  gint effective_bottom_attach;
-} AttachInfo;
-
-#define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
-
-static AttachInfo *
-get_attach_info (GtkWidget *child)
-{
-  GObject *object = G_OBJECT (child);
-  AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY);
-
-  if (!ai)
-    {
-      ai = g_new0 (AttachInfo, 1);
-      g_object_set_data_full (object, ATTACH_INFO_KEY, ai, g_free);
-    }
-
-  return ai;
-}
-
-/**
- * gtk_menu_attach:
- * @menu: a #GtkMenu.
- * @child: a #GtkMenuItem.
- * @left_attach: The column number to attach the left side of the item to.
- * @right_attach: The column number to attach the right side of the item to.
- * @top_attach: The row number to attach the top of the item to.
- * @bottom_attach: The row number to attach the bottom of the item to.
- *
- * Adds a new #GtkMenuItem to a (table) menu. The number of 'cells' that
- * an item will occupy is specified by @left_attach, @right_attach,
- * @top_attach and @bottom_attach. These each represent the leftmost,
- * rightmost, uppermost and lower column and row numbers of the table.
- * (Columns and rows are indexed from zero).
- *
- * Note that this function is not related to gtk_menu_detach().
- *
- * Since: 2.4
- **/
-static void
-gtk_menu_attach (GtkMenu   *menu,
-                 GtkWidget *child,
-                 guint      left_attach,
-                 guint      right_attach,
-                 guint      top_attach,
-                 guint      bottom_attach)
-{
-  GtkMenuShell *menu_shell;
-
-  g_return_if_fail (GTK_IS_MENU (menu));
-  g_return_if_fail (GTK_IS_MENU_ITEM (child));
-  g_return_if_fail (child->parent == NULL ||
-		    child->parent == GTK_WIDGET (menu));
-  g_return_if_fail (left_attach < right_attach);
-  g_return_if_fail (top_attach < bottom_attach);
-
-  menu_shell = GTK_MENU_SHELL (menu);
-
-  if (!child->parent)
-    {
-      AttachInfo *ai = get_attach_info (child);
-
-      ai->left_attach = left_attach;
-      ai->right_attach = right_attach;
-      ai->top_attach = top_attach;
-      ai->bottom_attach = bottom_attach;
-
-      menu_shell->children = g_list_append (menu_shell->children, child);
-
-      gtk_widget_set_parent (child, GTK_WIDGET (menu));
-
-      /*
-      menu_queue_resize (menu);
-      */
-    }
-  else
-    {
-      gtk_container_child_set (GTK_CONTAINER (child->parent), child,
-			       "left_attach",   left_attach,
-			       "right_attach",  right_attach,
-			       "top_attach",    top_attach,
-			       "bottom_attach", bottom_attach,
-			       NULL);
-    }
-}
-#endif /* Gtk 2.4 */
-
-gchar *
-gtk_combo_box_get_active_text (GtkComboBox *combo_box)
-{
-  GtkTreeIter iter;
-  gchar *text = NULL;
-
-  /* g_return_val_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model), NULL); */
-
-  if (gtk_combo_box_get_active_iter (combo_box, &iter))
-    gtk_tree_model_get (gtk_combo_box_get_model(combo_box), &iter,
-    			0, &text, -1);
-  return text;
-}
-
-#endif
--- a/pidgin/pidgincombobox.h	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/* gtkcombobox.h
- * Copyright (C) 2002, 2003  Kristian Rietveld <kris@gtk.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02111-1301, USA.
- */
-
-#ifndef __PIDGIN_COMBO_BOX_H__
-#define __PIDGIN_COMBO_BOX_H__
-
-#ifndef __GTK_COMBO_BOX_H__
-#define __GTK_COMBO_BOX_H__
-
-#include <gtk/gtkbin.h>
-#include <gtk/gtktreemodel.h>
-#include <gtk/gtktreeview.h>
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_COMBO_BOX             (gtk_combo_box_get_type ())
-#define GTK_COMBO_BOX(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_COMBO_BOX, GtkComboBox))
-#define GTK_COMBO_BOX_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_COMBO_BOX, GtkComboBoxClass))
-#define GTK_IS_COMBO_BOX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_COMBO_BOX))
-#define GTK_IS_COMBO_BOX_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_COMBO_BOX))
-#define GTK_COMBO_BOX_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_CLASS ((inst), GTK_TYPE_COMBO_BOX, GtkComboBoxClass))
-
-typedef struct _GtkComboBox        GtkComboBox;
-typedef struct _GtkComboBoxClass   GtkComboBoxClass;
-typedef struct _GtkComboBoxPrivate GtkComboBoxPrivate;
-
-struct _GtkComboBox
-{
-  GtkBin parent_instance;
-
-  /*< private >*/
-  GtkComboBoxPrivate *priv;
-};
-
-struct _GtkComboBoxClass
-{
-  GtkBinClass parent_class;
-
-  /* signals */
-  void     (* changed)          (GtkComboBox *combo_box);
-
-  /* Padding for future expansion */
-  void (*_gtk_reserved0) (void);
-  void (*_gtk_reserved1) (void);
-  void (*_gtk_reserved2) (void);
-  void (*_gtk_reserved3) (void);
-};
-
-
-/* construction */
-GType         gtk_combo_box_get_type         (void);
-GtkWidget    *gtk_combo_box_new              (void);
-GtkWidget    *gtk_combo_box_new_with_model   (GtkTreeModel    *model);
-
-/* grids */
-void          gtk_combo_box_set_wrap_width         (GtkComboBox *combo_box,
-                                                    gint         width);
-void          gtk_combo_box_set_row_span_column    (GtkComboBox *combo_box,
-                                                    gint         row_span);
-void          gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
-                                                    gint         column_span);
-
-/* get/set active item */
-gint          gtk_combo_box_get_active       (GtkComboBox     *combo_box);
-void          gtk_combo_box_set_active       (GtkComboBox     *combo_box,
-                                              gint             index_);
-gboolean      gtk_combo_box_get_active_iter  (GtkComboBox     *combo_box,
-                                              GtkTreeIter     *iter);
-void          gtk_combo_box_set_active_iter  (GtkComboBox     *combo_box,
-                                              GtkTreeIter     *iter);
-
-/* getters and setters */
-void          gtk_combo_box_set_model        (GtkComboBox     *combo_box,
-                                              GtkTreeModel    *model);
-GtkTreeModel *gtk_combo_box_get_model        (GtkComboBox     *combo_box);
-
-/* convenience -- text */
-GtkWidget    *gtk_combo_box_new_text         (void);
-void          gtk_combo_box_append_text      (GtkComboBox     *combo_box,
-                                              const gchar     *text);
-void          gtk_combo_box_insert_text      (GtkComboBox     *combo_box,
-                                              gint             position,
-                                              const gchar     *text);
-void          gtk_combo_box_prepend_text     (GtkComboBox     *combo_box,
-                                              const gchar     *text);
-void          gtk_combo_box_remove_text      (GtkComboBox     *combo_box,
-                                              gint             position);
-/* programmatic control */
-void          gtk_combo_box_popup            (GtkComboBox     *combo_box);
-void          gtk_combo_box_popdown          (GtkComboBox     *combo_box);
-
-G_END_DECLS
-
-#endif /* __GTK_COMBO_BOX_H__ */
-
-gchar        *gtk_combo_box_get_active_text  (GtkComboBox     *combo_box);
-
-#endif /* __PIDGIN_COMBOX_BOX_H__ */
--- a/pidgin/pidginstock.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pidginstock.c	Mon Mar 08 22:53:02 2010 +0000
@@ -53,49 +53,29 @@
 } const stock_icons[] = {
 
 	{ PIDGIN_STOCK_ACTION,          NULL,      GTK_STOCK_EXECUTE          },
-#if GTK_CHECK_VERSION(2,6,0)
 	{ PIDGIN_STOCK_ALIAS,           NULL,      GTK_STOCK_EDIT             },
-#else
-	{ PIDGIN_STOCK_ALIAS,           "buttons", "edit.png"                 },
-#endif
 	{ PIDGIN_STOCK_CHAT,            NULL,      GTK_STOCK_JUMP_TO          },
 	{ PIDGIN_STOCK_CLEAR,           NULL,      GTK_STOCK_CLEAR            },
 	{ PIDGIN_STOCK_CLOSE_TABS,      NULL,      GTK_STOCK_CLOSE            },
 	{ PIDGIN_STOCK_DEBUG,           NULL,      GTK_STOCK_PROPERTIES       },
 	{ PIDGIN_STOCK_DOWNLOAD,        NULL,      GTK_STOCK_GO_DOWN          },
-#if GTK_CHECK_VERSION(2,6,0)
 	{ PIDGIN_STOCK_DISCONNECT,      NULL,      GTK_STOCK_DISCONNECT       },
-#else
-	{ PIDGIN_STOCK_DISCONNECT,      "icons",   "stock_disconnect_16.png"  },
-#endif
 	{ PIDGIN_STOCK_FGCOLOR,         "buttons", "change-fgcolor-small.png" },
-#if GTK_CHECK_VERSION(2,6,0)
 	{ PIDGIN_STOCK_EDIT,            NULL,      GTK_STOCK_EDIT             },
-#else
-	{ PIDGIN_STOCK_EDIT,            "buttons", "edit.png"                 },
-#endif
 	{ PIDGIN_STOCK_FILE_CANCELED,   NULL,      GTK_STOCK_CANCEL           },
 	{ PIDGIN_STOCK_FILE_DONE,       NULL,      GTK_STOCK_APPLY            },
 	{ PIDGIN_STOCK_IGNORE,          NULL,      GTK_STOCK_DIALOG_ERROR     },
 	{ PIDGIN_STOCK_INVITE,          NULL,      GTK_STOCK_JUMP_TO          },
 	{ PIDGIN_STOCK_MODIFY,          NULL,      GTK_STOCK_PREFERENCES      },
 	{ PIDGIN_STOCK_ADD,             NULL,	   GTK_STOCK_ADD			  },
-#if GTK_CHECK_VERSION(2,6,0)
 	{ PIDGIN_STOCK_PAUSE,           NULL,      GTK_STOCK_MEDIA_PAUSE      },
-#else
-	{ PIDGIN_STOCK_PAUSE,           "buttons", "pause.png"                },
-#endif
 	{ PIDGIN_STOCK_POUNCE,          NULL,      GTK_STOCK_REDO             },
 	{ PIDGIN_STOCK_OPEN_MAIL,       NULL,      GTK_STOCK_JUMP_TO          },
 	{ PIDGIN_STOCK_SIGN_ON,         NULL,      GTK_STOCK_EXECUTE          },
 	{ PIDGIN_STOCK_SIGN_OFF,        NULL,      GTK_STOCK_CLOSE            },
 	{ PIDGIN_STOCK_TYPED,           "pidgin",  "typed.png"                },
 	{ PIDGIN_STOCK_UPLOAD,          NULL,      GTK_STOCK_GO_UP            },
-#if GTK_CHECK_VERSION(2,8,0)
 	{ PIDGIN_STOCK_INFO,            NULL,      GTK_STOCK_INFO             },
-#else
-	{ PIDGIN_STOCK_INFO,            "buttons", "info.png"                 },
-#endif
 };
 
 static const GtkStockItem stock_items[] =
@@ -204,6 +184,7 @@
 	{ PIDGIN_STOCK_TOOLBAR_VIDEO_CALL,       "toolbar", "video-call.png",       FALSE, TRUE,  TRUE,  TRUE,  TRUE,  FALSE, FALSE, NULL },
 	{ PIDGIN_STOCK_TOOLBAR_AUDIO_VIDEO_CALL, "toolbar", "audio-video-call.png", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
 #endif
+	{ PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, "toolbar", "get-attention.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  }
 };
 
 const SizedStockIcon sized_status_icons [] = {
@@ -218,17 +199,22 @@
 	{ PIDGIN_STOCK_STATUS_LOGOUT,    "status",  "log-out.png",       TRUE, TRUE, TRUE,  TRUE,  TRUE,  FALSE, TRUE,  NULL },
 	{ PIDGIN_STOCK_STATUS_OFFLINE,   "status",  "offline.png",       TRUE, TRUE, TRUE,  TRUE,  TRUE,  FALSE, FALSE, PIDGIN_STOCK_STATUS_OFFLINE_I  },
 	{ PIDGIN_STOCK_STATUS_PERSON,    "status",  "person.png",        TRUE, TRUE, TRUE,  TRUE,  TRUE,  FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_STATUS_MESSAGE,   "toolbar", "message-new.png",   TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+	{ PIDGIN_STOCK_STATUS_MESSAGE,   "toolbar", "message-new.png",   TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }
+};
 
-	{ PIDGIN_STOCK_TRAY_AVAILABLE, "tray", "tray-online.png",        FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_TRAY_INVISIBLE, "tray", "tray-invisible.png",     FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_TRAY_AWAY,      "tray", "tray-away.png",          FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_TRAY_BUSY,      "tray", "tray-busy.png",          FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_TRAY_XA,        "tray", "tray-extended-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_TRAY_OFFLINE,   "tray", "tray-offline.png",       FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_TRAY_CONNECT,   "tray", "tray-connecting.png",    FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_TRAY_PENDING,   "tray", "tray-new-im.png",        FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
-	{ PIDGIN_STOCK_TRAY_EMAIL,     "tray", "tray-message.png",       FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }
+const SizedStockIcon sized_tray_icons [] = {
+#define SIZED_TRAY_ICON(name) \
+	{ name, "tray/hicolor", "status/" name ".png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL }
+	SIZED_TRAY_ICON( PIDGIN_STOCK_TRAY_AVAILABLE ),
+	SIZED_TRAY_ICON( PIDGIN_STOCK_TRAY_INVISIBLE ),
+	SIZED_TRAY_ICON( PIDGIN_STOCK_TRAY_AWAY ),
+	SIZED_TRAY_ICON( PIDGIN_STOCK_TRAY_BUSY ),
+	SIZED_TRAY_ICON( PIDGIN_STOCK_TRAY_XA ),
+	SIZED_TRAY_ICON( PIDGIN_STOCK_TRAY_OFFLINE ),
+	SIZED_TRAY_ICON( PIDGIN_STOCK_TRAY_CONNECT ),
+	SIZED_TRAY_ICON( PIDGIN_STOCK_TRAY_PENDING ),
+	SIZED_TRAY_ICON( PIDGIN_STOCK_TRAY_EMAIL )
+#undef SIZED_TRAY_ICON
 };
 
 /*****************************************************************************
@@ -239,7 +225,6 @@
 find_file_common(const char *name)
 {
 	gchar *filename;
-#if GLIB_CHECK_VERSION(2,6,0)
 	const gchar *userdir;
 	const gchar * const *sysdirs;
 
@@ -256,7 +241,6 @@
 			return filename;
 		g_free(filename);
 	}
-#endif
 	filename = g_build_filename(DATADIR, name, NULL);
 	if (g_file_test(filename, G_FILE_TEST_EXISTS))
 		return filename;
@@ -286,37 +270,30 @@
 
 /* Altered from do_colorshift in gnome-panel */
 static void
-do_alphashift(GdkPixbuf *dest, GdkPixbuf *src)
+do_alphashift(GdkPixbuf *pixbuf)
 {
 	gint i, j;
-	gint width, height, has_alpha, srcrowstride, destrowstride;
-	guchar *target_pixels;
-	guchar *original_pixels;
-	guchar *pixsrc;
-	guchar *pixdest;
+	gint width, height, padding;
+	guchar *pixels;
 	guchar a;
 
-	has_alpha = gdk_pixbuf_get_has_alpha (src);
-	if (!has_alpha)
+	if (!gdk_pixbuf_get_has_alpha(pixbuf))
 		return;
 
-	width = gdk_pixbuf_get_width (src);
-	height = gdk_pixbuf_get_height (src);
-	srcrowstride = gdk_pixbuf_get_rowstride (src);
-	destrowstride = gdk_pixbuf_get_rowstride (dest);
-	target_pixels = gdk_pixbuf_get_pixels (dest);
-	original_pixels = gdk_pixbuf_get_pixels (src);
+	width = gdk_pixbuf_get_width(pixbuf);
+	height = gdk_pixbuf_get_height(pixbuf);
+	padding = gdk_pixbuf_get_rowstride(pixbuf) - width * 4;
+	pixels = gdk_pixbuf_get_pixels(pixbuf);
 
 	for (i = 0; i < height; i++) {
-		pixdest = target_pixels + i*destrowstride;
-		pixsrc = original_pixels + i*srcrowstride;
 		for (j = 0; j < width; j++) {
-			*(pixdest++) = *(pixsrc++);
-			*(pixdest++) = *(pixsrc++);
-			*(pixdest++) = *(pixsrc++);
-			a = *(pixsrc++);
-			*(pixdest++) = a / 2;
+			pixels++;
+			pixels++;
+			pixels++;
+			a = *(pixels);
+			*(pixels++) = a / 2;
 		}
+		pixels += padding;
 	}
 }
 
@@ -364,7 +341,7 @@
 	g_return_if_fail(filename != NULL);
 	pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
 	if (translucent)
-		do_alphashift(pixbuf, pixbuf);
+		do_alphashift(pixbuf);
 
 	source = gtk_icon_source_new();
 	gtk_icon_source_set_pixbuf(source, pixbuf);
@@ -394,7 +371,7 @@
 		g_return_if_fail(filename != NULL);
 		pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
 		if (translucent)
-			do_alphashift(pixbuf, pixbuf);
+			do_alphashift(pixbuf);
 
 		source = gtk_icon_source_new();
 		gtk_icon_source_set_pixbuf(source, pixbuf);
@@ -413,11 +390,9 @@
 static void
 reload_settings(void)
 {
-#if GTK_CHECK_VERSION(2,4,0)
 	GtkSettings *setting = NULL;
 	setting = gtk_settings_get_default();
 	gtk_rc_reset_styles(setting);
-#endif
 }
 
 /*****************************************************************************
@@ -480,6 +455,33 @@
 		}
 	}
 
+	for (i = 0; i < G_N_ELEMENTS(sized_tray_icons); i++)
+	{
+		normal = gtk_icon_set_new();
+		if (sized_tray_icons[i].translucent_name)
+			translucent = gtk_icon_set_new();
+
+#define ADD_SIZED_ICON(name, size) \
+		if (sized_tray_icons[i].name) { \
+			add_sized_icon(normal, name, PIDGIN_ICON_THEME(theme), size, sized_tray_icons[i], FALSE); \
+			if (sized_tray_icons[i].translucent_name) \
+				add_sized_icon(translucent, name, PIDGIN_ICON_THEME(theme), size, sized_tray_icons[i], TRUE); \
+		}
+		ADD_SIZED_ICON(extra_small, "16x16");
+		ADD_SIZED_ICON(small, "22x22");
+		ADD_SIZED_ICON(medium, "32x32");
+		ADD_SIZED_ICON(large, "48x48");
+#undef ADD_SIZED_ICON
+
+		gtk_icon_factory_add(icon_factory, sized_tray_icons[i].name, normal);
+		gtk_icon_set_unref(normal);
+
+		if (sized_tray_icons[i].translucent_name) {
+			gtk_icon_factory_add(icon_factory, sized_tray_icons[i].translucent_name, translucent);
+			gtk_icon_set_unref(translucent);
+		}
+	}
+
 	gtk_widget_destroy(win);
 	g_object_unref(G_OBJECT(icon_factory));
 	reload_settings();
--- a/pidgin/pidginstock.h	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pidginstock.h	Mon Mar 08 22:53:02 2010 +0000
@@ -159,6 +159,7 @@
 #define PIDGIN_STOCK_TOOLBAR_VIDEO_CALL   "pidgin-video-call"
 #define PIDGIN_STOCK_TOOLBAR_AUDIO_VIDEO_CALL "pidgin-audio-video-call"
 #endif
+#define PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION	"pidgin-send-attention"
 
 /* Tray icons */
 #define PIDGIN_STOCK_TRAY_AVAILABLE       "pidgin-tray-available"
--- a/pidgin/pidgintooltip.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pidgintooltip.c	Mon Mar 08 22:53:02 2010 +0000
@@ -119,9 +119,7 @@
 
 	tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
 	name = gtk_window_get_title(GTK_WINDOW(pidgin_tooltip.widget));
-#if GTK_CHECK_VERSION(2,10,0)
 	gtk_window_set_type_hint(GTK_WINDOW(tipwindow), GDK_WINDOW_TYPE_HINT_TOOLTIP);
-#endif
 	gtk_widget_set_app_paintable(tipwindow, TRUE);
 	gtk_window_set_title(GTK_WINDOW(tipwindow), name ? name : _("Pidgin Tooltip"));
 	gtk_window_set_resizable(GTK_WINDOW(tipwindow), FALSE);
@@ -136,41 +134,26 @@
 {
 	int sig;
 	int scr_w, scr_h, x, y, dy;
-#if GTK_CHECK_VERSION(2,2,0)
 	int mon_num;
 	GdkScreen *screen = NULL;
-#endif
 	GdkRectangle mon_size;
 	GtkWidget *tipwindow = pidgin_tooltip.tipwindow;
 	
-#if GTK_CHECK_VERSION(2,2,0)
 	gdk_display_get_pointer(gdk_display_get_default(), &screen, &x, &y, NULL);
 	mon_num = gdk_screen_get_monitor_at_point(screen, x, y);
 	gdk_screen_get_monitor_geometry(screen, mon_num, &mon_size);
 
 	scr_w = mon_size.width + mon_size.x;
 	scr_h = mon_size.height + mon_size.y;
-#else
-	scr_w = gdk_screen_width();
-	scr_h = gdk_screen_height();
-	gdk_window_get_pointer(NULL, &x, &y, NULL);
-	mon_size.x = 0;
-	mon_size.y = 0;
-#endif
 
-#if GTK_CHECK_VERSION(2,4,0)
 	dy = gdk_display_get_default_cursor_size(gdk_display_get_default()) / 2;
-#else
-	dy = 0;
-#endif
 
-#if GTK_CHECK_VERSION(2,2,0)
 	if (w > mon_size.width)
 		w = mon_size.width - 10;
 
 	if (h > mon_size.height)
 		h = mon_size.height - 10;
-#endif
+
 	x -= ((w >> 1) + 4);
 
 	if ((y + h + 4) > scr_h)
--- a/pidgin/pixmaps/Makefile.am	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pixmaps/Makefile.am	Mon Mar 08 22:53:02 2010 +0000
@@ -101,8 +101,6 @@
 		emblems/16/half-operator.png \
 		emblems/16/hiptop.png \
 		emblems/16/male.png \
-		emblems/16/mobile.png \
-		emblems/16/music.png \
 		emblems/16/not-authorized.png \
 		emblems/16/operator.png \
 		emblems/16/qq-member.png \
@@ -120,8 +118,6 @@
 		emblems/scalable/free-for-chat.svg \
 		emblems/scalable/game.svg \
 		emblems/scalable/male.svg \
-		emblems/scalable/mobile.svg \
-		emblems/scalable/music.svg \
 		emblems/scalable/not-authorized.svg \
 		emblems/scalable/qq-member.svg \
 		emblems/scalable/secure.svg \
@@ -207,6 +203,7 @@
 		emotes/default/24/scalable/yin-yang.svg
 
 EMOTES_SMALL_16_SCALABLE = \
+		emotes/small/16/scalable/mobile.svg \
 		emotes/small/16/scalable/pidgin-emotes.svg
 
 PROTOCOLS_16_SCALABLE = \
@@ -446,7 +443,8 @@
 		toolbar/16/send-file.png \
 		toolbar/16/transfer.png \
 		toolbar/16/unblock.png \
-		toolbar/16/video-call.png
+		toolbar/16/video-call.png \
+		toolbar/16/get-attention.png
 
 TOOLBAR_22_SCALABLE = \
 		toolbar/22/scalable/select-avatar.svg \
@@ -485,48 +483,48 @@
 		tray/16/offline_4bit.ico
 
 TRAY_16 = \
-		tray/16/tray-away.png \
-		tray/16/tray-busy.png \
-		tray/16/tray-invisible.png \
-		tray/16/tray-connecting.png \
-		tray/16/tray-extended-away.png \
-		tray/16/tray-message.png \
-		tray/16/tray-new-im.png \
-		tray/16/tray-offline.png \
-		tray/16/tray-online.png
+		tray/hicolor/16x16/status/pidgin-tray-away.png \
+		tray/hicolor/16x16/status/pidgin-tray-busy.png \
+		tray/hicolor/16x16/status/pidgin-tray-invisible.png \
+		tray/hicolor/16x16/status/pidgin-tray-connect.png \
+		tray/hicolor/16x16/status/pidgin-tray-xa.png \
+		tray/hicolor/16x16/status/pidgin-tray-email.png \
+		tray/hicolor/16x16/status/pidgin-tray-pending.png \
+		tray/hicolor/16x16/status/pidgin-tray-offline.png \
+		tray/hicolor/16x16/status/pidgin-tray-available.png
 
 TRAY_22 = \
-		tray/22/tray-away.png \
-		tray/22/tray-busy.png \
-		tray/22/tray-connecting.png \
-		tray/22/tray-extended-away.png \
-		tray/22/tray-invisible.png \
-		tray/22/tray-message.png \
-		tray/22/tray-new-im.png \
-		tray/22/tray-offline.png \
-		tray/22/tray-online.png
+		tray/hicolor/22x22/status/pidgin-tray-away.png \
+		tray/hicolor/22x22/status/pidgin-tray-busy.png \
+		tray/hicolor/22x22/status/pidgin-tray-connect.png \
+		tray/hicolor/22x22/status/pidgin-tray-xa.png \
+		tray/hicolor/22x22/status/pidgin-tray-invisible.png \
+		tray/hicolor/22x22/status/pidgin-tray-email.png \
+		tray/hicolor/22x22/status/pidgin-tray-pending.png \
+		tray/hicolor/22x22/status/pidgin-tray-offline.png \
+		tray/hicolor/22x22/status/pidgin-tray-available.png
 
 TRAY_32 = \
-		tray/32/tray-away.png \
-		tray/32/tray-busy.png \
-		tray/32/tray-connecting.png \
-		tray/32/tray-extended-away.png \
-		tray/32/tray-invisible.png \
-		tray/32/tray-message.png \
-		tray/32/tray-new-im.png \
-		tray/32/tray-offline.png \
-		tray/32/tray-online.png
+		tray/hicolor/32x32/status/pidgin-tray-away.png \
+		tray/hicolor/32x32/status/pidgin-tray-busy.png \
+		tray/hicolor/32x32/status/pidgin-tray-connect.png \
+		tray/hicolor/32x32/status/pidgin-tray-xa.png \
+		tray/hicolor/32x32/status/pidgin-tray-invisible.png \
+		tray/hicolor/32x32/status/pidgin-tray-email.png \
+		tray/hicolor/32x32/status/pidgin-tray-pending.png \
+		tray/hicolor/32x32/status/pidgin-tray-offline.png \
+		tray/hicolor/32x32/status/pidgin-tray-available.png
 
 TRAY_48 = \
-		tray/48/tray-away.png \
-		tray/48/tray-busy.png \
-		tray/48/tray-connecting.png \
-		tray/48/tray-extended-away.png \
-		tray/48/tray-invisible.png \
-		tray/48/tray-message.png \
-		tray/48/tray-new-im.png \
-		tray/48/tray-offline.png \
-		tray/48/tray-online.png
+		tray/hicolor/48x48/status/pidgin-tray-away.png \
+		tray/hicolor/48x48/status/pidgin-tray-busy.png \
+		tray/hicolor/48x48/status/pidgin-tray-connect.png \
+		tray/hicolor/48x48/status/pidgin-tray-xa.png \
+		tray/hicolor/48x48/status/pidgin-tray-invisible.png \
+		tray/hicolor/48x48/status/pidgin-tray-email.png \
+		tray/hicolor/48x48/status/pidgin-tray-pending.png \
+		tray/hicolor/48x48/status/pidgin-tray-offline.png \
+		tray/hicolor/48x48/status/pidgin-tray-available.png
 
 EXTRA_DIST = \
 		edit.png			\
Binary file pidgin/pixmaps/emblems/16/mobile.png has changed
Binary file pidgin/pixmaps/emblems/16/music.png has changed
--- a/pidgin/pixmaps/emblems/scalable/mobile.svg	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:xlink="http://www.w3.org/1999/xlink"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="16"
-   height="16"
-   id="svg2"
-   sodipodi:version="0.32"
-   inkscape:version="0.46"
-   version="1.0"
-   sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/emblems"
-   sodipodi:docname="mobile.svg"
-   inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/emblems/mobile.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90"
-   inkscape:output_extension="org.inkscape.output.svg.inkscape">
-  <defs
-     id="defs4">
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3335">
-      <stop
-         style="stop-color:#204a87;stop-opacity:1;"
-         offset="0"
-         id="stop3337" />
-      <stop
-         style="stop-color:#204a87;stop-opacity:0;"
-         offset="1"
-         id="stop3339" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3300">
-      <stop
-         style="stop-color:#204a87;stop-opacity:1;"
-         offset="0"
-         id="stop3302" />
-      <stop
-         style="stop-color:#204a87;stop-opacity:0;"
-         offset="1"
-         id="stop3304" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3300"
-       id="linearGradient3315"
-       gradientUnits="userSpaceOnUse"
-       x1="-0.0061673229"
-       y1="11.454797"
-       x2="5.6395993"
-       y2="11.454797"
-       gradientTransform="matrix(0.580679,0,0,0.709346,1.207372,-1.624473)" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3335"
-       id="linearGradient3341"
-       x1="-27.615904"
-       y1="21.696934"
-       x2="-19.2363"
-       y2="18.633484"
-       gradientUnits="userSpaceOnUse" />
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3300"
-       id="linearGradient3353"
-       x1="-28.160521"
-       y1="22.551313"
-       x2="-18.170761"
-       y2="17.799398"
-       gradientUnits="userSpaceOnUse" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="36.388908"
-     inkscape:cx="12.47515"
-     inkscape:cy="8.110346"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     fill="#888a85"
-     inkscape:window-width="1268"
-     inkscape:window-height="972"
-     inkscape:window-x="6"
-     inkscape:window-y="21"
-     inkscape:grid-points="true" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1">
-    <rect
-       style="opacity:1;fill:#d3d7cf;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000036;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect1331"
-       width="7.9919939"
-       height="12.993911"
-       x="7.5040998"
-       y="1.504545" />
-    <path
-       sodipodi:type="inkscape:offset"
-       inkscape:radius="-1.0228416"
-       inkscape:original="M 7.5 1.5 L 7.5 14.5 L 15.5 14.5 L 15.5 1.5 L 7.5 1.5 z "
-       xlink:href="#rect1331"
-       style="opacity:0.5;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.99199408px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="path2206"
-       inkscape:href="#rect1331"
-       d="M 8.53125,2.53125 L 8.53125,13.46875 L 14.46875,13.46875 L 14.46875,2.53125 L 8.53125,2.53125 z "
-       transform="matrix(1.010173,0,0,1.005973,-0.115518,-4.844059e-2)" />
-    <rect
-       style="fill:#3465a4;fill-opacity:1;fill-rule:evenodd;stroke:#204a87;stroke-width:0.99999928px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2208"
-       width="3.9807076"
-       height="3.0005636"
-       x="9.5027981"
-       y="3.499944" />
-    <path
-       style="opacity:0.38150288;color:#000000;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
-       d="M 9,3 L 9,6 C 9.6849325,5.8078592 10.39969,5.560911 11.098766,5.2718451 C 12.171483,4.8282801 13.137384,4.3213781 14,3.7572819 L 14,3 L 9,3 z "
-       id="path2220" />
-    <rect
-       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2233"
-       width="1"
-       height="1"
-       x="9"
-       y="8" />
-    <path
-       sodipodi:type="spiral"
-       style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3353);stroke-width:7.08695936px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="path3252"
-       sodipodi:cx="-11"
-       sodipodi:cy="17"
-       sodipodi:expansion="1"
-       sodipodi:revolution="1.1139722"
-       sodipodi:radius="17.723295"
-       sodipodi:argument="-9.424778"
-       sodipodi:t0="0.70807511"
-       d="M -14.027341,29.178804 C -22.234168,28.813068 -27.756867,20.82956 -27.038836,13.013154 C -26.789036,10.293863 -25.851755,7.6562253 -24.369854,5.3654734"
-       transform="matrix(0.120201,5.654854e-2,-4.742318e-2,0.143331,9.572983,5.110321)" />
-    <path
-       sodipodi:type="spiral"
-       style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3341);stroke-width:3.73703289px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="path3254"
-       sodipodi:cx="-11"
-       sodipodi:cy="17"
-       sodipodi:expansion="1"
-       sodipodi:revolution="1.1157676"
-       sodipodi:radius="17.751867"
-       sodipodi:argument="-9.424778"
-       sodipodi:t0="0.69832003"
-       d="M -13.258788,29.188958 C -21.390692,29.337435 -27.331334,21.779752 -27.099873,14.016463 C -27.00639,10.881001 -25.990716,7.8068052 -24.259099,5.1963947"
-       transform="matrix(0.219568,0.111334,-8.662678e-2,0.282195,10.94256,3.73758)" />
-    <path
-       style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3315);stroke-width:1.00000131px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       d="M 4.5539611,12.498317 C 2.0020373,11.459655 0.92343648,7.5201545 1.7880505,4.3367901 C 2.2231942,2.7346624 3.0921081,1.3783732 4.1882245,0.50356567"
-       id="path3256"
-       sodipodi:nodetypes="csc" />
-    <rect
-       style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect3355"
-       width="1"
-       height="1"
-       x="7"
-       y="1" />
-    <rect
-       style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect1340"
-       width="1"
-       height="1"
-       x="15"
-       y="1" />
-    <rect
-       style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect1342"
-       width="1"
-       height="1"
-       x="15"
-       y="14" />
-    <rect
-       style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect1344"
-       width="1"
-       height="1"
-       x="7"
-       y="14" />
-    <rect
-       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2220"
-       width="1"
-       height="1"
-       x="11"
-       y="8" />
-    <rect
-       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2222"
-       width="1"
-       height="1"
-       x="13"
-       y="8" />
-    <rect
-       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2224"
-       width="1"
-       height="1"
-       x="13"
-       y="10" />
-    <rect
-       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2226"
-       width="1"
-       height="1"
-       x="11"
-       y="10" />
-    <rect
-       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2228"
-       width="1"
-       height="1"
-       x="9"
-       y="10" />
-    <rect
-       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2230"
-       width="1"
-       height="1"
-       x="9"
-       y="12" />
-    <rect
-       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2232"
-       width="1"
-       height="1"
-       x="11"
-       y="12" />
-    <rect
-       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="rect2234"
-       width="1"
-       height="1"
-       x="13"
-       y="12" />
-  </g>
-</svg>
--- a/pidgin/pixmaps/emblems/scalable/music.svg	Mon Mar 08 22:51:42 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,189 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:xlink="http://www.w3.org/1999/xlink"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="16"
-   height="16"
-   id="svg2"
-   sodipodi:version="0.32"
-   inkscape:version="0.46"
-   sodipodi:modified="true"
-   version="1.0"
-   sodipodi:docname="music.svg"
-   inkscape:output_extension="org.inkscape.output.svg.inkscape">
-  <defs
-     id="defs4">
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3185">
-      <stop
-         style="stop-color:#4e9a06;stop-opacity:1;"
-         offset="0"
-         id="stop3187" />
-      <stop
-         style="stop-color:#4e9a06;stop-opacity:0"
-         offset="1"
-         id="stop3189" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3177">
-      <stop
-         style="stop-color:#4e9a06;stop-opacity:1;"
-         offset="0"
-         id="stop3179" />
-      <stop
-         style="stop-color:#4e9a06;stop-opacity:0;"
-         offset="1"
-         id="stop3181" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       id="linearGradient3153">
-      <stop
-         style="stop-color:#eeeeec;stop-opacity:1;"
-         offset="0"
-         id="stop3155" />
-      <stop
-         style="stop-color:#eeeeec;stop-opacity:0;"
-         offset="1"
-         id="stop3157" />
-    </linearGradient>
-    <linearGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3153"
-       id="linearGradient3159"
-       x1="2.5409546"
-       y1="10.048674"
-       x2="10.378205"
-       y2="15.928688"
-       gradientUnits="userSpaceOnUse" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3177"
-       id="radialGradient3183"
-       cx="5.2116022"
-       cy="8.4051199"
-       fx="5.2116022"
-       fy="8.4051199"
-       r="2.9404981"
-       gradientTransform="matrix(2.6050387,0,0,2.2888674,-8.415579,-10.767812)"
-       gradientUnits="userSpaceOnUse" />
-    <radialGradient
-       inkscape:collect="always"
-       xlink:href="#linearGradient3185"
-       id="radialGradient3191"
-       cx="5.1978397"
-       cy="8.4135866"
-       fx="5.1978397"
-       fy="8.4135866"
-       r="3.1428281"
-       gradientTransform="matrix(2.8202152,0,0,2.4999643,-9.461187,-12.455954)"
-       gradientUnits="userSpaceOnUse" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     gridtolerance="10000"
-     guidetolerance="10"
-     objecttolerance="10"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="22.4"
-     inkscape:cx="19.784002"
-     inkscape:cy="11.848"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     width="16px"
-     height="16px"
-     showgrid="true"
-     inkscape:window-width="1440"
-     inkscape:window-height="847"
-     inkscape:window-x="0"
-     inkscape:window-y="22" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1">
-    <path
-       sodipodi:type="arc"
-       style="opacity:1;fill:#555753;fill-opacity:1;stroke:#222728;stroke-width:1.14297926;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="path2160"
-       sodipodi:cx="6.4712896"
-       sodipodi:cy="14.484771"
-       sodipodi:rx="3.5986683"
-       sodipodi:ry="2.1781414"
-       d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1  2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1  10.069958 14.484771 z"
-       transform="matrix(0.8336417,0,0,0.918214,4.1052631,-0.8001194)" />
-    <rect
-       style="opacity:1;fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="rect3143"
-       width="1"
-       height="12"
-       x="12"
-       y="1"
-       rx="0.18940361"
-       ry="0.20662212" />
-    <path
-       style="fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
-       d="M 12.006464,3.0203051 L 11.981638,1 C 13.945163,1 17.837365,1.1548232 14.996311,7.9684328 C 15.596089,2.2547574 13.743811,3.0203051 12.006464,3.0203051 z "
-       id="rect3146"
-       sodipodi:nodetypes="cccc" />
-    <path
-       sodipodi:type="arc"
-       style="opacity:1;fill:#555753;fill-opacity:1;stroke:url(#linearGradient3159);stroke-width:1.97969818;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="path3151"
-       sodipodi:cx="6.4712896"
-       sodipodi:cy="14.484771"
-       sodipodi:rx="3.5986683"
-       sodipodi:ry="2.1781414"
-       d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1  2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1  10.069958 14.484771 z"
-       transform="matrix(0.5557611,0,0,0.4591071,5.903509,5.8499391)" />
-    <path
-       sodipodi:type="arc"
-       style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3191);stroke-width:0.98568761;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="path3173"
-       sodipodi:cx="8.0970039"
-       sodipodi:cy="11.122857"
-       sodipodi:rx="4.3089318"
-       sodipodi:ry="3.5513175"
-       d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672"
-       transform="matrix(1.0443424,0,0,0.9855497,-0.4560443,3.7870959e-2)"
-       sodipodi:start="2.8475788"
-       sodipodi:end="4.9442449"
-       sodipodi:open="true" />
-    <path
-       sodipodi:type="arc"
-       style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3183);stroke-width:0.58102763;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       id="path3175"
-       sodipodi:cx="8.0970039"
-       sodipodi:cy="11.122857"
-       sodipodi:rx="4.3089318"
-       sodipodi:ry="3.5513175"
-       d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672"
-       transform="matrix(1.6981233,0,0,1.7443645,-5.9310642,-8.697544)"
-       sodipodi:start="2.8475788"
-       sodipodi:end="4.9442449"
-       sodipodi:open="true" />
-  </g>
-</svg>
--- a/pidgin/pixmaps/emotes/default/24/Makefile.am	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pixmaps/emotes/default/24/Makefile.am	Mon Mar 08 22:53:02 2010 +0000
@@ -2,6 +2,7 @@
     act-up.png \
     airplane.png \
     alien.png \
+    amorous.png \
     angel.png \
     angry.png \
     arrogant.png \
@@ -56,8 +57,9 @@
     dont-know.png \
     drink.png \
     drool.png \
-    eat.png \
+    hungry.png \
     embarrassed.png \
+    excited.png \
     excruciating.png \
     eyeroll.png \
     female-fighter.png \
@@ -81,7 +83,7 @@
     hug-left.png \
     hug-right.png \
     hypnotized.png \
-    in-love.png \
+    in_love.png \
     island.png \
     jump.png \
     kissed.png \
@@ -94,7 +96,6 @@
     liquor.png \
     loser.png \
     love-over.png \
-    love.png \
     lying.png \
     mad-tongue.png \
     mail.png \
@@ -114,7 +115,7 @@
     msn.png \
     musical-note.png \
     music.png \
-    nailbiting.png \
+    nervous.png \
     neutral.png \
     on-the-phone.png \
     party.png \
@@ -144,7 +145,7 @@
     secret.png \
     shame.png \
     sheep.png \
-    shock.png \
+    shocked.png \
     shout.png \
     shut-mouth.png \
     sick.png \
@@ -153,9 +154,9 @@
     sinister.png \
     skeleton.png \
     skywalker.png \
+    sleeping.png \
     sleepy.png \
-    smile-big.png \
-    smile.png \
+    happy.png \
     smirk.png \
     snail.png \
     snicker.png \
@@ -167,7 +168,7 @@
     stop.png \
     struggle.png \
     sun.png \
-    sweat.png \
+    hot.png \
     talktohand.png \
     teeth.png \
     terror.png \
@@ -175,7 +176,7 @@
     thunder.png \
     time-out.png \
     tongue.png \
-    tremble.png \
+    afraid.png \
     turtle.png \
     tv.png \
     umbrella.png \
@@ -188,7 +189,6 @@
     wilt.png \
     wink.png \
     worship.png \
-    yawn.png \
     yin-yang.png
 
 
Binary file pidgin/pixmaps/emotes/default/24/afraid.png has changed
Binary file pidgin/pixmaps/emotes/default/24/amorous.png has changed
--- a/pidgin/pixmaps/emotes/default/24/default.theme.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pixmaps/emotes/default/24/default.theme.in	Mon Mar 08 22:53:02 2010 +0000
@@ -6,16 +6,16 @@
 
 # Default smileys
 [default]
-smile.png           :)      :-)
-smile-big.png       :-D     :-d     :D      :d
+happy.png           :)      :-)
+excited.png         :-D     :-d     :D      :d
 sad.png             :-(     :(
 wink.png            ;-)     ;)
-tongue.png          :P      :-P     :-p     :p
-shock.png           =-O     =-o
+tongue.png          :P      :p      :-P     :-p
+shocked.png         =-O     =-o
 kiss.png            :-*
 glasses-cool.png    8-)
 embarrassed.png     :-[
-crying.png          :'(
+crying.png          :'(     :'-(
 thinking.png        :-/     :-\\
 angel.png           O:-)    o:-)
 shut-mouth.png      :-X
@@ -23,20 +23,70 @@
 foot-in-mouth.png   :-!
 shout.png           >:o     >:O
 ! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)  :(|)     8-|)
-! cyclops.png       O-) o-)
+! monkey.png        :-(|)   :(|)    8-|)
+! cyclops.png       O-)     o-)
+
+
+[XMPP]
+# Following XEP-0038 + GTalk + our default set, in default set order
+# The GTalk strings come from ticket #3307.
+happy.png           :)      :-)     =)
+excited.png         :-D     :-d     :D      :d      =D      =d
+sad.png             :-(     :(
+wink.png            ;-)     ;)      ;^)
+tongue.png          :P      :p      :-P     :-p
+shocked.png         =-O     =-o     :-O     :-o
+kiss.png            :kiss:  :-*
+glasses-cool.png    8-)     B-)
+embarrassed.png     :-[
+crying.png          :'-(    :'(
+thinking.png        :-/     :-\\
+angel.png           O:-)    o:-)
+shut-mouth.png      :-X
+moneymouth.png      :-$
+foot-in-mouth.png   :-!
+shout.png           >:o     >:O
+
+# Following XEP-0038 + GTalk
+angry.png           >:-(    >:(     X-(    x-(
+good.png            :yes:
+bad.png             :no:
+stop.png            :wait:
+rose.png            @->--   :rose:
+phone.png           :telephone:
+mail.png            :email:
+lamp.png            :jabber:
+cake.png            :cake:
+in_love.png         :heart: :love:  <3
+love-over.png       :brokenheart:
+musical-note.png    :music:
+beer.png            :beer:
+coffee.png          :coffee:
+coins.png           :money:
+moon.png            :moon:
+sun.png             :sun:
+star.png            :star:
+
+# Others
+neutral.png         :|      :-|
+victory.png         \\m/
+
+# Hidden icons from the default set.
+! skywalker.png     C:-)    c:-)    C:)     c:)
+! monkey.png        :-(|)   :(|)    8-|)
+! cyclops.png       O-)     o-)
 
 
 # Following AIM 6.1
 [AIM]
-smile.png           :-)     :)
+happy.png           :-)     :)
 wink.png            ;-)     ;)
 sad.png             :-(     :(
-tongue.png          :-P     :P      :-p    :p
-shock.png           =-O
+tongue.png          :P      :p      :-P     :-p
+shocked.png         =-O
 kiss.png            :-*
 shout.png           >:o
-smile-big.png       :-D     :D
+excited.png         :-D     :D
 moneymouth.png      :-$
 foot-in-mouth.png   :-!
 embarrassed.png     :-[
@@ -46,26 +96,27 @@
 shut-mouth.png      :-X
 glasses-cool.png    8-)
 ! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)  :(|)
+! monkey.png        :-(|)   :(|)    8-|)
+! cyclops.png       O-)     o-)
 
 
 # Following Windows Live Messenger 8.1
 [MSN]
-smile.png           :)      :-)
-smile-big.png       :D      :d :-D :-d
+happy.png           :)      :-)
+excited.png         :D      :d      :-D     :-d
 wink.png            ;)      ;-)
-shock.png           :-O     :-o :O :o
-tongue.png          :P      :p :-P :-p
+shocked.png         :-O     :-o     :O      :o
+tongue.png          :-P     :P      :-p     :p
 glasses-cool.png    (H)     (h)
 angry.png           :@      :-@
 embarrassed.png     :$      :-$
-confused.png        :S      :s :-S :-s
+confused.png        :S      :s      :-S     :-s
 sad.png             :(      :-(
 crying.png          :'(
 neutral.png         :|      :-|
 devil.png           (6)
 angel.png           (A)     (a)
-love.png            (L)     (l)
+in_love.png         (L)     (l)
 love-over.png       (U)     (u)
 msn.png             (M)     (m)
 cat.png             (@)
@@ -125,10 +176,11 @@
 thunder.png         (li)
 party.png           <:o)
 eyeroll.png         8-)
-yawn.png            |-) 
+sleepy.png          |-) 
 bunny.png           ('.')
 ! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)  :(|)
+! monkey.png        :-(|)   :(|)    8-|)
+! cyclops.png       O-)     o-)
 
 # Hidden MSN emotes
 cigarette.png      	(ci)    (CI)
@@ -139,7 +191,7 @@
 
 # Following QQ 2006
 [QQ]
-shock.png           /:O      /jy       /surprised
+shocked.png         /:O      /jy       /surprised
 curl-lip.png        /:~      /pz       /curl_lip
 desire.png          /:*      /se       /desire
 dazed.png           /:|      /dazed
@@ -147,13 +199,13 @@
 crying.png          /:<      /ll       /cry
 bashful.png         /:$      /hx       /bashful
 shut-mouth.png      /:X      /bz       /shut_mouth
-sleepy.png          /:Z      /shui     /sleep
+sleeping.png        /:Z      /shui     /sleep
 weep.png            /:'(     /dk       /weep
 embarrassed.png     /:-|     /gg       /embarassed
 pissed-off.png      /:@      /fn       /pissed_off
 act-up.png          /:P      /tp       /act_up
-smile-big.png       /:D      /cy       /toothy_smile
-smile.png           /:)      /wx       /small_smile
+excited.png         /:D      /cy       /toothy_smile
+happy.png           /:)      /wx       /small_smile
 sad.png             /:(      /ng       /sad
 glasses-cool.png    /:+      /kuk      /cool
 doctor.png          /:#      /feid     /SARS
@@ -164,9 +216,9 @@
 disdain.png         /;d      /by       /disdain
 arrogant.png        /;o      /am       /arrogant
 starving.png        /:g      /jie      /starving
-yawn.png            /|-)     /kun      /sleepy
+sleepy.png          /|-)     /kun      /sleepy
 terror.png          /:!      /jk       /terror
-sweat.png           /:L      /sweat
+hot.png             /:L      /sweat
 smirk.png           /:>      /hanx     /smirk
 soldier.png         /:;      /db       /soldier
 struggle.png        /;f      /fendou   /struggle
@@ -180,8 +232,8 @@
 hammer.png          /xx      /qiao     /hammer
 bye.png             /bye     /zj       /bye
 go-away.png         /go      /shan     /go
-tremble.png         /shake   /fad      /shake
-in-love.png         /love    /aiq      /love
+afraid.png          /shake   /fad      /shake
+amorous.png         /love    /aiq      /love
 jump.png            /jump    /tiao     /jump
 search.png          /find    /zhao     /search
 lashes.png          /&       /mm       /beautiful_eyebrows
@@ -200,12 +252,12 @@
 musical-note.png    /music   /yy       /music
 poop.png            /shit    /bb       /shit
 coffee.png          /coffee  /kf       /coffee
-eat.png             /eat     /fan      /eat
+hungry.png          /eat     /fan      /eat
 pill.png            /pill    /yw       /pill
 rose.png            /rose    /mg       /rose
 wilt.png            /fade    /dx       /wilt
 kiss.png            /kiss    /wen      /kiss
-love.png            /heart   /xin      /heart
+in_love.png         /heart   /xin      /heart
 love-over.png       /break   /xs       /broken_heart
 meeting.png         /meeting /hy       /meeting
 present.png         /gift    /lw       /gift
@@ -233,20 +285,21 @@
 girl.png            /<00>    /nv       /woman
 boy.png             /<11>    /nan      /man
 ! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)  :(|)
+! monkey.png        :-(|)   :(|)    8-|)
+! cyclops.png       O-)     o-)
 
 
 # Following ICQ 6.0
 [ICQ]
-smile.png           :-)     :)
+happy.png           :-)     :)
 neutral.png         :-$
 sad.png             :-(     :(
-shock.png           =-O
+shocked.png         =-O
 wink.png            ;-)     ;)
 tongue.png          :-P     :P      :-p     :p
 music.png           [:-}
 laugh.png           *JOKINGLY*
-sleepy.png          *TIRED*
+sleeping.png        *TIRED*
 crying.png          :'(    :'-(
 sick.png            :-!
 kissed.png          *KISSED*
@@ -265,26 +318,27 @@
 good.png            *THUMBS\ UP*
 shout.png           >:o     >:O     :-@
 beer.png            *DRINK*
-smile-big.png       :-D     :D
+excited.png         :-D     :D
 glasses-cool.png    8-)
-in-love.png         *IN\ LOVE*
+amorous.png         *IN\ LOVE*
 ! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)  :(|)
+! monkey.png        :-(|)   :(|)    8-|)
+! cyclops.png       O-)     o-)
 
 
 # Following Yahoo! Messenger 8.1
 [Yahoo]
-smile.png           :)      :-)
+happy.png           :)      :-)
 question.png        :-/     :-\\
-shock.png           :-O     :O      :-o     :o 
+shocked.png         :-O     :O      :-o     :o 
 devil.png           >:)
 angel.png           O:-)    o:-)    0:-)
 sick.png            :-&
-yawn.png            (:|
+sleepy.png          (:|
 hypnotized.png      @-)
 on-the-phone.png    :)]
 sad.png             :(      :-(
-in-love.png         :x      :-x     :X      :-X
+amorous.png         :x      :-x     :X      :-X
 angry.png           X-(     x-(     X(      x(
 crying.png          :((
 glasses-nerdy.png   :-B     :-b
@@ -301,11 +355,11 @@
 thinking.png        :-?
 waiting.png         :-w     :-W
 at-wits-end.png     ~x(     ~X(
-smile-big.png       :D      :-D     :d      :-d
+excited.png         :D      :-D     :d      :-d
 tongue.png          :-P     :P      :-p     :p
 glasses-cool.png    B-)     b-)
 neutral.png         :|      :-|
-sleepy.png          I-)     i-)     |-)
+sleeping.png        I-)     i-)     |-)
 clown.png           :o)     :O)
 doh.png             #-o     #-O
 weep.png            :-<
@@ -321,107 +375,16 @@
 time-out.png        :-t     :-T
 hug-left.png        >:D<    >:d<
 love-over.png       =((
-sweat.png           #:-S    #:-s
+hot.png             #:-S    #:-s
 rotfl.png           =)) :-j :-J 
 loser.png           L-)     l-)
 party.png           <:-P    <:-p
-nailbiting.png      :-SS	:-Ss	:-sS	:-ss
+nervous.png         :-SS	:-Ss	:-sS	:-ss
 cowboy.png          <):)
 desire.png          8->
 ! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)  :(|)
-
-# Hidden Yahoo emotes
-alien.png           =:)     >-)
-beat-up.png         b-(     B-(
-chicken.png         ~:>
-coffee.png          ~o)     ~O)
-cow.png             3:-O    3:-o
-dance.png           \\:D/   \\:d/
-rose.png            @};-
-dont-know.png       :-L     :-l
-skeleton.png        8-X     8-x
-lamp.png            *-:)
-monkey.png          :(|)
-coins.png           $-)
-peace.png           :)>-
-pig.png             :@)
-pray.png            [-o<    [-O<
-pumpkin.png         (~~)
-shame.png           [-X     [-x
-flag.png            **==
-clover.png          %%-
-musical-note.png    :-"
-giggle.png          ;))
-worship.png         ^:)^
-star.png            (*)
-waving.png          >:/
-talktohand.png      :-@
-
-# Only available after activating the Yahoo! Fighter IMVironment
-male-fighter1.png   o->     O->
-male-fighter2.png   o=>     O=>
-female-fighter.png  o-+     O-+
-yin-yang.png        (%)
-
-# Following Yahoo! Messenger 8.1
-[Yahoo JAPAN]
-smile.png           :)      :-)
-question.png        :-/     :-\\
-shock.png           :-O     :O      :-o     :o 
-devil.png           >:)
-angel.png           O:-)    o:-)    0:-)
-sick.png            :-&
-yawn.png            (:|
-hypnotized.png      @-)
-on-the-phone.png    :)]
-sad.png             :(      :-(
-in-love.png         :x      :-x     :X      :-X
-angry.png           X-(     x-(     X(      x(
-crying.png          :((
-glasses-nerdy.png   :-B     :-b
-quiet.png           :-$
-drool.png           =P~     =p~
-lying.png           :^O     :^o
-call-me.png         :-c
-wink.png            ;)      ;-)
-embarrassed.png     :">
-mean.png            :->     :>
-laugh.png           :))     :-))
-bye.png             =;
-arrogant.png        [-(
-thinking.png        :-?
-waiting.png         :-w     :-W
-at-wits-end.png     ~x(     ~X(
-smile-big.png       :D      :-D     :d      :-d
-tongue.png          :-P     :P      :-p     :p
-glasses-cool.png    B-)     b-)
-neutral.png         :|      :-|
-sleepy.png          I-)     i-)     |-)
-clown.png           :o)     :O)
-doh.png             #-o     #-O
-weep.png            :-<
-go-away.png         :-h
-lashes.png          ;;)
-kiss.png            :-*     :*
-confused.png        :-S     :-s
-sarcastic.png       /:)
-eyeroll.png         8-|
-silly.png           8-}
-clap.png            =D>     =d>
-mad-tongue.png      >:P     >:p
-time-out.png        :-t     :-T
-hug-left.png        >:D<    >:d<
-love-over.png       =((
-sweat.png           #:-S    #:-s
-rotfl.png           =)) :-j :-J 
-loser.png           L-)     l-)
-party.png           <:-P    <:-p
-nailbiting.png      :-SS	:-Ss	:-sS	:-ss
-cowboy.png          <):)
-desire.png          8->
-! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)  :(|)
+! monkey.png        :-(|)   :(|)    8-|)
+! cyclops.png       O-)     o-)
 
 # Hidden Yahoo emotes
 alien.png           =:)     >-)
@@ -459,14 +422,14 @@
 
 # Following MySpaceIM Beta 1.0.697.0
 [MySpaceIM]
-smile-big.png	    :D      :-D
+excited.png  	    :D      :-D
 devil.png	    }:)
 confused.png	    :Z
 glasses-nerdy.png   B)
 bulgy-eyes.png	    %)
 freaked-out.png	    :E
 smile.png	    :)      :-)
-in-love.png	    :X
+amorous.png	    :X
 laugh.png	    :))
 mohawk.png	    -:
 mad-tongue.png	    X(
@@ -474,7 +437,7 @@
 glasses-nerdy.png   Q)
 doh.png		    :G
 pirate.png	    P)
-shock.png	    :O
+shocked.png	    :O
 sidefrown.png	    :{
 sinister.png	    :B
 smirk.png	    :,
@@ -484,23 +447,7 @@
 wink.png	    ;-)     ;)
 sad.png		    :[
 kiss.png            :x
+! skywalker.png     C:-)    c:-)    C:)     c:)
+! monkey.png        :-(|)   :(|)    8-|)
+! cyclops.png       O-)     o-)
 
-[XMPP]
-# XMPP emoticons
-smile.png                 :)    :-)    =)
-smile-big.png             :D    :-D    =D
-wink.png                  ;)    ;-)    ;^)
-shock.png                 :-o
-tongue.png                :P    :-P    :-p    :p
-glasses-cool.png          B-)
-angry.png                 X-(
-sad.png                   :(    :-(    =(
-crying.png                :'(
-neutral.png               :-|
-thinking.png              :-/
-love.png                  <3
-monkey.png                :(|)
-victory.png               \\m/
-! skywalker.png     C:-)    c:-)    C:)     c:)
-! monkey.png        :-(|)  :(|)     8-|)
-! cyclops.png       O-) o-)
Binary file pidgin/pixmaps/emotes/default/24/eat.png has changed
Binary file pidgin/pixmaps/emotes/default/24/excited.png has changed
Binary file pidgin/pixmaps/emotes/default/24/happy.png has changed
Binary file pidgin/pixmaps/emotes/default/24/hot.png has changed
Binary file pidgin/pixmaps/emotes/default/24/hungry.png has changed
Binary file pidgin/pixmaps/emotes/default/24/in-love.png has changed
Binary file pidgin/pixmaps/emotes/default/24/in_love.png has changed
Binary file pidgin/pixmaps/emotes/default/24/love.png has changed
Binary file pidgin/pixmaps/emotes/default/24/nailbiting.png has changed
Binary file pidgin/pixmaps/emotes/default/24/nervous.png has changed
Binary file pidgin/pixmaps/emotes/default/24/shock.png has changed
Binary file pidgin/pixmaps/emotes/default/24/shocked.png has changed
Binary file pidgin/pixmaps/emotes/default/24/sleeping.png has changed
Binary file pidgin/pixmaps/emotes/default/24/sleepy.png has changed
Binary file pidgin/pixmaps/emotes/default/24/smile-big.png has changed
Binary file pidgin/pixmaps/emotes/default/24/smile.png has changed
Binary file pidgin/pixmaps/emotes/default/24/sweat.png has changed
Binary file pidgin/pixmaps/emotes/default/24/tremble.png has changed
Binary file pidgin/pixmaps/emotes/default/24/yawn.png has changed
--- a/pidgin/pixmaps/emotes/small/16/Makefile.am	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pixmaps/emotes/small/16/Makefile.am	Mon Mar 08 22:53:02 2010 +0000
@@ -1,4 +1,24 @@
+# These are mood images that are NOT also used in the smiley theme.
+MOODS = \
+    afraid.png \
+    bathing.png \
+    cinema.png \
+    disappointed.png \
+    embarrassed.png \
+    internet.png \
+    music.png \
+    restroom.png \
+    search.png \
+    shopping.png \
+    studying.png \
+    suit.png \
+    surfing.png \
+    typing.png \
+    working.png \
+    writing.png
+
 SMILEYS = \
+    amorous.png \
     angel.png \
     angry.png \
     beer.png \
@@ -12,12 +32,15 @@
     crying.png \
     devil.png \
     dont-know.png \
+    excited.png \
     grin.png \
+    happy.png \
     hug-left.png \
     hug-right.png \
+    in_love.png \
     kiss.png \
-    love.png \
     meeting.png \
+    mobile.png \
     musical-note.png \
     nerdy.png \
     neutral.png \
@@ -27,18 +50,16 @@
     question.png \
     sad.png \
     shame.png \
-    shock.png \
+    shocked.png \
     sick.png \
     silent.png \
+    sleeping.png \
     sleepy.png \
-    smile-big.png \
-    smile.png \
     thinking.png \
     tongue.png \
     tv.png \
     uhm-yeah.png \
-    wink.png \
-    yawn.png
+    wink.png
 
 
 pidginsmileypix_in_files = small.theme.in
@@ -46,6 +67,7 @@
 if INSTALL_PIXMAPS
 pidginsmileypixdir = $(datadir)/pixmaps/pidgin/emotes/small
 pidginsmileypix_DATA = \
+        $(MOODS) \
         $(SMILEYS) \
         theme
 
@@ -56,4 +78,4 @@
 	$< > $@
 endif
 
-EXTRA_DIST = $(SMILEYS) $(pidginsmileypix_in_files) theme
+EXTRA_DIST = $(MOODS) $(SMILEYS) $(pidginsmileypix_in_files) theme
--- a/pidgin/pixmaps/emotes/small/16/TODO	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pixmaps/emotes/small/16/TODO	Mon Mar 08 22:53:02 2010 +0000
@@ -1,7 +1,18 @@
 The following icons where just scaled down from the 24x24 and may need work:
+	afraid.png
+	amorous.png
+	disappointed.png
+	embarrassed.png
+	happy.png
+	hot.png
+	hungry.png
+	mean.png
 	meeting.png
+	nervous.png
 	question.png
+	sarcastic.png
 	search.png
+	shocked.png
 	sleepy.png
 	smile-big.png
 	tv.png
@@ -13,7 +24,7 @@
 	cigarette.png
 	coffee.png
 	console.png
-	love.png
+	in_love.png
 	musical-note.png
 	party.png
 	phone.png
Binary file pidgin/pixmaps/emotes/small/16/afraid.png has changed
Binary file pidgin/pixmaps/emotes/small/16/amorous.png has changed
Binary file pidgin/pixmaps/emotes/small/16/bathing.png has changed
Binary file pidgin/pixmaps/emotes/small/16/cinema.png has changed
Binary file pidgin/pixmaps/emotes/small/16/disappointed.png has changed
Binary file pidgin/pixmaps/emotes/small/16/embarrassed.png has changed
Binary file pidgin/pixmaps/emotes/small/16/excited.png has changed
Binary file pidgin/pixmaps/emotes/small/16/happy.png has changed
Binary file pidgin/pixmaps/emotes/small/16/hot.png has changed
Binary file pidgin/pixmaps/emotes/small/16/hungry.png has changed
Binary file pidgin/pixmaps/emotes/small/16/in_love.png has changed
Binary file pidgin/pixmaps/emotes/small/16/internet.png has changed
Binary file pidgin/pixmaps/emotes/small/16/love.png has changed
Binary file pidgin/pixmaps/emotes/small/16/mean.png has changed
Binary file pidgin/pixmaps/emotes/small/16/mobile.png has changed
Binary file pidgin/pixmaps/emotes/small/16/music.png has changed
Binary file pidgin/pixmaps/emotes/small/16/nervous.png has changed
Binary file pidgin/pixmaps/emotes/small/16/restroom.png has changed
Binary file pidgin/pixmaps/emotes/small/16/sarcastic.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/emotes/small/16/scalable/mobile.svg	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,264 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   version="1.0"
+   sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/emblems"
+   sodipodi:docname="mobile.svg"
+   inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/emblems/mobile.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3335">
+      <stop
+         style="stop-color:#204a87;stop-opacity:1;"
+         offset="0"
+         id="stop3337" />
+      <stop
+         style="stop-color:#204a87;stop-opacity:0;"
+         offset="1"
+         id="stop3339" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3300">
+      <stop
+         style="stop-color:#204a87;stop-opacity:1;"
+         offset="0"
+         id="stop3302" />
+      <stop
+         style="stop-color:#204a87;stop-opacity:0;"
+         offset="1"
+         id="stop3304" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3300"
+       id="linearGradient3315"
+       gradientUnits="userSpaceOnUse"
+       x1="-0.0061673229"
+       y1="11.454797"
+       x2="5.6395993"
+       y2="11.454797"
+       gradientTransform="matrix(0.580679,0,0,0.709346,1.207372,-1.624473)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3335"
+       id="linearGradient3341"
+       x1="-27.615904"
+       y1="21.696934"
+       x2="-19.2363"
+       y2="18.633484"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3300"
+       id="linearGradient3353"
+       x1="-28.160521"
+       y1="22.551313"
+       x2="-18.170761"
+       y2="17.799398"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="36.388908"
+     inkscape:cx="12.47515"
+     inkscape:cy="8.110346"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     fill="#888a85"
+     inkscape:window-width="1268"
+     inkscape:window-height="972"
+     inkscape:window-x="6"
+     inkscape:window-y="21"
+     inkscape:grid-points="true" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <rect
+       style="opacity:1;fill:#d3d7cf;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000036;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect1331"
+       width="7.9919939"
+       height="12.993911"
+       x="7.5040998"
+       y="1.504545" />
+    <path
+       sodipodi:type="inkscape:offset"
+       inkscape:radius="-1.0228416"
+       inkscape:original="M 7.5 1.5 L 7.5 14.5 L 15.5 14.5 L 15.5 1.5 L 7.5 1.5 z "
+       xlink:href="#rect1331"
+       style="opacity:0.5;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.99199408px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="path2206"
+       inkscape:href="#rect1331"
+       d="M 8.53125,2.53125 L 8.53125,13.46875 L 14.46875,13.46875 L 14.46875,2.53125 L 8.53125,2.53125 z "
+       transform="matrix(1.010173,0,0,1.005973,-0.115518,-4.844059e-2)" />
+    <rect
+       style="fill:#3465a4;fill-opacity:1;fill-rule:evenodd;stroke:#204a87;stroke-width:0.99999928px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2208"
+       width="3.9807076"
+       height="3.0005636"
+       x="9.5027981"
+       y="3.499944" />
+    <path
+       style="opacity:0.38150288;color:#000000;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+       d="M 9,3 L 9,6 C 9.6849325,5.8078592 10.39969,5.560911 11.098766,5.2718451 C 12.171483,4.8282801 13.137384,4.3213781 14,3.7572819 L 14,3 L 9,3 z "
+       id="path2220" />
+    <rect
+       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2233"
+       width="1"
+       height="1"
+       x="9"
+       y="8" />
+    <path
+       sodipodi:type="spiral"
+       style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3353);stroke-width:7.08695936px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="path3252"
+       sodipodi:cx="-11"
+       sodipodi:cy="17"
+       sodipodi:expansion="1"
+       sodipodi:revolution="1.1139722"
+       sodipodi:radius="17.723295"
+       sodipodi:argument="-9.424778"
+       sodipodi:t0="0.70807511"
+       d="M -14.027341,29.178804 C -22.234168,28.813068 -27.756867,20.82956 -27.038836,13.013154 C -26.789036,10.293863 -25.851755,7.6562253 -24.369854,5.3654734"
+       transform="matrix(0.120201,5.654854e-2,-4.742318e-2,0.143331,9.572983,5.110321)" />
+    <path
+       sodipodi:type="spiral"
+       style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3341);stroke-width:3.73703289px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="path3254"
+       sodipodi:cx="-11"
+       sodipodi:cy="17"
+       sodipodi:expansion="1"
+       sodipodi:revolution="1.1157676"
+       sodipodi:radius="17.751867"
+       sodipodi:argument="-9.424778"
+       sodipodi:t0="0.69832003"
+       d="M -13.258788,29.188958 C -21.390692,29.337435 -27.331334,21.779752 -27.099873,14.016463 C -27.00639,10.881001 -25.990716,7.8068052 -24.259099,5.1963947"
+       transform="matrix(0.219568,0.111334,-8.662678e-2,0.282195,10.94256,3.73758)" />
+    <path
+       style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3315);stroke-width:1.00000131px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 4.5539611,12.498317 C 2.0020373,11.459655 0.92343648,7.5201545 1.7880505,4.3367901 C 2.2231942,2.7346624 3.0921081,1.3783732 4.1882245,0.50356567"
+       id="path3256"
+       sodipodi:nodetypes="csc" />
+    <rect
+       style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect3355"
+       width="1"
+       height="1"
+       x="7"
+       y="1" />
+    <rect
+       style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect1340"
+       width="1"
+       height="1"
+       x="15"
+       y="1" />
+    <rect
+       style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect1342"
+       width="1"
+       height="1"
+       x="15"
+       y="14" />
+    <rect
+       style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect1344"
+       width="1"
+       height="1"
+       x="7"
+       y="14" />
+    <rect
+       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2220"
+       width="1"
+       height="1"
+       x="11"
+       y="8" />
+    <rect
+       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2222"
+       width="1"
+       height="1"
+       x="13"
+       y="8" />
+    <rect
+       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2224"
+       width="1"
+       height="1"
+       x="13"
+       y="10" />
+    <rect
+       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2226"
+       width="1"
+       height="1"
+       x="11"
+       y="10" />
+    <rect
+       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2228"
+       width="1"
+       height="1"
+       x="9"
+       y="10" />
+    <rect
+       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2230"
+       width="1"
+       height="1"
+       x="9"
+       y="12" />
+    <rect
+       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2232"
+       width="1"
+       height="1"
+       x="11"
+       y="12" />
+    <rect
+       style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="rect2234"
+       width="1"
+       height="1"
+       x="13"
+       y="12" />
+  </g>
+</svg>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pixmaps/emotes/small/16/scalable/music.svg	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:modified="true"
+   version="1.0"
+   sodipodi:docname="music.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3185">
+      <stop
+         style="stop-color:#4e9a06;stop-opacity:1;"
+         offset="0"
+         id="stop3187" />
+      <stop
+         style="stop-color:#4e9a06;stop-opacity:0"
+         offset="1"
+         id="stop3189" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3177">
+      <stop
+         style="stop-color:#4e9a06;stop-opacity:1;"
+         offset="0"
+         id="stop3179" />
+      <stop
+         style="stop-color:#4e9a06;stop-opacity:0;"
+         offset="1"
+         id="stop3181" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient3153">
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1;"
+         offset="0"
+         id="stop3155" />
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:0;"
+         offset="1"
+         id="stop3157" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3153"
+       id="linearGradient3159"
+       x1="2.5409546"
+       y1="10.048674"
+       x2="10.378205"
+       y2="15.928688"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3177"
+       id="radialGradient3183"
+       cx="5.2116022"
+       cy="8.4051199"
+       fx="5.2116022"
+       fy="8.4051199"
+       r="2.9404981"
+       gradientTransform="matrix(2.6050387,0,0,2.2888674,-8.415579,-10.767812)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3185"
+       id="radialGradient3191"
+       cx="5.1978397"
+       cy="8.4135866"
+       fx="5.1978397"
+       fy="8.4135866"
+       r="3.1428281"
+       gradientTransform="matrix(2.8202152,0,0,2.4999643,-9.461187,-12.455954)"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="22.4"
+     inkscape:cx="19.784002"
+     inkscape:cy="11.848"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     width="16px"
+     height="16px"
+     showgrid="true"
+     inkscape:window-width="1440"
+     inkscape:window-height="847"
+     inkscape:window-x="0"
+     inkscape:window-y="22" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:#555753;fill-opacity:1;stroke:#222728;stroke-width:1.14297926;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path2160"
+       sodipodi:cx="6.4712896"
+       sodipodi:cy="14.484771"
+       sodipodi:rx="3.5986683"
+       sodipodi:ry="2.1781414"
+       d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1  2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1  10.069958 14.484771 z"
+       transform="matrix(0.8336417,0,0,0.918214,4.1052631,-0.8001194)" />
+    <rect
+       style="opacity:1;fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="rect3143"
+       width="1"
+       height="12"
+       x="12"
+       y="1"
+       rx="0.18940361"
+       ry="0.20662212" />
+    <path
+       style="fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
+       d="M 12.006464,3.0203051 L 11.981638,1 C 13.945163,1 17.837365,1.1548232 14.996311,7.9684328 C 15.596089,2.2547574 13.743811,3.0203051 12.006464,3.0203051 z "
+       id="rect3146"
+       sodipodi:nodetypes="cccc" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:#555753;fill-opacity:1;stroke:url(#linearGradient3159);stroke-width:1.97969818;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3151"
+       sodipodi:cx="6.4712896"
+       sodipodi:cy="14.484771"
+       sodipodi:rx="3.5986683"
+       sodipodi:ry="2.1781414"
+       d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1  2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1  10.069958 14.484771 z"
+       transform="matrix(0.5557611,0,0,0.4591071,5.903509,5.8499391)" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3191);stroke-width:0.98568761;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3173"
+       sodipodi:cx="8.0970039"
+       sodipodi:cy="11.122857"
+       sodipodi:rx="4.3089318"
+       sodipodi:ry="3.5513175"
+       d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672"
+       transform="matrix(1.0443424,0,0,0.9855497,-0.4560443,3.7870959e-2)"
+       sodipodi:start="2.8475788"
+       sodipodi:end="4.9442449"
+       sodipodi:open="true" />
+    <path
+       sodipodi:type="arc"
+       style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3183);stroke-width:0.58102763;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3175"
+       sodipodi:cx="8.0970039"
+       sodipodi:cy="11.122857"
+       sodipodi:rx="4.3089318"
+       sodipodi:ry="3.5513175"
+       d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672"
+       transform="matrix(1.6981233,0,0,1.7443645,-5.9310642,-8.697544)"
+       sodipodi:start="2.8475788"
+       sodipodi:end="4.9442449"
+       sodipodi:open="true" />
+  </g>
+</svg>
Binary file pidgin/pixmaps/emotes/small/16/shock.png has changed
Binary file pidgin/pixmaps/emotes/small/16/shocked.png has changed
Binary file pidgin/pixmaps/emotes/small/16/shopping.png has changed
Binary file pidgin/pixmaps/emotes/small/16/sleeping.png has changed
Binary file pidgin/pixmaps/emotes/small/16/sleepy.png has changed
--- a/pidgin/pixmaps/emotes/small/16/small.theme.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/pixmaps/emotes/small/16/small.theme.in	Mon Mar 08 22:53:02 2010 +0000
@@ -6,27 +6,58 @@
 
 # Default smileys
 [default]
-smile.png           :)      :-)
-smile-big.png       :-D     :-d     :D      :d
+happy.png           :)      :-)
+excited.png         :-D     :-d     :D      :d
 sad.png             :-(     :(
 wink.png            ;-)     ;)
-tongue.png          :P      :-P     :-p     :p
-shock.png           =-O     =-o
+tongue.png          :P      :p      :-P     :-p
+shocked.png         =-O     =-o
 kiss.png            :-*
-crying.png          :'(
+embarrassed.png     :-[
+crying.png          :'(     :'-(
 thinking.png        :-/     :-\\
 angel.png           O:-)    o:-)
 
 
+[XMPP]
+# Following XEP-0038 + GTalk + our default set, in default set order
+# The GTalk strings come from ticket #3307.
+happy.png           :)      :-)     =)
+excited.png         :-D     :-d     :D      :d      =D      =d
+sad.png             :-(     :(
+wink.png            ;-)     ;)      ;^)
+tongue.png          :P      :p      :-P     :-p
+shocked.png         =-O     =-o     :-O     :-o
+kiss.png            :kiss:  :-*
+embarrassed.png     :-[
+crying.png          :'-(    :'(
+thinking.png        :-/     :-\\
+angel.png           O:-)    o:-)
+
+# Following XEP-0038 + GTalk
+angry.png           >:-(    >:(     X-(    x-(
+phone.png           :telephone:
+in_love.png         :heart: :love:  <3
+musical-note.png    :music:
+beer.png            :beer:
+coffee.png          :coffee:
+
+# Others
+neutral.png         :|      :-|
+
+# Hidden icons from the default set.
+
+
 # Following AIM 6.1
 [AIM]
-smile.png           :-)     :)
+happy.png           :-)     :)
 wink.png            ;-)     ;)
 sad.png             :-(     :(
-tongue.png          :-P     :P      :-p    :p
-shock.png           =-O
+tongue.png          :P      :p      :-P     :-p
+shocked.png         =-O
 kiss.png            :-*
-smile-big.png       :-D     :D
+excited.png         :-D     :D
+embarrassed.png     :-[
 angel.png           O:-)
 thinking.png        :-\\    :-/
 crying.png          :'(
@@ -34,19 +65,20 @@
 
 # Following Windows Live Messenger 8.1
 [MSN]
-smile.png           :)      :-)
-smile-big.png       :D      :d :-D :-d
+happy.png           :)      :-)
+excited.png         :D      :d      :-D     :-d
 wink.png            ;)      ;-)
-shock.png           :-O     :-o :O :o
-tongue.png          :P      :p :-P :-p
+shocked.png         :-O     :-o     :O      :o
+tongue.png          :-P     :P      :-p     :p
 angry.png           :@      :-@
-confused.png        :S      :s :-S :-s
+embarrassed.png     :$      :-$
+confused.png        :S      :s      :-S     :-s
 sad.png             :(      :-(
 crying.png          :'(
 neutral.png         :|      :-|
 devil.png           (6)
 angel.png           (A)     (a)
-love.png            (L)     (l)
+in_love.png         (L)     (l)
 musical-note.png    (8)
 kiss.png            (K)     (k)
 camera.png          (P)     (p)
@@ -55,12 +87,14 @@
 hug-left.png        ({)
 hug-right.png       (})
 beer.png            (B)     (b)
+sarcastic.png       ^o)
 sick.png            +o(
 plate.png           (pl)
+mobile.png          (mp)
 dont-know.png       :^)
 thinking.png        *-)
 party.png           <:o)
-yawn.png            |-) 
+sleepy.png          |-) 
 
 # Hidden MSN emotes
 cigarette.png      	(ci)    (CI)
@@ -69,22 +103,27 @@
 
 # Following QQ 2006
 [QQ]
-shock.png           /:O      /jy       /surprised
+shocked.png         /:O      /jy       /surprised
 party.png           /8-)     /dy       /revel
 crying.png          /:<      /ll       /cry
-sleepy.png          /:Z      /shui     /sleep
-smile-big.png       /:D      /cy       /toothy_smile
-smile.png           /:)      /wx       /small_smile
+sleeping.png        /:Z      /shui     /sleep
+embarrassed.png     /:-|     /gg       /embarassed
+excited.png         /:D      /cy       /toothy_smile
+happy.png           /:)      /wx       /small_smile
 sad.png             /:(      /ng       /sad
 sick.png            /:T      /tu       /vomit
-yawn.png            /|-)     /kun      /sleepy
+sleepy.png          /|-)     /kun      /sleepy
+hot.png             /:L      /sweat
 question.png        /?       /yiw      /question
+afraid.png          /shake   /fad      /shake
+amorous.png         /love    /aiq      /love
 search.png          /find    /zhao     /search
 hug-left.png        /hug     /yb       /hug
 musical-note.png    /music   /yy       /music
 coffee.png          /coffee  /kf       /coffee
+hungry.png          /eat     /fan      /eat
 kiss.png            /kiss    /wen      /kiss
-love.png            /heart   /xin      /heart
+in_love.png         /heart   /xin      /heart
 meeting.png         /meeting /hy       /meeting
 phone.png           /phone   /dh       /phone
 tv.png              /TV      /ds       /TV
@@ -93,45 +132,54 @@
 
 # Following ICQ 6.0
 [ICQ]
-smile.png           :-)     :)
+happy.png           :-)     :)
 neutral.png         :-$
 sad.png             :-(     :(
-shock.png           =-O
+shocked.png         =-O
 wink.png            ;-)     ;)
 tongue.png          :-P     :P      :-p     :p
-sleepy.png          *TIRED*
+music.png           [:-}
+sleeping.png        *TIRED*
 crying.png          :'(    :'-(
 sick.png            :-!
 kiss.png            :-{} :-*
+embarrassed.png     :-[
 devil.png           ]:->
 angel.png           O:-)
 thinking.png        :-\\    :-/
 beer.png            *DRINK*
-smile-big.png       :-D     :D
+excited.png         :-D     :D
+amorous.png         *IN\ LOVE*
 
 
 # Following Yahoo! Messenger 8.1
 [Yahoo]
-smile.png           :)      :-)
+happy.png           :)      :-)
 question.png        :-/     :-\\
-shock.png           :-O     :O      :-o     :o 
+shocked.png         :-O     :O      :-o     :o 
 devil.png           >:)
 angel.png           O:-)    o:-)    0:-)
 sick.png            :-&
-yawn.png            (:|
+sleepy.png          (:|
 sad.png             :(      :-(
+amorous.png         :x      :-x     :X      :-X
 angry.png           X-(     x-(     X(      x(
 crying.png          :((
 wink.png            ;)      ;-)
+embarrassed.png     :">
+mean.png            :->     :>
 thinking.png        :-?
-smile-big.png       :D      :-D     :d      :-d
+excited.png         :D      :-D     :d      :-d
 tongue.png          :-P     :P      :-p     :p
 neutral.png         :|      :-|
-sleepy.png          I-)     i-)     |-)
+sleeping.png        I-)     i-)     |-)
 kiss.png            :-*     :*
 confused.png        :-S     :-s
+sarcastic.png       /:)
 hug-left.png        >:D<    >:d<
+hot.png             #:-S    #:-s
 party.png           <:-P    <:-p
+nervous.png         :-SS	:-Ss	:-sS	:-ss
 
 # Hidden Yahoo emotes
 coffee.png          ~o)     ~O)
@@ -171,30 +219,14 @@
 
 # Following MySpaceIM Beta 1.0.697.0
 [MySpaceIM]
-smile-big.png	    :D      :-D
+excited.png  	    :D      :-D
 devil.png	    }:)
 confused.png	    :Z
-smile.png	    :)      :-)
-shock.png	    :O
+amorous.png	    :X
+shocked.png	    :O
 neutral.png	    :|
 tongue.png	    :P      :p
 wink.png	    ;-)     ;)
 sad.png		    :[
 kiss.png            :x
 
-[XMPP]
-# XMPP emoticons
-smile.png                 :)    :-)    =)
-smile-big.png             :D    :-D    =D
-wink.png                  ;)    ;-)    ;^)
-shock.png                 :-o
-tongue.png                :P    :-P    :-p    :p
-glasses-cool.png          B-)
-angry.png                 X-(
-sad.png                   :(    :-(    =(
-crying.png                :'(
-neutral.png               :-|
-thinking.png              :-/
-love.png                  <3
-monkey.png                :(|)
-victory.png               \\m/
Binary file pidgin/pixmaps/emotes/small/16/smile-big.png has changed
Binary file pidgin/pixmaps/emotes/small/16/smile.png has changed
Binary file pidgin/pixmaps/emotes/small/16/studying.png has changed
Binary file pidgin/pixmaps/emotes/small/16/suit.png has changed
Binary file pidgin/pixmaps/emotes/small/16/surfing.png has changed
Binary file pidgin/pixmaps/emotes/small/16/typing.png has changed
Binary file pidgin/pixmaps/emotes/small/16/working.png has changed
Binary file pidgin/pixmaps/emotes/small/16/writing.png has changed
Binary file pidgin/pixmaps/emotes/small/16/yawn.png has changed
Binary file pidgin/pixmaps/tray/16/tray-away.png has changed
Binary file pidgin/pixmaps/tray/16/tray-busy.png has changed
Binary file pidgin/pixmaps/tray/16/tray-connecting.png has changed
Binary file pidgin/pixmaps/tray/16/tray-extended-away.png has changed
Binary file pidgin/pixmaps/tray/16/tray-invisible.png has changed
Binary file pidgin/pixmaps/tray/16/tray-message.png has changed
Binary file pidgin/pixmaps/tray/16/tray-new-im.png has changed
Binary file pidgin/pixmaps/tray/16/tray-offline.png has changed
Binary file pidgin/pixmaps/tray/16/tray-online.png has changed
Binary file pidgin/pixmaps/tray/22/tray-away.png has changed
Binary file pidgin/pixmaps/tray/22/tray-busy.png has changed
Binary file pidgin/pixmaps/tray/22/tray-connecting.png has changed
Binary file pidgin/pixmaps/tray/22/tray-extended-away.png has changed
Binary file pidgin/pixmaps/tray/22/tray-invisible.png has changed
Binary file pidgin/pixmaps/tray/22/tray-message.png has changed
Binary file pidgin/pixmaps/tray/22/tray-new-im.png has changed
Binary file pidgin/pixmaps/tray/22/tray-offline.png has changed
Binary file pidgin/pixmaps/tray/22/tray-online.png has changed
Binary file pidgin/pixmaps/tray/32/tray-away.png has changed
Binary file pidgin/pixmaps/tray/32/tray-busy.png has changed
Binary file pidgin/pixmaps/tray/32/tray-connecting.png has changed
Binary file pidgin/pixmaps/tray/32/tray-extended-away.png has changed
Binary file pidgin/pixmaps/tray/32/tray-invisible.png has changed
Binary file pidgin/pixmaps/tray/32/tray-message.png has changed
Binary file pidgin/pixmaps/tray/32/tray-new-im.png has changed
Binary file pidgin/pixmaps/tray/32/tray-offline.png has changed
Binary file pidgin/pixmaps/tray/32/tray-online.png has changed
Binary file pidgin/pixmaps/tray/48/tray-away.png has changed
Binary file pidgin/pixmaps/tray/48/tray-busy.png has changed
Binary file pidgin/pixmaps/tray/48/tray-connecting.png has changed
Binary file pidgin/pixmaps/tray/48/tray-extended-away.png has changed
Binary file pidgin/pixmaps/tray/48/tray-invisible.png has changed
Binary file pidgin/pixmaps/tray/48/tray-message.png has changed
Binary file pidgin/pixmaps/tray/48/tray-new-im.png has changed
Binary file pidgin/pixmaps/tray/48/tray-offline.png has changed
Binary file pidgin/pixmaps/tray/48/tray-online.png has changed
Binary file pidgin/pixmaps/tray/hicolor/16x16/status/pidgin-tray-available.png has changed
Binary file pidgin/pixmaps/tray/hicolor/16x16/status/pidgin-tray-away.png has changed
Binary file pidgin/pixmaps/tray/hicolor/16x16/status/pidgin-tray-busy.png has changed
Binary file pidgin/pixmaps/tray/hicolor/16x16/status/pidgin-tray-connect.png has changed
Binary file pidgin/pixmaps/tray/hicolor/16x16/status/pidgin-tray-email.png has changed
Binary file pidgin/pixmaps/tray/hicolor/16x16/status/pidgin-tray-invisible.png has changed
Binary file pidgin/pixmaps/tray/hicolor/16x16/status/pidgin-tray-offline.png has changed
Binary file pidgin/pixmaps/tray/hicolor/16x16/status/pidgin-tray-pending.png has changed
Binary file pidgin/pixmaps/tray/hicolor/16x16/status/pidgin-tray-xa.png has changed
Binary file pidgin/pixmaps/tray/hicolor/22x22/status/pidgin-tray-available.png has changed
Binary file pidgin/pixmaps/tray/hicolor/22x22/status/pidgin-tray-away.png has changed
Binary file pidgin/pixmaps/tray/hicolor/22x22/status/pidgin-tray-busy.png has changed
Binary file pidgin/pixmaps/tray/hicolor/22x22/status/pidgin-tray-connect.png has changed
Binary file pidgin/pixmaps/tray/hicolor/22x22/status/pidgin-tray-email.png has changed
Binary file pidgin/pixmaps/tray/hicolor/22x22/status/pidgin-tray-invisible.png has changed
Binary file pidgin/pixmaps/tray/hicolor/22x22/status/pidgin-tray-offline.png has changed
Binary file pidgin/pixmaps/tray/hicolor/22x22/status/pidgin-tray-pending.png has changed
Binary file pidgin/pixmaps/tray/hicolor/22x22/status/pidgin-tray-xa.png has changed
Binary file pidgin/pixmaps/tray/hicolor/32x32/status/pidgin-tray-available.png has changed
Binary file pidgin/pixmaps/tray/hicolor/32x32/status/pidgin-tray-away.png has changed
Binary file pidgin/pixmaps/tray/hicolor/32x32/status/pidgin-tray-busy.png has changed
Binary file pidgin/pixmaps/tray/hicolor/32x32/status/pidgin-tray-connect.png has changed
Binary file pidgin/pixmaps/tray/hicolor/32x32/status/pidgin-tray-email.png has changed
Binary file pidgin/pixmaps/tray/hicolor/32x32/status/pidgin-tray-invisible.png has changed
Binary file pidgin/pixmaps/tray/hicolor/32x32/status/pidgin-tray-offline.png has changed
Binary file pidgin/pixmaps/tray/hicolor/32x32/status/pidgin-tray-pending.png has changed
Binary file pidgin/pixmaps/tray/hicolor/32x32/status/pidgin-tray-xa.png has changed
Binary file pidgin/pixmaps/tray/hicolor/48x48/status/pidgin-tray-available.png has changed
Binary file pidgin/pixmaps/tray/hicolor/48x48/status/pidgin-tray-away.png has changed
Binary file pidgin/pixmaps/tray/hicolor/48x48/status/pidgin-tray-busy.png has changed
Binary file pidgin/pixmaps/tray/hicolor/48x48/status/pidgin-tray-connect.png has changed
Binary file pidgin/pixmaps/tray/hicolor/48x48/status/pidgin-tray-email.png has changed
Binary file pidgin/pixmaps/tray/hicolor/48x48/status/pidgin-tray-invisible.png has changed
Binary file pidgin/pixmaps/tray/hicolor/48x48/status/pidgin-tray-offline.png has changed
Binary file pidgin/pixmaps/tray/hicolor/48x48/status/pidgin-tray-pending.png has changed
Binary file pidgin/pixmaps/tray/hicolor/48x48/status/pidgin-tray-xa.png has changed
--- a/pidgin/plugins/gestures/gestures.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/plugins/gestures/gestures.c	Mon Mar 08 22:53:02 2010 +0000
@@ -57,11 +57,7 @@
 {
 	int count, current;
 
-#if GTK_CHECK_VERSION(2,2,0)
 	count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(win->notebook));
-#else
-	count = g_list_length(GTK_NOTEBOOK(win->notebook)->children);
-#endif
 	current = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook));
 
 	if (dir == GTK_DIR_LEFT)
@@ -149,6 +145,15 @@
 }
 
 #if 0
+#if GTK_CHECK_VERSION(2,4,0)
+static void
+mouse_button_menu_cb(GtkComboBox *opt, gpointer data)
+{
+	int button = gtk_combo_box_get_active(opt);
+
+	gstroke_set_mouse_button(button + 2);
+}
+#else
 static void
 mouse_button_menu_cb(GtkMenuItem *item, gpointer data)
 {
@@ -157,6 +162,7 @@
 	gstroke_set_mouse_button(button + 2);
 }
 #endif
+#endif
 
 static void
 toggle_draw_cb(GtkToggleButton *toggle, gpointer data)
@@ -224,8 +230,10 @@
 	GtkWidget *toggle;
 #if 0
 	GtkWidget *opt;
+#if GTK_CHECK_VERSION(2,4,0)
 	GtkWidget *menu, *item;
 #endif
+#endif
 
 	/* Outside container */
 	ret = gtk_vbox_new(FALSE, 18);
@@ -235,6 +243,19 @@
 	vbox = pidgin_make_frame(ret, _("Mouse Gestures Configuration"));
 
 #if 0
+#if GTK_CHECK_VERSION(2,4,0)
+	/* Mouse button drop-down menu */
+	opt = gtk_combo_box_new_text();
+
+	gtk_combo_box_append_text(_("Middle mouse button"));
+	gtk_combo_box_append_text(_("Right mouse button"));
+	g_signal_connect(G_OBJECT(opt), "changed",
+	                 G_CALLBACK(mouse_button_menu_cb), NULL);
+
+	gtk_box_pack_start(GTK_BOX(vbox), opt, FALSE, FALSE, 0);
+	gtk_combo_box_set_active(GTK_COMBO_BOX(opt),
+							gstroke_get_mouse_button() - 2);
+#else
 	/* Mouse button drop-down menu */
 	menu = gtk_menu_new();
 	opt = gtk_option_menu_new();
@@ -254,6 +275,7 @@
 	gtk_option_menu_set_history(GTK_OPTION_MENU(opt),
 								gstroke_get_mouse_button() - 2);
 #endif
+#endif
 
 	/* "Visual gesture display" checkbox */
 	toggle = gtk_check_button_new_with_mnemonic(_("_Visual gesture display"));
--- a/pidgin/plugins/gevolution/assoc-buddy.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/plugins/gevolution/assoc-buddy.c	Mon Mar 08 22:53:02 2010 +0000
@@ -20,7 +20,6 @@
  */
 #include "internal.h"
 #include "gtkblist.h"
-#include "gtkexpander.h"
 #include "pidgin.h"
 #include "gtkutils.h"
 #include "gtkimhtml.h"
--- a/pidgin/plugins/gtkbuddynote.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/plugins/gtkbuddynote.c	Mon Mar 08 22:53:02 2010 +0000
@@ -31,11 +31,13 @@
 		const gchar *note = purple_blist_node_get_string(node, "notes");
 
 		if ((note != NULL) && (*note != '\0')) {
-			char *tmp;
+			char *tmp, *esc;
 			purple_markup_html_to_xhtml(note, NULL, &tmp);
+			esc = g_markup_escape_text(tmp, -1);
+			g_free(tmp);
 			g_string_append_printf(text, _("\n<b>Buddy Note</b>: %s"),
-			                       tmp);
-			g_free(tmp);
+			                       esc);
+			g_free(esc);
 		}
 	}
 }
--- a/pidgin/plugins/perl/common/Makefile.mingw	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/plugins/perl/common/Makefile.mingw	Mon Mar 08 22:53:02 2010 +0000
@@ -25,6 +25,7 @@
 			-I$(PIDGIN_TOP)/win32 \
 			-I$(GTK_TOP)/include \
 			-I$(GTK_TOP)/include/atk-1.0 \
+			-I$(GTK_TOP)/include/cairo \
 			-I$(GTK_TOP)/include/glib-2.0 \
 			-I$(GTK_TOP)/include/gtk-2.0 \
 			-I$(GTK_TOP)/include/pango-1.0 \
--- a/pidgin/plugins/pidginrc.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/plugins/pidginrc.c	Mon Mar 08 22:53:02 2010 +0000
@@ -201,17 +201,13 @@
 purplerc_make_changes(void)
 {
 	GString *str = make_gtkrc_string();
-#if GTK_CHECK_VERSION(2,4,0)
 	GtkSettings *setting = NULL;
-#endif
 
 	gtk_rc_parse_string(str->str);
 	g_string_free(str, TRUE);
 
-#if GTK_CHECK_VERSION(2,4,0)
 	setting = gtk_settings_get_default();
 	gtk_rc_reset_styles(setting);
-#endif
 }
 
 static void
--- a/pidgin/plugins/spellchk.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/plugins/spellchk.c	Mon Mar 08 22:53:02 2010 +0000
@@ -2081,24 +2081,11 @@
 	g_string_free(data, TRUE);
 }
 
-#if !GTK_CHECK_VERSION(2,2,0)
-static void
-count_selected_helper(GtkTreeModel *model, GtkTreePath *path,
-					GtkTreeIter *iter, gpointer user_data)
-{
-	(*(gint *)user_data)++;
-}
-#endif
-
 static void on_selection_changed(GtkTreeSelection *sel,
 	gpointer data)
 {
 	gint num_selected;
-#if GTK_CHECK_VERSION(2,2,0)
 	num_selected = gtk_tree_selection_count_selected_rows(sel);
-#else
-	gtk_tree_selection_selected_foreach(sel, count_selected_helper, &num_selected);
-#endif
 	gtk_widget_set_sensitive((GtkWidget*)data, (num_selected > 0));
 }
 
--- a/pidgin/plugins/win32/transparency/win2ktrans.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/plugins/win32/transparency/win2ktrans.c	Mon Mar 08 22:53:02 2010 +0000
@@ -72,8 +72,6 @@
 static const char *OPT_WINTRANS_BL_ONTOP  = "/plugins/gtk/win32/wintrans/bl_always_on_top";
 static GSList *window_list = NULL;
 
-static BOOL (*MySetLayeredWindowAttributes)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags) = NULL;
-
 /*
  *  CODE
  */
@@ -81,31 +79,31 @@
 /* Set window transparency level */
 static void set_wintrans(GtkWidget *window, int alpha, gboolean enabled,
 		gboolean always_on_top) {
-	if (MySetLayeredWindowAttributes) {
-		HWND hWnd = GDK_WINDOW_HWND(window->window);
-		LONG style = GetWindowLong(hWnd, GWL_EXSTYLE);
-		if (enabled) {
-			style |= WS_EX_LAYERED;
-		} else {
-			style &= ~WS_EX_LAYERED;
-		}
-		SetWindowLong(hWnd, GWL_EXSTYLE, style);
+
+	HWND hWnd = GDK_WINDOW_HWND(window->window);
+	LONG style = GetWindowLong(hWnd, GWL_EXSTYLE);
+	if (enabled) {
+		style |= WS_EX_LAYERED;
+	} else {
+		style &= ~WS_EX_LAYERED;
+	}
+	SetWindowLong(hWnd, GWL_EXSTYLE, style);
 
 
-		if (enabled) {
-			SetWindowPos(hWnd,
-				always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST,
-				0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
-			MySetLayeredWindowAttributes(hWnd, 0, alpha, LWA_ALPHA);
-		} else {
-			/* Ask the window and its children to repaint */
-			SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
-				SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+	if (enabled) {
+		SetWindowPos(hWnd,
+			always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST,
+			0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+		SetLayeredWindowAttributes(hWnd, 0, alpha, LWA_ALPHA);
+	} else {
+		/* Ask the window and its children to repaint */
+		SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0,
+			SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
 
-			RedrawWindow(hWnd, NULL, NULL,
-				RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
-		}
+		RedrawWindow(hWnd, NULL, NULL,
+			RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
 	}
+
 }
 
 /* When a conv window is focused, if we're only transparent when unfocused,
@@ -491,14 +489,6 @@
  *  EXPORTED FUNCTIONS
  */
 static gboolean plugin_load(PurplePlugin *plugin) {
-	MySetLayeredWindowAttributes = (void*) wpurple_find_and_loadproc(
-		"user32.dll", "SetLayeredWindowAttributes");
-
-	if (!MySetLayeredWindowAttributes) {
-		purple_debug_error(WINTRANS_PLUGIN_ID,
-			"SetLayeredWindowAttributes API not found (Required W2K+)\n");
-		return FALSE;
-	}
 
 	purple_signal_connect(purple_conversations_get_handle(),
 		"conversation-created", plugin,
--- a/pidgin/plugins/xmppconsole.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/plugins/xmppconsole.c	Mon Mar 08 22:53:02 2010 +0000
@@ -26,9 +26,6 @@
 #include "xmlnode.h"
 
 #include "gtkimhtml.h"
-#if !GTK_CHECK_VERSION(2,4,0)
-#include "pidgincombobox.h"
-#endif
 #include "gtkutils.h"
 
 typedef struct {
@@ -624,7 +621,7 @@
 }
 
 static void
-signed_on_cb(PurpleConnection *gc)
+signing_on_cb(PurpleConnection *gc)
 {
 	if (!console)
 		return;
@@ -633,7 +630,9 @@
 	console->accounts = g_list_append(console->accounts, gc);
 	console->count++;
 
-	if (console->count > 1)
+	if (console->count == 1)
+		console->gc = gc;
+	else
 		gtk_widget_show_all(console->hbox);
 }
 
@@ -683,8 +682,8 @@
 			    PURPLE_CALLBACK(xmlnode_received_cb), NULL);
 	purple_signal_connect(jabber, "jabber-sending-text", xmpp_console_handle,
 			    PURPLE_CALLBACK(xmlnode_sent_cb), NULL);
-	purple_signal_connect(purple_connections_get_handle(), "signed-on",
-			    plugin, PURPLE_CALLBACK(signed_on_cb), NULL);
+	purple_signal_connect(purple_connections_get_handle(), "signing-on",
+			    plugin, PURPLE_CALLBACK(signing_on_cb), NULL);
 	purple_signal_connect(purple_connections_get_handle(), "signed-off",
 			    plugin, PURPLE_CALLBACK(signed_off_cb), NULL);
 
@@ -733,9 +732,7 @@
 	GtkTextBuffer *buffer;
 	GtkWidget *toolbar;
 	GList *connections;
-#if GTK_CHECK_VERSION(2,4,0)
 	GtkToolItem *button;
-#endif
 
 	if (console) {
 		gtk_window_present(GTK_WINDOW(console->window));
@@ -782,32 +779,17 @@
 	gtk_container_add(GTK_CONTAINER(sw), console->imhtml);
 
 	toolbar = gtk_toolbar_new();
-#if GTK_CHECK_VERSION(2,4,0)
 	button = gtk_tool_button_new(NULL, "<iq/>");
 	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(iq_clicked_cb), NULL);
 	gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
-#else
-	gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "<iq/>",
-			           _("Insert an <iq/> stanza."), "foo", NULL, GTK_SIGNAL_FUNC(iq_clicked_cb), NULL);
-#endif
 
-#if GTK_CHECK_VERSION(2,4,0)
 	button = gtk_tool_button_new(NULL, "<presence/>");
 	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(presence_clicked_cb), NULL);
 	gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
-#else
-	gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "<presence/>",
-			           _("Insert a <presence/> stanza."), NULL, gtk_label_new(NULL), GTK_SIGNAL_FUNC(presence_clicked_cb), NULL);
-#endif
 
-#if GTK_CHECK_VERSION(2,4,0)
 	button = gtk_tool_button_new(NULL, "<message/>");
 	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(message_clicked_cb), NULL);
 	gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
-#else
-	gtk_toolbar_append_item(GTK_TOOLBAR(toolbar), "<message/>",
-			           _("Insert a <message/> stanza."), "foo", gtk_label_new(NULL), GTK_SIGNAL_FUNC(message_clicked_cb), NULL);
-#endif
 
 	gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
 
--- a/pidgin/win32/gtkdocklet-win32.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/gtkdocklet-win32.c	Mon Mar 08 22:53:02 2010 +0000
@@ -21,7 +21,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02111-1301, USA.
  */
-
+#define _WIN32_IE 0x0500
 #include <windows.h>
 #include <gdk/gdkwin32.h>
 #include <gdk/gdk.h>
@@ -51,7 +51,7 @@
 /* This is used to trigger click events on so they appear to GTK+ as if they are triggered by input */
 static GtkWidget *dummy_button = NULL;
 static GtkWidget *dummy_window = NULL;
-static NOTIFYICONDATA _nicon_data;
+static NOTIFYICONDATAW _nicon_data;
 
 static gboolean dummy_button_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
 	pidgin_docklet_clicked(event->button);
@@ -64,7 +64,7 @@
 	switch(msg) {
 	case WM_CREATE:
 		purple_debug_info("docklet", "WM_CREATE\n");
-		taskbarRestartMsg = RegisterWindowMessage("TaskbarCreated");
+		taskbarRestartMsg = RegisterWindowMessageW(L"TaskbarCreated");
 		break;
 
 	case WM_TIMER:
@@ -114,7 +114,7 @@
 		if (msg == taskbarRestartMsg) {
 			/* explorer crashed and left us hanging...
 			   This will put the systray icon back in it's place, when it restarts */
-			Shell_NotifyIcon(NIM_ADD, &_nicon_data);
+			Shell_NotifyIconW(NIM_ADD, &_nicon_data);
 		}
 		break;
 	}/* end switch */
@@ -124,10 +124,10 @@
 
 /* Create hidden window to process systray messages */
 static HWND systray_create_hiddenwin() {
-	WNDCLASSEX wcex;
-	LPCTSTR wname;
+	WNDCLASSEXW wcex;
+	wchar_t *wname;
 
-	wname = TEXT("WinpidginSystrayWinCls");
+	wname = L"WinpidginSystrayWinCls";
 
 	wcex.cbSize = sizeof(wcex);
 	wcex.style		= 0;
@@ -142,22 +142,25 @@
 	wcex.lpszClassName	= wname;
 	wcex.hIconSm		= NULL;
 
-	RegisterClassEx(&wcex);
+	RegisterClassExW(&wcex);
 
 	/* Create the window */
-	return (CreateWindow(wname, "", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, winpidgin_exe_hinstance(), 0));
+	return (CreateWindowW(wname, L"", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, winpidgin_exe_hinstance(), 0));
 }
 
 static void systray_init_icon(HWND hWnd) {
+	wchar_t *w;
 	ZeroMemory(&_nicon_data, sizeof(_nicon_data));
-	_nicon_data.cbSize = sizeof(NOTIFYICONDATA);
+	_nicon_data.cbSize = sizeof(NOTIFYICONDATAW);
 	_nicon_data.hWnd = hWnd;
 	_nicon_data.uID = 0;
 	_nicon_data.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
 	_nicon_data.uCallbackMessage = WM_TRAYMESSAGE;
 	_nicon_data.hIcon = NULL;
-	lstrcpy(_nicon_data.szTip, PIDGIN_NAME);
-	Shell_NotifyIcon(NIM_ADD, &_nicon_data);
+	w = g_utf8_to_utf16(PIDGIN_NAME, -1, NULL, NULL, NULL);
+	wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t));
+	g_free(w);
+	Shell_NotifyIconW(NIM_ADD, &_nicon_data);
 	pidgin_docklet_embedded();
 }
 
@@ -486,11 +489,11 @@
 	g_return_if_fail(hicon != NULL);
 
 	_nicon_data.hIcon = hicon;
-	Shell_NotifyIcon(NIM_MODIFY, &_nicon_data);
+	Shell_NotifyIconW(NIM_MODIFY, &_nicon_data);
 }
 
 static void systray_remove_nid(void) {
-	Shell_NotifyIcon(NIM_DELETE, &_nicon_data);
+	Shell_NotifyIconW(NIM_DELETE, &_nicon_data);
 }
 
 static void winpidgin_tray_update_icon(PurpleStatusPrimitive status,
@@ -547,19 +550,19 @@
 
 static void winpidgin_tray_blank_icon() {
 	_nicon_data.hIcon = NULL;
-	Shell_NotifyIcon(NIM_MODIFY, &_nicon_data);
+	Shell_NotifyIconW(NIM_MODIFY, &_nicon_data);
 }
 
 static void winpidgin_tray_set_tooltip(gchar *tooltip) {
-	if (tooltip) {
-		char *locenc = NULL;
-		locenc = g_locale_from_utf8(tooltip, -1, NULL, NULL, NULL);
-		lstrcpyn(_nicon_data.szTip, locenc, sizeof(_nicon_data.szTip) / sizeof(TCHAR));
-		g_free(locenc);
-	} else {
-		lstrcpy(_nicon_data.szTip, PIDGIN_NAME);
-	}
-	Shell_NotifyIcon(NIM_MODIFY, &_nicon_data);
+	const char *value = tooltip;
+	wchar_t *w;
+	if (value == NULL) {
+		value = PIDGIN_NAME;
+	}	
+	w = g_utf8_to_utf16(value, -1, NULL, NULL, NULL);
+	wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t));
+	g_free(w);
+	Shell_NotifyIconW(NIM_MODIFY, &_nicon_data);
 }
 
 static void winpidgin_tray_minimize(PidginBuddyList *gtkblist) {
@@ -585,12 +588,7 @@
 			 G_CALLBACK(dummy_button_cb), NULL);
 
 	image = gtk_image_new();
-#if GLIB_CHECK_VERSION(2,10,0)
 	g_object_ref_sink(image);
-#else
-	g_object_ref(image);
-	gtk_object_sink(GTK_OBJECT(image));
-#endif
 
 	osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
 	GetVersionEx(&osinfo);
--- a/pidgin/win32/gtkwin32dep.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/gtkwin32dep.c	Mon Mar 08 22:53:02 2010 +0000
@@ -47,8 +47,6 @@
 #include "zlib.h"
 #include "untar.h"
 
-#include <libintl.h>
-
 #include "gtkwin32dep.h"
 #include "win32dep.h"
 #include "gtkconv.h"
@@ -64,8 +62,6 @@
 HWND messagewin_hwnd;
 static int gtkwin32_handle;
 
-typedef BOOL (CALLBACK* LPFNFLASHWINDOWEX)(PFLASHWINFO);
-static LPFNFLASHWINDOWEX MyFlashWindowEx = NULL;
 static gboolean pwm_handles_connections = TRUE;
 
 
@@ -142,58 +138,34 @@
 
 void winpidgin_shell_execute(const char *target, const char *verb, const char *clazz) {
 
+	SHELLEXECUTEINFOW wsinfo;
+	wchar_t *w_uri, *w_verb, *w_clazz = NULL;
+
 	g_return_if_fail(target != NULL);
 	g_return_if_fail(verb != NULL);
 
-	if (G_WIN32_HAVE_WIDECHAR_API()) {
-		SHELLEXECUTEINFOW wsinfo;
-		wchar_t *w_uri, *w_verb, *w_clazz = NULL;
-
-		w_uri = g_utf8_to_utf16(target, -1, NULL, NULL, NULL);
-		w_verb = g_utf8_to_utf16(verb, -1, NULL, NULL, NULL);
-
-		memset(&wsinfo, 0, sizeof(wsinfo));
-		wsinfo.cbSize = sizeof(wsinfo);
-		wsinfo.lpVerb = w_verb;
-		wsinfo.lpFile = w_uri;
-		wsinfo.nShow = SW_SHOWNORMAL;
-		wsinfo.fMask |= SEE_MASK_FLAG_NO_UI;
-		if (clazz != NULL) {
-			w_clazz = g_utf8_to_utf16(clazz, -1, NULL, NULL, NULL);
-			wsinfo.fMask |= SEE_MASK_CLASSNAME;
-			wsinfo.lpClass = w_clazz;
-		}
-
-		if(!ShellExecuteExW(&wsinfo))
-			purple_debug_error("winpidgin", "Error opening URI: %s error: %d\n",
-				target, (int) wsinfo.hInstApp);
+	w_uri = g_utf8_to_utf16(target, -1, NULL, NULL, NULL);
+	w_verb = g_utf8_to_utf16(verb, -1, NULL, NULL, NULL);
 
-		g_free(w_uri);
-		g_free(w_verb);
-		g_free(w_clazz);
-	} else {
-		SHELLEXECUTEINFOA sinfo;
-		gchar *locale_uri;
-
-		locale_uri = g_locale_from_utf8(target, -1, NULL, NULL, NULL);
+	memset(&wsinfo, 0, sizeof(wsinfo));
+	wsinfo.cbSize = sizeof(wsinfo);
+	wsinfo.lpVerb = w_verb;
+	wsinfo.lpFile = w_uri;
+	wsinfo.nShow = SW_SHOWNORMAL;
+	wsinfo.fMask |= SEE_MASK_FLAG_NO_UI;
+	if (clazz != NULL) {
+		w_clazz = g_utf8_to_utf16(clazz, -1, NULL, NULL, NULL);
+		wsinfo.fMask |= SEE_MASK_CLASSNAME;
+		wsinfo.lpClass = w_clazz;
+	}
 
-		memset(&sinfo, 0, sizeof(sinfo));
-		sinfo.cbSize = sizeof(sinfo);
-		sinfo.lpVerb = verb;
-		sinfo.lpFile = locale_uri;
-		sinfo.nShow = SW_SHOWNORMAL;
-		sinfo.fMask |= SEE_MASK_FLAG_NO_UI;
-		if (clazz != NULL) {
-			sinfo.fMask |= SEE_MASK_CLASSNAME;
-			sinfo.lpClass = clazz;
-		}
+	if(!ShellExecuteExW(&wsinfo))
+		purple_debug_error("winpidgin", "Error opening URI: %s error: %d\n",
+			target, (int) wsinfo.hInstApp);
 
-		if(!ShellExecuteExA(&sinfo))
-			purple_debug_error("winpidgin", "Error opening URI: %s error: %d\n",
-				target, (int) sinfo.hInstApp);
-
-		g_free(locale_uri);
-	}
+	g_free(w_uri);
+	g_free(w_verb);
+	g_free(w_clazz);
 
 }
 
@@ -332,6 +304,7 @@
 void
 winpidgin_window_flash(GtkWindow *window, gboolean flash) {
 	GdkWindow * gdkwin;
+	FLASHWINFO info;
 
 	g_return_if_fail(window != NULL);
 
@@ -343,25 +316,19 @@
 	if(GDK_WINDOW_DESTROYED(gdkwin))
 		return;
 
-	if(MyFlashWindowEx) {
-		FLASHWINFO info;
+	memset(&info, 0, sizeof(FLASHWINFO));
+	info.cbSize = sizeof(FLASHWINFO);
+	info.hwnd = GDK_WINDOW_HWND(gdkwin);
+	if (flash) {
+		DWORD flashCount;
+		info.uCount = 3;
+		if (SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT, 0, &flashCount, 0))
+			info.uCount = flashCount;
+		info.dwFlags = FLASHW_ALL | FLASHW_TIMER;
+	} else
+		info.dwFlags = FLASHW_STOP;
+	info.dwTimeout = 0;
 
-		memset(&info, 0, sizeof(FLASHWINFO));
-		info.cbSize = sizeof(FLASHWINFO);
-		info.hwnd = GDK_WINDOW_HWND(gdkwin);
-		if (flash) {
-			DWORD flashCount;
-			info.uCount = 3;
-			if (SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT, 0, &flashCount, 0))
-				info.uCount = flashCount;
-			info.dwFlags = FLASHW_ALL | FLASHW_TIMER;
-		} else
-			info.dwFlags = FLASHW_STOP;
-		info.dwTimeout = 0;
-
-		MyFlashWindowEx(&info);
-	} else
-		FlashWindow(GDK_WINDOW_HWND(gdkwin), flash);
 }
 
 void
@@ -408,11 +375,27 @@
 }
 
 void winpidgin_init(HINSTANCE hint) {
+	FARPROC proc;
 
 	purple_debug_info("winpidgin", "winpidgin_init start\n");
 
 	exe_hInstance = hint;
 
+	proc = wpurple_find_and_loadproc("exchndl.dll", "SetLogFile");
+	if (proc) {
+		gchar *debug_dir, *locale_debug_dir;
+		
+		debug_dir = g_build_filename(purple_user_dir(), "pidgin.RPT", NULL);
+		locale_debug_dir = g_locale_from_utf8(debug_dir, -1, NULL, NULL, NULL);
+
+		purple_debug_info("winpidgin", "Setting exchndl.dll LogFile to %s\n", debug_dir);
+
+		(proc)(locale_debug_dir);
+
+		g_free(debug_dir);
+		g_free(locale_debug_dir);
+	}
+
 	/* IdleTracker Initialization */
 	if(!winpidgin_set_idlehooks())
 		purple_debug_error("winpidgin", "Failed to initialize idle tracker\n");
@@ -423,8 +406,6 @@
 
 	messagewin_hwnd = winpidgin_message_window_init();
 
-	MyFlashWindowEx = (LPFNFLASHWINDOWEX) wpurple_find_and_loadproc("user32.dll", "FlashWindowEx");
-
 	purple_debug_info("winpidgin", "winpidgin_init end\n");
 }
 
@@ -460,36 +441,16 @@
 	return TRUE;
 }
 
-typedef HMONITOR WINAPI _MonitorFromWindow(HWND, DWORD);
-typedef BOOL WINAPI _GetMonitorInfo(HMONITOR, LPMONITORINFO);
-
 static gboolean
 get_WorkingAreaRectForWindow(HWND hwnd, RECT *workingAreaRc) {
-	static _MonitorFromWindow *the_MonitorFromWindow;
-	static _GetMonitorInfo *the_GetMonitorInfo;
-	static gboolean initialized = FALSE;
 
 	HMONITOR monitor;
 	MONITORINFO info;
 
-	if(!initialized) {
-		the_MonitorFromWindow = (_MonitorFromWindow*)
-			wpurple_find_and_loadproc("user32", "MonitorFromWindow");
-		the_GetMonitorInfo = (_GetMonitorInfo*)
-			wpurple_find_and_loadproc("user32", "GetMonitorInfoA");
-		initialized = TRUE;
-	}
-
-	if(!the_MonitorFromWindow)
-		return FALSE;
-
-	if(!the_GetMonitorInfo)
-		return FALSE;
-
-	monitor = the_MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
+	monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
 
 	info.cbSize = sizeof(info);
-	if(!the_GetMonitorInfo(monitor, &info))
+	if(!GetMonitorInfo(monitor, &info))
 		return FALSE;
 
 	CopyRect(workingAreaRc, &(info.rcWork));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/win32/nsis/available.lst	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,100 @@
+#This file is from ftp://ftp.services.openoffice.org/pub/OpenOffice.org/contrib/dictionaries/available.lst
+af,ZA,af_ZA,Afrikaans (South Africa),af_ZA.zip
+ak,GH,ak_GH,Akan (Ghana),ak_GH.zip
+sq,AL,sq_AL,Albanian (Albania),sq_AL.zip
+bg,BG,bg_BG,Bulgarian (Bulgaria),bg_BG.zip
+ca,ES,ca_ES,Catalan (Spain),ca_ES.zip
+cop,EG,cop_EG,Coptic (Egypt),cop_EG.zip
+hr,HR,hr_HR,Croatian (Croatia),hr_HR.zip
+cs,CZ,cs_CZ,Czech (Czech Republic),cs_CZ.zip
+da,DK,da_DK,Danish (Denmark),da_DK.zip
+nl,NL,nl_NL,Dutch (Netherlands),nl_NL.zip
+nl,NL,nl_med,Dutch Medical (Netherlands),nl_med.zip
+en,AU,en_AU,English (Australia),en_AU.zip
+en,CA,en_CA,English (Canada),en_CA.zip
+en,NZ,en_NZ,English (New Zealand),en_NZ.zip
+en,ZA,en_ZA,English (South Africa),en_ZA.zip
+en,GB,en_GB,English (United Kingdom),en_GB.zip
+en,US,en_US,English (United States),en_US.zip
+eo,ANY,eo_l3,Esperanto,eo.zip
+et,EE,et_EE,Estonian (Estonia),et_EE.zip
+fo,FO,fo_FO,Faroese (Faroe Islands),fo_FO.zip
+fr,FR,fr_FR-classique,Franais Classique,fr_FR-classique_1-3-2.zip
+fr,FR,fr_FR-1990,Franais Rforme 1990,fr_FR-1990_1-3-2.zip
+fr,FR,fr_FR,Franais Rforme 1990 & Classique,fr_FR_1-3-2.zip
+fy,NL,fy_NL,Frisian (Netherlands),fy_NL.zip
+gl,ES,gl_ES,Galician (Spain),gl_ES.zip
+gsc,FR,gsc_FR,Gascon (France),gsc_FR.zip
+de,AT,de_AT,German (Austria Extension),de_AT.zip
+de,AT,de_AT_frami,German (Austria) neu 08/2006 (frami),de_AT_frami.zip
+de,DE,de_DE,German (Germany),de_DE.zip
+de,DE,de_DE_frami,German (Germany) neu 08/2006 (frami),de_DE_frami.zip
+de,CH,de_CH,German (Switzerland),de_CH.zip
+de,CT,de_CH_frami,German (Switzerland) neu 08/2006 (frami),de_CH_frami.zip
+el,GR,el_GR,Greek (Greece),el_GR.zip
+gu,IN,gu_IN,Gujarati (India),gu_IN.zip
+he,IL,he_IL,Hebrew (Israel),he_IL.zip
+hil,PH,hil_PH,Hiligaynon (Philippines),hil_PH.zip
+hu,HU,hu_HU,Hungarian (Hungary),hu_HU.zip
+hu,HU,hu_HU_comb,Hungarian (Hungary) collected compounds,hu_HU_comb.zip
+id,ID,id_ID,Indonesian (Indonesia),id_ID.zip
+ga,IE,ga_IE,Irish (Ireland),ga_IE.zip
+it,IT,it_IT,Italian (Italy),it_IT.zip
+sw,KE,sw_KE,Kiswahili (Africa),sw_KE.zip
+ku,TR,ku_TR,Kurdish (Turkey),ku_TR.zip
+it,IT,la,Latin,la.zip
+lv,LV,lv_LV,Latvian (Latvia),lv_LV.zip
+lt,LT,lt_LT,Lithuanian (Lithuania),lt_LT.zip
+mk,MK,mk_MK,Macedonian (Macedonia),mk_MK.zip
+ms,MY,ms_MY,Malay (Malaysia),ms_MY.zip
+mi,NZ,mi_NZ,Maori (New Zealand),mi_NZ.zip
+mr,IN,mr_IN,Marathi (India),mr_IN.zip
+mos,BF,mos_BF,Moore,ms_BF.zip
+nr,ZA,nr_ZA,Ndebele (South Africa),nr_ZA.zip
+ne,NP,ne_NP,Nepali (Nepal),ne_NP.zip
+ns,ZA,ns_ZA,Northern Sotho (South Africa),ns_ZA.zip
+nb,NO,nb_NO,Norwegian Bokmaal (Norway),nb_NO.zip
+nn,NO,nn_NO,Norwegian Nynorsk (Norway),nn_NO.zip
+oc,FR,oc_FR,Occitan (Languedoc),oc_FR.zip
+pl,PL,pl_PL,Polish (Poland),pl_PL.zip
+pt,BR,pt_BR,Portuguese (Brazil),pt_BR.zip
+pt,PT,pt_PT,Portuguese (Portugal),pt_PT.zip
+ro,RO,ro_RO,Romanian (Romania),ro_RO.zip
+ru,RU,ru_RU,Russian (Russia),ru_RU.zip
+ru,RU,ru_RU_ye,Russian_ye (Russia),ru_RU_ye.zip
+ru,RU,ru_RU_yo,Russian_yo (Russia),ru_RU_yo.zip
+gd,GB,gd_GB,Scots Gaelic (Scotland),gd_GB.zip
+tn,ZA,tn_ZA,Setswana (Africa),tn_ZA.zip
+sk,SK,sk_SK,Slovak (Slovakia),sk_SK.zip
+sl,SI,sl_SI,Slovenian (Slovenia),sl_SI.zip
+st,ZA,st_ZA,Southern Sotho (South Africa),st_ZA.zip
+es,AR,es_AR,Spanish (Argentina),es_AR.zip
+es,BZ,es_HN,Spanish (Belize),es_HN.zip
+es,BO,es_BO,Spanish (Bolivia),es_BO.zip
+es,CL,es_CL,Spanish (Chile),es_CL.zip
+es,CO,es_CO,Spanish (Colombia),es_CO.zip
+es,CR,es_CR,Spanish (Costa Rica),es_CR.zip
+es,CU,es_CU,Spanish (Cuba),es_CU.zip
+es,DO,es_DO,Spanish (Dominican Republic),es_DO.zip
+es,EC,es_EC,Spanish (Ecuador),es_EC.zip
+es,SV,es_SV,Spanish (El Salvador),es_SV.zip
+es,GT,es_GT,Spanish (Guatemala),es_GT.zip
+es,MX,es_MX,Spanish (Mexico),es_MX.zip
+es,NI,es_NI,Spanish (Nicaragua),es_NI.zip
+es,PA,es_PA,Spanish (Panama),es_PA.zip
+es,PY,es_PY,Spanish (Paraguay),es_PY.zip
+es,PE,es_PE,Spanish (Peru),es_PE.zip
+es,PR,es_PR,Spanish (Puerto Rico),es_PR.zip
+es,ES,es_ES,Spanish (Spain),es_ES.zip
+es,UY,es_UY,Spanish (Uruguay),es_UY.zip
+es,VE,es_VE,Spanish (Venezuela),es_VE.zip
+ss,ZA,ss_ZA,Swati (South Africa),ss_ZA.zip
+sv,SE,sv_SE,Swedish (Sweden),sv_SE.zip
+ts,ZA,ts_ZA,Tsonga (South Africa),ts_ZA.zip
+uk,UA,uk_UA,Ukrainian (Ukraine),uk_UA.zip
+ur,PK,ur_PK,Urdu,ur_PK.zip
+ve,ZA,ve_ZA,Venda (South Africa),ve_ZA.zip
+vi,VN,vi_VN,Vietnamese (Viet-Nam),vi_VN.zip
+cy,GB,cy_GB,Welsh (Wales),cy_GB.zip
+xh,ZA,xh_ZA,Xhosa (South Africa),xh_ZA.zip
+zu,ZA,zu_ZA,Zulu (South Africa),zu_ZA.zip
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/win32/nsis/generate_gtk_zip.sh	Mon Mar 08 22:53:02 2010 +0000
@@ -0,0 +1,80 @@
+#!/bin/bash
+# Script to generate zip file for GTK+ runtime to be included in Pidgin installer
+
+PIDGIN_BASE=$1
+
+if [ ! -e $PIDGIN_BASE/ChangeLog.win32 ]; then
+	echo `basename $0` must must have the pidgin base dir specified as a parameter.
+	exit 1
+fi
+
+STAGE_DIR=$PIDGIN_BASE/pidgin/win32/nsis/gtk_runtime_stage
+#Subdirectory of $STAGE_DIR
+INSTALL_DIR=Gtk
+CONTENTS_FILE=$INSTALL_DIR/CONTENTS
+
+#This needs to be changed every time there is any sort of change.
+BUNDLE_VERSION=2.14.7.0
+
+ATK="http://ftp.gnome.org/pub/gnome/binaries/win32/atk/1.24/atk_1.24.0-1_win32.zip ATK 1.24.0-1"
+CAIRO="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/cairo_1.8.10-1_win32.zip Cairo 1.8.10-1"
+EXPAT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/expat_2.0.1-1_win32.zip Expat 2.0.1-1"
+FONTCONFIG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/fontconfig_2.8.0-2_win32.zip Fontconfig 2.8.0-2"
+FREETYPE="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/freetype_2.3.11-2_win32.zip Freetype 2.3.11-2"
+GETTEXT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-runtime-0.17-1.zip Gettext 0.17-1"
+GLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.20/glib_2.20.5-1_win32.zip Glib 2.20.5-1"
+GTK="http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.14/gtk+_2.14.7-1_win32.zip GTK+ 2.14.7-1"
+LIBJPEG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/jpeg-6b-4.zip libjpeg 6b-4"
+#Used by GTK+
+LIBPNG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.2.39-1_win32.zip libpng 1.2.39-1"
+#Used by Cairo
+LIBPNG2="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.4.0-1_win32.zip libpng 1.4.0-1"
+LIBTIFF="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libtiff_3.9.1-1_win32.zip libtiff 3.9.1-1"
+PANGO="http://ftp.gnome.org/pub/gnome/binaries/win32/pango/1.26/pango_1.26.2-1_win32.zip Pango 1.26.2-1"
+ZLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/zlib-1.2.3.zip zlib 1.2.3"
+
+ALL="ATK CAIRO EXPAT FONTCONFIG FREETYPE GETTEXT GLIB GTK LIBJPEG LIBPNG LIBPNG2 LIBTIFF PANGO ZLIB"
+
+mkdir -p $STAGE_DIR
+cd $STAGE_DIR
+
+rm -rf $INSTALL_DIR
+mkdir $INSTALL_DIR
+
+#new CONTENTS file
+echo Bundle Version $BUNDLE_VERSION > $CONTENTS_FILE
+
+function download_and_extract {
+	URL=${1%%\ *}
+	NAME=${1#*\ }
+	FILE=`basename $URL`
+	if [ ! -e $FILE ]; then
+		echo Downloading $NAME
+		wget $URL
+	fi
+	unzip -q $FILE -d $INSTALL_DIR
+	echo "$NAME" >> $CONTENTS_FILE
+}
+
+for VAL in $ALL
+do
+	VAR=${!VAL}
+	download_and_extract "$VAR"
+done
+
+#Default GTK+ Theme to MS-Windows
+echo gtk-theme-name = \"MS-Windows\" > $INSTALL_DIR/etc/gtk-2.0/gtkrc
+
+#Blow away translations that we don't have in Pidgin
+for LOCALE_DIR in $INSTALL_DIR/share/locale/*
+do
+	LOCALE=`basename $LOCALE_DIR`
+	if [ ! -e $PIDGIN_BASE/po/$LOCALE.po ]; then
+		echo Remove $LOCALE translation as it is missing from Pidgin
+		rm -r $LOCALE_DIR
+	fi
+done
+
+#Generate zip file to be included in installer
+zip -9 -r ../gtk-runtime-$BUNDLE_VERSION.zip Gtk
+
--- a/pidgin/win32/nsis/langmacros.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/langmacros.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -31,7 +31,6 @@
   ; Startup checks
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT INSTALLER_IS_RUNNING		${CUR_LANG}
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_IS_RUNNING			${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT GTK_INSTALLER_NEEDED		${CUR_LANG}
 
   ; License Page
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_LICENSE_BUTTON			${CUR_LANG}
@@ -48,10 +47,8 @@
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	${CUR_LANG}
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DESKTOP_SHORTCUT_DESC		${CUR_LANG}
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_STARTMENU_SHORTCUT_DESC	${CUR_LANG}
-
-  ; GTK+ Directory Page
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT GTK_UPGRADE_PROMPT			${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT GTK_WINDOWS_INCOMPATIBLE		${CUR_LANG}
+  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT DEBUG_SYMBOLS_SECTION_TITLE ${CUR_LANG}
+  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT TRANSLATIONS_SECTION_TITLE ${CUR_LANG}
 
   ; Installer Finish Page
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_FINISH_VISIT_WEB_SITE		${CUR_LANG}
@@ -59,10 +56,6 @@
   ; Pidgin Section Prompts and Texts
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	${CUR_LANG}
 
-  ; GTK+ Section Prompts
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT GTK_INSTALL_ERROR			${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT GTK_BAD_INSTALL_PATH		${CUR_LANG}
-
   ; URI Handler section
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT URI_HANDLERS_SECTION_TITLE		${CUR_LANG}
 
@@ -73,31 +66,10 @@
   ; Spellcheck Section Prompts
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_TITLE	${CUR_LANG}
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ERROR		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DICT_ERROR		${CUR_LANG}
   !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT ASPELL_INSTALL_FAILED		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_BRETON		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CATALAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CZECH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_WELSH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DANISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GERMAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ENGLISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GREEK		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ESPERANTO		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SPANISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FAROESE		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FRENCH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ITALIAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DUTCH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_NORWEGIAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_POLISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_PORTUGUESE		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ROMANIAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_RUSSIAN		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SLOVAK		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SWEDISH		${CUR_LANG}
-  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_UKRAINIAN		${CUR_LANG}
+
+  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DEBUGSYMBOLS_ERROR		${CUR_LANG}
+  !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_GTK_DOWNLOAD_ERROR		${CUR_LANG}
 
   !undef CUR_LANG
 !macroend
--- a/pidgin/win32/nsis/pidgin-installer.nsi	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/pidgin-installer.nsi	Mon Mar 08 22:53:02 2010 +0000
@@ -8,11 +8,9 @@
 ;--------------------------------
 ;Global Variables
 Var name
-Var GTK_FOLDER
 Var ISSILENT
 Var STARTUP_RUN_KEY
 Var SPELLCHECK_SEL
-Var LANGUAGE_SET
 
 ;--------------------------------
 ;Configuration
@@ -20,20 +18,17 @@
 ;The name var is set in .onInit
 Name $name
 
-!ifdef WITH_GTK
-OutFile "pidgin-${PIDGIN_VERSION}.exe"
+!ifdef OFFLINE_INSTALLER
+OutFile "pidgin-${PIDGIN_VERSION}-offline.exe"
 !else
-!ifdef DEBUG
-OutFile "pidgin-${PIDGIN_VERSION}-debug.exe"
-!else
-OutFile "pidgin-${PIDGIN_VERSION}-no-gtk.exe"
-!endif
+OutFile "pidgin-${PIDGIN_VERSION}.exe"
 !endif
 
 SetCompressor /SOLID lzma
 ShowInstDetails show
 ShowUninstDetails show
 SetDateSave on
+RequestExecutionLevel highest
 
 ; $name and $INSTDIR are set in .onInit function..
 
@@ -41,6 +36,7 @@
 !include "Sections.nsh"
 !include "WinVer.nsh"
 !include "LogicLib.nsh"
+!include "Memento.nsh"
 
 !include "FileFunc.nsh"
 !insertmacro GetParameters
@@ -52,11 +48,12 @@
 !insertmacro WordFind
 !insertmacro un.WordFind
 
+!include "TextFunc.nsh"
+
 ;--------------------------------
 ;Defines
 
 !define PIDGIN_NSIS_INCLUDE_PATH		"."
-!define PIDGIN_INSTALLER_DEPS			"..\..\..\..\win32-dev\pidgin-inst-deps"
 
 ; Remove these and the stuff that uses them at some point
 !define OLD_GAIM_REG_KEY			"SOFTWARE\gaim"
@@ -70,15 +67,15 @@
 !define STARTUP_RUN_KEY				"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
 !define PIDGIN_UNINST_EXE			"pidgin-uninst.exe"
 
-!define GTK_MIN_VERSION				"2.6.10"
-!define GTK_REG_KEY				"SOFTWARE\GTK\2.0"
+!define GTK_MIN_VERSION				"2.14.0"
 !define PERL_REG_KEY				"SOFTWARE\Perl"
 !define PERL_DLL				"perl510.dll"
-!define GTK_DEFAULT_INSTALL_PATH		"$COMMONFILES\GTK\2.0"
-!define GTK_RUNTIME_INSTALLER			"..\..\..\..\gtk_installer\gtk-runtime-${GTK_INSTALL_VERSION}*.exe"
 
-!define ASPELL_REG_KEY				"SOFTWARE\Aspell"
 !define DOWNLOADER_URL				"http://pidgin.im/win32/download_redir.php"
+!define SPELL_DOWNLOAD_URL			"http://ftp.services.openoffice.org/pub/OpenOffice.org/contrib/dictionaries"
+
+!define MEMENTO_REGISTRY_ROOT			HKLM
+!define MEMENTO_REGISTRY_KEY			"${PIDGIN_UNINSTALL_KEY}"
 
 ;--------------------------------
 ;Version resource
@@ -87,14 +84,10 @@
 VIAddVersionKey "FileVersion" "${PIDGIN_VERSION}"
 VIAddVersionKey "ProductVersion" "${PIDGIN_VERSION}"
 VIAddVersionKey "LegalCopyright" ""
-!ifdef WITH_GTK
-VIAddVersionKey "FileDescription" "Pidgin Installer (w/ GTK+ Installer)"
+!ifdef OFFLINE_INSTALLER
+VIAddVersionKey "FileDescription" "Pidgin Installer (Offline)"
 !else
-!ifdef DEBUG
-VIAddVersionKey "FileDescription" "Pidgin Installer (Debug Version)"
-!else
-VIAddVersionKey "FileDescription" "Pidgin Installer (w/o GTK+ Installer)"
-!endif
+VIAddVersionKey "FileDescription" "Pidgin Installer"
 !endif
 
 ;--------------------------------
@@ -139,14 +132,6 @@
   !insertmacro MUI_PAGE_LICENSE			"../../../COPYING"
   !insertmacro MUI_PAGE_COMPONENTS
 
-!ifdef WITH_GTK
-  ; GTK+ install dir page
-  !define MUI_PAGE_CUSTOMFUNCTION_PRE		preGtkDirPage
-  !define MUI_PAGE_CUSTOMFUNCTION_LEAVE		postGtkDirPage
-  !define MUI_DIRECTORYPAGE_VARIABLE		$GTK_FOLDER
-  !insertmacro MUI_PAGE_DIRECTORY
-!endif
-
   ; Pidgin install dir page
   !insertmacro MUI_PAGE_DIRECTORY
 
@@ -317,7 +302,7 @@
           IfErrors uninstall_problem
             ; Ready to uninstall..
             ClearErrors
-            ExecWait '"$TEMP\$R6" /S _?=$R1'
+            ExecWait '"$TEMP\$R6" /S /UPGRADE=1 _?=$R1'
             IfErrors exec_error
               Delete "$TEMP\$R6"
             Goto done
@@ -348,77 +333,40 @@
 ;--------------------------------
 ;GTK+ Runtime Install Section
 
-!ifdef WITH_GTK
 Section $(GTK_SECTION_TITLE) SecGtk
 
-  Call CheckUserInstallRights
-  Pop $R1
-
-  SetOutPath $TEMP
-  SetOverwrite on
-  File /oname=gtk-runtime.exe ${GTK_RUNTIME_INSTALLER}
-  SetOverwrite off
-
-  Call DoWeNeedGtk
-  Pop $R0
-  Pop $R6
+  InitPluginsDir
+  StrCpy $R1 "$PLUGINSDIR\gtk.zip"
+!ifdef OFFLINE_INSTALLER
 
-  StrCmp $R0 "0" have_gtk
-  StrCmp $R0 "1" upgrade_gtk
-  StrCmp $R0 "2" upgrade_gtk
-  ;StrCmp $R0 "3" no_gtk no_gtk
+  SetOutPath $PLUGINSDIR
+  File /oname=gtk.zip ".\gtk-runtime-${GTK_INSTALL_VERSION}.zip"
 
-  ;no_gtk:
-    StrCmp $R1 "NONE" gtk_no_install_rights
-    ClearErrors
-    ExecWait '"$TEMP\gtk-runtime.exe" /L=$LANGUAGE $ISSILENT /D=$GTK_FOLDER'
-    IfErrors gtk_install_error done
-
-  upgrade_gtk:
-    StrCpy $GTK_FOLDER $R6
-    StrCmp $R0 "2" +2 ; Upgrade isn't optional
-    MessageBox MB_YESNO $(GTK_UPGRADE_PROMPT) /SD IDYES IDNO done
-    ClearErrors
-    ExecWait '"$TEMP\gtk-runtime.exe" /L=$LANGUAGE $ISSILENT /D=$GTK_FOLDER'
-    IfErrors gtk_install_error done
+!else
 
-    gtk_install_error:
-      Delete "$TEMP\gtk-runtime.exe"
-      MessageBox MB_OK $(GTK_INSTALL_ERROR) /SD IDOK
-      Quit
-
-  have_gtk:
-    StrCpy $GTK_FOLDER $R6
-    StrCmp $R1 "NONE" done ; If we have no rights, we can't re-install
-    ; Even if we have a sufficient version of GTK+, we give user choice to re-install.
-    ClearErrors
-    ExecWait '"$TEMP\gtk-runtime.exe" /L=$LANGUAGE $ISSILENT'
-    IfErrors gtk_install_error
-    Goto done
+  ; We need to download the GTK+ runtime
+  retry:
+  StrCpy $R2 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&gtk_version=${GTK_INSTALL_VERSION}&dl_pkg=gtk"
+  DetailPrint "Downloading GTK+ Runtime ... ($R2)"
+  NSISdl::download /TIMEOUT=10000 $R2 $R1
+  Pop $R0
+  StrCmp $R0 "cancel" done
+  StrCmp $R0 "success" +2
+    MessageBox MB_RETRYCANCEL "$(PIDGIN_GTK_DOWNLOAD_ERROR) : $R2" /SD IDCANCEL IDRETRY retry IDCANCEL done
 
-  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-  ; end got_install rights
+!endif
+
+  ;Delete the old Gtk directory
+  RMDir /r "$INSTDIR\Gtk"
 
-  gtk_no_install_rights:
-    ; Install GTK+ to Pidgin install dir
-    StrCpy $GTK_FOLDER $INSTDIR
-    ClearErrors
-    ExecWait '"$TEMP\gtk-runtime.exe" /L=$LANGUAGE $ISSILENT /D=$GTK_FOLDER'
-    IfErrors gtk_install_error
-      SetOverwrite on
-      ClearErrors
-      CopyFiles /FILESONLY "$GTK_FOLDER\bin\*.dll" $GTK_FOLDER
-      SetOverwrite off
-      IfErrors gtk_install_error
-        Delete "$GTK_FOLDER\bin\*.dll"
-        Goto done
-  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-  ; end gtk_no_install_rights
+  SetOutPath "$INSTDIR"
+  nsisunz::UnzipToLog $R1 "$INSTDIR"
+  Pop $R0
+  StrCmp $R0 "success" +2
+    DetailPrint "$R0" ;print error message to log
 
   done:
-    Delete "$TEMP\gtk-runtime.exe"
 SectionEnd ; end of GTK+ section
-!endif
 
 ;--------------------------------
 ;Pidgin Install Section
@@ -430,15 +378,12 @@
   Call CheckUserInstallRights
   Pop $R0
 
-  ; Get GTK+ lib dir if we have it..
-
-  StrCmp $R0 "NONE" pidgin_none
+  StrCmp $R0 "NONE" pidgin_install_files
   StrCmp $R0 "HKLM" pidgin_hklm pidgin_hkcu
 
   pidgin_hklm:
-    ReadRegStr $R1 HKLM ${GTK_REG_KEY} "Path"
     WriteRegStr HKLM "${HKLM_APP_PATHS_KEY}" "" "$INSTDIR\pidgin.exe"
-    WriteRegStr HKLM "${HKLM_APP_PATHS_KEY}" "Path" "$R1\bin"
+    WriteRegStr HKLM "${HKLM_APP_PATHS_KEY}" "Path" "$INSTDIR\Gtk\bin"
     WriteRegStr HKLM ${PIDGIN_REG_KEY} "" "$INSTDIR"
     WriteRegStr HKLM ${PIDGIN_REG_KEY} "Version" "${PIDGIN_VERSION}"
     WriteRegStr HKLM "${PIDGIN_UNINSTALL_KEY}" "DisplayName" "Pidgin"
@@ -452,10 +397,6 @@
     Goto pidgin_install_files
 
   pidgin_hkcu:
-    ReadRegStr $R1 HKCU ${GTK_REG_KEY} "Path"
-    StrCmp $R1 "" 0 +2
-      ReadRegStr $R1 HKLM ${GTK_REG_KEY} "Path"
-
     WriteRegStr HKCU ${PIDGIN_REG_KEY} "" "$INSTDIR"
     WriteRegStr HKCU ${PIDGIN_REG_KEY} "Version" "${PIDGIN_VERSION}"
     WriteRegStr HKCU "${PIDGIN_UNINSTALL_KEY}" "DisplayName" "Pidgin"
@@ -466,9 +407,6 @@
     WriteRegStr HKCU "${PIDGIN_UNINSTALL_KEY}" "UninstallString" "$INSTDIR\${PIDGIN_UNINST_EXE}"
     Goto pidgin_install_files
 
-  pidgin_none:
-    ReadRegStr $R1 HKLM ${GTK_REG_KEY} "Path"
-
   pidgin_install_files:
     SetOutPath "$INSTDIR"
     ; Pidgin files
@@ -478,18 +416,7 @@
     Delete "$INSTDIR\plugins\liboscar.dll"
     Delete "$INSTDIR\plugins\libjabber.dll"
 
-    File /r ..\..\..\${PIDGIN_INSTALL_DIR}\*.*
-    !ifdef DEBUG
-    File "${PIDGIN_INSTALLER_DEPS}\exchndl.dll"
-    !endif
-
-    ; Install shfolder.dll if need be..
-    SearchPath $R4 "shfolder.dll"
-    StrCmp $R4 "" 0 got_shfolder
-      SetOutPath "$SYSDIR"
-      File "${PIDGIN_INSTALLER_DEPS}\shfolder.dll"
-      SetOutPath "$INSTDIR"
-    got_shfolder:
+    File /r /x locale ..\..\..\${PIDGIN_INSTALL_DIR}\*.*
 
     ; Check if Perl is installed, if so add it to the AppPaths
     ReadRegStr $R2 HKLM ${PERL_REG_KEY} ""
@@ -574,99 +501,67 @@
 SectionGroupEnd
 
 ;--------------------------------
+;Translations
+
+!macro LANG_SECTION lang
+  ${MementoUnselectedSection} "${lang}" SecLang_${lang}
+    SetOutPath "$INSTDIR\locale\${lang}\LC_MESSAGES"
+    File "..\..\..\${PIDGIN_INSTALL_DIR}\locale\${lang}\LC_MESSAGES\*.mo"
+    SetOutPath "$INSTDIR"
+  ${MementoSectionEnd}
+!macroend
+SectionGroup $(TRANSLATIONS_SECTION_TITLE) SecTranslations
+  # pidgin-translations is generated based on the contents of the locale directory
+  !include "pidgin-translations.nsh"
+SectionGroupEnd
+${MementoSectionDone}
+
+;--------------------------------
 ;Spell Checking
 
-SectionGroup /e $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck
-  Section /o $(PIDGIN_SPELLCHECK_BRETON) SecSpellCheckBreton
-    Push ${SecSpellCheckBreton}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_CATALAN) SecSpellCheckCatalan
-    Push ${SecSpellCheckCatalan}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_CZECH) SecSpellCheckCzech
-    Push ${SecSpellCheckCzech}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_WELSH) SecSpellCheckWelsh
-    Push ${SecSpellCheckWelsh}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_DANISH) SecSpellCheckDanish
-    Push ${SecSpellCheckDanish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_GERMAN) SecSpellCheckGerman
-    Push ${SecSpellCheckGerman}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_GREEK) SecSpellCheckGreek
-    Push ${SecSpellCheckGreek}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_ENGLISH) SecSpellCheckEnglish
-    Push ${SecSpellCheckEnglish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_ESPERANTO) SecSpellCheckEsperanto
-    Push ${SecSpellCheckEsperanto}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_SPANISH) SecSpellCheckSpanish
-    Push ${SecSpellCheckSpanish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_FAROESE) SecSpellCheckFaroese
-    Push ${SecSpellCheckFaroese}
-    Call InstallAspellAndDict
+!macro SPELLCHECK_SECTION lang lang_name lang_file
+  Section /o "${lang_name}" SecSpell_${lang}
+    Push ${lang_file}
+    Push ${lang}
+    Call InstallDict
   SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_FRENCH) SecSpellCheckFrench
-    Push ${SecSpellCheckFrench}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_ITALIAN) SecSpellCheckItalian
-    Push ${SecSpellCheckItalian}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_DUTCH) SecSpellCheckDutch
-    Push ${SecSpellCheckDutch}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_NORWEGIAN) SecSpellCheckNorwegian
-    Push ${SecSpellCheckNorwegian}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_POLISH) SecSpellCheckPolish
-    Push ${SecSpellCheckPolish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_PORTUGUESE) SecSpellCheckPortuguese
-    Push ${SecSpellCheckPortuguese}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_ROMANIAN) SecSpellCheckRomanian
-    Push ${SecSpellCheckRomanian}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_RUSSIAN) SecSpellCheckRussian
-    Push ${SecSpellCheckRussian}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_SLOVAK) SecSpellCheckSlovak
-    Push ${SecSpellCheckSlovak}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_SWEDISH) SecSpellCheckSwedish
-    Push ${SecSpellCheckSwedish}
-    Call InstallAspellAndDict
-  SectionEnd
-  Section /o $(PIDGIN_SPELLCHECK_UKRAINIAN) SecSpellCheckUkrainian
-    Push ${SecSpellCheckUkrainian}
-    Call InstallAspellAndDict
-  SectionEnd
+!macroend
+SectionGroup $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck
+  !include "pidgin-spellcheck.nsh"
 SectionGroupEnd
 
+Section /o $(DEBUG_SYMBOLS_SECTION_TITLE) SecDebugSymbols
+  
+  InitPluginsDir
+  StrCpy $R1 "$PLUGINSDIR\dbgsym.zip"
+!ifdef OFFLINE_INSTALLER
+
+  SetOutPath $PLUGINSDIR
+  File /oname=dbgsym.zip "..\..\..\pidgin-${PIDGIN_VERSION}-dbgsym.zip"
+
+!else
+
+  ; We need to download the debug symbols
+  retry:
+  StrCpy $R2 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=dbgsym"
+  DetailPrint "Downloading Debug Symbols... ($R2)"
+  NSISdl::download /TIMEOUT=10000 $R2 $R1
+  Pop $R0
+  StrCmp $R0 "cancel" done
+  StrCmp $R0 "success" +2
+    MessageBox MB_RETRYCANCEL "$(PIDGIN_DEBUGSYMBOLS_ERROR) : $R2" /SD IDCANCEL IDRETRY retry IDCANCEL done
+
+!endif
+
+  SetOutPath "$INSTDIR"
+  nsisunz::UnzipToLog $R1 "$INSTDIR"
+  Pop $R0
+  StrCmp $R0 "success" +2
+    DetailPrint "$R0" ;print error message to log
+
+  done:
+SectionEnd
+
 ;--------------------------------
 ;Uninstaller Section
 
@@ -796,34 +691,55 @@
     Delete "$INSTDIR\sounds\purple\send.wav"
     RMDir "$INSTDIR\sounds\purple"
     RMDir "$INSTDIR\sounds"
+    Delete "$INSTDIR\spellcheck\libenchant.dll"
+    Delete "$INSTDIR\spellcheck\libgtkspell-0.dll"
+    Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_aspell.dll"
+    Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_ispell.dll"
+    Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_myspell.dll"
+    RMDir "$INSTDIR\spellcheck\lib\enchant"
+    RMDir "$INSTDIR\spellcheck\lib"
+    RMDir "$INSTDIR\spellcheck"
     Delete "$INSTDIR\freebl3.dll"
     Delete "$INSTDIR\idletrack.dll"
-    Delete "$INSTDIR\libgtkspell.dll"
     Delete "$INSTDIR\libjabber.dll"
+    Delete "$INSTDIR\libnspr4.dll"
     Delete "$INSTDIR\libmeanwhile-1.dll"
     Delete "$INSTDIR\liboscar.dll"
+    Delete "$INSTDIR\libplc4.dll"
+    Delete "$INSTDIR\libplds4.dll"
     Delete "$INSTDIR\libpurple.dll"
     Delete "$INSTDIR\libsasl.dll"
     Delete "$INSTDIR\libsilc-1-1-2.dll"
     Delete "$INSTDIR\libsilcclient-1-1-2.dll"
-    Delete "$INSTDIR\libxml2.dll"
+    Delete "$INSTDIR\libxml2-2.dll"
     Delete "$INSTDIR\libymsg.dll"
-    Delete "$INSTDIR\nspr4.dll"
     Delete "$INSTDIR\nss3.dll"
+    Delete "$INSTDIR\nssutil3.dll"
     Delete "$INSTDIR\nssckbi.dll"
     Delete "$INSTDIR\pidgin.dll"
     Delete "$INSTDIR\pidgin.exe"
-    Delete "$INSTDIR\plc4.dll"
-    Delete "$INSTDIR\plds4.dll"
     Delete "$INSTDIR\smime3.dll"
     Delete "$INSTDIR\softokn3.dll"
+    Delete "$INSTDIR\sqlite3.dll"
     Delete "$INSTDIR\ssl3.dll"
     Delete "$INSTDIR\${PIDGIN_UNINST_EXE}"
-    !ifdef DEBUG
     Delete "$INSTDIR\exchndl.dll"
-    !endif
     Delete "$INSTDIR\install.log"
 
+    ; Remove the debug symbols
+    RMDir /r "$INSTDIR\pidgin-${PIDGIN_VERSION}-dbgsym"
+
+    ; Remove the local GTK+ copy (if we're not just upgrading)
+    ${GetParameters} $R0
+    ClearErrors
+    ${GetOptions} "$R0" "/UPGRADE=" $R1
+    IfErrors +2
+    StrCmp $R1 "1" upgrade_done
+    RMDir /r "$INSTDIR\Gtk"
+    ; Remove the downloaded spellcheck dictionaries (if we're not just upgrading)
+    RMDir /r "$INSTDIR\spellcheck"
+    upgrade_done:
+
     ;Try to remove Pidgin install dir (only if empty)
     RMDir "$INSTDIR"
 
@@ -849,10 +765,8 @@
 !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
   !insertmacro MUI_DESCRIPTION_TEXT ${SecPidgin} \
         $(PIDGIN_SECTION_DESCRIPTION)
-!ifdef WITH_GTK
   !insertmacro MUI_DESCRIPTION_TEXT ${SecGtk} \
         $(GTK_SECTION_DESCRIPTION)
-!endif
 
   !insertmacro MUI_DESCRIPTION_TEXT ${SecShortcuts} \
         $(PIDGIN_SHORTCUTS_SECTION_DESCRIPTION)
@@ -863,50 +777,7 @@
 
   !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheck} \
         $(PIDGIN_SPELLCHECK_SECTION_DESCRIPTION)
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckBreton} \
-        "$(PIDGIN_SPELLCHECK_BRETON) (862kb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckCatalan} \
-        "$(PIDGIN_SPELLCHECK_CATALAN) (3.9Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckCzech} \
-        "$(PIDGIN_SPELLCHECK_CZECH) (17Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckWelsh} \
-        "$(PIDGIN_SPELLCHECK_WELSH) (4.2Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckDanish} \
-        "$(PIDGIN_SPELLCHECK_DANISH) (6.9Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckGerman} \
-        "$(PIDGIN_SPELLCHECK_GERMAN) (5.4Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckGreek} \
-        "$(PIDGIN_SPELLCHECK_GREEK) (7.1Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckEnglish} \
-        "$(PIDGIN_SPELLCHECK_ENGLISH) (2.3Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckEsperanto} \
-        "$(PIDGIN_SPELLCHECK_ESPERANTO) (5.7Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSpanish} \
-        "$(PIDGIN_SPELLCHECK_SPANISH) (7.0Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckFaroese} \
-        "$(PIDGIN_SPELLCHECK_FAROESE) (913kb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckFrench} \
-        "$(PIDGIN_SPELLCHECK_FRENCH) (9.3Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckItalian} \
-        "$(PIDGIN_SPELLCHECK_ITALIAN) (770kb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckDutch} \
-        "$(PIDGIN_SPELLCHECK_DUTCH) (3.7Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckNorwegian} \
-        "$(PIDGIN_SPELLCHECK_NORWEGIAN) (3.2Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckPolish} \
-        "$(PIDGIN_SPELLCHECK_POLISH) (9.3Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckPortuguese} \
-        "$(PIDGIN_SPELLCHECK_PORTUGUESE) (5.5Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckRomanian} \
-        "$(PIDGIN_SPELLCHECK_ROMANIAN) (906kb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckRussian} \
-        "$(PIDGIN_SPELLCHECK_RUSSIAN) (11Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSlovak} \
-        "$(PIDGIN_SPELLCHECK_SLOVAK) (8.0Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSwedish} \
-        "$(PIDGIN_SPELLCHECK_SWEDISH) (2.2Mb)"
-  !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckUkrainian} \
-        "$(PIDGIN_SPELLCHECK_UKRAINIAN) (12Mb)"
+
 !insertmacro MUI_FUNCTION_DESCRIPTION_END
 
 ;--------------------------------
@@ -1145,93 +1016,42 @@
 ; Call DoWeNeedGtk
 ; First Pop:
 ;   0 - We have the correct version
-;       Second Pop: Key where Version was found
 ;   1 - We have an old version that should work, prompt user for optional upgrade
-;       Second Pop: HKLM or HKCU depending on where GTK was found.
 ;   2 - We have an old version that needs to be upgraded
-;       Second Pop: HKLM or HKCU depending on where GTK was found.
 ;   3 - We don't have Gtk+ at all
-;       Second Pop: "NONE, HKLM or HKCU" depending on our rights..
 ;
 Function DoWeNeedGtk
-  ; Logic should be:
-  ; - Check what user rights we have (HKLM or HKCU)
-  ;   - If HKLM rights..
-  ;     - Only check HKLM key for GTK+
-  ;       - If installed to HKLM, check it and return.
-  ;   - If HKCU rights..
-  ;     - First check HKCU key for GTK+
-  ;       - if good or bad exists stop and ret.
-  ;     - If no hkcu gtk+ install, check HKLM
-  ;       - If HKLM ver exists but old, return as if no ver exits.
-  ;   - If no rights
-  ;     - Check HKLM
   Push $0
   Push $1
-  Push $2
-  Push $3
 
-  Call CheckUserInstallRights
-  Pop $1
-  StrCmp $1 "HKLM" check_hklm
-  StrCmp $1 "HKCU" check_hkcu check_hklm
-    check_hkcu:
-      ReadRegStr $0 HKCU ${GTK_REG_KEY} "Version"
-      StrCpy $2 "HKCU"
-      StrCmp $0 "" check_hklm have_gtk
+  IfFileExists "$INSTDIR\Gtk\CONTENTS" +3
+  Push "3"
+  Goto done
 
-    check_hklm:
-      ReadRegStr $0 HKLM ${GTK_REG_KEY} "Version"
-      StrCpy $2 "HKLM"
-      StrCmp $0 "" no_gtk have_gtk
-
-  have_gtk:
-    ; GTK+ is already installed; check version.
-    ; Change this to not even run the GTK installer if this version is already installed.
-    ${VersionCompare} ${GTK_INSTALL_VERSION} $0 $3
-    IntCmp $3 1 +1 good_version good_version
-    ${VersionCompare} ${GTK_MIN_VERSION} $0 $3
+  ClearErrors
+  ${ConfigRead} "$INSTDIR\Gtk\CONTENTS" "Bundle Version " $0
+  IfErrors 0 +3
+  Push "3"
+  Goto done
 
-      ; Bad version. If hklm ver and we have hkcu or no rights.. return no gtk
-      StrCmp $1 "NONE" no_gtk ; if no rights.. can't upgrade
-      StrCmp $1 "HKCU" 0 +2   ; if HKLM can upgrade..
-      StrCmp $2 "HKLM" no_gtk ; have hkcu rights.. if found hklm ver can't upgrade..
-      Push $2
-      IntCmp $3 1 +3
-        Push "1" ; Optional Upgrade
-        Goto done
-        Push "2" ; Mandatory Upgrade
-        Goto done
+  ${VersionCompare} ${GTK_INSTALL_VERSION} $0 $1
+  IntCmp $1 1 +3
+  Push "0" ; Have a good version
+  Goto done
 
-  good_version:
-    StrCmp $2 "HKLM" have_hklm_gtk have_hkcu_gtk
-      have_hkcu_gtk:
-        ; Have HKCU version
-        ReadRegStr $0 HKCU ${GTK_REG_KEY} "Path"
-        Goto good_version_cont
-
-      have_hklm_gtk:
-        ReadRegStr $0 HKLM ${GTK_REG_KEY} "Path"
-        Goto good_version_cont
-
-    good_version_cont:
-      Push $0  ; The path to existing GTK+
-      Push "0"
-      Goto done
-
-  no_gtk:
-    Push $1 ; our rights
-    Push "3"
-    Goto done
+  ${VersionCompare} ${GTK_MIN_VERSION} $0 $1
+  IntCmp $1 1 +3
+  Push "1" ; Optional Upgrade
+  Goto done
+  Push "2" ; Mandatory Upgrade
+  Goto done
 
   done:
-  ; The top two items on the stack are what we want to return
-  Exch 4
+  ; The item on the stack is what we want to return
+  Exch
   Pop $1
-  Exch 4
+  Exch
   Pop $0
-  Pop $3
-  Pop $2
 FunctionEnd
 
 
@@ -1302,11 +1122,7 @@
   DeleteRegValue HKCU "${OLD_GAIM_REG_KEY}" "Installer Language"
   WriteRegStr HKCU "${PIDGIN_REG_KEY}" "Installer Language" "$R0"
 
-  !insertmacro SetSectionFlag ${SecSpellCheck} ${SF_RO}
-  !insertmacro UnselectSection ${SecSpellCheck}
-
-  ;Mark the dictionaries that are already installed as readonly
-  Call SelectAndDisableInstalledDictionaries
+  ${MementoSectionRestore}
 
   ;Preselect the URI handlers as appropriate
   Call SelectURIHandlerSelections
@@ -1346,12 +1162,10 @@
   IfSilent 0 +2
     StrCpy $ISSILENT "/NOUI"
 
-  StrCpy $LANGUAGE_SET "0"
   ClearErrors
   ${GetOptions} "$R3" "/L=" $R1
-  IfErrors +4
+  IfErrors +3
   StrCpy $LANGUAGE $R1
-  StrCpy $LANGUAGE_SET "1"
   Goto skip_lang
 
   ; Select Language
@@ -1408,6 +1222,10 @@
     StrCpy $INSTDIR "$R2\Pidgin"
 
   instdir_done:
+
+  ;Mark the dictionaries that are already installed as readonly
+  Call SelectAndDisableInstalledDictionaries
+
 ;LogSet on
   Pop $R3
   Pop $R2
@@ -1416,17 +1234,14 @@
 FunctionEnd
 
 Function .onInstSuccess
-  ; NSIS doesn't appear to save the language when in Silent Mode, so we do so manually
-  IfSilent 0 done
 
-  StrCmp $LANGUAGE_SET "0" done
+  ${MementoSectionSave}
 
-  WriteRegStr "${MUI_LANGDLL_REGISTRY_ROOT}" "${MUI_LANGDLL_REGISTRY_KEY}" "${MUI_LANGDLL_REGISTRY_VALUENAME}" $LANGUAGE
-
-  done:
 FunctionEnd
 
+
 Function un.onInit
+
   Call un.RunCheck
   StrCpy $name "Pidgin ${PIDGIN_VERSION}"
 ;LogSet on
@@ -1440,339 +1255,99 @@
 
 Function preWelcomePage
   Push $R0
-
-!ifndef WITH_GTK
-  ; If this installer dosn't have GTK, check whether we need it.
-  ; We do this here and not in .onInit because language change in
-  ; .onInit doesn't take effect until it is finished.
-  Call DoWeNeedGtk
-  Pop $R0
-  Pop $GTK_FOLDER
+  Push $R1
 
-  IntCmp $R0 1 done done
-  MessageBox MB_OK $(GTK_INSTALLER_NEEDED) /SD IDOK
-  Quit
-
-  done:
-
-!else
-  Push $R1
-  Push $R2
+!ifdef OFFLINE_INSTALLER
+    !insertmacro SelectSection ${SecDebugSymbols}
+!endif
 
   Call DoWeNeedGtk
   Pop $R0
-  Pop $R2
-  IntCmp $R0 1 gtk_selection_done gtk_not_mandatory
+  IntCmp $R0 1 done gtk_not_mandatory
     ; Make the GTK+ Section RO if it is required.
     !insertmacro SetSectionFlag ${SecGtk} ${SF_RO}
-    Goto gtk_selection_done
+    Goto done
   gtk_not_mandatory:
     ; Don't select the GTK+ section if we already have this version or newer installed
     !insertmacro UnselectSection ${SecGtk}
-  gtk_selection_done:
-
-  ; If on Win95/98/ME warn them that the GTK+ version wont work
-  ${Unless} ${IsNT}
-    !insertmacro UnselectSection ${SecGtk}
-    !insertmacro SetSectionFlag ${SecGtk} ${SF_RO}
-    MessageBox MB_OK $(GTK_WINDOWS_INCOMPATIBLE) /SD IDOK
-    IntCmp $R0 1 done done ; Upgrade isn't optional - abort if we don't have a suitable version
-    Quit
-  ${EndIf}
 
   done:
-  Pop $R2
-  Pop $R1
-!endif
-  Pop $R0
-FunctionEnd
-
-!ifdef WITH_GTK
-Function preGtkDirPage
-  Push $R0
-  Push $R1
-  Call DoWeNeedGtk
-  Pop $R0
-  Pop $R1
-
-  IntCmp $R0 2 +2 +2 no_gtk
-  StrCmp $R0 "3" no_gtk no_gtk
-
-  ; Don't show dir selector.. Upgrades are done to existing path..
   Pop $R1
   Pop $R0
-  Abort
-
-  no_gtk:
-    StrCmp $R1 "NONE" 0 no_gtk_cont
-      ; Got no install rights..
-      Pop $R1
-      Pop $R0
-      Abort
-    no_gtk_cont:
-      ; Suggest path..
-      StrCmp $R1 "HKCU" 0 hklm1
-        ${GetParent} $SMPROGRAMS $R0
-        ${GetParent} $R0 $R0
-        StrCpy $R0 "$R0\GTK\2.0"
-        Goto got_path
-      hklm1:
-        StrCpy $R0 "${GTK_DEFAULT_INSTALL_PATH}"
-
-   got_path:
-     StrCpy $name "GTK+ ${GTK_INSTALL_VERSION}"
-     StrCpy $GTK_FOLDER $R0
-     Pop $R1
-     Pop $R0
 FunctionEnd
 
-Function postGtkDirPage
-  Push $R0
-  StrCpy $name "Pidgin ${PIDGIN_VERSION}"
-  Push $GTK_FOLDER
-  Call VerifyDir
-  Pop $R0
-  StrCmp $R0 "0" 0 done
-    MessageBox MB_OK $(GTK_BAD_INSTALL_PATH) /SD IDOK
-    Pop $R0
-    Abort
-  done:
-  Pop $R0
-FunctionEnd
-!endif
-
 ; SpellChecker Related Functions
 ;-------------------------------
 
 ; Convert the a Section index to the language code
 ; Push the section index onto the stack and pop off the language code after the call
 ; This will set the error code, if no match is found
-Function GetLangCodeForSection
-  ClearErrors
-  Push $R0
-  Exch
-  Pop $R0 ;This is the section index
-
-  IntCmp $R0 ${SecSpellCheckBreton} 0 +3 +3
-  StrCpy $R0 "br"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckCatalan} 0 +3 +3
-  StrCpy $R0 "ca"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckCzech} 0 +3 +3
-  StrCpy $R0 "cs"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckWelsh} 0 +3 +3
-  StrCpy $R0 "cy"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckDanish} 0 +3 +3
-  StrCpy $R0 "da"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckGerman} 0 +3 +3
-  StrCpy $R0 "de"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckGreek} 0 +3 +3
-  StrCpy $R0 "el"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckEnglish} 0 +3 +3
-  StrCpy $R0 "en"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckEsperanto} 0 +3 +3
-  StrCpy $R0 "eo"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckSpanish} 0 +3 +3
-  StrCpy $R0 "es"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckFaroese} 0 +3 +3
-  StrCpy $R0 "fo"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckFrench} 0 +3 +3
-  StrCpy $R0 "fr"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckItalian} 0 +3 +3
-  StrCpy $R0 "it"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckDutch} 0 +3 +3
-  StrCpy $R0 "nl"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckNorwegian} 0 +3 +3
-  StrCpy $R0 "no"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckPolish} 0 +3 +3
-  StrCpy $R0 "pl"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckPortuguese} 0 +3 +3
-  StrCpy $R0 "pt"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckRomanian} 0 +3 +3
-  StrCpy $R0 "ro"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckRussian} 0 +3 +3
-  StrCpy $R0 "ru"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckSlovak} 0 +3 +3
-  StrCpy $R0 "sk"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckSwedish} 0 +3 +3
-  StrCpy $R0 "sv"
-  Goto done
-  IntCmp $R0 ${SecSpellCheckUkrainian} 0 +3 +3
-  StrCpy $R0 "uk"
-  Goto done
-
-  SetErrors
-
-  done:
-  Exch $R0
-FunctionEnd ;GetLangCodeForSection
 
 ; Select and Disable any Sections that have currently installed dictionaries
+!macro CHECK_SPELLCHECK_SECTION lang
+  ;Advance to the next (correct) section index
+  IntOp $R0 $R0 + 1
+  IfFileExists "$INSTDIR\spellcheck\share\enchant\myspell\${lang}.dic" 0 done_${lang}
+  SectionGetFlags $R0 $R1
+  IntOp $R1 $R1 | ${SF_RO} ; Mark Readonly
+  IntOp $R1 $R1 | ${SF_SELECTED} ; Select
+  SectionSetFlags $R0 $R1
+  done_${lang}:
+!macroend
 Function SelectAndDisableInstalledDictionaries
   Push $R0
   Push $R1
-  Push $R2
-
-  ; Start with the first language dictionary
-  IntOp $R0 ${SecSpellCheck} + 1
-
-  start:
-  ; If it is the end of the section group, stop
-  SectionGetFlags $R0 $R1
-  IntOp $R2 $R1 & ${SF_SECGRPEND}
-  IntCmp $R2 ${SF_SECGRPEND} done
-
-  Push $R0
-  Call GetLangCodeForSection
-  Pop $R2
-  IfErrors end_loop
-  ReadRegStr $R2 HKLM "${ASPELL_REG_KEY}-$R2" "" ; Check that the dictionary is installed
-  StrCmp $R2 "" end_loop ; If it isn't installed, skip to the next item
-  IntOp $R1 $R1 | ${SF_RO} ; Mark Readonly
-  IntOp $R1 $R1 | ${SF_SELECTED} ; Select
-  SectionSetFlags $R0 $R1
-
-  end_loop:
-  IntOp $R0 $R0 + 1 ;Advance to the next section
-  Goto start
 
-  done:
-  Pop $R2
-  Pop $R1
-  Pop $R0
-FunctionEnd
-
-Function InstallAspellAndDict
-  Push $R0
-  Exch
-  Call GetLangCodeForSection
-  Pop $R0 ;This is the language code
-  Push $R1
-
-  IfErrors done ; We weren't able to convert the section to lang code
+  !insertmacro SetSectionFlag ${SecSpellCheck} ${SF_RO}
+  !insertmacro UnselectSection ${SecSpellCheck}
 
-  retry:
-    Call InstallAspell
-    Pop $R1
-    StrCmp $R1 "" +3
-    StrCmp $R1 "cancel" done
-    MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_ERROR) : $R1" /SD IDCANCEL IDRETRY retry IDCANCEL done
-
-  retry_dict:
-    Push $R0
-    Call InstallAspellDictionary
-    Pop $R1
-    StrCmp $R1 "" +3
-    StrCmp $R1 "cancel" done
-    MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_DICT_ERROR) : $R1" /SD IDCANCEL IDRETRY retry_dict
-
-  done:
+  IntOp $R0 ${SecSpellCheck} + 0
+  !include "pidgin-spellcheck-preselect.nsh"
 
   Pop $R1
   Pop $R0
 FunctionEnd
 
-Function InstallAspell
-  Push $R0
-  Push $R1
-  Push $R2
-
-  check:
-  ClearErrors
-  ReadRegDWORD $R0 HKLM ${ASPELL_REG_KEY} "AspellVersion"
-  IntCmp $R0 15 installed
-
-  ; If this is the check after installation, don't infinite loop on failure
-  StrCmp $R1 "$TEMP\aspell_installer.exe" 0 +3
-    StrCpy $R0 $(ASPELL_INSTALL_FAILED)
-    Goto done
-
-  ; We need to download and install aspell
-  StrCpy $R1 "$TEMP\aspell_installer.exe"
-  StrCpy $R2 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=aspell_core"
-  DetailPrint "Downloading Aspell... ($R2)"
-  NSISdl::download /TIMEOUT=10000 $R2 $R1
-  Pop $R0
-  StrCmp $R0 "success" +2
-    Goto done
-  ExecWait '"$R1"'
-  Delete $R1
-  Goto check ; Check that it is now installed correctly
-
-  installed: ;Aspell is currently installed, no error message
-    DetailPrint "Aspell is installed"
-    StrCpy $R0 ''
-
-  done:
-  Pop $R2
-  Pop $R1
-  Exch $R0
-FunctionEnd
-
-Function InstallAspellDictionary
+Function InstallDict
   Push $R0
   Exch
   Pop $R0 ;This is the language code
   Push $R1
+  Exch 2
+  Pop $R1 ;This is the language file
   Push $R2
   Push $R3
-  Push $R4
 
-  check:
   ClearErrors
-  ReadRegStr $R2 HKLM "${ASPELL_REG_KEY}-$R0" ""
-  StrCmp $R2 "" 0 installed
+  IfFileExists "$INSTDIR\spellcheck\share\enchant\myspell\$R0.dic" installed
+
+  InitPluginsDir
 
-  ; If this is the check after installation, don't infinite loop on failure
-  StrCmp $R1 "$TEMP\aspell_dict-$R0.exe" 0 +3
-    StrCpy $R0 $(ASPELL_INSTALL_FAILED)
+  ; We need to download and install dictionary
+  StrCpy $R2 "$PLUGINSDIR\$R1"
+  StrCpy $R3 "${SPELL_DOWNLOAD_URL}/$R1"
+  DetailPrint "Downloading the $R0 Dictionary... ($R3)"
+  retry:
+  NSISdl::download /TIMEOUT=10000 "$R3" "$R2"
+  Pop $R3
+  StrCmp $R3 "cancel" done
+  StrCmp $R3 "success" +3
+    MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_ERROR) : $R3" /SD IDCANCEL IDRETRY retry IDCANCEL done
+    Goto done
+  SetOutPath "$INSTDIR\spellcheck\share\enchant\myspell"
+  nsisunz::UnzipToLog "$R2" "$OUTDIR"
+  SetOutPath "$INSTDIR"
+  Pop $R3
+  StrCmp $R3 "success" installed
+    DetailPrint "$R3" ;print error message to log
     Goto done
 
-  ; We need to download and install aspell
-  StrCpy $R1 "$TEMP\aspell_dict-$R0.exe"
-  StrCpy $R3 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=lang_$R0"
-  DetailPrint "Downloading the Aspell $R0 Dictionary... ($R3)"
-  NSISdl::download /TIMEOUT=10000 $R3 $R1
-  Pop $R3
-  StrCmp $R3 "success" +3
-    StrCpy $R0 $R3
-    Goto done
-  ; Use a specific temporary $OUTDIR for each dictionary because the installer doesn't clean up after itself
-  StrCpy $R4 "$OUTDIR"
-  SetOutPath "$TEMP\aspell_dict-$R0"
-  ExecWait '"$R1"'
-  SetOutPath "$R4"
-  RMDir /r "$TEMP\aspell_dict-$R0"
-  Delete $R1
-  Goto check ; Check that it is now installed correctly
-
   installed: ;The dictionary is currently installed, no error message
-    DetailPrint "Aspell $R0 Dictionary is installed"
-    StrCpy $R0 ''
+    DetailPrint "$R0 Dictionary is installed"
 
   done:
-  Pop $R4
   Pop $R3
   Pop $R2
-  Pop $R1
-  Exch $R0
+  Pop $R0
+  Exch $R1
 FunctionEnd
--- a/pidgin/win32/nsis/translations/afrikaans.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/afrikaans.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -12,7 +12,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Die installeerder loop reeds."
 !define PIDGIN_IS_RUNNING			"Pidgin loop reeds rens.  Verlaat Pidgin eers en probeer dan weer."
-!define GTK_INSTALLER_NEEDED			"Die GTK+-looptydomgewing is f soek f moet opgegradeer word.$\rInstalleer asb. v{GTK_MIN_VERSION} of hor van die GTK+-looptyd"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Volgende >"
@@ -31,8 +30,6 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Skep 'n Begin-kieslysinskrywing vir Pidgin"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"'n Ou weergawe van die GTK+-looptyd is gevind. Wil u opgradeer?$\rLet wel: $(^Name) werk dalk net as u so maak."
-!define GTK_WINDOWS_INCOMPATIBLE		"Windows 95/98/Me is onversoenbaar met GTK+ 2.8.0 en nuwer.  GTK+ ${GTK_INSTALL_VERSION} sal nie installeer nie.$\rIndien u nie GTK+ ${GTK_MIN_VERSION} of nuwer reeds genstalleer het nie, sal die installasie nou staak."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Besoek die WinPidgin-webblad"
@@ -41,8 +38,6 @@
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Kan nie die tans genstalleerde weergawe van Pidgin verwyder nie. Die nuwe weergawe sal genstalleer word sonder om die huidige een te verwyder."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Fout met installering van GTK+-looptyd."
-!define GTK_BAD_INSTALL_PATH			"Die pad wat u verskaf het, is ontoeganklik of kan nie geskep word nie."
 
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"URI-hanteerders"
@@ -54,29 +49,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"Speltoets-ondersteuning"
 !define PIDGIN_SPELLCHECK_ERROR		"Fout met installering van speltoetser"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Fout met installering van speltoetswoordeboek"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Ondersteuning vir speltoeter.  (Internetverbinding benodigd vir installasie)"
-!define ASPELL_INSTALL_FAILED			"Installasie het misluk"
-!define PIDGIN_SPELLCHECK_BRETON		"Bretons"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalaans"
-!define PIDGIN_SPELLCHECK_CZECH		"Tsjeggies"
-!define PIDGIN_SPELLCHECK_WELSH		"Wallies"
-!define PIDGIN_SPELLCHECK_DANISH		"Deens"
-!define PIDGIN_SPELLCHECK_GERMAN		"Duits"
-!define PIDGIN_SPELLCHECK_GREEK		"Grieks"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Engels"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spaans"
-!define PIDGIN_SPELLCHECK_FAROESE		"Faroes"
-!define PIDGIN_SPELLCHECK_FRENCH		"Frans"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italiaans"
-!define PIDGIN_SPELLCHECK_DUTCH		"Nederlands"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Noorweegs"
-!define PIDGIN_SPELLCHECK_POLISH		"Pools"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugees"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Roemeens"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Russies"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slowaaks"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Sweeds"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Oekraens"
 
--- a/pidgin/win32/nsis/translations/albanian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/albanian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -8,8 +8,6 @@
 ;;  Author: Besnik Bleta <besnik@spymac.com>
 ;;
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"Ose mungon mjedisi GTK+ runtime ose lyp përditësim.$\rJu lutem instaloni GTK+ runtime v${GTK_MIN_VERSION} ose më të vonshëm"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Më tej >"
@@ -23,14 +21,11 @@
 
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"U gjet një version i vjetër për GTK+ runtime. Doni të përditësohet?$\rShënim: Pidgin-i mund të mos punojë nëse nuk e bëni."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Vizitoni Faqen Web të Pidgin-it për Windows"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"gabim gjatë instalimit të GTK+ runtime."
-!define GTK_BAD_INSTALL_PATH			"Shtegu që treguat nuk mund të arrihet ose krijohet."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Çinstaluesi nuk gjeti dot zëra regjistri për Pidgin-in.$\rKa mundësi që këtë zbatim ta ketë instaluar një tjetër përdorues."
--- a/pidgin/win32/nsis/translations/arabic.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/arabic.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -9,7 +9,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"  ."
 !define PIDGIN_IS_RUNNING			"  .        ."
-!define GTK_INSTALLER_NEEDED			" + (GTK+)    .$\r   v${GTK_MIN_VERSION}     +"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			" >"
@@ -29,8 +28,6 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"     "
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"     +.  ˿$\r:    $(^Name)   ."
-!define GTK_WINDOWS_INCOMPATIBLE		"   95/98/Me  + 2.8.0  .  + ${GTK_INSTALL_VERSION}  .$\r    + ${GTK_MIN_VERSION}      ."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"     "
@@ -39,8 +36,6 @@
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"    .        ."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"    +."
-!define GTK_BAD_INSTALL_PATH			"       ."
 
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		" "
@@ -52,29 +47,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"  "
 !define PIDGIN_SPELLCHECK_ERROR		"    "
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"     "
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"  .  (   )"
-!define ASPELL_INSTALL_FAILED			" "
-!define PIDGIN_SPELLCHECK_BRETON		"Breton"
-!define PIDGIN_SPELLCHECK_CATALAN		"Catalan"
-!define PIDGIN_SPELLCHECK_CZECH		"Czech"
-!define PIDGIN_SPELLCHECK_WELSH		"Welsh"
-!define PIDGIN_SPELLCHECK_DANISH		"Danish"
-!define PIDGIN_SPELLCHECK_GERMAN		"German"
-!define PIDGIN_SPELLCHECK_GREEK		"Greek"
-!define PIDGIN_SPELLCHECK_ENGLISH		"English"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spanish"
-!define PIDGIN_SPELLCHECK_FAROESE		"Faroese"
-!define PIDGIN_SPELLCHECK_FRENCH		"French"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italian"
-!define PIDGIN_SPELLCHECK_DUTCH		"Dutch"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norwegian"
-!define PIDGIN_SPELLCHECK_POLISH		"Polish"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portuguese"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Romanian"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Russian"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slovak"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Swedish"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainian"
 
--- a/pidgin/win32/nsis/translations/basque.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/basque.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -1,80 +1,51 @@
-;;
-;;  basque.nsh
-;;Abio-menua - Istanteko Mezularitza
-;;  Basque language strings for the Windows Pidgin NSIS installer.
-;;  Windows Code page: 1252
-;;
-;;  Author: Mikel Pascual Aldabaldetreku <mikel.paskual@gmail.com>, 2007.
-
-; Startup Checks
-!define INSTALLER_IS_RUNNING			"Instalatzailea martxan dago."
-!define PIDGIN_IS_RUNNING			"Pidgin istantzia bat dago martxan. Pidgin itxi eta berriro saiatu."
-!define GTK_INSTALLER_NEEDED			"GTK+ exekuzio-ingurunea falta da, edo eguneratu egin beharko litzateke.$\rGTK+ exekuzio-ingurunearen ${GTK_MIN_VERSION} bertsioa edo berriagoa instalatu"
-
-; License Page
-!define PIDGIN_LICENSE_BUTTON			"Jarraitu >"
-!define PIDGIN_LICENSE_BOTTOM_TEXT		"GNU Lizentzia Orokor Publikopean (GPL) argitaratzen da $(^Name). Informatzeko helburu soilarekin aurkezten da hemen lizentzia. $_CLICK"
-
-; Components Page
-!define PIDGIN_SECTION_TITLE			"Pidgin Istanteko Mezularitza Bezeroa (beharrezkoa)"
-!define GTK_SECTION_TITLE			"GTK+ exekuzio ingurunea (beharrezkoa)"
-!define PIDGIN_SHORTCUTS_SECTION_TITLE		"Lasterbideak"
-!define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE	"Mahaigaina"
-!define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE	"Abio-menua"
-!define PIDGIN_SECTION_DESCRIPTION		"Funtsezko Pidgin fitxategi eta dll-ak"
-!define GTK_SECTION_DESCRIPTION		"Plataforma anitzeko GUI tresna-sorta, Pidgin-ek erabilia"
-
-!define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	"Pidgin abiarazteko lasterbideak"
-!define PIDGIN_DESKTOP_SHORTCUT_DESC		"Pidgin-entzako lasterbidea Mahaigainean"
-!define PIDGIN_STARTMENU_SHORTCUT_DESC		"Pidgin-entzako lasterbidea Abio-Menuan"
-
-; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"GTK+ exekuzio-ingurunearen bertsio zahar bat aurkitu da. Eguneratu egin nahi al duzu?$\rOharra: Bestela, posible da $(^Name) ez ibiltzea."
-!define GTK_WINDOWS_INCOMPATIBLE		"GTK+ 2.8.0 eta berriagoekin bateraezinak dira Windows 95/98/Me.  Ez da GTK+ ${GTK_INSTALL_VERSION} instalatuko.$\rJadanik ez badaukazu GTK+ ${GTK_MIN_VERSION} edo berriagorik instalatuta, bertan behera utziko da instalazioa."
-
-; Installer Finish Page
-!define PIDGIN_FINISH_VISIT_WEB_SITE		"Pidgin Webgunera etorri"
-
-; Pidgin Section Prompts and Texts
-!define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Ezin izan da jadanik instalatuta zegoen Pidgin bertsioa kendu. Aurreko bertsioa kendu gabe instalatuko da bertsio berria."
-
-; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Errorea GTK+ exekuzio-ingurunea instalatzean."
-!define GTK_BAD_INSTALL_PATH			"The path you entered can not be accessed or created."
-
-; URL Handler section
-!define URI_HANDLERS_SECTION_TITLE		"URI Kudeatzaileak"
-
-; Uninstall Section Prompts
-!define un.PIDGIN_UNINSTALL_ERROR_1		"Ezin izan dira Pidgin-en erregistro-sarrerak aurkitu.$\rZiurrenik, beste erabiltzaile batek instalatu zuen aplikazio hau."
-!define un.PIDGIN_UNINSTALL_ERROR_2		"Ez daukazu aplikazio hau kentzeko baimenik."
-
-; Spellcheck Section Prompts
-!define PIDGIN_SPELLCHECK_SECTION_TITLE	"Zuzentzaile Ortografikoa"
-!define PIDGIN_SPELLCHECK_ERROR		"Errorea Zuzentzaile Ortografikoa instalatzean"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Errorea Zuzentzaile Ortografikoarentzako hiztegia instalatzean"
-!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Zuzentzaile Ortografikoa.  (Internet konexioa behar du instalatzeko)"
-!define ASPELL_INSTALL_FAILED			"Ezin izan da instalatu"
-!define PIDGIN_SPELLCHECK_BRETON		"Britaniera"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalana"
-!define PIDGIN_SPELLCHECK_CZECH		"Txekiera"
-!define PIDGIN_SPELLCHECK_WELSH		"Gaelikoa"
-!define PIDGIN_SPELLCHECK_DANISH		"Daniera"
-!define PIDGIN_SPELLCHECK_GERMAN		"Alemana"
-!define PIDGIN_SPELLCHECK_GREEK		"Grekoa"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Ingelesa"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperantoa"
-!define PIDGIN_SPELLCHECK_SPANISH		"Gaztelania"
-!define PIDGIN_SPELLCHECK_FAROESE		"Faroera"
-!define PIDGIN_SPELLCHECK_FRENCH		"Frantsesa"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italiera"
-!define PIDGIN_SPELLCHECK_DUTCH		"Nederlandera"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norvegiera"
-!define PIDGIN_SPELLCHECK_POLISH		"Poloniera"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugesa"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Errumaniera"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Errusiera"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Eslovakiera"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Suediera"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukraniera"
-
+;;
+;;  basque.nsh
+;;Abio-menua - Istanteko Mezularitza
+;;  Basque language strings for the Windows Pidgin NSIS installer.
+;;  Windows Code page: 1252
+;;
+;;  Author: Mikel Pascual Aldabaldetreku <mikel.paskual@gmail.com>, 2007.
+
+; Startup Checks
+!define INSTALLER_IS_RUNNING			"Instalatzailea martxan dago."
+!define PIDGIN_IS_RUNNING			"Pidgin istantzia bat dago martxan. Pidgin itxi eta berriro saiatu."
+
+; License Page
+!define PIDGIN_LICENSE_BUTTON			"Jarraitu >"
+!define PIDGIN_LICENSE_BOTTOM_TEXT		"GNU Lizentzia Orokor Publikopean (GPL) argitaratzen da $(^Name). Informatzeko helburu soilarekin aurkezten da hemen lizentzia. $_CLICK"
+
+; Components Page
+!define PIDGIN_SECTION_TITLE			"Pidgin Istanteko Mezularitza Bezeroa (beharrezkoa)"
+!define GTK_SECTION_TITLE			"GTK+ exekuzio ingurunea (beharrezkoa)"
+!define PIDGIN_SHORTCUTS_SECTION_TITLE		"Lasterbideak"
+!define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE	"Mahaigaina"
+!define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE	"Abio-menua"
+!define PIDGIN_SECTION_DESCRIPTION		"Funtsezko Pidgin fitxategi eta dll-ak"
+!define GTK_SECTION_DESCRIPTION		"Plataforma anitzeko GUI tresna-sorta, Pidgin-ek erabilia"
+
+!define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	"Pidgin abiarazteko lasterbideak"
+!define PIDGIN_DESKTOP_SHORTCUT_DESC		"Pidgin-entzako lasterbidea Mahaigainean"
+!define PIDGIN_STARTMENU_SHORTCUT_DESC		"Pidgin-entzako lasterbidea Abio-Menuan"
+
+; GTK+ Directory Page
+
+; Installer Finish Page
+!define PIDGIN_FINISH_VISIT_WEB_SITE		"Pidgin Webgunera etorri"
+
+; Pidgin Section Prompts and Texts
+!define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Ezin izan da jadanik instalatuta zegoen Pidgin bertsioa kendu. Aurreko bertsioa kendu gabe instalatuko da bertsio berria."
+
+; GTK+ Section Prompts
+
+; URL Handler section
+!define URI_HANDLERS_SECTION_TITLE		"URI Kudeatzaileak"
+
+; Uninstall Section Prompts
+!define un.PIDGIN_UNINSTALL_ERROR_1		"Ezin izan dira Pidgin-en erregistro-sarrerak aurkitu.$\rZiurrenik, beste erabiltzaile batek instalatu zuen aplikazio hau."
+!define un.PIDGIN_UNINSTALL_ERROR_2		"Ez daukazu aplikazio hau kentzeko baimenik."
+
+; Spellcheck Section Prompts
+!define PIDGIN_SPELLCHECK_SECTION_TITLE	"Zuzentzaile Ortografikoa"
+!define PIDGIN_SPELLCHECK_ERROR		"Errorea Zuzentzaile Ortografikoa instalatzean"
+!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Zuzentzaile Ortografikoa.  (Internet konexioa behar du instalatzeko)"
+
--- a/pidgin/win32/nsis/translations/bulgarian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/bulgarian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -8,9 +8,6 @@
 ;;
 
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"GTK+ runtime      .$\r   v${GTK_MIN_VERSION}  -"
-
 ; Components Page
 !define PIDGIN_SECTION_TITLE			"Pidgin     ( )"
 !define GTK_SECTION_TITLE			"GTK+ Runtime  (required)"
@@ -19,11 +16,8 @@
 
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"  GTK+ runtime  .    ?$\rNote: Pidgin        ."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"    GTK+ runtime."
-!define GTK_BAD_INSTALL_PATH			"        ."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"         Pidgin.$\r      ."
--- a/pidgin/win32/nsis/translations/catalan.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/catalan.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -12,7 +12,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"L'instal.lador encara est executant-se."
 !define PIDGIN_IS_RUNNING			"Hi ha una instncia del Pidgin executant-se. Surt del Pidgin i torna a intentar-ho."
-!define GTK_INSTALLER_NEEDED			"L'entorn d'execuci GTK+ no existeix o necessita sser actualitzat.$\rSius plau instal.la la versi${GTK_MIN_VERSION} o superior de l'entonr GTK+"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Segent >"
@@ -34,14 +33,11 @@
 
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"S'ha trobat una versi antiga de l'entorn d'execuci GTK. Vols actualitzar-la?$\rNota: $(^Name) no funcionar sino ho fas."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Visita la pgina web de Pidgin per Windows"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Error installlant l'entorn d'execuci GTK+."
-!define GTK_BAD_INSTALL_PATH			"El directori que has introdut no pot sser accedit o creat."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"L'instal.lador podria no trobar les entrades del registre de Pidgin.$\rProbablement un altre usuari ha instal.lat aquesta aplicaci."
@@ -50,29 +46,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Suport a la Verificaci de l'Ortografia "
 !define PIDGIN_SPELLCHECK_ERROR			"Error instal.lant verificaci de l'ortografia"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Error Instal.lant Diccionari  per a Verificaci de l'Ortografia"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Suport per a Verificaci de l'Ortografia.  (s necesaria connexi a internet per dur a terme la instal.laci)"
-!define ASPELL_INSTALL_FAILED			"La instal.laci ha fallat"
-!define PIDGIN_SPELLCHECK_BRETON			"Bret"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catal"
-!define PIDGIN_SPELLCHECK_CZECH			"Txec"
-!define PIDGIN_SPELLCHECK_WELSH			"Galls"
-!define PIDGIN_SPELLCHECK_DANISH			"Dans"
-!define PIDGIN_SPELLCHECK_GERMAN			"Alemany"
-!define PIDGIN_SPELLCHECK_GREEK			"Grec"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Angls"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Espanyol"
-!define PIDGIN_SPELLCHECK_FAROESE			"Feros"
-!define PIDGIN_SPELLCHECK_FRENCH			"Francs"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Itali"
-!define PIDGIN_SPELLCHECK_DUTCH			"Holands"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Noruec"
-!define PIDGIN_SPELLCHECK_POLISH			"Polons"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugus"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Romans"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rus"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Eslovac"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Suec"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ucrans"
 
--- a/pidgin/win32/nsis/translations/czech.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/czech.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -8,8 +8,6 @@
 ;;  Version 2
 ;;
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"GTK+ runtime buto chyb, nebo je poteba provst upgrade.$\rProvete instalaci verze${GTK_MIN_VERSION} nebo vy."
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Dal >"
@@ -23,14 +21,11 @@
 
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Byla nalezena star verze GTK+ runtime. Chcete provst upgrade?$\rUpozornn: Bez upgradu $(^Name) nemus pracovat sprvn."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Navtvit Windows Pidgin Web Page"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Chyba pi instalaci GTK+ runtime."
-!define GTK_BAD_INSTALL_PATH			"Zadan cesta je nedostupn, nebo ji nelze vytvoit."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Odinstaln proces neme najt zznamy pro Pidgin v registrech.$\rPravdpodobn instalaci tto aplikace provedl jin uivatel."
--- a/pidgin/win32/nsis/translations/danish.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/danish.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -8,9 +8,6 @@
 ;;  Version 2
 ;;
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"GTK+ runtime environment enten mangler eller skal opgraderes.$\rInstallr venligst GTK+ runtime version v${GTK_MIN_VERSION} eller hjere."
-
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Nste >"
 !define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) er frigivet under GPL licensen. Licensen er kun medtaget her til generel orientering. $_CLICK"
@@ -23,14 +20,11 @@
 
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Der blev fundet en ldre version af GTK+ runtime. nsker du at opgradere?$\rNB: $(^Name) virker muligvis ikke uden denne opgradering."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Besg Windows Pidgin's hjemmeside"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Fejl under installeringen af GTK+ runtime."
-!define GTK_BAD_INSTALL_PATH			"Stien du har angivet kan ikke findes eller oprettes."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Afinstallationen kunne ikke finde Pidgin i registreringsdatabasen.$\rMuligvis har en anden bruger installeret programmet."
--- a/pidgin/win32/nsis/translations/dutch.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/dutch.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -11,7 +11,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Er is al een installatie actief."
 !define PIDGIN_IS_RUNNING			"Pidgin wordt op dit moment uitgevoerd. Sluit Pidgin af en start de installatie opnieuw."
-!define GTK_INSTALLER_NEEDED			"De GTK+ runtime-omgeving is niet aanwezig of moet vernieuwd worden.$\rInstalleer v${GTK_MIN_VERSION} of nieuwer van de GTK+ runtime-omgeving"
 
 
 ; License Page
@@ -26,14 +25,11 @@
 
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Er is een oude versie van GTK+ gevonden. Wilt u deze bijwerken?$\rLet op: $(^Name) werkt misschien niet als u dit niet doet."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Neem een kijkje op de Windows Pidgin webpagina"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Fout bij installatie van GTK+ runtime omgeving."
-!define GTK_BAD_INSTALL_PATH			"Het door u gegeven pad kan niet benaderd worden."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Het verwijderingsprogramma voor Pidgin kon geen register-ingangen voor Pidgin vinden.$\rWaarschijnlijk heeft een andere gebruiker het programma genstalleerd."
@@ -43,28 +39,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Spellingscontrole"
 !define PIDGIN_SPELLCHECK_ERROR			"Fout bij installatie van spellingscontrole"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Fout bij installatie van woordenboek voor spellingscontrole"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Ondersteuning voor spellingscontrole. (Internetverbinding nodig voor installatie)"
-!define ASPELL_INSTALL_FAILED			"Installatie mislukt"
-!define PIDGIN_SPELLCHECK_BRETON			"Bretons"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catalaans"
-!define PIDGIN_SPELLCHECK_CZECH			"Tsjechisch"
-!define PIDGIN_SPELLCHECK_WELSH			"Welsh"
-!define PIDGIN_SPELLCHECK_DANISH			"Deens"
-!define PIDGIN_SPELLCHECK_GERMAN			"Duits"
-!define PIDGIN_SPELLCHECK_GREEK			"Grieks"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Engels"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Spaans"
-!define PIDGIN_SPELLCHECK_FAROESE			"Faroese"
-!define PIDGIN_SPELLCHECK_FRENCH			"Frans"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Italiaans"
-!define PIDGIN_SPELLCHECK_DUTCH			"Nederlands"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Noors"
-!define PIDGIN_SPELLCHECK_POLISH			"Pools"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugees"
-!define PIDGIN_SPELLCHECK_ROMANIAN			"Roemeens"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Russisch"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slowaaks"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Zweeds"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Oekrans"
--- a/pidgin/win32/nsis/translations/english.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/english.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -14,7 +14,6 @@
 ; Startup Checks
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING INSTALLER_IS_RUNNING			"The installer is already running."
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_IS_RUNNING			"An instance of Pidgin is currently running.  Please exit Pidgin and try again."
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING GTK_INSTALLER_NEEDED			"The GTK+ runtime environment is either missing or needs to be upgraded.$\rPlease install v${GTK_MIN_VERSION} or higher of the GTK+ runtime"
 
 ; License Page
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_LICENSE_BUTTON			"Next >"
@@ -26,16 +25,16 @@
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SHORTCUTS_SECTION_TITLE		"Shortcuts"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE	"Desktop"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE	"Start Menu"
+!insertmacro PIDGIN_MACRO_DEFAULT_STRING TRANSLATIONS_SECTION_TITLE		"Localizations"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SECTION_DESCRIPTION		"Core Pidgin files and dlls"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING GTK_SECTION_DESCRIPTION		"A multi-platform GUI toolkit, used by Pidgin"
 
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	"Shortcuts for starting Pidgin"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DESKTOP_SHORTCUT_DESC		"Create a shortcut to Pidgin on the Desktop"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_STARTMENU_SHORTCUT_DESC		"Create a Start Menu entry for Pidgin"
+!insertmacro PIDGIN_MACRO_DEFAULT_STRING DEBUG_SYMBOLS_SECTION_TITLE		"Debug Symbols (for reporting crashes)"
 
 ; GTK+ Directory Page
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING GTK_UPGRADE_PROMPT			"An old version of the GTK+ runtime was found. Do you wish to upgrade?$\rNote: $(^Name) may not work unless you do."
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING GTK_WINDOWS_INCOMPATIBLE		"Windows 95/98/Me are incompatible with GTK+ 2.8.0 or newer.  GTK+ ${GTK_INSTALL_VERSION} will not be installed.$\rIf you don't have GTK+ ${GTK_MIN_VERSION} or newer already installed, installation will now abort."
 
 ; Installer Finish Page
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_FINISH_VISIT_WEB_SITE		"Visit the Pidgin Web Page"
@@ -44,8 +43,6 @@
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Unable to uninstall the currently installed version of Pidgin. The new version will be installed without removing the currently installed version."
 
 ; GTK+ Section Prompts
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING GTK_INSTALL_ERROR			"Error installing GTK+ runtime."
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING GTK_BAD_INSTALL_PATH			"The path you entered can not be accessed or created."
 
 ; URL Handler section
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING URI_HANDLERS_SECTION_TITLE		"URI Handlers"
@@ -57,29 +54,9 @@
 ; Spellcheck Section Prompts
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_TITLE	"Spellchecking Support"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ERROR		"Error Installing Spellchecking"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DICT_ERROR		"Error Installing Spellchecking Dictionary"
 !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Support for Spellchecking.  (Internet connection required for installation)"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING ASPELL_INSTALL_FAILED			"Installation Failed"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_BRETON		"Breton"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CATALAN		"Catalan"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CZECH		"Czech"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_WELSH		"Welsh"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DANISH		"Danish"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GERMAN		"German"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GREEK		"Greek"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ENGLISH		"English"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SPANISH		"Spanish"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FAROESE		"Faroese"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FRENCH		"French"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ITALIAN		"Italian"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DUTCH		"Dutch"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_NORWEGIAN		"Norwegian"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_POLISH		"Polish"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_PORTUGUESE		"Portuguese"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ROMANIAN		"Romanian"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_RUSSIAN		"Russian"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SLOVAK		"Slovak"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SWEDISH		"Swedish"
-!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainian"
+
+!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DEBUGSYMBOLS_ERROR		"Error Installing Debug Symbols"
 
+!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_GTK_DOWNLOAD_ERROR		"Error Downloading the GTK+ Runtime"
+
--- a/pidgin/win32/nsis/translations/finnish.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/finnish.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -13,7 +13,6 @@
 ; Startup checks
 !define INSTALLER_IS_RUNNING			"Asennusohjelma on jo kynniss."
 !define PIDGIN_IS_RUNNING			"Pidgin on tll hetkell kynniss. Poistu Pidginist ja yrit uudelleen."
-!define GTK_INSTALLER_NEEDED			"Ajonaikainen GTK+-ymprist joko puuttuu tai tarvitsee pivityst.$\rOle hyv ja asenna v${GTK_MIN_VERSION} tai uudempi ajonaikainen GTK+-ymprist."
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Seuraava >"
@@ -33,14 +32,11 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC   	"Tee Pidgin-pikakuvake kynnistysvalikkoon"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Vanha versio ajonaikaisesta GTK+-ympristst lytynyt. Tahdotko pivitt?$\rHuomio: $(^Name) ei vlttmtt toimi mikli jtt pivittmtt."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Vieraile Pidginin WWW-sivustolla"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Virhe asennettaessa ajonaikaista GTK+-ymprist."
-!define GTK_BAD_INSTALL_PATH			"Antamasi polku ei toimi tai sit ei voi luoda."
 
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"URI-ksittelijt"
@@ -52,29 +48,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Oikolukutuki"
 !define PIDGIN_SPELLCHECK_ERROR			"Virhe asennettaessa oikolukua"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Virhe asennettaessa oikoluvun sanakirjaa"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Tuki oikoluvulle.  (Asennukseen tarvitaan Internet-yhteys)"
-!define ASPELL_INSTALL_FAILED			"Asennus eponnistui"
-!define PIDGIN_SPELLCHECK_BRETON		"bretoni"
-!define PIDGIN_SPELLCHECK_CATALAN		"katalaani"
-!define PIDGIN_SPELLCHECK_CZECH		"tshekki"
-!define PIDGIN_SPELLCHECK_WELSH		"kymri"
-!define PIDGIN_SPELLCHECK_DANISH		"tanska"
-!define PIDGIN_SPELLCHECK_GERMAN		"saksa"
-!define PIDGIN_SPELLCHECK_GREEK		"kreikka"
-!define PIDGIN_SPELLCHECK_ENGLISH		"englanti"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"espanja"
-!define PIDGIN_SPELLCHECK_FAROESE		"fri"
-!define PIDGIN_SPELLCHECK_FRENCH		"ranska"
-!define PIDGIN_SPELLCHECK_ITALIAN		"italia"
-!define PIDGIN_SPELLCHECK_DUTCH		"hollanti"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"norja"
-!define PIDGIN_SPELLCHECK_POLISH		"puola"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"portugali"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"romania"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"venj"
-!define PIDGIN_SPELLCHECK_SLOVAK		"slovakia"
-!define PIDGIN_SPELLCHECK_SWEDISH		"ruotsi"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ukraina"
 
--- a/pidgin/win32/nsis/translations/french.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/french.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -1,4 +1,3 @@
-;;  vim:syn=winbatch:fileencoding=cp1252:
 ;;
 ;;  french.nsh
 ;;
@@ -15,7 +14,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Le programme d'installation est dj en cours d'excution."
 !define PIDGIN_IS_RUNNING				"Une instance de Pidgin est en cours d'excution. Veuillez quitter Pidgin et ressayer."
-!define GTK_INSTALLER_NEEDED			"Les bibliothques de l'environnement GTK+ ne sont pas installes ou ont besoin d'une mise  jour.$\rVeuillez installer la version ${GTK_MIN_VERSION} ou plus rcente des bibliothques GTK+."
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Suivant >"
@@ -35,8 +33,6 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Crer un raccourci pour Pidgin dans le menu Dmarrer"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Une ancienne version des bibliothques GTK+ a t trouve. Voulez-vous la mettre  jour ?$\rNote : $(^Name) peut ne pas fonctionner si vous ne le faites pas."
-!define GTK_WINDOWS_INCOMPATIBLE		"Windows 95/98/Me est incompatible avec GTK+ version 2.8.0 ou plus rcentes.  GTK+ ${GTK_INSTALL_VERSION} ne sera pas install.$\rSi vous n'avez pas install GTK+ version ${GTK_MIN_VERSION} ou pkus rcente, l'installation s'arrtera."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Visitez la page web de Pidgin Windows" 
@@ -45,8 +41,6 @@
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Impossible de dsinstaller la version de Pidgin en place. La nouvelle version sera installe sans supprimer la version en place."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Erreur lors de l'installation des bibliothques GTK+"
-!define GTK_BAD_INSTALL_PATH			"Le dossier d'installation ne peut pas tre cr ou n'est pas accessible."
 
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"Gestion des liens (URI)"
@@ -58,28 +52,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Correction orthographique"
 !define PIDGIN_SPELLCHECK_ERROR			"Erreur  l'installation du correcteur orthographique"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Erreur  l'installation du dictionnaire pour le correcteur orthographique"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Correction orthogaphique. (Une connexion internet est ncessaire pour son installation)"
-!define ASPELL_INSTALL_FAILED			"chec de l'installation"
-!define PIDGIN_SPELLCHECK_BRETON			"Breton"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catalan"
-!define PIDGIN_SPELLCHECK_CZECH			"Tchque"
-!define PIDGIN_SPELLCHECK_WELSH			"Gallois"
-!define PIDGIN_SPELLCHECK_DANISH			"Danois"
-!define PIDGIN_SPELLCHECK_GERMAN			"Allemand"
-!define PIDGIN_SPELLCHECK_GREEK			"Grec"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Anglais"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Espranto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Espagnol"
-!define PIDGIN_SPELLCHECK_FAROESE			"Fringien"
-!define PIDGIN_SPELLCHECK_FRENCH			"Franais"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Italien"
-!define PIDGIN_SPELLCHECK_DUTCH			"Hollandais"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norvgien"
-!define PIDGIN_SPELLCHECK_POLISH			"Polonais"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugais"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Roumain"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Russe"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slovaque"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Sudois"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainien"
--- a/pidgin/win32/nsis/translations/galician.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/galician.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -9,7 +9,6 @@
 ;;
 
 ; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"O entorno de execucin de GTK+ falta ou necesita ser actualizado.$\rPor favor, instale a versin v${GTK_MIN_VERSION} do executable GTK+ ou algunha posterior."
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Seguinte >"
@@ -23,14 +22,11 @@
 
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Atopouse unha versin antiga do executable de GTK+. Desexa actualizala?$\rObservacin: $(^Name) non funcionar a menos que o faga."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Visite a pxina Web de Pidgin Windows"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Erro ao instalar o executable GTK+."
-!define GTK_BAD_INSTALL_PATH			"Non se puido acceder ou crear a ruta que vd. indicou."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1         "O desinstalador non puido atopar as entradas no rexistro de Pidgin.$\r probable que outro usuario instalara a aplicacin."
--- a/pidgin/win32/nsis/translations/german.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/german.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -1,82 +1,53 @@
-;; vim:syn=winbatch:encoding=cp1252:
-;;
-;;  german.nsh
-;;
-;;  German language strings for the Windows Pidgin NSIS installer.
-;;  Windows Code page: 1252
-;;
-;;  Author: Bjoern Voigt <bjoern@cs.tu-berlin.de>, 2008.
-;;  Version 3
-;;
- 
-; Startup checks
-!define INSTALLER_IS_RUNNING			"Der Installer luft schon."
-!define PIDGIN_IS_RUNNING			"Eine Instanz von Pidgin luft momentan schon. Beenden Sie Pidgin und versuchen Sie es nochmal."
-!define GTK_INSTALLER_NEEDED			"Die GTK+ Runtime Umgebung fehlt entweder oder muss aktualisiert werden.$\rBitte installieren Sie v${GTK_MIN_VERSION} oder hher der GTK+ Runtime"
- 
-; License Page
-!define PIDGIN_LICENSE_BUTTON			"Weiter >"
-!define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) wird unter der GNU General Public License (GPL) verffentlicht. Die Lizenz dient hier nur der Information. $_CLICK"
- 
-; Components Page
-!define PIDGIN_SECTION_TITLE			"Pidgin Instant Messaging Client (erforderlich)"
-!define GTK_SECTION_TITLE			"GTK+ Runtime Umgebung (erforderlich)"
-!define PIDGIN_SHORTCUTS_SECTION_TITLE	"Verknpfungen"
-!define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE	"Desktop"
-!define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE	"Startmen"
-!define PIDGIN_SECTION_DESCRIPTION		"Pidgin-Basisdateien und -DLLs"
-!define GTK_SECTION_DESCRIPTION		"Ein Multi-Plattform-GUI-Toolkit, verwendet von Pidgin"
-
-!define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	"Verknpfungen zum Starten von Pidgin"
-!define PIDGIN_DESKTOP_SHORTCUT_DESC	"Erstellt eine Verknpfung zu Pidgin auf dem Desktop"
-!define PIDGIN_STARTMENU_SHORTCUT_DESC	"Erstellt einen Eintrag fr Pidgin im Startmen"
- 
-; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Eine alte Version der GTK+ Runtime wurde gefunden. Mchten Sie aktualisieren?$\rHinweis: $(^Name) funktioniert evtl. nicht, wenn Sie nicht aktualisieren."
-!define GTK_WINDOWS_INCOMPATIBLE		"Windows 95/98/Me sind inkompatibel zu GTK+ 2.8.0 oder neuer.  GTK+ ${GTK_INSTALL_VERSION} wird nicht installiert.$\rWenn Sie nicht GTK+ ${GTK_MIN_VERSION} oder neuer installiert haben, wird die Installation jetzt abgebrochen."
- 
-; Installer Finish Page
-!define PIDGIN_FINISH_VISIT_WEB_SITE	"Besuchen Sie die Pidgin Webseite"
- 
-; Pidgin Section Prompts and Texts
-!define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Die aktuell installierte Version von Pidgin kann nicht deinstalliert werden. Die neue Version wird installiert, ohne dass die aktuell installierte Version gelscht wird."
- 
-; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Fehler beim Installieren der GTK+ Runtime."
-!define GTK_BAD_INSTALL_PATH			"Der Pfad, den Sie eingegeben haben, existiert nicht und kann nicht erstellt werden."
-
-; URL Handler section
-!define URI_HANDLERS_SECTION_TITLE		"URI-Behandlung"
-
-; Uninstall Section Prompts
-!define un.PIDGIN_UNINSTALL_ERROR_1		"Der Deinstaller konnte keine Registrierungsschlssel fr Pidgin finden.$\rEs ist wahrscheinlich, da ein anderer Benutzer diese Anwendung installiert hat."
-!define un.PIDGIN_UNINSTALL_ERROR_2		"Sie haben keine Berechtigung, diese Anwendung zu deinstallieren."
-
-; Spellcheck Section Prompts
-!define PIDGIN_SPELLCHECK_SECTION_TITLE	"Untersttzung fr Rechtschreibkontrolle"
-!define PIDGIN_SPELLCHECK_ERROR		"Fehler bei der Installation der Rechtschreibkontrolle"
-!define PIDGIN_SPELLCHECK_DICT_ERROR	"Fehler bei der Installation des Wrterbuches fr die Rechtschreibkontrolle"
-!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Untersttzung fr Rechtschreibkontrolle.  (Fr die Installation ist eine Internet-Verbindung ntig)"
-!define ASPELL_INSTALL_FAILED			"Installation gescheitert"
-!define PIDGIN_SPELLCHECK_BRETON		"Bretonisch"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalanisch"
-!define PIDGIN_SPELLCHECK_CZECH		"Tschechisch"
-!define PIDGIN_SPELLCHECK_WELSH		"Walisisch"
-!define PIDGIN_SPELLCHECK_DANISH		"Dnisch"
-!define PIDGIN_SPELLCHECK_GERMAN		"Deutsch"
-!define PIDGIN_SPELLCHECK_GREEK		"Griechisch"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Englisch"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spanisch"
-!define PIDGIN_SPELLCHECK_FAROESE		"Farersprache"
-!define PIDGIN_SPELLCHECK_FRENCH		"Franzsisch"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italienisch"
-!define PIDGIN_SPELLCHECK_DUTCH		"Hollndisch"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norwegisch"
-!define PIDGIN_SPELLCHECK_POLISH		"Polnisch"
-!define PIDGIN_SPELLCHECK_PORTUGUESE	"Portugiesisch"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumnisch"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Russisch"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slowakisch"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Schwedisch"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainisch"
+;; vim:syn=winbatch:encoding=cp1252:
+;;
+;;  german.nsh
+;;
+;;  German language strings for the Windows Pidgin NSIS installer.
+;;  Windows Code page: 1252
+;;
+;;  Author: Bjoern Voigt <bjoern@cs.tu-berlin.de>, 2008.
+;;  Version 3
+;;
+ 
+; Startup checks
+!define INSTALLER_IS_RUNNING			"Der Installer luft schon."
+!define PIDGIN_IS_RUNNING			"Eine Instanz von Pidgin luft momentan schon. Beenden Sie Pidgin und versuchen Sie es nochmal."
+ 
+; License Page
+!define PIDGIN_LICENSE_BUTTON			"Weiter >"
+!define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) wird unter der GNU General Public License (GPL) verffentlicht. Die Lizenz dient hier nur der Information. $_CLICK"
+ 
+; Components Page
+!define PIDGIN_SECTION_TITLE			"Pidgin Instant Messaging Client (erforderlich)"
+!define GTK_SECTION_TITLE			"GTK+ Runtime Umgebung (erforderlich)"
+!define PIDGIN_SHORTCUTS_SECTION_TITLE	"Verknpfungen"
+!define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE	"Desktop"
+!define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE	"Startmen"
+!define PIDGIN_SECTION_DESCRIPTION		"Pidgin-Basisdateien und -DLLs"
+!define GTK_SECTION_DESCRIPTION		"Ein Multi-Plattform-GUI-Toolkit, verwendet von Pidgin"
+
+!define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	"Verknpfungen zum Starten von Pidgin"
+!define PIDGIN_DESKTOP_SHORTCUT_DESC	"Erstellt eine Verknpfung zu Pidgin auf dem Desktop"
+!define PIDGIN_STARTMENU_SHORTCUT_DESC	"Erstellt einen Eintrag fr Pidgin im Startmen"
+ 
+; GTK+ Directory Page
+ 
+; Installer Finish Page
+!define PIDGIN_FINISH_VISIT_WEB_SITE	"Besuchen Sie die Pidgin Webseite"
+ 
+; Pidgin Section Prompts and Texts
+!define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Die aktuell installierte Version von Pidgin kann nicht deinstalliert werden. Die neue Version wird installiert, ohne dass die aktuell installierte Version gelscht wird."
+ 
+; GTK+ Section Prompts
+
+; URL Handler section
+!define URI_HANDLERS_SECTION_TITLE		"URI-Behandlung"
+
+; Uninstall Section Prompts
+!define un.PIDGIN_UNINSTALL_ERROR_1		"Der Deinstaller konnte keine Registrierungsschlssel fr Pidgin finden.$\rEs ist wahrscheinlich, da ein anderer Benutzer diese Anwendung installiert hat."
+!define un.PIDGIN_UNINSTALL_ERROR_2		"Sie haben keine Berechtigung, diese Anwendung zu deinstallieren."
+
+; Spellcheck Section Prompts
+!define PIDGIN_SPELLCHECK_SECTION_TITLE	"Untersttzung fr Rechtschreibkontrolle"
+!define PIDGIN_SPELLCHECK_ERROR		"Fehler bei der Installation der Rechtschreibkontrolle"
+!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Untersttzung fr Rechtschreibkontrolle.  (Fr die Installation ist eine Internet-Verbindung ntig)"
--- a/pidgin/win32/nsis/translations/hebrew.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/hebrew.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -1,83 +1,54 @@
-;;
-;;  hebrew.nsh
-;;
-;;  Hebrew language strings for the Windows Pidgin NSIS installer.
-;;  Windows Code page: 1255
-;;
-;;  Updated: Shalom Craimer <scraimer@gmail.com>
-;;  Origional Author: Eugene Shcherbina <eugene@websterworlds.com>
-;;  Version 3
-;;
-
-; Startup checks
-!define INSTALLER_IS_RUNNING			"   ."
-!define PIDGIN_IS_RUNNING			"  '  .    '  ."
-!define GTK_INSTALLER_NEEDED			".     GTK+ $\r  v${GTK_MIN_VERSION} .GTK+     "
-
-; License Page
-!define PIDGIN_LICENSE_BUTTON			" >"
-!define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) .      .GPL    $_CLICK"
-
-; Components Page
-!define PIDGIN_SECTION_TITLE			"() .Pidgin "
-!define GTK_SECTION_TITLE			"() .GTK+ "
-!define PIDGIN_SHORTCUTS_SECTION_TITLE		" "
-!define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE	" "
-!define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE	" "
-!define PIDGIN_SECTION_DESCRIPTION		". DLL- Pidgin "
-!define GTK_SECTION_DESCRIPTION		"-,   ' GUI "
-
-!define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	"-  '"
-!define PIDGIN_DESKTOP_SHORTCUT_DESC		" -  '   "
-!define PIDGIN_STARTMENU_SHORTCUT_DESC		" -  '  "
-
-; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"A?.  GTK+   $\rNote: .     $(^Name)"
-!define GTK_WINDOWS_INCOMPATIBLE		" 95/98/   -GTK+ 2.8.0 . GTK+ ${GTK_INSTALL_VERSION}  .$\r   GTK+ ${GTK_MIN_VERSION}  ,   ."
-
-; Installer Finish Page
-!define PIDGIN_FINISH_VISIT_WEB_SITE		".Pidgin   "
-
-; Pidgin Section Prompts and Texts
-!define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"       '.       ."
-
-; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			".GTK+   "
-!define GTK_BAD_INSTALL_PATH			".    "
-
-; URL Handler section
-!define URI_HANDLERS_SECTION_TITLE		" URI"
-
-; Uninstall Section Prompts
-!define un.PIDGIN_UNINSTALL_ERROR_1		".GTK+      $\r.       "
-!define un.PIDGIN_UNINSTALL_ERROR_2		".     "
-
-; Spellcheck Section Prompts
-!define PIDGIN_SPELLCHECK_SECTION_TITLE	"  "
-!define PIDGIN_SPELLCHECK_ERROR		"   "
-!define PIDGIN_SPELLCHECK_DICT_ERROR	"    "
-!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"    (   )"
-!define ASPELL_INSTALL_FAILED			" "
-!define PIDGIN_SPELLCHECK_BRETON		""
-!define PIDGIN_SPELLCHECK_CATALAN		""
-!define PIDGIN_SPELLCHECK_CZECH		"'"
-!define PIDGIN_SPELLCHECK_WELSH		""
-!define PIDGIN_SPELLCHECK_DANISH		""
-!define PIDGIN_SPELLCHECK_GERMAN		""
-!define PIDGIN_SPELLCHECK_GREEK		""
-!define PIDGIN_SPELLCHECK_ENGLISH		""
-!define PIDGIN_SPELLCHECK_ESPERANTO		""
-!define PIDGIN_SPELLCHECK_SPANISH		""
-!define PIDGIN_SPELLCHECK_FAROESE		""
-!define PIDGIN_SPELLCHECK_FRENCH		""
-!define PIDGIN_SPELLCHECK_ITALIAN		""
-!define PIDGIN_SPELLCHECK_DUTCH		""
-!define PIDGIN_SPELLCHECK_NORWEGIAN		""
-!define PIDGIN_SPELLCHECK_POLISH		""
-!define PIDGIN_SPELLCHECK_PORTUGUESE	""
-!define PIDGIN_SPELLCHECK_ROMANIAN		""
-!define PIDGIN_SPELLCHECK_RUSSIAN		""
-!define PIDGIN_SPELLCHECK_SLOVAK		""
-!define PIDGIN_SPELLCHECK_SWEDISH		""
-!define PIDGIN_SPELLCHECK_UKRAINIAN		""
-
+;;
+;;  hebrew.nsh
+;;
+;;  Hebrew language strings for the Windows Pidgin NSIS installer.
+;;  Windows Code page: 1255
+;;
+;;  Updated: Shalom Craimer <scraimer@gmail.com>
+;;  Origional Author: Eugene Shcherbina <eugene@websterworlds.com>
+;;  Version 3
+;;
+
+; Startup checks
+!define INSTALLER_IS_RUNNING			"   ."
+!define PIDGIN_IS_RUNNING			"  '  .    '  ."
+
+; License Page
+!define PIDGIN_LICENSE_BUTTON			" >"
+!define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) .      .GPL    $_CLICK"
+
+; Components Page
+!define PIDGIN_SECTION_TITLE			"() .Pidgin "
+!define GTK_SECTION_TITLE			"() .GTK+ "
+!define PIDGIN_SHORTCUTS_SECTION_TITLE		" "
+!define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE	" "
+!define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE	" "
+!define PIDGIN_SECTION_DESCRIPTION		". DLL- Pidgin "
+!define GTK_SECTION_DESCRIPTION		"-,   ' GUI "
+
+!define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	"-  '"
+!define PIDGIN_DESKTOP_SHORTCUT_DESC		" -  '   "
+!define PIDGIN_STARTMENU_SHORTCUT_DESC		" -  '  "
+
+; GTK+ Directory Page
+
+; Installer Finish Page
+!define PIDGIN_FINISH_VISIT_WEB_SITE		".Pidgin   "
+
+; Pidgin Section Prompts and Texts
+!define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"       '.       ."
+
+; GTK+ Section Prompts
+
+; URL Handler section
+!define URI_HANDLERS_SECTION_TITLE		" URI"
+
+; Uninstall Section Prompts
+!define un.PIDGIN_UNINSTALL_ERROR_1		".GTK+      $\r.       "
+!define un.PIDGIN_UNINSTALL_ERROR_2		".     "
+
+; Spellcheck Section Prompts
+!define PIDGIN_SPELLCHECK_SECTION_TITLE	"  "
+!define PIDGIN_SPELLCHECK_ERROR		"   "
+!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"    (   )"
+
--- a/pidgin/win32/nsis/translations/hungarian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/hungarian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -9,7 +9,6 @@
 ;;
 
 ; Startup Checks
-!define GTK_INSTALLER_NEEDED			"A GTK+ futtat krnyezet hinyzik vagy frisstse szksges.$\rKrem teleptse a v${GTK_MIN_VERSION} vagy magasabb verzij GTK+ futtat krnyezetet."
 !define INSTALLER_IS_RUNNING			"A telept mr fut."
 !define PIDGIN_IS_RUNNING				"Jelenleg fut a Pidgin egy pldnya. Lpjen ki a Pidginbl s azutn prblja jra."
 
@@ -31,8 +30,6 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Start Men bejegyzs ltrehozsa a Pidginhez"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Egy rgi verzij GTK+ futtatkrnyezet van teleptve. Kvnja frissteni?$\rMegjegyzs: a Pidgin nem fog mkdni, ha nem frissti."
-!define GTK_WINDOWS_INCOMPATIBLE		"A Windows 95/98/Me nem kompatibillisek a GTK+ 2.8.0 vagy jabb vltozatokkal. A GTK+ ${GTK_INSTALL_VERSION} nem kerl teleptsre. $\rHa a GTK+ ${GTK_MIN_VERSION} vagy jabb mg nincs teleptve, akkor a telepts most megszakad."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"A Windows Pidgin weboldalnak felkeresse"
@@ -42,8 +39,6 @@
 
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Hiba a GTK+ futtatkrnyezet teleptse kzben."
-!define GTK_BAD_INSTALL_PATH			"A megadott elrsi t nem rhet el, vagy nem hozhat ltre."
 
 !define URI_HANDLERS_SECTION_TITLE		"URI kezelk"
 
@@ -54,29 +49,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Helyesrs-ellenrzs tmogatsa"
 !define PIDGIN_SPELLCHECK_ERROR			"Hiba a helyesrs-ellenrzs teleptse kzben"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Hiba a helyesrs-ellenrzsi sztr teleptse kzben"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Helyesrs-ellenrzs tmogatsa. (Internetkapcsolat szksges a teleptshez)"
-!define ASPELL_INSTALL_FAILED			"A telepts sikertelen"
-!define PIDGIN_SPELLCHECK_BRETON			"Breton"
-!define PIDGIN_SPELLCHECK_CATALAN			"Kataln"
-!define PIDGIN_SPELLCHECK_CZECH			"Cseh"
-!define PIDGIN_SPELLCHECK_WELSH			"Walesi"
-!define PIDGIN_SPELLCHECK_DANISH			"Dn"
-!define PIDGIN_SPELLCHECK_GERMAN			"Nmet"
-!define PIDGIN_SPELLCHECK_GREEK			"Grg"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Angol"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Eszperant"
-!define PIDGIN_SPELLCHECK_SPANISH			"Spanyol"
-!define PIDGIN_SPELLCHECK_FAROESE			"Farai"
-!define PIDGIN_SPELLCHECK_FRENCH			"Francia"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Olasz"
-!define PIDGIN_SPELLCHECK_DUTCH			"Holland"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norvg"
-!define PIDGIN_SPELLCHECK_POLISH			"Lengyel"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugl"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Romn"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Orosz"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Szlovk"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Svd"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrn"
 
--- a/pidgin/win32/nsis/translations/italian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/italian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -11,7 +11,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Il programma di installazione  gi in esecuzione"
 !define PIDGIN_IS_RUNNING			" attualmente in esecuzione un'istanza di Pidgin. Esci da Pidgin e riprova."
-!define GTK_INSTALLER_NEEDED			"L'ambiente di runtime GTK+ non  presente o deve essere aggiornato.$\rInstallare GTK+ versione ${GTK_MIN_VERSION} o maggiore"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Avanti >"
@@ -31,8 +30,6 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Crea una voce per Pidgin nel Menu Avvio"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			" stata trovata una versione precedente di GTK+. Vuoi aggiornarla?$\rNota: $(^Name) potrebbe non funzionare senza l'aggiornamento."
-!define GTK_WINDOWS_INCOMPATIBLE		"Windows 95/98/Me non  incompatible con GTK+ 2.8.0 o successivo. GTK+ ${GTK_INSTALL_VERSION} non sar installato.$\rSe non hai GTK+ ${GTK_MIN_VERSION} o successivo gi installato sul tuo computer, questa installazione sar interrotta."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Visita la pagina web di Pidgin"
@@ -41,8 +38,6 @@
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Impossibile rimuovere la versione di Pidgin attualmente presente sul tuo computer. La nuova versione sar installata senza rimuovere la versione precedente."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Error nell'installazione del runtime GTK+."
-!define GTK_BAD_INSTALL_PATH			"Il percorso scelto non pu essere raggiunto o creato."
 
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"Gestori degli URI"
@@ -54,29 +49,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"Supporto per il correttore ortografico"
 !define PIDGIN_SPELLCHECK_ERROR		"Errore nell'installazione del correttore ortografico"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Errore nell'installazione del dizionario per il correttore ortografico"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Supporto per il correttore ortografico.  ( richiesta una connessione a internet per l'installazione)"
-!define ASPELL_INSTALL_FAILED			"Installazione fallita"
-!define PIDGIN_SPELLCHECK_BRETON		"Bretone"
-!define PIDGIN_SPELLCHECK_CATALAN		"Catalano"
-!define PIDGIN_SPELLCHECK_CZECH		"Ceco"
-!define PIDGIN_SPELLCHECK_WELSH		"Gallese"
-!define PIDGIN_SPELLCHECK_DANISH		"Danese"
-!define PIDGIN_SPELLCHECK_GERMAN		"Tedesco"
-!define PIDGIN_SPELLCHECK_GREEK		"Greco"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Inglese"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spagnolo"
-!define PIDGIN_SPELLCHECK_FAROESE		"Faroese"
-!define PIDGIN_SPELLCHECK_FRENCH		"Francese"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italiano"
-!define PIDGIN_SPELLCHECK_DUTCH		"Olandese"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norvegese"
-!define PIDGIN_SPELLCHECK_POLISH		"Polacco"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portoghese"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumeno"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Russo"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slovacco"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Svedese"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ucraino"
 
--- a/pidgin/win32/nsis/translations/japanese.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/japanese.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -12,7 +12,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"CXg[ɎsĂ܂"
 !define PIDGIN_IS_RUNNING				"Pidgin sĂ܂BPidgin IĂēxsĂ"
-!define GTK_INSTALLER_NEEDED			"GTK+^C‹̓AbvO[hKv܂B$\rv${GTK_MIN_VERSION}͂ȏGTK+^CCXg[ĂB"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			" >"
@@ -33,14 +32,11 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"X^[gj[ Pidgin ̍ڂ쐬"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Âo[WGTK+^C‚܂BAbvO[h܂?$\r: $(^Name)̓AbvO[hȂ蓮Ȃł傤B"
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Windows PidginWeby[WKĂB"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"GTK+^C̃CXg[ŃG[܂B"
-!define GTK_BAD_INSTALL_PATH			"Ȃ̓͂pXɃANZX܂͍쐬ł܂B"
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"ACXg[PidgiñWXgGg𔭌ł܂łB$\r炭ʂ̃[UɃCXg[ꂽł傤B"
@@ -49,29 +45,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Xy`FbÑT|[g"
 !define PIDGIN_SPELLCHECK_ERROR			"Xy`FbÑCXg[Ɏs܂"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Xy`FbÑCXg[Ɏs܂B"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Xy`FbÑT|[g  (C^[lbgڑCXg[ɕKvł)"
-!define ASPELL_INSTALL_FAILED			"CXg[Ɏs܂"
-!define PIDGIN_SPELLCHECK_BRETON			"u^[j"
-!define PIDGIN_SPELLCHECK_CATALAN			"J^[j"
-!define PIDGIN_SPELLCHECK_CZECH			"`FR"
-!define PIDGIN_SPELLCHECK_WELSH			"EF[Y"
-!define PIDGIN_SPELLCHECK_DANISH			"f}[N"
-!define PIDGIN_SPELLCHECK_GERMAN			"hCc"
-!define PIDGIN_SPELLCHECK_GREEK			"MV"
-!define PIDGIN_SPELLCHECK_ENGLISH			"p"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"GXyg"
-!define PIDGIN_SPELLCHECK_SPANISH			"XyC"
-!define PIDGIN_SPELLCHECK_FAROESE			"tF["
-!define PIDGIN_SPELLCHECK_FRENCH			"tX"
-!define PIDGIN_SPELLCHECK_ITALIAN			"C^A"
-!define PIDGIN_SPELLCHECK_DUTCH			"I_"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"mEF["
-!define PIDGIN_SPELLCHECK_POLISH			"|[h"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"|gK"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"[}jA"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"VA"
-!define PIDGIN_SPELLCHECK_SLOVAK			"X@LA"
-!define PIDGIN_SPELLCHECK_SWEDISH			"XEF[f"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ENCi"
 
--- a/pidgin/win32/nsis/translations/korean.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/korean.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -7,8 +7,6 @@
 ;;  Author: Kyung-uk Son <vvs740@chol.com>
 ;;
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"GTK+ Ÿ ȯ濡  ְų ׷̵尡 ʿմϴ.$\rGTK+ Ÿ ȯ v${GTK_MIN_VERSION}̳  ̻  ġּ."
 
 ; Components Page
 !define PIDGIN_SECTION_TITLE			" ޽ (ʼ)"
@@ -17,11 +15,8 @@
 !define GTK_SECTION_DESCRIPTION		" ϴ Ƽ ÷ GUI Ŷ"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"  GTK+ Ÿ ãҽϴ. ׷̵ұ?$\rNote: ׷̵      ֽϴ."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"GTK+ Ÿ ġ   ߻."
-!define GTK_BAD_INSTALL_PATH			"ԷϽ ο   ų   ϴ."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1         "ν緯  Ʈ Ʈ ã  ϴ.$\r α׷ ٸ   ġ  ϴ."
--- a/pidgin/win32/nsis/translations/kurdish.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/kurdish.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -12,7 +12,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Sazker jixwe dimee."
 !define PIDGIN_IS_RUNNING			"Pidgin niha jixwe dimee. Ji Pidgin  derkeve  careke din biceribne."
-!define GTK_INSTALLER_NEEDED			"Derdora runtime ya GTK+ an tune an rojanekirina w pwst e. $\rJi kerema xwe v${GTK_MIN_VERSION} an bilindtir a GTK+ saz bike."
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"P >"
@@ -32,14 +31,11 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC   "Pidgin  binivse menuya destpk"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Guhertoyeke kevn a GTK+ hatiye dtin. Tu dixwaz bilind bik?$\rNot: Heke tu nek, dibe ku $(^Name) naxebite."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Were Malpera Pidgin a Windows"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Di sazkirina GTK+ de ewt derket."
-!define GTK_BAD_INSTALL_PATH			"rya te nivsand nay gihitin an afirandin."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Raker tketiyn registry yn Pidgin nedt. $\rQey bikarhnereke din v bername saz kir."
@@ -48,29 +44,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Desteka kontrola rastnivs"
 !define PIDGIN_SPELLCHECK_ERROR			"Di sazkirina kontrola rastnivs de ewt derket."
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Di sazkirina ferhenga rastnivs de ewt derket."
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Desteka kontrola rastnivs.  (Ji bo sazkirin nternet pwst e)"
-!define ASPELL_INSTALL_FAILED			"Sazkirin Serneket"
-!define PIDGIN_SPELLCHECK_BRETON			"Breton"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catalan"
-!define PIDGIN_SPELLCHECK_CZECH			"ek"
-!define PIDGIN_SPELLCHECK_WELSH			"Welsh"
-!define PIDGIN_SPELLCHECK_DANISH			"Danik"
-!define PIDGIN_SPELLCHECK_GERMAN			"Alman"
-!define PIDGIN_SPELLCHECK_GREEK			"Yewnan"
-!define PIDGIN_SPELLCHECK_ENGLISH			"ngilz"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Span"
-!define PIDGIN_SPELLCHECK_FAROESE			"Faroese"
-!define PIDGIN_SPELLCHECK_FRENCH			"Frans"
-!define PIDGIN_SPELLCHECK_ITALIAN			"tal"
-!define PIDGIN_SPELLCHECK_DUTCH			"Dutch"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norwec"
-!define PIDGIN_SPELLCHECK_POLISH			"Pol"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portekiz"
-!define PIDGIN_SPELLCHECK_ROMANIAN			"Roman"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rus"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slovak"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Swd"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrayn"
 
--- a/pidgin/win32/nsis/translations/lithuanian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/lithuanian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -9,7 +9,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Diegimo programa jau paleista."
 !define PIDGIN_IS_RUNNING			"iuo metu Pidgin yra paleistas. Udarykite i program ir pabandykite i naujo."
-!define GTK_INSTALLER_NEEDED			"GTK+ vykdymo meto aplinkos nra arba ji turi bti atnaujinta.$\rdiekite v${GTK_MIN_VERSION} arba naujesn GTK+ vykdymo meto aplinkos versij"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Toliau >"
@@ -29,8 +28,6 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Sukurti pradinio meniu ra, skirt Pidgin."
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Rasta sena GTK+ vykdymo meto aplinkos versija. Ar norite j atnaujinti?$\rPastaba: $(^Name) gali neveikti, jeigu to nepadarysite."
-!define GTK_WINDOWS_INCOMPATIBLE		"Windows 95/98/Me yra nesuderinami su GTK+ 2.8.0 ir naujesnmis versijomis.  GTK+ ${GTK_INSTALL_VERSION} nebus diegta.$\rJeigu neturite sidieg GTK+ ${GTK_MIN_VERSION} ar naujesns versijos, diegimas dabar bus nutrauktas."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Aplankyti Pidgin tinklalap"
@@ -39,8 +36,6 @@
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Nepavyko idiegti anksiau diegtos Pidgin versijos.  Nauja versija bus diegta neidiegus senosios."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"GTK+ vykdymo meto aplinkos diegimo klaida"
-!define GTK_BAD_INSTALL_PATH			"Js vestas kelias negali bti pasiektas ar sukurtas."
 
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"URI dorokls"
@@ -52,29 +47,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	"Raybos tikrinimo palaikymas"
 !define PIDGIN_SPELLCHECK_ERROR		"Raybos tikrinimo palaikymo diegimo klaida"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Raybos tikrinimo odyno diegimo klaida"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Raybos tikrinimo palaikymas.  (Diegimui btina interneto jungtis)"
-!define ASPELL_INSTALL_FAILED			"Diegimas nepavyko"
-!define PIDGIN_SPELLCHECK_BRETON		"Breton kalba"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalon kalba"
-!define PIDGIN_SPELLCHECK_CZECH		"ek kalba"
-!define PIDGIN_SPELLCHECK_WELSH		"Val kalba"
-!define PIDGIN_SPELLCHECK_DANISH		"Dan kalba"
-!define PIDGIN_SPELLCHECK_GERMAN		"Vokiei kalba"
-!define PIDGIN_SPELLCHECK_GREEK		"Graik kalba"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Angl kalba"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto kalba"
-!define PIDGIN_SPELLCHECK_SPANISH		"Ispan kalba"
-!define PIDGIN_SPELLCHECK_FAROESE		"Farer kalba"
-!define PIDGIN_SPELLCHECK_FRENCH		"Prancz kalba"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Ital kalba"
-!define PIDGIN_SPELLCHECK_DUTCH		"Oland kalba"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norveg kalba"
-!define PIDGIN_SPELLCHECK_POLISH		"Lenk kalba"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugal kalba"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumun kalba"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Rus kalba"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slovak kalba"
-!define PIDGIN_SPELLCHECK_SWEDISH		"ved kalba"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainiei kalba"
 
--- a/pidgin/win32/nsis/translations/norwegian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/norwegian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -11,7 +11,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Installeren kjrer allerede."
 !define PIDGIN_IS_RUNNING				"En instans av Pidgin kjrer fra fr. Avslutt Pidgin og prv igjen."
-!define GTK_INSTALLER_NEEDED			"GTK+ runtime environment mangler eller trenger en oppgradering.$\rVennligst installr GTK+ v${GTK_MIN_VERSION} eller hyere"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Neste >"
@@ -31,14 +30,11 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Legg til Pidgin i Startmenyen"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"En eldre versjon av GTK+ runtime ble funnet. nsker du  oppgradere?$\rMerk: $(^Name) vil kanskje ikke virke hvis du ikke oppgraderer."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Besk Pidgin for Windows' Nettside"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"En feil oppstod ved installering av GTK+ runtime."
-!define GTK_BAD_INSTALL_PATH			"Stien du oppga kan ikke aksesseres eller lages."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Avinstalleringsprogrammet kunne ikke finne noen registeroppfring for Pidgin.$\rTrolig har en annen bruker avinstallert denne applikasjonen."
@@ -49,28 +45,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Sttte for stavekontroll"
 !define PIDGIN_SPELLCHECK_ERROR			"Det oppstod en feil ved installering av stavekontroll"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Det oppstod en feil ved installering av ordboken for stavekontroll"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Sttte for stavekontroll. (Internettoppkobling pkrevd for installasjon)"
-!define ASPELL_INSTALL_FAILED			"Installasjonen mislyktes."
-!define PIDGIN_SPELLCHECK_BRETON			"Bretagnsk"
-!define PIDGIN_SPELLCHECK_CATALAN			"Katalansk"
-!define PIDGIN_SPELLCHECK_CZECH			"Tsjekkisk"
-!define PIDGIN_SPELLCHECK_WELSH			"Walisisk"
-!define PIDGIN_SPELLCHECK_DANISH			"Dansk"
-!define PIDGIN_SPELLCHECK_GERMAN			"Tysk"
-!define PIDGIN_SPELLCHECK_GREEK			"Gresk"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Engelsk"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Spansk"
-!define PIDGIN_SPELLCHECK_FAROESE			"Frysk"
-!define PIDGIN_SPELLCHECK_FRENCH			"Fransk"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Italiensk"
-!define PIDGIN_SPELLCHECK_DUTCH			"Nederlandsk"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norsk"
-!define PIDGIN_SPELLCHECK_POLISH			"Polsk"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugisisk"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumensk"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Russisk"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slovakisk"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Svensk"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainsk"
--- a/pidgin/win32/nsis/translations/norwegian_nynorsk.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/norwegian_nynorsk.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -9,7 +9,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Installasjonsprogrammet kjrer allereie."
 !define PIDGIN_IS_RUNNING			"Pidgin kjrer no. Lukk programmet og prv igjen."
-!define GTK_INSTALLER_NEEDED			"GTK+-kjremiljet manglar eller treng  bli oppdatert.$\rInstaller v${GTK_MIN_VERSION} eller nyare av GTK+-kjremiljet"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Neste >"
@@ -28,20 +27,12 @@
 !define PIDGIN_DESKTOP_SHORTCUT_DESC		"Lag ein snarveg til Pidgin p skrivebordet"
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Lag ein snarveg til Pidgin p startmenyen"
 
-; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Fann ei gammal utgve av GTK+-kjremiljet. Vil du oppdatera ho?$\rMerk: $(^Name) vil kanskje ikkje fungera om du ikkje oppdaterer."
-!define GTK_WINDOWS_INCOMPATIBLE		"Windows 95/98/Me er ikkje kompatibelt med GTK+ 2.8.0 eller nyare. GTK+ ${GTK_INSTALL_VERSION} kjem ikkje til  bli installert.$\rInstallasjonen vil bli abvroten om ikkje GTK+ ${GTK_MIN_VERSION} eller nyare allereie er installert."
-
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Besk Pidgin si nettside"
 
 ; Pidgin Section Prompts and Texts
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Klarte ikkje  avinstallera Pidgin-utgva som er i bruk. Den nye utgva kjem til  bli installert utan  ta vekk den gjeldande."
 
-; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Klarte ikkje  installera GTK+-kjremiljet."
-!define GTK_BAD_INSTALL_PATH			"Klarer ikkje  laga eller f tilgang til bana du skreiv."
-
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"URI-referanse"
 
--- a/pidgin/win32/nsis/translations/persian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/persian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -13,7 +13,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"ȝ      ."
 !define PIDGIN_IS_RUNNING			"       .         ."
-!define GTK_INSTALLER_NEEDED			"   GTK+         .$\r  ${GTK_MIN_VERSION}       GTK+  "
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			" >"
@@ -33,8 +32,6 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"      "
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"     GTK+  .      Ͽ$\r: $(^Name)       ."
-!define GTK_WINDOWS_INCOMPATIBLE		" 95/98/Me  GTK+  2.8.0   Ґ . GTK+ ${GTK_INSTALL_VERSION}   .$\r ǐ  GTK+ ${GTK_MIN_VERSION}      ϡ    ."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"    "
@@ -43,8 +40,6 @@
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"           .        ."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"      GTK+."
-!define GTK_BAD_INSTALL_PATH			"        ."
 
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"  "
@@ -56,29 +51,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE	" ؝ "
 !define PIDGIN_SPELLCHECK_ERROR		"   ؝ "
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"   ʝ ؝ "
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	" ؝ . (     )"
-!define ASPELL_INSTALL_FAILED			" Ԙ "
-!define PIDGIN_SPELLCHECK_BRETON		""
-!define PIDGIN_SPELLCHECK_CATALAN		""
-!define PIDGIN_SPELLCHECK_CZECH		""
-!define PIDGIN_SPELLCHECK_WELSH		""
-!define PIDGIN_SPELLCHECK_DANISH		"ј"
-!define PIDGIN_SPELLCHECK_GERMAN		""
-!define PIDGIN_SPELLCHECK_GREEK		""
-!define PIDGIN_SPELLCHECK_ENGLISH		""
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Ӂ"
-!define PIDGIN_SPELLCHECK_SPANISH		"Ӂ"
-!define PIDGIN_SPELLCHECK_FAROESE		""
-!define PIDGIN_SPELLCHECK_FRENCH		""
-!define PIDGIN_SPELLCHECK_ITALIAN		""
-!define PIDGIN_SPELLCHECK_DUTCH		""
-!define PIDGIN_SPELLCHECK_NORWEGIAN		""
-!define PIDGIN_SPELLCHECK_POLISH		""
-!define PIDGIN_SPELLCHECK_PORTUGUESE		""
-!define PIDGIN_SPELLCHECK_ROMANIAN		""
-!define PIDGIN_SPELLCHECK_RUSSIAN		""
-!define PIDGIN_SPELLCHECK_SLOVAK		"ǘ"
-!define PIDGIN_SPELLCHECK_SWEDISH		""
-!define PIDGIN_SPELLCHECK_UKRAINIAN		""
 
--- a/pidgin/win32/nsis/translations/polish.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/polish.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -8,13 +8,6 @@
 ;;  Note: If translating this file, replace '!insertmacro PIDGIN_MACRO_DEFAULT_STRING'
 ;;  with '!define'.
 
-; Make sure to update the PIDGIN_MACRO_LANGUAGEFILE_END macro in
-; langmacros.nsh when updating this file
-
-; Startup Checks
-!define INSTALLER_IS_RUNNING			"Instalator jest ju uruchomiony."
-!define PIDGIN_IS_RUNNING			"Program Pidgin jest obecnie uruchomiony. Prosz zakoczy dziaanie programu Pidgin i sprbowa ponownie."
-!define GTK_INSTALLER_NEEDED			"Brak biblioteki GTK+ lub wymaga zaktualizowania.$\rProsz zainstalowa wersj ${GTK_MIN_VERSION} lub wysz biblioteki GTK+"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Dalej >"
@@ -27,59 +20,14 @@
 !define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE	"Pulpit"
 !define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE	"Menu Start"
 !define PIDGIN_SECTION_DESCRIPTION		"Gwne pliki programu Pidgin i biblioteki DLL"
-!define GTK_SECTION_DESCRIPTION		"Wieloplatformowy zestaw narzdzi do tworzenia interfejsu graficznego, uywany przez program Pidgin"
 
 !define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION	"Skrty do uruchamiania programu Pidgin"
 !define PIDGIN_DESKTOP_SHORTCUT_DESC		"Utworzenie skrtu do programu Pidgin na pulpicie"
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Utworzenie wpisu w menu Start dla programu Pidgin"
 
-; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Odnaleziono star wersj biblioteki GTK+. Zaktualizowa j?$\rUwaga: program $(^Name) moe bez tego nie dziaa."
-!define GTK_WINDOWS_INCOMPATIBLE		"Systemy Windows 95/98/Me s niezgodne z bibliotek GTK+ 2.8.0 lub nowsz. Biblioteka GTK+ ${GTK_INSTALL_VERSION} nie zostanie zainstalowana.$\rJeli brak zainstalowanej biblioteki GTK+ ${GTK_MIN_VERSION} lub nowszej, instalacja zostanie przerwana."
-
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Odwied stron WWW programu Pidgin"
 
-; Pidgin Section Prompts and Texts
-!define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Nie mona odinstalowa obecnie zainstalowanej wersji programu Pidgin. Nowa wersja zostanie zainstalowana bez usuwania obecnie zainstalowanej wersji."
-
-; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Bd podczas instalowania biblioteki GTK+."
-!define GTK_BAD_INSTALL_PATH			"Nie mona uzyska dostpu do podanej cieki lub jej utworzy."
-
-; URL Handler section
-!define URI_HANDLERS_SECTION_TITLE		"Obsuga adresw URI"
-
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Instalator nie moe odnale wpisw w rejestrze dla programu Pidgin.$\rMoliwe, e inny uytkownik zainstalowa ten program."
 !define un.PIDGIN_UNINSTALL_ERROR_2		"Brak uprawnie do odinstalowania tego programu."
-
-; Spellcheck Section Prompts
-!define PIDGIN_SPELLCHECK_SECTION_TITLE	"Obsuga sprawdzania pisowni"
-!define PIDGIN_SPELLCHECK_ERROR		"Bd podczas instalowania sprawdzania pisowni"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Bd podczas instalowania sownika dla sprawdzania pisowni"
-!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Obsuga sprawdzania pisowni (do jej instalacji wymagane jest poczenie z Internetem)."
-!define ASPELL_INSTALL_FAILED			"Instalacja nie powioda si"
-!define PIDGIN_SPELLCHECK_BRETON		"bretoski"
-!define PIDGIN_SPELLCHECK_CATALAN		"kataloski"
-!define PIDGIN_SPELLCHECK_CZECH		"czeski"
-!define PIDGIN_SPELLCHECK_WELSH		"walijski"
-!define PIDGIN_SPELLCHECK_DANISH		"duski"
-!define PIDGIN_SPELLCHECK_GERMAN		"niemiecki"
-!define PIDGIN_SPELLCHECK_GREEK		"grecki"
-!define PIDGIN_SPELLCHECK_ENGLISH		"angielski"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"hiszpaski"
-!define PIDGIN_SPELLCHECK_FAROESE		"farerski"
-!define PIDGIN_SPELLCHECK_FRENCH		"francuski"
-!define PIDGIN_SPELLCHECK_ITALIAN		"woski"
-!define PIDGIN_SPELLCHECK_DUTCH		"holenderski"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"norweski"
-!define PIDGIN_SPELLCHECK_POLISH		"polski"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"portugalski"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"rumuski"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"rosyjski"
-!define PIDGIN_SPELLCHECK_SLOVAK		"sowacki"
-!define PIDGIN_SPELLCHECK_SWEDISH		"szwedzki"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ukraiski"
-
--- a/pidgin/win32/nsis/translations/portuguese-br.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/portuguese-br.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -8,8 +8,6 @@
 ;;  Version 3
 ;;
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"O ambiente de tempo de execuo do GTK+ est ausente ou precisa ser atualizado.$\rFavor instalar a verso v${GTK_MIN_VERSION} ou superior do ambiente de tempo de execuo do GTK+."
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Avanar >"
@@ -29,11 +27,8 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC   "Crie uma entrada no Menu Iniciar para o Pidgin"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Uma verso antiga do ambiente de tempo de execuo do GTK+ foi encontrada. Voc deseja atualiz-lo?$\rNota: O $(^Name) poder no funcionar a menos que voc o faa."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Erro ao instalar o ambiente de tempo de execuo do GTK+."
-!define GTK_BAD_INSTALL_PATH			"O caminho que voc digitou no pde ser acessado ou criado."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Visite a pgina da web do Pidgin para Windows"
@@ -44,33 +39,8 @@
 
 !define INSTALLER_IS_RUNNING                   "O instalador j est em execuo."
 !define PIDGIN_IS_RUNNING                      "Uma instncia do Pidgin est em execuo. Feche o Pidgin e tente novamente."
-!define GTK_WINDOWS_INCOMPATIBLE               "O Windows 95/98/Me  incompatvel com o GTK+ 2.8.0 ou superior. O GTK+ ${GTK_INSTALL_VERSION} no ser instalado.$\rSe voc no possuir o GTK+ verso ${GTK_MIN_VERSION} ou superior j instalado, o instalador ir fechar agora."
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL       "No foi possvel desinstalar a verso do Pidgin que est instalada atualmente. A nova verso ser instalada sem que a verso antiga seja removida."
 !define URI_HANDLERS_SECTION_TITLE             "Handlers para endereos"
 !define PIDGIN_SPELLCHECK_SECTION_TITLE        "Suporte a verificao ortogrfica"
 !define PIDGIN_SPELLCHECK_ERROR                "Erro ao instalar a verificao ortogrfica"
-!define PIDGIN_SPELLCHECK_DICT_ERROR           "Erro ao instalar o dicionrio da verificao ortogrfica"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION  "Suporte a verificao ortogrfica (A instalao necessita de conexo a internet)"
-!define ASPELL_INSTALL_FAILED                  "Falha na instalao"
-!define PIDGIN_SPELLCHECK_BRETON               "Breto"
-!define PIDGIN_SPELLCHECK_CATALAN              "Catalo"
-!define PIDGIN_SPELLCHECK_CZECH                "Tcheco"
-!define PIDGIN_SPELLCHECK_WELSH                "Gals" 
-!define PIDGIN_SPELLCHECK_DANISH               "Dinamarqus"
-!define PIDGIN_SPELLCHECK_GERMAN               "Alemo" 
-!define PIDGIN_SPELLCHECK_GREEK                "Grego"
-!define PIDGIN_SPELLCHECK_ENGLISH              "Ingls"
-!define PIDGIN_SPELLCHECK_ESPERANTO            "Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH              "Espanhol"
-!define PIDGIN_SPELLCHECK_FAROESE              "Feros"
-!define PIDGIN_SPELLCHECK_FRENCH               "Francs"
-!define PIDGIN_SPELLCHECK_ITALIAN              "Italiano"
-!define PIDGIN_SPELLCHECK_DUTCH                "Holands"
-!define PIDGIN_SPELLCHECK_NORWEGIAN            "Noruegus" 
-!define PIDGIN_SPELLCHECK_POLISH               "Polons"
-!define PIDGIN_SPELLCHECK_PORTUGUESE           "Portugus"
-!define PIDGIN_SPELLCHECK_ROMANIAN             "Romeno"
-!define PIDGIN_SPELLCHECK_RUSSIAN              "Russo" 
-!define PIDGIN_SPELLCHECK_SLOVAK               "Eslovaco"
-!define PIDGIN_SPELLCHECK_SWEDISH              "Sueco"
-!define PIDGIN_SPELLCHECK_UKRAINIAN            "Ucraniano"
--- a/pidgin/win32/nsis/translations/portuguese.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/portuguese.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -11,7 +11,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"O instalador j est a ser executado."
 !define PIDGIN_IS_RUNNING			"Uma instncia do Pidgin j est a ser executada. Saia do Pidgin e tente de novo."
-!define GTK_INSTALLER_NEEDED			"O ambiente de GTK+ est ausente ou precisa de ser actualizado.$\rPor favor instale a verso v${GTK_MIN_VERSION} ou mais recente do ambiente de GTK+."
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Seguinte >"
@@ -31,14 +30,11 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC   "Criar uma entrada para o Pidgin na Barra de Iniciar"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Foi encontrada uma verso antiga do ambiente de execuo GTK+. Deseja actualiz-lo?$\rNota: O $(^Name) poder no funcionar se no o fizer."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Visite a Pgina Web do Pidgin para Windows"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Erro ao instalar o ambiente de execuo GTK+."
-!define GTK_BAD_INSTALL_PATH			"O caminho que digitou no pode ser acedido nem criado."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"O desinstalador no encontrou entradas de registo do Pidgin.$\r provvel que outro utilizador tenha instalado este programa."
--- a/pidgin/win32/nsis/translations/romanian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/romanian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -10,7 +10,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING                     "Instalarea este deja pornit."
 !define PIDGIN_IS_RUNNING                  "O instan a programului Pidgin este deja pornit. nchidei-o i ncercai din nou."
-!define GTK_INSTALLER_NEEDED			"Mediul GTK+ nu e prezent sau avei o versiune prea veche.$\rInstalai cel puin versiunea v${GTK_MIN_VERSION} a mediului GTK+"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON                      "nainte >"
@@ -30,14 +29,11 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC   "Creeaz o intrare Pidgin n meniul Start"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Avei o versiune veche a mediului GTK+. Dorii s o actualizai?$\rNot: E posibil ca $(^Name) s nu funcioneze cu versiunea veche."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE               "Vizitai pagina de web Windows Pidgin"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Eroare la instalarea mediului GTK+."
-!define GTK_BAD_INSTALL_PATH			"Directorul specificat nu poate fi accesat sau creat."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1         "Programul de dezinstalare nu a gsit intrri Pidgin n regitri.$\rProbabil un alt utilizator a instalat aceast aplicaie."
--- a/pidgin/win32/nsis/translations/russian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/russian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -8,9 +8,6 @@
 ;;  Version 2
 ;;
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"   GTK+     .$\r  v${GTK_MIN_VERSION}     GTK+."
-
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			" >"
 !define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name)    GPL.      . $_CLICK"
@@ -22,14 +19,11 @@
 !define GTK_SECTION_DESCRIPTION		"  ,  Pidgin."
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"   GTK+.       ?$\r: Pidgin        ."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		" - Pidgin   Windows."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"    GTK+."
-!define GTK_BAD_INSTALL_PATH			"       ."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"      Pidgin  ..$\r     ."
--- a/pidgin/win32/nsis/translations/serbian-latin.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/serbian-latin.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -7,8 +7,6 @@
 ;;  Author: Danilo Segan <dsegan@gmx.net>
 ;;
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"GTK+ okolina za izvravanje ili nije naena ili se moraunaprediti.$\rMolimo instalirajte v${GTK_MIN_VERSION} ili veu GTK+ okoline za izvravanje"
 
 ; Components Page
 !define PIDGIN_SECTION_TITLE			"Pidgin klijent za brze poruke (neophodno)"
@@ -17,11 +15,8 @@
 !define GTK_SECTION_DESCRIPTION		"Skup orua za grafiko okruenje, za vie platformi, koristi ga Pidgin "
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Naena je stara verzija GTK+ izvrne okoline. Da li elite da je unapredite?$\rPrimedba: Ukoliko to ne uradite, $(^Name) moda nee raditi."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Greka prilikom instalacije GTK+ okoline za izvravanje."
-!define GTK_BAD_INSTALL_PATH			"Putanja koju ste naveli se ne moe ni napraviti niti joj se moe prii."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1         "Program za uklanjanje instalacije ne moe da pronae stavke registra za Pidgin.$\rVerovatno je ovu aplikaciju instalirao drugi korisnik."
--- a/pidgin/win32/nsis/translations/simp-chinese.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/simp-chinese.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -11,7 +11,6 @@
 ; Startup GTK+ check
 !define INSTALLER_IS_RUNNING			"װѾС"
 !define PIDGIN_IS_RUNNING			"Pidgin ʵС˳ Pidgin ȻһΡ"
-!define GTK_INSTALLER_NEEDED			"ȱ GTK+ ʱ̻Ҫ¸û$\r밲װ v${GTK_MIN_VERSION} ߰汾 GTK+ ʱ̻"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"һ >"
@@ -31,14 +30,11 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC   "ڿʼ˵д Pidgin Ŀݷʽ"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"˾ɰ汾 GTK+ ʱ̡Ҫ?$\rע:  $(^Name) ޷"
 
 ; Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		" Windows Pidgin ҳ"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"װ GTK+ ʱʧܡ"
-!define GTK_BAD_INSTALL_PATH			"޷ʻ򴴽·"
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1         "жسҲ Pidgin עĿ$\rûװ˴˳"
@@ -47,28 +43,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"ƴд֧"
 !define PIDGIN_SPELLCHECK_ERROR			"װƴд"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"װƴдֵ"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"ƴд֧֡(װҪӵ Internet)"
-!define ASPELL_INSTALL_FAILED			"װʧ"
-!define PIDGIN_SPELLCHECK_BRETON			""
-!define PIDGIN_SPELLCHECK_CATALAN			"̩"
-!define PIDGIN_SPELLCHECK_CZECH			"ݿ"
-!define PIDGIN_SPELLCHECK_WELSH			"ʿ"
-!define PIDGIN_SPELLCHECK_DANISH			""
-!define PIDGIN_SPELLCHECK_GERMAN			""
-!define PIDGIN_SPELLCHECK_GREEK			"ϣ"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Ӣ"
-!define PIDGIN_SPELLCHECK_ESPERANTO		""
-!define PIDGIN_SPELLCHECK_SPANISH			""
-!define PIDGIN_SPELLCHECK_FAROESE			""
-!define PIDGIN_SPELLCHECK_FRENCH			""
-!define PIDGIN_SPELLCHECK_ITALIAN			""
-!define PIDGIN_SPELLCHECK_DUTCH			""
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Ų"
-!define PIDGIN_SPELLCHECK_POLISH			""
-!define PIDGIN_SPELLCHECK_PORTUGUESE		""
-!define PIDGIN_SPELLCHECK_ROMANIAN			""
-!define PIDGIN_SPELLCHECK_RUSSIAN			""
-!define PIDGIN_SPELLCHECK_SLOVAK			"˹工"
-!define PIDGIN_SPELLCHECK_SWEDISH			""
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ڿ"
--- a/pidgin/win32/nsis/translations/slovak.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/slovak.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -11,7 +11,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"Intalcia je u spusten"
 !define PIDGIN_IS_RUNNING				"Pidgin je prve spusten. Vypnite ho a skste znova."
-!define GTK_INSTALLER_NEEDED			"GTK+ runtime prostredie chba alebo mus by upgradovan.$\rNaintalujte, prosm, GTK+ runtime verziu v${GTK_MIN_VERSION}, alebo noviu"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"alej >"
@@ -31,14 +30,11 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Vytvori odkaz na Pidgin v tart Menu"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Bola njden staria verzia GTK+ runtime. Prajete si upgradova sasn verziu?$\rPoznmka: $(^Name) nemus po upgradovan fungova sprvne."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Navtvi webstrnku Windows Pidgin"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Chyba pri intalcii GTK+ runtime."
-!define GTK_BAD_INSTALL_PATH			"Zadan cesta nie je prstupn alebo ju nie je mon vytvori."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Intaltoru sa nepodarilo njs poloky v registri pre Pidgin.$\rJe mon, e tto aplikciu naintaloval in pouvate."
@@ -47,29 +43,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Podpora kontroly pravopisu"
 !define PIDGIN_SPELLCHECK_ERROR			"Chyba pri intalcii kontroly pravopisu"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Chyba pri intalcii slovnka kontroly pravopisu"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Podpora kontroly pravopisu (Nutn pripojenie k Internetu)"
-!define ASPELL_INSTALL_FAILED			"Intalcia zlyhala"
-!define PIDGIN_SPELLCHECK_BRETON			"Bretnsky"
-!define PIDGIN_SPELLCHECK_CATALAN			"Katalnsky"
-!define PIDGIN_SPELLCHECK_CZECH			"esk"
-!define PIDGIN_SPELLCHECK_WELSH			"Welshsk"
-!define PIDGIN_SPELLCHECK_DANISH			"Dnsky"
-!define PIDGIN_SPELLCHECK_GERMAN			"Nemeck"
-!define PIDGIN_SPELLCHECK_GREEK			"Grcky"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Anglick"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperantsk"
-!define PIDGIN_SPELLCHECK_SPANISH			"panielsk"
-!define PIDGIN_SPELLCHECK_FAROESE			"Faroesk"
-!define PIDGIN_SPELLCHECK_FRENCH			"Franczsky"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Taliansk"
-!define PIDGIN_SPELLCHECK_DUTCH			"Holandsk"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Nrsky"
-!define PIDGIN_SPELLCHECK_POLISH			"Posk"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugalsk"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumunsk"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rusk"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Slovensk"
-!define PIDGIN_SPELLCHECK_SWEDISH			"vdsky"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrajinsk"
 
--- a/pidgin/win32/nsis/translations/slovenian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/slovenian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -11,7 +11,6 @@
 ; Startup GTK+ check
 !define INSTALLER_IS_RUNNING			"Nameanje e poteka."
 !define PIDGIN_IS_RUNNING			"Trenutno e tee ena razliica Pidgina. Prosimo, zaprite aplikacijo in poskusite znova."
-!define GTK_INSTALLER_NEEDED			"Izvajalno okolje GTK+ manjka ali pa ga je potrebno nadgraditi.$\rProsimo, namestite v${GTK_MIN_VERSION} ali novejo razliico izvajalnega okolja GTK+"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Naprej >"
@@ -31,8 +30,6 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"Ustvari izbiro Pidgin v meniju Start"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Nameeno imate starejo razliico izvajalnega okolja GTK+. Jo elite nadgraditi?$\rOpomba: e je ne boste nadgradili, $(^Name) morda ne bo deloval."
-!define GTK_WINDOWS_INCOMPATIBLE		"Okolja Windows 95/98/Me z GTK+ 2.8.0 ali novejimi niso zdruljiva.  GTK+ ${GTK_INSTALL_VERSION} will not be installed.$\rIf you don't have GTK+ ${GTK_MIN_VERSION} ali noveji je e nameen, zato se bo namestitev zdaj prekinila."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Obiite spletno stran Windows Pidgin"
@@ -41,8 +38,6 @@
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Trenutno nameene razliice Pidgina ni mogoe odstraniti. Nova razliica bo nameena brez odstranitve trenutno nameene razliice."
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Napaka pri namestitvi izvajalnega okolja GTK+."
-!define GTK_BAD_INSTALL_PATH			"Pot, ki ste jo vnesli, ni dosegljiva ali je ni mogoe ustvariti."
 
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"URI Handlers"
@@ -54,29 +49,4 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Podpora preverjanja rkovanja"
 !define PIDGIN_SPELLCHECK_ERROR			"Napaka pri nameanju preverjanja rkovanja"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Napaka pri nameanju slovarja za preverjanje rkovanja"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Podpora preverjanja rkovanja.  (Za namestitev je potrebna spletna povezava)"
-!define ASPELL_INSTALL_FAILED			"Namestitev ni uspela."
-!define PIDGIN_SPELLCHECK_BRETON		"bretonski"
-!define PIDGIN_SPELLCHECK_CATALAN		"katalonski"
-!define PIDGIN_SPELLCHECK_CZECH			"eki"
-!define PIDGIN_SPELLCHECK_WELSH			"velki"
-!define PIDGIN_SPELLCHECK_DANISH		"danski"
-!define PIDGIN_SPELLCHECK_GERMAN		"nemki"
-!define PIDGIN_SPELLCHECK_GREEK			"grki"
-!define PIDGIN_SPELLCHECK_ENGLISH		"angleki"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"esperantski"
-!define PIDGIN_SPELLCHECK_SPANISH		"panski"
-!define PIDGIN_SPELLCHECK_FAROESE		"farojski"
-!define PIDGIN_SPELLCHECK_FRENCH		"francoski"
-!define PIDGIN_SPELLCHECK_ITALIAN		"italijanski"
-!define PIDGIN_SPELLCHECK_DUTCH			"nizozemski"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"norveki"
-!define PIDGIN_SPELLCHECK_POLISH		"poljski"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"portugalski"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"romunski"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"ruski"
-!define PIDGIN_SPELLCHECK_SLOVAK		"slovaki"
-!define PIDGIN_SPELLCHECK_SLOVENIAN		"slovenski"
-!define PIDGIN_SPELLCHECK_SWEDISH		"vedski"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"ukrajinski"
--- a/pidgin/win32/nsis/translations/spanish.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/spanish.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -8,9 +8,6 @@
 ;;  Version 2
 ;;
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"El entorno de ejecucin de GTK+ falta o necesita ser actualizado.$\rPor favor, instale la versin v${GTK_MIN_VERSION} del ejecutable GTK+ o alguna posterior."
-
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Siguiente >"
 !define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) se distribuye bajo la licencia GPL. Esta licencia se incluye aqu slo con propsito informativo: $_CLICK"
@@ -22,14 +19,11 @@
 !define GTK_SECTION_DESCRIPTION		"Una suite de herramientas GUI multiplataforma, utilizada por Pidgin"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Se ha encontrado una versin antiga del ejecutable de GTK+. Desea actualizarla?$\rObservacin: $(^Name) no funcionar a menos que lo haga."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Visite la pgina Web de Pidgin Windows"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Error al instalar el ejecutable GTK+."
-!define GTK_BAD_INSTALL_PATH			"No se pudo acceder o crear la ruta que vd. indic."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1         "El desinstalador no pudo encontrar las entradas en el registro de Pidgin.$\rEs probable que otro usuario instalara la aplicacin."
--- a/pidgin/win32/nsis/translations/swedish.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/swedish.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -1,80 +1,52 @@
-;;
-;;  swedish.nsh
-;;
-;;  Swedish language strings for the Windows Pidgin NSIS installer.
-;;  Windows Code page: 1252
-;;
-;;  Author: Tore Lundqvist <tlt@mima.x.se>, 2003.
-;;  Author: Peter Hjalmarsson <xake@telia.com>, 2005.
-;;  Version 3
-
-; Startup Checks
-!define INSTALLER_IS_RUNNING			"Installationsprogrammet krs redan."
-!define PIDGIN_IS_RUNNING			"En instans av Pidgin krs redan. Avsluta Pidgin och frsk igen."
-!define GTK_INSTALLER_NEEDED			"Krmiljn GTK+ r antingen inte installerat eller behver uppgraderas.$\rVar  god installera v${GTK_MIN_VERSION} eller hgre av GTK+-krmiljn."
-
-; License Page
-!define PIDGIN_LICENSE_BUTTON			"Nsta >"
-!define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) r utgivet under GPL. Licensen finns tillgnglig hr fr informationssyften enbart. $_CLICK"
-
-; Components Page
-!define PIDGIN_SECTION_TITLE			"Pidgin Snabbmeddelandeklient (obligatorisk)"
-!define GTK_SECTION_TITLE			"GTK+-krmilj (obligatorisk)"
-!define PIDGIN_SHORTCUTS_SECTION_TITLE 		"Genvgar"
-!define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE 	"Skrivbord"
-!define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE "Startmeny"
-!define PIDGIN_SECTION_DESCRIPTION		"Pidgins krnfiler och DLL:er"
-!define GTK_SECTION_DESCRIPTION			"En GUI-verktygsuppsttning fr flera olika plattformar som Pidgin anvnder."
-
-!define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION   	"Genvgar fr att starta Pidgin"
-!define PIDGIN_DESKTOP_SHORTCUT_DESC   		"Skapar en genvg till Pidgin p skrivbordet"
-!define PIDGIN_STARTMENU_SHORTCUT_DESC   	"Skapar ett tillgg i startmenyn fr Pidgin"
-
-; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"En ldre version av GTK+ runtime hittades, vill du uppgradera den?$\rOBS! $(^Name) kommer kanske inte att fungera om du inte uppgraderar."
-
-; Installer Finish Page
-!define PIDGIN_FINISH_VISIT_WEB_SITE		"Besk Windows-Pidgin hemsida"
-
-; Pidgin Section Prompts and Texts
-!define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Kunde inte avinstallera den nuvarande versionen av Pidgin. Den nya versionen kommer att installeras utan att ta bort den fr nrvarande installerade versionen."
-
-; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Fel vid installation av GTK+ runtime."
-!define GTK_BAD_INSTALL_PATH			"Den skvg du angivit gr inte att komma t eller skapa."
-
-; URL Handler section
-!define URI_HANDLERS_SECTION_TITLE		"URI Hanterare"
-
-; Uninstall Section Prompts
-!define un.PIDGIN_UNINSTALL_ERROR_1         	"Avinstalleraren kunde inte hitta registervrden fr Pidgin.$\rAntagligen har en annan anvndare installerat applikationen."
-!define un.PIDGIN_UNINSTALL_ERROR_2         	"Du har inte rttigheter att avinstallera den hr applikationen."
-
-; Spellcheck Section Prompts
-!define PIDGIN_SPELLCHECK_SECTION_TITLE		"Std fr rttstavning"
-!define PIDGIN_SPELLCHECK_ERROR			"Fel vid installation fr rttstavning"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Fel vid installation av rttstavningsordlista"
-!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Std fr Rttstavning.  (Internetanslutning krvs fr installation)"
-!define ASPELL_INSTALL_FAILED			"Installationen misslyckades"
-!define PIDGIN_SPELLCHECK_BRETON		"Bretonska"
-!define PIDGIN_SPELLCHECK_CATALAN		"Katalanska"
-!define PIDGIN_SPELLCHECK_CZECH			"Tjeckiska"
-!define PIDGIN_SPELLCHECK_WELSH			"Kymriska"
-!define PIDGIN_SPELLCHECK_DANISH		"Danska"
-!define PIDGIN_SPELLCHECK_GERMAN		"Tyska"
-!define PIDGIN_SPELLCHECK_GREEK			"Grekiska"
-!define PIDGIN_SPELLCHECK_ENGLISH		"Engelska"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH		"Spanska"
-!define PIDGIN_SPELLCHECK_FAROESE		"Friska"
-!define PIDGIN_SPELLCHECK_FRENCH		"Franska"
-!define PIDGIN_SPELLCHECK_ITALIAN		"Italienska"
-!define PIDGIN_SPELLCHECK_DUTCH			"Nederlndska"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Norska"
-!define PIDGIN_SPELLCHECK_POLISH		"Polska"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugisiska"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Rumnska"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"Ryska"
-!define PIDGIN_SPELLCHECK_SLOVAK		"Slovakiska"
-!define PIDGIN_SPELLCHECK_SWEDISH		"Svenska"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ukrainska"
+;;
+;;  swedish.nsh
+;;
+;;  Swedish language strings for the Windows Pidgin NSIS installer.
+;;  Windows Code page: 1252
+;;
+;;  Author: Tore Lundqvist <tlt@mima.x.se>, 2003.
+;;  Author: Peter Hjalmarsson <xake@telia.com>, 2005.
+;;  Version 3
+
+; Startup Checks
+!define INSTALLER_IS_RUNNING			"Installationsprogrammet krs redan."
+!define PIDGIN_IS_RUNNING			"En instans av Pidgin krs redan. Avsluta Pidgin och frsk igen."
+
+; License Page
+!define PIDGIN_LICENSE_BUTTON			"Nsta >"
+!define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) r utgivet under GPL. Licensen finns tillgnglig hr fr informationssyften enbart. $_CLICK"
+
+; Components Page
+!define PIDGIN_SECTION_TITLE			"Pidgin Snabbmeddelandeklient (obligatorisk)"
+!define GTK_SECTION_TITLE			"GTK+-krmilj (obligatorisk)"
+!define PIDGIN_SHORTCUTS_SECTION_TITLE 		"Genvgar"
+!define PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE 	"Skrivbord"
+!define PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE "Startmeny"
+!define PIDGIN_SECTION_DESCRIPTION		"Pidgins krnfiler och DLL:er"
+!define GTK_SECTION_DESCRIPTION			"En GUI-verktygsuppsttning fr flera olika plattformar som Pidgin anvnder."
+
+!define PIDGIN_SHORTCUTS_SECTION_DESCRIPTION   	"Genvgar fr att starta Pidgin"
+!define PIDGIN_DESKTOP_SHORTCUT_DESC   		"Skapar en genvg till Pidgin p skrivbordet"
+!define PIDGIN_STARTMENU_SHORTCUT_DESC   	"Skapar ett tillgg i startmenyn fr Pidgin"
+
+; GTK+ Directory Page
+
+; Installer Finish Page
+!define PIDGIN_FINISH_VISIT_WEB_SITE		"Besk Windows-Pidgin hemsida"
+
+; Pidgin Section Prompts and Texts
+!define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Kunde inte avinstallera den nuvarande versionen av Pidgin. Den nya versionen kommer att installeras utan att ta bort den fr nrvarande installerade versionen."
+
+; GTK+ Section Prompts
+
+; URL Handler section
+!define URI_HANDLERS_SECTION_TITLE		"URI Hanterare"
+
+; Uninstall Section Prompts
+!define un.PIDGIN_UNINSTALL_ERROR_1         	"Avinstalleraren kunde inte hitta registervrden fr Pidgin.$\rAntagligen har en annan anvndare installerat applikationen."
+!define un.PIDGIN_UNINSTALL_ERROR_2         	"Du har inte rttigheter att avinstallera den hr applikationen."
+
+; Spellcheck Section Prompts
+!define PIDGIN_SPELLCHECK_SECTION_TITLE		"Std fr rttstavning"
+!define PIDGIN_SPELLCHECK_ERROR			"Fel vid installation fr rttstavning"
+!define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Std fr Rttstavning.  (Internetanslutning krvs fr installation)"
--- a/pidgin/win32/nsis/translations/trad-chinese.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/trad-chinese.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -13,7 +13,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"w˵{b椤C"
 !define PIDGIN_IS_RUNNING				"Pidgin b椤AХoӵ{AwˡC"
-!define GTK_INSTALLER_NEEDED			"䤣ŦX GTK+ ҩάOݭnQsC$\rЦw v${GTK_MIN_VERSION} ΥHW GTK+ ҡC"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"U@B >"
@@ -33,8 +32,6 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC		"b}l\إ߱|"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"o{@ª GTK+ ҡCznNɯŶܡH$\rЪ`NGpGzɯšA $(^Name) iLkTQC"
-!define GTK_WINDOWS_INCOMPATIBLE		"۪ 2.8.0 }lAGTK P Windows 95/98/Me wAۮeAGTK+ ${GTK_INSTALL_VERSION} ]N|QwˡC$\rpGtΤwgw˪ GTK+ ${GTK_MIN_VERSION} ΧsAw˵{NHYC"
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"X Windows Pidgin "
@@ -43,8 +40,6 @@
 !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL	"Lkثeww˪ PidginAsNbgªpUiwˡC"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"w GTK+ ҮɵoͿ~C"
-!define GTK_BAD_INSTALL_PATH			"zҿJw˥ؿLksΫإߡC"
 
 ; URL Handler section
 !define URI_HANDLERS_SECTION_TITLE		"URI Bz{"
@@ -56,29 +51,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"rˬd\"
 !define PIDGIN_SPELLCHECK_ERROR			"w˫rˬd~oͿ~"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"w˫rˬdΪ~oͿ~"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"rˬd䴩]w˶ںsu^C"
-!define ASPELL_INSTALL_FAILED			"w˥"
-!define PIDGIN_SPELLCHECK_BRETON		"h"
-!define PIDGIN_SPELLCHECK_CATALAN		"["
-!define PIDGIN_SPELLCHECK_CZECH			"J"
-!define PIDGIN_SPELLCHECK_WELSH			"º"
-!define PIDGIN_SPELLCHECK_DANISH		""
-!define PIDGIN_SPELLCHECK_GERMAN		"w"
-!define PIDGIN_SPELLCHECK_GREEK			"þ"
-!define PIDGIN_SPELLCHECK_ENGLISH		"^"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"@ɻy"
-!define PIDGIN_SPELLCHECK_SPANISH		"Z"
-!define PIDGIN_SPELLCHECK_FAROESE		"kùsq"
-!define PIDGIN_SPELLCHECK_FRENCH		"k"
-!define PIDGIN_SPELLCHECK_ITALIAN		"NjQ"
-!define PIDGIN_SPELLCHECK_DUTCH			""
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"¤"
-!define PIDGIN_SPELLCHECK_POLISH		"i"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		""
-!define PIDGIN_SPELLCHECK_ROMANIAN		"ùȤ"
-!define PIDGIN_SPELLCHECK_RUSSIAN		"X"
-!define PIDGIN_SPELLCHECK_SLOVAK		"J"
-!define PIDGIN_SPELLCHECK_SWEDISH		""
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"QJ"
 
--- a/pidgin/win32/nsis/translations/valencian.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/valencian.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -14,7 +14,6 @@
 ; Startup Checks
 !define INSTALLER_IS_RUNNING			"L'instalador encara est eixecutant-se."
 !define PIDGIN_IS_RUNNING				"Una instancia de Pidgin est eixecutant-se. Ix del Pidgin i torna a intentar-ho."
-!define GTK_INSTALLER_NEEDED			"L'entorn d'eixecucio GTK+ no es troba o necessita ser actualisat.$\rPer favor instala la versio${GTK_MIN_VERSION} o superior de l'entorn GTK+"
 
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Segent >"
@@ -34,14 +33,11 @@
 !define PIDGIN_STARTMENU_SHORTCUT_DESC   		"Crear una entrada per a Pidgin en Menu Inici"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Una versio antiua de l'entorn GTK+ fon trobada. Vols actualisar-la?$\rNota: $(^Name) no funcionar si no ho fas."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Visita la pagina de Pidgin per a Windows"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Erro instalant l'entorn GTK+."
-!define GTK_BAD_INSTALL_PATH			"La ruta introduida no pot ser accedida o creada."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"El desinstalador podria no trobar les entrades del registre de Pidgin.$\rProbablement un atre usuari instal esta aplicacio."
@@ -50,29 +46,5 @@
 ; Spellcheck Section Prompts
 !define PIDGIN_SPELLCHECK_SECTION_TITLE		"Soport de Correccio Ortografica"
 !define PIDGIN_SPELLCHECK_ERROR			"Erro Instalant Correccio Ortografica"
-!define PIDGIN_SPELLCHECK_DICT_ERROR		"Erro Instalant Diccionari de Correccio Ortografica"
 !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION	"Soport per a Correccio Ortografica.  (es requerix conexio a Internet per a fer l'instalacio)"
-!define ASPELL_INSTALL_FAILED			"L'Instalacio fall"
-!define PIDGIN_SPELLCHECK_BRETON			"Breto"
-!define PIDGIN_SPELLCHECK_CATALAN			"Catal"
-!define PIDGIN_SPELLCHECK_CZECH			"Chec"
-!define PIDGIN_SPELLCHECK_WELSH			"Gals"
-!define PIDGIN_SPELLCHECK_DANISH			"Danes"
-!define PIDGIN_SPELLCHECK_GERMAN			"Alem"
-!define PIDGIN_SPELLCHECK_GREEK			"Grec"
-!define PIDGIN_SPELLCHECK_ENGLISH			"Angles"
-!define PIDGIN_SPELLCHECK_ESPERANTO		"Esperanto"
-!define PIDGIN_SPELLCHECK_SPANISH			"Espanyol"
-!define PIDGIN_SPELLCHECK_FAROESE			"Feroes"
-!define PIDGIN_SPELLCHECK_FRENCH			"Frances"
-!define PIDGIN_SPELLCHECK_ITALIAN			"Itali"
-!define PIDGIN_SPELLCHECK_DUTCH			"Holandes"
-!define PIDGIN_SPELLCHECK_NORWEGIAN		"Noruec"
-!define PIDGIN_SPELLCHECK_POLISH			"Polac"
-!define PIDGIN_SPELLCHECK_PORTUGUESE		"Portugues"
-!define PIDGIN_SPELLCHECK_ROMANIAN		"Romanes"
-!define PIDGIN_SPELLCHECK_RUSSIAN			"Rus"
-!define PIDGIN_SPELLCHECK_SLOVAK			"Eslovac"
-!define PIDGIN_SPELLCHECK_SWEDISH			"Suec"
-!define PIDGIN_SPELLCHECK_UKRAINIAN		"Ucrani"
 
--- a/pidgin/win32/nsis/translations/vietnamese.nsh	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/nsis/translations/vietnamese.nsh	Mon Mar 08 22:53:02 2010 +0000
@@ -9,9 +9,6 @@
 ;;  it does, these translations can not be used. 
 ;;
 
-; Startup GTK+ check
-!define GTK_INSTALLER_NEEDED			"The GTK+ runtime environment không có hoặc cần được nâng cấp.$\rHãy cài đặt GTK+ runtime v${GTK_MIN_VERSION} hoặc mới hơn"
-
 ; License Page
 !define PIDGIN_LICENSE_BUTTON			"Tiếp theo >"
 !define PIDGIN_LICENSE_BOTTOM_TEXT		"$(^Name) được phát hành theo giấy  phép GPL. Giấy phép thấy ở đây chỉ là để cung cấp thông tin mà thôi. $_CLICK"
@@ -23,14 +20,11 @@
 !define GTK_SECTION_DESCRIPTION		"Bộ công cụ giao diện đồ họa đa nền để dùng cho Pidgin"
 
 ; GTK+ Directory Page
-!define GTK_UPGRADE_PROMPT			"Phát hiện thấy có phiên bản cũ của  GTK+ runtime. Bạn muốn nâng cấp không?$\rNote: $(^Name) có thể không chạy nếu không nâng cấp."
 
 ; Installer Finish Page
 !define PIDGIN_FINISH_VISIT_WEB_SITE		"Hãy xem trang chủ Windows Pidgin"
 
 ; GTK+ Section Prompts
-!define GTK_INSTALL_ERROR			"Lỗi cài đặt GTK+ runtime."
-!define GTK_BAD_INSTALL_PATH			"Đường dẫn mà bạn nhập có thể không truy cập được hay không tạo được."
 
 ; Uninstall Section Prompts
 !define un.PIDGIN_UNINSTALL_ERROR_1		"Trình gỡ cài đặt không tìm được các  registry entry cho Pidgin.$\rCó thể là chương trình được người dùng khác cài đặt."
--- a/pidgin/win32/pidgin_dll_rc.rc.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/pidgin_dll_rc.rc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -19,7 +19,7 @@
         VALUE "FileDescription", "GTK+ Pidgin Library"
         VALUE "FileVersion", "@PIDGIN_VERSION@"
         VALUE "InternalName", "libpidgin"
-        VALUE "LegalCopyright", "Copyright (C) 1998-2007 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
+        VALUE "LegalCopyright", "Copyright (C) 1998-2010 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
         VALUE "OriginalFilename", "pidgin.dll"
         VALUE "ProductName", "Pidgin"
         VALUE "ProductVersion", "@PIDGIN_VERSION@"
--- a/pidgin/win32/pidgin_exe_rc.rc.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/pidgin_exe_rc.rc.in	Mon Mar 08 22:53:02 2010 +0000
@@ -19,7 +19,7 @@
         VALUE "FileDescription", "Pidgin"
         VALUE "FileVersion", "@PIDGIN_VERSION@"
         VALUE "InternalName", "pidgin"
-        VALUE "LegalCopyright", "Copyright (C) 1998-2007 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
+        VALUE "LegalCopyright", "Copyright (C) 1998-2010 The Pidgin developer community (See the COPYRIGHT file in the source distribution)."
         VALUE "OriginalFilename", "@ORIGINAL_FILENAME@"
         VALUE "ProductName", "Pidgin"
         VALUE "ProductVersion", "@PIDGIN_VERSION@"
--- a/pidgin/win32/untar.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/untar.c	Mon Mar 08 22:53:02 2010 +0000
@@ -80,14 +80,7 @@
 #include "untar.h"
 #include <glib.h>
 
-#if GLIB_CHECK_VERSION(2,6,0)
-#	include <glib/gstdio.h>
-#else
-#define mkdir(a,b) _mkdir((a))
-#define g_mkdir mkdir
-#define g_fopen fopen
-#define g_unlink unlink
-#endif
+#include <glib/gstdio.h>
 
 #define untar_error( error, args... )      purple_debug(PURPLE_DEBUG_ERROR, "untar", error, ## args )
 #define untar_warning( warning, args... )  purple_debug(PURPLE_DEBUG_WARNING, "untar", warning, ## args )
--- a/pidgin/win32/winpidgin.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/winpidgin.c	Mon Mar 08 22:53:02 2010 +0000
@@ -26,33 +26,23 @@
  */
 
 /* This is for ATTACH_PARENT_PROCESS */
+#define UNICODE
+#define _UNICODE
 #ifndef _WIN32_WINNT
 #define _WIN32_WINNT 0x501
 #endif
 #include <windows.h>
+#include <tchar.h>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
-
-/* These will hopefully be in the win32api next time it is updated - at which point, we'll remove them */
-#ifndef LANG_PERSIAN
-#define LANG_PERSIAN 0x29
-#endif
-#ifndef LANG_BOSNIAN
-#define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN	0x05
-#define SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC	0x08
-#endif
-#ifndef SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN
-#define SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN	0x04
-#endif
-#ifndef LANG_XHOSA
-#define LANG_XHOSA 0x34
-#endif
-
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "config.h"
 
 typedef int (CALLBACK* LPFNPIDGINMAIN)(HINSTANCE, int, char**);
-typedef void (CALLBACK* LPFNSETDLLDIRECTORY)(LPCTSTR);
+typedef void (CALLBACK* LPFNSETDLLDIRECTORY)(LPCWSTR);
 typedef BOOL (CALLBACK* LPFNATTACHCONSOLE)(DWORD);
 
 static BOOL portable_mode = FALSE;
@@ -63,19 +53,19 @@
 static LPFNPIDGINMAIN pidgin_main = NULL;
 static LPFNSETDLLDIRECTORY MySetDllDirectory = NULL;
 
-static const char *get_win32_error_message(DWORD err) {
-	static char err_msg[512];
+static const TCHAR *get_win32_error_message(DWORD err) {
+	static TCHAR err_msg[512];
 
 	FormatMessage(
 		FORMAT_MESSAGE_FROM_SYSTEM,
 		NULL, err,
 		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-		(LPTSTR) &err_msg, sizeof(err_msg), NULL);
+		(LPTSTR) &err_msg, sizeof(err_msg) / sizeof(TCHAR), NULL);
 
 	return err_msg;
 }
 
-static BOOL read_reg_string(HKEY key, char* sub_key, char* val_name, LPBYTE data, LPDWORD data_len) {
+static BOOL read_reg_string(HKEY key, TCHAR *sub_key, TCHAR *val_name, LPBYTE data, LPDWORD data_len) {
 	HKEY hkey;
 	BOOL ret = FALSE;
 	LONG retv;
@@ -86,11 +76,11 @@
 						NULL, NULL, data, data_len)))
 			ret = TRUE;
 		else {
-			const char *err_msg = get_win32_error_message(retv);
+			const TCHAR *err_msg = get_win32_error_message(retv);
 
-			printf("Could not read reg key '%s' subkey '%s' value: '%s'.\nMessage: (%ld) %s\n",
-					(key == HKEY_LOCAL_MACHINE) ? "HKLM"
-					 : ((key == HKEY_CURRENT_USER) ? "HKCU" : "???"),
+			_tprintf(_T("Could not read reg key '%s' subkey '%s' value: '%s'.\nMessage: (%ld) %s\n"),
+					(key == HKEY_LOCAL_MACHINE) ? _T("HKLM")
+					 : ((key == HKEY_CURRENT_USER) ? _T("HKCU") : _T("???")),
 					sub_key, val_name, retv, err_msg);
 		}
 		RegCloseKey(hkey);
@@ -99,23 +89,36 @@
 		TCHAR szBuf[80];
 
 		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, retv, 0,
-				(LPTSTR) &szBuf, sizeof(szBuf), NULL);
-		printf("Could not open reg subkey: %s\nError: (%ld) %s\n",
+				(LPTSTR) &szBuf, sizeof(szBuf) / sizeof(TCHAR), NULL);
+		_tprintf(_T("Could not open reg subkey: %s\nError: (%ld) %s\n"),
 				sub_key, retv, szBuf);
 	}
 
 	return ret;
 }
 
-static void common_dll_prep(const char *path) {
+static BOOL common_dll_prep(const TCHAR *path) {
 	HMODULE hmod;
 	HKEY hkey;
+	struct _stat stat_buf;
+	TCHAR test_path[MAX_PATH + 1];
 
-	printf("GTK+ path found: %s\n", path);
+	_sntprintf(test_path, sizeof(test_path) / sizeof(TCHAR),
+		_T("%s\\libgtk-win32-2.0-0.dll"), path);
+	test_path[sizeof(test_path) / sizeof(TCHAR) - 1] = _T('\0');
 
-	if ((hmod = GetModuleHandle("kernel32.dll"))) {
+	if (_tstat(test_path, &stat_buf) != 0) {
+		printf("Unable to determine GTK+ path. \n"
+			"Assuming GTK+ is in the PATH.\n");
+		return FALSE;
+	}
+
+
+	_tprintf(_T("GTK+ path found: %s\n"), path);
+
+	if ((hmod = GetModuleHandle(_T("kernel32.dll")))) {
 		MySetDllDirectory = (LPFNSETDLLDIRECTORY) GetProcAddress(
-			hmod, "SetDllDirectoryA");
+			hmod, "SetDllDirectoryW");
 		if (!MySetDllDirectory)
 			printf("SetDllDirectory not supported\n");
 	} else
@@ -141,20 +144,21 @@
 		 */
 		osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
 		GetVersionEx(&osinfo);
-		if ((osinfo.dwMajorVersion == 5 &&
-			osinfo.dwMinorVersion == 0 &&
-			strcmp(osinfo.szCSDVersion, "Service Pack 3") >= 0) ||
-			(osinfo.dwMajorVersion == 5 &&
-			osinfo.dwMinorVersion == 1 &&
-			strcmp(osinfo.szCSDVersion, "") >= 0)
+		if ((osinfo.dwMajorVersion == 5
+				&& osinfo.dwMinorVersion == 0
+				&& _tcscmp(osinfo.szCSDVersion, _T("Service Pack 3")) >= 0)
+			||
+			(osinfo.dwMajorVersion == 5
+				&& osinfo.dwMinorVersion == 1
+				&& _tcscmp(osinfo.szCSDVersion, _T("")) >= 0)
 		) {
 			DWORD regval = 1;
 			DWORD reglen = sizeof(DWORD);
 
 			printf("Using Win2k (SP3+) / WinXP (No SP)... Checking SafeDllSearch\n");
 			read_reg_string(HKEY_LOCAL_MACHINE,
-				"System\\CurrentControlSet\\Control\\Session Manager",
-				"SafeDllSearchMode",
+				_T("System\\CurrentControlSet\\Control\\Session Manager"),
+				_T("SafeDllSearchMode"),
 				(LPBYTE) &regval,
 				&reglen);
 
@@ -162,16 +166,16 @@
 				printf("Trying to set SafeDllSearchMode to 0\n");
 				regval = 0;
 				if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
-					"System\\CurrentControlSet\\Control\\Session Manager",
+					_T("System\\CurrentControlSet\\Control\\Session Manager"),
 					0,  KEY_SET_VALUE, &hkey
 				) == ERROR_SUCCESS) {
 					if (RegSetValueEx(hkey,
-						"SafeDllSearchMode", 0,
+						_T("SafeDllSearchMode"), 0,
 						REG_DWORD, (LPBYTE) &regval,
 						sizeof(DWORD)
 					) != ERROR_SUCCESS)
 						printf("Error writing SafeDllSearchMode. Error: %u\n",
-						(UINT) GetLastError());
+							(UINT) GetLastError());
 					RegCloseKey(hkey);
 				} else
 					printf("Error opening Session Manager key for writing. Error: %u\n",
@@ -180,31 +184,39 @@
 				printf("SafeDllSearchMode is set to 0\n");
 		}/*end else*/
 	}
+
+	return TRUE;
 }
 
-static void portable_mode_dll_prep(const char *pidgin_dir) {
+static BOOL dll_prep(const TCHAR *pidgin_dir) {
+	TCHAR path[MAX_PATH + 1];
+	path[0] = _T('\0');
+
+	if (*pidgin_dir) {
+		_sntprintf(path, sizeof(path) / sizeof(TCHAR), _T("%s\\Gtk\\bin"), pidgin_dir);
+		path[sizeof(path) / sizeof(TCHAR)] = _T('\0');
+	}
+
+	return common_dll_prep(path);
+}
+
+static void portable_mode_dll_prep(const TCHAR *pidgin_dir) {
 	/* need to be able to fit MAX_PATH + "PIDGIN_ASPELL_DIR=\\Aspell\\bin" in path2 */
-	char path[MAX_PATH + 1];
-	char path2[MAX_PATH + 33];
-	const char *prev = NULL;
+	TCHAR path[MAX_PATH + 1];
+	TCHAR path2[MAX_PATH + 33];
+	const TCHAR *prev = NULL;
 
 	/* We assume that GTK+ is installed under \\path\to\Pidgin\..\GTK
 	 * First we find \\path\to
 	 */
-	if (*pidgin_dir) {
+	if (*pidgin_dir)
 		/* pidgin_dir points to \\path\to\Pidgin */
-		const char *tmp = pidgin_dir;
-
-		while ((tmp = strchr(tmp, '\\'))) {
-			prev = tmp;
-			tmp++;
-		}
-	}
+		prev = _tcsrchr(pidgin_dir, _T('\\'));
 
 	if (prev) {
 		int cnt = (prev - pidgin_dir);
-		strncpy(path, pidgin_dir, cnt);
-		path[cnt] = '\0';
+		_tcsncpy(path, pidgin_dir, cnt);
+		path[cnt] = _T('\0');
 	} else {
 		printf("Unable to determine current executable path. \n"
 			"This will prevent the settings dir from being set.\n"
@@ -213,160 +225,130 @@
 	}
 
 	/* Set $HOME so that the GTK+ settings get stored in the right place */
-	_snprintf(path2, sizeof(path2), "HOME=%s", path);
-	_putenv(path2);
+	_sntprintf(path2, sizeof(path2) / sizeof(TCHAR), _T("HOME=%s"), path);
+	_tputenv(path2);
 
 	/* Set up the settings dir base to be \\path\to
 	 * The actual settings dir will be \\path\to\.purple */
-	_snprintf(path2, sizeof(path2), "PURPLEHOME=%s", path);
-	printf("Setting settings dir: %s\n", path2);
-	_putenv(path2);
+	_sntprintf(path2, sizeof(path2) / sizeof(TCHAR), _T("PURPLEHOME=%s"), path);
+	_tprintf(_T("Setting settings dir: %s\n"), path2);
+	_tputenv(path2);
 
-	_snprintf(path2, sizeof(path2), "PIDGIN_ASPELL_DIR=%s\\Aspell\\bin", path);
-	printf("%s\n", path2);
-	_putenv(path2);
+	_sntprintf(path2, sizeof(path2) / sizeof(TCHAR), _T("PIDGIN_ASPELL_DIR=%s\\Aspell\\bin"), path);
+	_tprintf(_T("%s\n"), path2);
+	_tputenv(path2);
 
-	/* set the GTK+ path to be \\path\to\GTK\bin */
-	strcat(path, "\\GTK\\bin");
-
-	common_dll_prep(path);
+	if (!dll_prep(pidgin_dir)) {
+		/* set the GTK+ path to be \\path\to\GTK\bin */
+		_tcscat(path, _T("\\GTK\\bin"));
+		common_dll_prep(path);
+	}
 }
 
-static void dll_prep() {
-	char path[MAX_PATH + 1];
-	HKEY hkey;
-	char gtkpath[MAX_PATH + 1];
-	DWORD plen;
-
-	plen = sizeof(gtkpath);
-	hkey = HKEY_CURRENT_USER;
-	if (!read_reg_string(hkey, "SOFTWARE\\GTK\\2.0", "Path",
-			(LPBYTE) &gtkpath, &plen)) {
-		hkey = HKEY_LOCAL_MACHINE;
-		if (!read_reg_string(hkey, "SOFTWARE\\GTK\\2.0", "Path",
-				(LPBYTE) &gtkpath, &plen)) {
-			printf("GTK+ Path Registry Key not found. "
-				"Assuming GTK+ is in the PATH.\n");
-			return;
-		}
-	}
-
-	/* this value is replaced during a successful RegQueryValueEx() */
-	plen = sizeof(path);
-	/* Determine GTK+ dll path .. */
-	if (!read_reg_string(hkey, "SOFTWARE\\GTK\\2.0", "DllPath",
-				(LPBYTE) &path, &plen)) {
-		strcpy(path, gtkpath);
-		strcat(path, "\\bin");
-	}
-
-	common_dll_prep(path);
-}
-
-static char* winpidgin_lcid_to_posix(LCID lcid) {
-	char *posix = NULL;
+static TCHAR* winpidgin_lcid_to_posix(LCID lcid) {
+	TCHAR *posix = NULL;
 	int lang_id = PRIMARYLANGID(lcid);
 	int sub_id = SUBLANGID(lcid);
 
 	switch (lang_id) {
-		case LANG_AFRIKAANS: posix = "af"; break;
-		case LANG_ARABIC: posix = "ar"; break;
-		case LANG_AZERI: posix = "az"; break;
-		case LANG_BENGALI: posix = "bn"; break;
-		case LANG_BULGARIAN: posix = "bg"; break;
-		case LANG_CATALAN: posix = "ca"; break;
-		case LANG_CZECH: posix = "cs"; break;
-		case LANG_DANISH: posix = "da"; break;
-		case LANG_ESTONIAN: posix = "et"; break;
-		case LANG_PERSIAN: posix = "fa"; break;
-		case LANG_GERMAN: posix = "de"; break;
-		case LANG_GREEK: posix = "el"; break;
+		case LANG_AFRIKAANS: posix = _T("af"); break;
+		case LANG_ARABIC: posix = _T("ar"); break;
+		case LANG_AZERI: posix = _T("az"); break;
+		case LANG_BENGALI: posix = _T("bn"); break;
+		case LANG_BULGARIAN: posix = _T("bg"); break;
+		case LANG_CATALAN: posix = _T("ca"); break;
+		case LANG_CZECH: posix = _T("cs"); break;
+		case LANG_DANISH: posix = _T("da"); break;
+		case LANG_ESTONIAN: posix = _T("et"); break;
+		case LANG_PERSIAN: posix = _T("fa"); break;
+		case LANG_GERMAN: posix = _T("de"); break;
+		case LANG_GREEK: posix = _T("el"); break;
 		case LANG_ENGLISH:
 			switch (sub_id) {
 				case SUBLANG_ENGLISH_UK:
-					posix = "en_GB"; break;
+					posix = _T("en_GB"); break;
 				case SUBLANG_ENGLISH_AUS:
-					posix = "en_AU"; break;
+					posix = _T("en_AU"); break;
 				case SUBLANG_ENGLISH_CAN:
-					posix = "en_CA"; break;
+					posix = _T("en_CA"); break;
 				default:
-					posix = "en"; break;
+					posix = _T("en"); break;
 			}
 			break;
-		case LANG_SPANISH: posix = "es"; break;
-		case LANG_BASQUE: posix = "eu"; break;
-		case LANG_FINNISH: posix = "fi"; break;
-		case LANG_FRENCH: posix = "fr"; break;
-		case LANG_GALICIAN: posix = "gl"; break;
-		case LANG_GUJARATI: posix = "gu"; break;
-		case LANG_HEBREW: posix = "he"; break;
-		case LANG_HINDI: posix = "hi"; break;
-		case LANG_HUNGARIAN: posix = "hu"; break;
+		case LANG_SPANISH: posix = _T("es"); break;
+		case LANG_BASQUE: posix = _T("eu"); break;
+		case LANG_FINNISH: posix = _T("fi"); break;
+		case LANG_FRENCH: posix = _T("fr"); break;
+		case LANG_GALICIAN: posix = _T("gl"); break;
+		case LANG_GUJARATI: posix = _T("gu"); break;
+		case LANG_HEBREW: posix = _T("he"); break;
+		case LANG_HINDI: posix = _T("hi"); break;
+		case LANG_HUNGARIAN: posix = _T("hu"); break;
 		case LANG_ICELANDIC: break;
-		case LANG_INDONESIAN: posix = "id"; break;
-		case LANG_ITALIAN: posix = "it"; break;
-		case LANG_JAPANESE: posix = "ja"; break;
-		case LANG_GEORGIAN: posix = "ka"; break;
-		case LANG_KANNADA: posix = "kn"; break;
-		case LANG_KOREAN: posix = "ko"; break;
-		case LANG_LITHUANIAN: posix = "lt"; break;
-		case LANG_MACEDONIAN: posix = "mk"; break;
-		case LANG_DUTCH: posix = "nl"; break;
-		case LANG_NEPALI: posix = "ne"; break;
+		case LANG_INDONESIAN: posix = _T("id"); break;
+		case LANG_ITALIAN: posix = _T("it"); break;
+		case LANG_JAPANESE: posix = _T("ja"); break;
+		case LANG_GEORGIAN: posix = _T("ka"); break;
+		case LANG_KANNADA: posix = _T("kn"); break;
+		case LANG_KOREAN: posix = _T("ko"); break;
+		case LANG_LITHUANIAN: posix = _T("lt"); break;
+		case LANG_MACEDONIAN: posix = _T("mk"); break;
+		case LANG_DUTCH: posix = _T("nl"); break;
+		case LANG_NEPALI: posix = _T("ne"); break;
 		case LANG_NORWEGIAN:
 			switch (sub_id) {
 				case SUBLANG_NORWEGIAN_BOKMAL:
-					posix = "nb"; break;
+					posix = _T("nb"); break;
 				case SUBLANG_NORWEGIAN_NYNORSK:
-					posix = "nn"; break;
+					posix = _T("nn"); break;
 			}
 			break;
-		case LANG_PUNJABI: posix = "pa"; break;
-		case LANG_POLISH: posix = "pl"; break;
-		case LANG_PASHTO: posix = "ps"; break;
+		case LANG_PUNJABI: posix = _T("pa"); break;
+		case LANG_POLISH: posix = _T("pl"); break;
+		case LANG_PASHTO: posix = _T("ps"); break;
 		case LANG_PORTUGUESE:
 			switch (sub_id) {
 				case SUBLANG_PORTUGUESE_BRAZILIAN:
-					posix = "pt_BR"; break;
+					posix = _T("pt_BR"); break;
 				default:
-				posix = "pt"; break;
+				posix = _T("pt"); break;
 			}
 			break;
-		case LANG_ROMANIAN: posix = "ro"; break;
-		case LANG_RUSSIAN: posix = "ru"; break;
-		case LANG_SLOVAK: posix = "sk"; break;
-		case LANG_SLOVENIAN: posix = "sl"; break;
-		case LANG_ALBANIAN: posix = "sq"; break;
+		case LANG_ROMANIAN: posix = _T("ro"); break;
+		case LANG_RUSSIAN: posix = _T("ru"); break;
+		case LANG_SLOVAK: posix = _T("sk"); break;
+		case LANG_SLOVENIAN: posix = _T("sl"); break;
+		case LANG_ALBANIAN: posix = _T("sq"); break;
 		/* LANG_CROATIAN == LANG_SERBIAN == LANG_BOSNIAN */
 		case LANG_SERBIAN:
 			switch (sub_id) {
 				case SUBLANG_SERBIAN_LATIN:
-					posix = "sr@Latn"; break;
+					posix = _T("sr@Latn"); break;
 				case SUBLANG_SERBIAN_CYRILLIC:
-					posix = "sr"; break;
+					posix = _T("sr"); break;
 				case SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC:
 				case SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN:
-					posix = "bs"; break;
+					posix = _T("bs"); break;
 				case SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN:
-					posix = "hr"; break;
+					posix = _T("hr"); break;
 			}
 			break;
-		case LANG_SWEDISH: posix = "sv"; break;
-		case LANG_TAMIL: posix = "ta"; break;
-		case LANG_TELUGU: posix = "te"; break;
-		case LANG_THAI: posix = "th"; break;
-		case LANG_TURKISH: posix = "tr"; break;
-		case LANG_UKRAINIAN: posix = "uk"; break;
-		case LANG_VIETNAMESE: posix = "vi"; break;
-		case LANG_XHOSA: posix = "xh"; break;
+		case LANG_SWEDISH: posix = _T("sv"); break;
+		case LANG_TAMIL: posix = _T("ta"); break;
+		case LANG_TELUGU: posix = _T("te"); break;
+		case LANG_THAI: posix = _T("th"); break;
+		case LANG_TURKISH: posix = _T("tr"); break;
+		case LANG_UKRAINIAN: posix = _T("uk"); break;
+		case LANG_VIETNAMESE: posix = _T("vi"); break;
+		case LANG_XHOSA: posix = _T("xh"); break;
 		case LANG_CHINESE:
 			switch (sub_id) {
 				case SUBLANG_CHINESE_SIMPLIFIED:
-					posix = "zh_CN"; break;
+					posix = _T("zh_CN"); break;
 				case SUBLANG_CHINESE_TRADITIONAL:
-					posix = "zh_TW"; break;
+					posix = _T("zh_TW"); break;
 				default:
-					posix = "zh"; break;
+					posix = _T("zh"); break;
 			}
 			break;
 		case LANG_URDU: break;
@@ -397,8 +379,8 @@
 	/* Deal with exceptions */
 	if (posix == NULL) {
 		switch (lcid) {
-			case 0x0455: posix = "my_MM"; break; /* Myanmar (Burmese) */
-			case 9999: posix = "ku"; break; /* Kurdish (from NSIS) */
+			case 0x0455: posix = _T("my_MM"); break; /* Myanmar (Burmese) */
+			case 9999: posix = _T("ku"); break; /* Kurdish (from NSIS) */
 		}
 	}
 
@@ -410,19 +392,19 @@
    - Check NSIS Installer Language reg value
    - Use default user locale
 */
-static const char *winpidgin_get_locale() {
-	const char *locale = NULL;
+static const TCHAR *winpidgin_get_locale() {
+	const TCHAR *locale = NULL;
 	LCID lcid;
-	char data[10];
-	DWORD datalen = 10;
+	TCHAR data[10];
+	DWORD datalen = sizeof(data) / sizeof(TCHAR);
 
 	/* Check if user set PIDGINLANG env var */
-	if ((locale = getenv("PIDGINLANG")))
+	if ((locale = _tgetenv(_T("PIDGINLANG"))))
 		return locale;
 
-	if (!portable_mode && read_reg_string(HKEY_CURRENT_USER, "SOFTWARE\\pidgin",
-			"Installer Language", (LPBYTE) &data, &datalen)) {
-		if ((locale = winpidgin_lcid_to_posix(atoi(data))))
+	if (!portable_mode && read_reg_string(HKEY_CURRENT_USER, _T("SOFTWARE\\pidgin"),
+			_T("Installer Language"), (LPBYTE) &data, &datalen)) {
+		if ((locale = winpidgin_lcid_to_posix(_ttoi(data))))
 			return locale;
 	}
 
@@ -430,39 +412,39 @@
 	if ((locale = winpidgin_lcid_to_posix(lcid)))
 		return locale;
 
-	return "en";
+	return _T("en");
 }
 
 static void winpidgin_set_locale() {
-	const char *locale = NULL;
-	char envstr[25];
+	const TCHAR *locale;
+	TCHAR envstr[25];
 
 	locale = winpidgin_get_locale();
 
-	_snprintf(envstr, 25, "LANG=%s", locale);
-	printf("Setting locale: %s\n", envstr);
-	_putenv(envstr);
+	_sntprintf(envstr, sizeof(envstr) / sizeof(TCHAR), _T("LANG=%s"), locale);
+	_tprintf(_T("Setting locale: %s\n"), envstr);
+	_tputenv(envstr);
 }
 
 
 static void winpidgin_add_stuff_to_path() {
-	char perl_path[MAX_PATH + 1];
-	char *ppath = NULL;
-	char mit_kerberos_path[MAX_PATH + 1];
-	char *mpath = NULL;
+	TCHAR perl_path[MAX_PATH + 1];
+	TCHAR *ppath = NULL;
+	TCHAR mit_kerberos_path[MAX_PATH + 1];
+	TCHAR *mpath = NULL;
 	DWORD plen;
 
 	printf("%s", "Looking for Perl... ");
 
-	plen = sizeof(perl_path);
-	if (read_reg_string(HKEY_LOCAL_MACHINE, "SOFTWARE\\Perl", "",
+	plen = sizeof(perl_path) / sizeof(TCHAR);
+	if (read_reg_string(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Perl"), _T(""),
 			    (LPBYTE) &perl_path, &plen)) {
 		/* We *could* check for perl510.dll, but it seems unnecessary. */
-		printf("found in '%s'.\n", perl_path);
+		_tprintf(_T("found in '%s'.\n"), perl_path);
 
-		if (perl_path[strlen(perl_path) - 1] != '\\')
-			strcat(perl_path, "\\");
-		strcat(perl_path, "bin");
+		if (perl_path[_tcslen(perl_path) - 1] != _T('\\'))
+			_tcscat(perl_path, _T("\\"));
+		_tcscat(perl_path, _T("bin"));
 
 		ppath = perl_path;
 	} else
@@ -470,48 +452,47 @@
 
 	printf("%s", "Looking for MIT Kerberos... ");
 
-	plen = sizeof(mit_kerberos_path);
-	if (read_reg_string(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", "InstallDir",
+	plen = sizeof(mit_kerberos_path) / sizeof(TCHAR);
+	if (read_reg_string(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\MIT\\Kerberos"), _T("InstallDir"),
 			    (LPBYTE) &mit_kerberos_path, &plen)) {
 		/* We *could* check for gssapi32.dll */
-		printf("found in '%s'.\n", mit_kerberos_path);
+		_tprintf(_T("found in '%s'.\n"), mit_kerberos_path);
 
-		if (mit_kerberos_path[strlen(mit_kerberos_path) - 1] != '\\')
-			strcat(mit_kerberos_path, "\\");
-		strcat(mit_kerberos_path, "bin");
+		if (mit_kerberos_path[_tcslen(mit_kerberos_path) - 1] != _T('\\'))
+			_tcscat(mit_kerberos_path, _T("\\"));
+		_tcscat(mit_kerberos_path, _T("bin"));
 
 		mpath = mit_kerberos_path;
 	} else
 		printf("%s", "not found.\n");
 
 	if (ppath != NULL || mpath != NULL) {
-		const char *path = getenv("PATH");
-		BOOL add_ppath = ppath != NULL && (path == NULL || !strstr(path, ppath));
-		BOOL add_mpath = mpath != NULL && (path == NULL || !strstr(path, mpath));
-		char *newpath;
+		const TCHAR *path = _tgetenv(_T("PATH"));
+		BOOL add_ppath = ppath != NULL && (path == NULL || !_tcsstr(path, ppath));
+		BOOL add_mpath = mpath != NULL && (path == NULL || !_tcsstr(path, mpath));
+		TCHAR *newpath;
 		int newlen;
 
 		if (add_ppath || add_mpath) {
 			/* Enough to add "PATH=" + path + ";"  + ppath + ";" + mpath + \0 */
-			newlen = 6 + (path ? strlen(path) + 1 : 0);
+			newlen = 6 + (path ? _tcslen(path) + 1 : 0);
 			if (add_ppath)
-				newlen += strlen(ppath) + 1;
+				newlen += _tcslen(ppath) + 1;
 			if (add_mpath)
-				newlen += strlen(mpath) + 1;
-			newpath = malloc(newlen);
-			*newpath = '\0';
+				newlen += _tcslen(mpath) + 1;
+			newpath = malloc(newlen * sizeof(TCHAR));
 
-			_snprintf(newpath, newlen, "PATH=%s%s%s%s%s%s",
-				  path ? path : "",
-				  path ? ";" : "",
-				  add_ppath ? ppath : "",
-				  add_ppath ? ";" : "",
-				  add_mpath ? mpath : "",
-				  add_mpath ? ";" : "");
+			_sntprintf(newpath, newlen, _T("PATH=%s%s%s%s%s%s"),
+				  path ? path : _T(""),
+				  path ? _T(";") : _T(""),
+				  add_ppath ? ppath : _T(""),
+				  add_ppath ? _T(";") : _T(""),
+				  add_mpath ? mpath : _T(""),
+				  add_mpath ? _T(";") : _T(""));
 
-			printf("New PATH: %s\n", newpath);
+			_tprintf(_T("New PATH: %s\n"), newpath);
 
-			_putenv(newpath);
+			_tputenv(newpath);
 			free(newpath);
 		}
 	}
@@ -523,7 +504,7 @@
 static BOOL winpidgin_set_running(BOOL fail_if_running) {
 	HANDLE h;
 
-	if ((h = CreateMutex(NULL, FALSE, "pidgin_is_running"))) {
+	if ((h = CreateMutex(NULL, FALSE, _T("pidgin_is_running")))) {
 		DWORD err = GetLastError();
 		if (err == ERROR_ALREADY_EXISTS) {
 			if (fail_if_running) {
@@ -531,14 +512,14 @@
 
 				printf("An instance of Pidgin is already running.\n");
 
-				if((msg_win = FindWindowEx(NULL, NULL, TEXT("WinpidginMsgWinCls"), NULL)))
+				if((msg_win = FindWindowEx(NULL, NULL, _T("WinpidginMsgWinCls"), NULL)))
 					if(SendMessage(msg_win, PIDGIN_WM_FOCUS_REQUEST, (WPARAM) NULL, (LPARAM) NULL))
 						return FALSE;
 
 				/* If we get here, the focus request wasn't successful */
 
 				MessageBox(NULL,
-					"An instance of Pidgin is already running",
+					_T("An instance of Pidgin is already running"),
 					NULL, MB_OK | MB_TOPMOST);
 
 				return FALSE;
@@ -549,80 +530,98 @@
 	return TRUE;
 }
 
-#define PROTO_HANDLER_SWITCH "--protocolhandler="
+#define PROTO_HANDLER_SWITCH L"--protocolhandler="
 
-static void handle_protocol(char *cmd) {
-	char *remote_msg, *tmp1, *tmp2;
-	int len;
+static void handle_protocol(wchar_t *cmd) {
+	char *remote_msg, *utf8msg;
+	wchar_t *tmp1, *tmp2;
+	int len, wlen;
 	SIZE_T len_written;
 	HWND msg_win;
 	DWORD pid;
 	HANDLE process;
 
 	/* The start of the message */
-	tmp1 = cmd + strlen(PROTO_HANDLER_SWITCH);
+	tmp1 = cmd + wcslen(PROTO_HANDLER_SWITCH);
 
 	/* The end of the message */
-	if ((tmp2 = strchr(tmp1, ' ')))
-		len = (tmp2 - tmp1);
+	if ((tmp2 = wcschr(tmp1, L' ')))
+		wlen = (tmp2 - tmp1);
 	else
-		len = strlen(tmp1);
+		wlen = wcslen(tmp1);
 
-	if (len == 0) {
+	if (wlen == 0) {
 		printf("No protocol message specified.\n");
 		return;
 	}
 
-	if (!(msg_win = FindWindowEx(NULL, NULL, TEXT("WinpidginMsgWinCls"), NULL))) {
+	if (!(msg_win = FindWindowEx(NULL, NULL, _T("WinpidginMsgWinCls"), NULL))) {
 		printf("Unable to find an instance of Pidgin to handle protocol message.\n");
 		return;
 	}
 
+	len = WideCharToMultiByte(CP_UTF8, 0, tmp1,
+			wlen, NULL, 0, NULL, NULL);
+	if (len) {
+		utf8msg = malloc(len * sizeof(char));
+		len = WideCharToMultiByte(CP_UTF8, 0, tmp1,
+			wlen, utf8msg, len, NULL, NULL);
+	}
+
+	if (len == 0) {
+		printf("No protocol message specified.\n");
+		return;
+	}
+
 	GetWindowThreadProcessId(msg_win, &pid);
 	if (!(process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, pid))) {
 		DWORD dw = GetLastError();
-		const char *err_msg = get_win32_error_message(dw);
-		printf("Unable to open Pidgin process. (%u) %s\n", (UINT) dw, err_msg);
+		const TCHAR *err_msg = get_win32_error_message(dw);
+		_tprintf(_T("Unable to open Pidgin process. (%u) %s\n"), (UINT) dw, err_msg);
 		return;
 	}
 
-	printf("Trying to handle protocol message:\n'%.*s'\n", len, tmp1);
+	wprintf(L"Trying to handle protocol message:\n'%.*s'\n", wlen, tmp1);
 
-	/* MEM_COMMIT initializes the memory to zero,
-	 * so we don't need to worry that our section of tmp1 isn't nul-terminated */
+	/* MEM_COMMIT initializes the memory to zero
+	 * so we don't need to worry that our section of utf8msg isn't nul-terminated */
 	if ((remote_msg = (char*) VirtualAllocEx(process, NULL, len + 1, MEM_COMMIT, PAGE_READWRITE))) {
-		if (WriteProcessMemory(process, remote_msg, tmp1, len, &len_written)) {
-			if (!SendMessage(msg_win, PIDGIN_WM_PROTOCOL_HANDLE, len_written, (LPARAM) remote_msg))
+		if (WriteProcessMemory(process, remote_msg, utf8msg, len, &len_written)) {
+			if (!SendMessageA(msg_win, PIDGIN_WM_PROTOCOL_HANDLE, len_written, (LPARAM) remote_msg))
 				printf("Unable to send protocol message to Pidgin instance.\n");
 		} else {
 			DWORD dw = GetLastError();
-			const char *err_msg = get_win32_error_message(dw);
-			printf("Unable to write to remote memory. (%u) %s\n", (UINT) dw, err_msg);
+			const TCHAR *err_msg = get_win32_error_message(dw);
+			_tprintf(_T("Unable to write to remote memory. (%u) %s\n"), (UINT) dw, err_msg);
 		}
 
 		VirtualFreeEx(process, remote_msg, 0, MEM_RELEASE);
 	} else {
 		DWORD dw = GetLastError();
-		const char *err_msg = get_win32_error_message(dw);
-		printf("Unable to allocate remote memory. (%u) %s\n", (UINT) dw, err_msg);
+		const TCHAR *err_msg = get_win32_error_message(dw);
+		_tprintf(_T("Unable to allocate remote memory. (%u) %s\n"), (UINT) dw, err_msg);
 	}
 
 	CloseHandle(process);
+	free(utf8msg);
 }
 
 
 int _stdcall
 WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
 		char *lpszCmdLine, int nCmdShow) {
-	char errbuf[512];
-	char pidgin_dir[MAX_PATH];
-	char exe_name[MAX_PATH];
+	TCHAR errbuf[512];
+	TCHAR pidgin_dir[MAX_PATH];
+	TCHAR exe_name[MAX_PATH];
 	HMODULE hmod;
-	char *tmp;
-	int pidgin_argc = __argc;
-	char **pidgin_argv = __argv;
-	int i;
-	BOOL debug = FALSE, help = FALSE, version = FALSE, multiple = FALSE;
+	TCHAR *tmp;
+	wchar_t *wtmp;
+	int pidgin_argc;
+	char **pidgin_argv; /* This is in utf-8 */
+	int i, j, k;
+	BOOL debug = FALSE, help = FALSE, version = FALSE, multiple = FALSE, success;
+	LPWSTR *szArglist;
+	LPWSTR cmdLine;
 
 	/* If debug or help or version flag used, create console for output */
 	for (i = 1; i < __argc; i++) {
@@ -655,7 +654,7 @@
 		 *  (_istty() doesn't work for stuff using the GUI subsystem) */
 		if (_fileno(stdout) == -1 || _fileno(stdout) == -2) {
 			LPFNATTACHCONSOLE MyAttachConsole = NULL;
-			if ((hmod = GetModuleHandle("kernel32.dll"))) {
+			if ((hmod = GetModuleHandle(_T("kernel32.dll")))) {
 				MyAttachConsole =
 					(LPFNATTACHCONSOLE)
 					GetProcAddress(hmod, "AttachConsole");
@@ -668,68 +667,104 @@
 		}
 	}
 
+	cmdLine = GetCommandLineW();
+
 	/* If this is a protocol handler invocation, deal with it accordingly */
-	if ((tmp = strstr(lpszCmdLine, PROTO_HANDLER_SWITCH)) != NULL) {
-		handle_protocol(tmp);
+	if ((wtmp = wcsstr(cmdLine, PROTO_HANDLER_SWITCH)) != NULL) {
+		handle_protocol(wtmp);
 		return 0;
 	}
 
 	/* Load exception handler if we have it */
 	if (GetModuleFileName(NULL, pidgin_dir, MAX_PATH) != 0) {
-		char *prev = NULL;
-		tmp = pidgin_dir;
 
 		/* primitive dirname() */
-		while ((tmp = strchr(tmp, '\\'))) {
-			prev = tmp;
-			tmp++;
-		}
+		tmp = _tcsrchr(pidgin_dir, _T('\\'));
 
-		if (prev) {
-			prev[0] = '\0';
+		if (tmp) {
+			HMODULE hmod;
+			tmp[0] = _T('\0');
+
+			/* tmp++ will now point to the executable file name */
+			_tcscpy(exe_name, tmp + 1);
 
-			/* prev++ will now point to the executable file name */
-			strcpy(exe_name, prev + 1);
+			_tcscat(pidgin_dir, _T("\\exchndl.dll"));
+			if ((hmod = LoadLibrary(pidgin_dir))) {
+				FARPROC proc;
+				/* exchndl.dll is built without UNICODE */
+				char debug_dir[MAX_PATH];
+				printf("Loaded exchndl.dll\n");
+				/* Temporarily override exchndl.dll's logfile
+				 * to something sane (Pidgin will override it
+				 * again when it initializes) */
+				proc = GetProcAddress(hmod, "SetLogFile");
+				if (proc) {
+					if (GetTempPathA(sizeof(debug_dir) * sizeof(char), debug_dir) != 0) {
+						strcat(debug_dir, "pidgin.RPT");
+						printf(" Setting exchndl.dll LogFile to %s\n",
+							debug_dir);
+						(proc)(debug_dir);
+					}
+				}
+				proc = GetProcAddress(hmod, "SetDebugInfoDir");
+				if (proc) {
+					char *pidgin_dir_ansi = NULL;
+					tmp[0] = _T('\0');
+#ifdef _UNICODE
+					i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir,
+						-1, NULL, 0, NULL, NULL);
+					if (i != 0) {
+						pidgin_dir_ansi = malloc(i * sizeof(char));
+						i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir,
+							-1, pidgin_dir_ansi, i, NULL, NULL);
+						if (i == 0) {
+							free(pidgin_dir_ansi);
+							pidgin_dir_ansi = NULL;
+						}
+					}
+#else
+					pidgin_dir_ansi = pidgin_dir;
+#endif
+					if (pidgin_dir_ansi != NULL) {
+						_snprintf(debug_dir, sizeof(debug_dir) / sizeof(char),
+							"%s\\pidgin-%s-dbgsym",
+							pidgin_dir_ansi,  VERSION);
+						debug_dir[sizeof(debug_dir) / sizeof(char) - 1] = '\0';
+						printf(" Setting exchndl.dll DebugInfoDir to %s\n",
+							debug_dir);
+						(proc)(debug_dir);
+#ifdef _UNICODE
+						free(pidgin_dir_ansi);
+#endif
+					}
+				}
 
-			strcat(pidgin_dir, "\\exchndl.dll");
-			if (LoadLibrary(pidgin_dir))
-				printf("Loaded exchndl.dll\n");
+			}
 
-			prev[0] = '\0';
+			tmp[0] = _T('\0');
 		}
 	} else {
 		DWORD dw = GetLastError();
-		const char *err_msg = get_win32_error_message(dw);
-		_snprintf(errbuf, 512,
-			"Error getting module filename.\nError: (%u) %s",
+		const TCHAR *err_msg = get_win32_error_message(dw);
+		_sntprintf(errbuf, 512,
+			_T("Error getting module filename.\nError: (%u) %s"),
 			(UINT) dw, err_msg);
-		printf("%s\n", errbuf);
+		_tprintf(_T("%s\n"), errbuf);
 		MessageBox(NULL, errbuf, NULL, MB_OK | MB_TOPMOST);
-		pidgin_dir[0] = '\0';
+		pidgin_dir[0] = _T('\0');
 	}
 
 	/* Determine if we're running in portable mode */
-	if (strstr(lpszCmdLine, "--portable-mode")
-			|| (exe_name != NULL && strstr(exe_name, "-portable.exe"))) {
-		int i = 0, c = 0;
-
+	if (wcsstr(cmdLine, L"--portable-mode")
+			|| (exe_name != NULL && _tcsstr(exe_name, _T("-portable.exe")))) {
 		printf("Running in PORTABLE mode.\n");
 		portable_mode = TRUE;
-
-		/* Remove the --portable-mode arg from the args passed to pidgin so it doesn't choke */
-		pidgin_argv = malloc(sizeof(char*) * pidgin_argc);
-		for (; i < __argc; i++) {
-			if (strstr(__argv[i], "--portable-mode") == NULL)
-				pidgin_argv[c++] = __argv[i];
-			else
-				pidgin_argc--;
-		}
 	}
 
 	if (portable_mode)
 		portable_mode_dll_prep(pidgin_dir);
 	else if (!getenv("PIDGIN_NO_DLL_CHECK"))
-		dll_prep();
+		dll_prep(pidgin_dir);
 
 	winpidgin_set_locale();
 
@@ -741,23 +776,53 @@
 			return 0;
 
 	/* Now we are ready for Pidgin .. */
-	if ((hmod = LoadLibrary("pidgin.dll")))
+	if ((hmod = LoadLibrary(_T("pidgin.dll"))))
 		pidgin_main = (LPFNPIDGINMAIN) GetProcAddress(hmod, "pidgin_main");
 
 	if (!pidgin_main) {
 		DWORD dw = GetLastError();
 		BOOL mod_not_found = (dw == ERROR_MOD_NOT_FOUND || dw == ERROR_DLL_NOT_FOUND);
-		const char *err_msg = get_win32_error_message(dw);
+		const TCHAR *err_msg = get_win32_error_message(dw);
 
-		_snprintf(errbuf, 512, "Error loading pidgin.dll.\nError: (%u) %s%s%s",
+		_sntprintf(errbuf, 512, _T("Error loading pidgin.dll.\nError: (%u) %s%s%s"),
 			(UINT) dw, err_msg,
-			mod_not_found ? "\n" : "",
-			mod_not_found ? "This probably means that GTK+ can't be found." : "");
-		printf("%s\n", errbuf);
-		MessageBox(NULL, errbuf, TEXT("Error"), MB_OK | MB_TOPMOST);
+			mod_not_found ? _T("\n") : _T(""),
+			mod_not_found ? _T("This probably means that GTK+ can't be found.") : _T(""));
+		_tprintf(_T("%s\n"), errbuf);
+		MessageBox(NULL, errbuf, _T("Error"), MB_OK | MB_TOPMOST);
 
 		return 0;
 	}
 
+	/* Convert argv to utf-8*/
+	szArglist = CommandLineToArgvW(cmdLine, &j);
+	pidgin_argc = j;
+	pidgin_argv = malloc(pidgin_argc* sizeof(char*));
+	k = 0;
+	for (i = 0; i < j; i++) {
+		success = FALSE;
+		/* Remove the --portable-mode arg from the args passed to pidgin so it doesn't choke */
+		if (wcsstr(szArglist[i], L"--portable-mode") == NULL) {
+			int len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i],
+				-1, NULL, 0, NULL, NULL);
+			if (len != 0) {
+				char *arg = malloc(len * sizeof(char));
+				len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i],
+					-1, arg, len, NULL, NULL);
+				if (len != 0) {
+					pidgin_argv[k++] = arg;
+					success = TRUE;
+				}
+			}
+			if (!success)
+				wprintf(L"Error converting argument '%s' to UTF-8\n",
+					szArglist[i]);
+		}
+		if (!success)
+			pidgin_argc--;
+	}
+	LocalFree(szArglist);
+
+
 	return pidgin_main(hInstance, pidgin_argc, pidgin_argv);
 }
--- a/pidgin/win32/wspell.c	Mon Mar 08 22:51:42 2010 +0000
+++ b/pidgin/win32/wspell.c	Mon Mar 08 22:53:02 2010 +0000
@@ -3,7 +3,7 @@
  *
  * File: wspell.c
  * Date: March, 2003
- * Description: Windows Purple gtkspell interface.
+ * Description: Windows Pidgin gtkspell interface.
  *
  * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com>
  *
@@ -32,8 +32,22 @@
 #include "win32dep.h"
 #include "wspell.h"
 
+/* Intermediate function so that we can eat Enchant error popups when it doesn't find a DLL
+ * This is fixed upstream, but not released */
+GtkSpell*         (*wpidginspell_new_attach_proxy)              (GtkTextView *,
+							     const gchar *,
+							     GError **) = NULL;
+
 /* GTKSPELL DUMMY FUNCS */
-static GtkSpell* wgtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) {return NULL;}
+static GtkSpell* wgtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) {
+	GtkSpell *ret = NULL;
+	if (wpidginspell_new_attach_proxy) {
+		UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
+		ret = wpidginspell_new_attach_proxy(view, lang, error);
+		SetErrorMode(old_error_mode);
+	}
+	return ret;
+}
 static GtkSpell* wgtkspell_get_from_text_view(GtkTextView *view) {return NULL;}
 static void wgtkspell_detach(GtkSpell *spell) {}
 static gboolean wgtkspell_set_language(GtkSpell *spell, const gchar *lang, GError **error) {return FALSE;}
@@ -54,28 +68,44 @@
 
 void              (*wpidginspell_recheck_all)             (GtkSpell*) = wgtkspell_recheck_all;
 
+#define GTKSPELL_DLL "libgtkspell-0.dll"
+
 static void load_gtkspell() {
-	wpidginspell_new_attach = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_new_attach" );
-	wpidginspell_get_from_text_view = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_get_from_text_view");
-	wpidginspell_detach = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_detach");
-	wpidginspell_set_language = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_set_language");
-	wpidginspell_recheck_all = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_recheck_all");
+	UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
+	gchar *tmp, *tmp2;
+	const char *path = g_getenv("PATH");
+	tmp = g_build_filename(wpurple_install_dir(), "spellcheck", NULL);
+	tmp2 = g_strdup_printf("%s%s%s", (path ? path : ""),
+		(path ? G_SEARCHPATH_SEPARATOR_S : ""),
+		tmp);
+	g_free(tmp);
+	g_setenv("PATH", tmp2, TRUE);
+
+	/* Suppress error popups */
+	wpidginspell_new_attach_proxy = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_new_attach" );
+	if (wpidginspell_new_attach_proxy) {
+		wpidginspell_get_from_text_view = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_get_from_text_view");
+		wpidginspell_detach = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_detach");
+		wpidginspell_set_language = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_set_language");
+		wpidginspell_recheck_all = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_recheck_all");
+	} else {
+		purple_debug_warning("wspell", "Couldn't load gtkspell (%s) \n", GTKSPELL_DLL);
+		/*wpidginspell_new_attach = wgtkspell_new_attach;*/
+	}
+	SetErrorMode(old_error_mode);
 }
 
-static char* lookup_aspell_path() {
+static void lookup_aspell_path() {
 	const char *tmp;
+	gchar *aspell_path;
 
 	if ((tmp = g_getenv("PIDGIN_ASPELL_DIR")))
-		return g_strdup(tmp);
-
-	return wpurple_read_reg_string(HKEY_LOCAL_MACHINE, "Software\\Aspell", "Path");
-}
-
-void winpidgin_spell_init() {
-	char *aspell_path = lookup_aspell_path();
+		aspell_path = g_strdup(tmp);
+	else
+		aspell_path = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, "Software\\Aspell", "Path");
 
 	if (aspell_path != NULL) {
-		char *tmp = g_strconcat(aspell_path, "\\aspell-15.dll", NULL);
+		char *tmp = g_build_filename(aspell_path, "aspell-15.dll", NULL);
 		if (g_file_test(tmp, G_FILE_TEST_EXISTS)) {
 			const char *path = g_getenv("PATH");
 			purple_debug_info("wspell", "Found Aspell in %s\n", aspell_path);
@@ -88,14 +118,18 @@
 
 			g_setenv("PATH", tmp, TRUE);
 
-			load_gtkspell();
-		} else {
+		} else
 			purple_debug_warning("wspell", "Couldn't find aspell-15.dll\n");
-		}
 
 		g_free(tmp);
 		g_free(aspell_path);
-	} else {
-		purple_debug_warning("wspell", "Couldn't find path for Aspell\n");
-	}
+	} else
+		purple_debug_warning("wspell", "Aspell installation not found (this isn't necessarily a problem)\n");
 }
+
+void winpidgin_spell_init() {
+	/* We keep doing the aspell path thing so that previously installed dictionaries still work */
+	lookup_aspell_path();
+
+	load_gtkspell();
+}
--- a/po/ChangeLog	Mon Mar 08 22:51:42 2010 +0000
+++ b/po/ChangeLog	Mon Mar 08 22:53:02 2010 +0000
@@ -1,5 +1,7 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
+version 2.7.0
+
 version 2.6.6
 	* Afrikaans translation updated (Friedel Wolff)
 	* Albanian translation updated (Besnik Bleta)
--- a/po/POTFILES.in	Mon Mar 08 22:51:42 2010 +0000
+++ b/po/POTFILES.in	Mon Mar 08 22:53:02 2010 +0000
@@ -50,8 +50,7 @@
 libpurple/ft.c
 libpurple/gconf/purple.schemas.in
 libpurple/log.c
-libpurple/media.c
-libpurple/mediamanager.c
+libpurple/media/backend-fs2.c
 libpurple/plugin.c
 libpurple/plugins/autoaccept.c
 libpurple/plugins/buddynote.c
@@ -146,6 +145,7 @@
 libpurple/protocols/novell/novell.c
 libpurple/protocols/oscar/clientlogin.c
 libpurple/protocols/oscar/family_chatnav.c
+libpurple/protocols/oscar/family_locate.c
 libpurple/protocols/oscar/flap_connection.c
 libpurple/protocols/oscar/libaim.c
 libpurple/protocols/oscar/libicq.c
@@ -214,14 +214,12 @@
 pidgin/gtkaccount.c
 pidgin/gtkblist-theme.c
 pidgin/gtkblist.c
-pidgin/gtkcellview.c
 pidgin/gtkcertmgr.c
 pidgin/gtkconn.c
 pidgin/gtkconv.c
 pidgin/gtkdebug.c
 pidgin/gtkdialogs.c
 pidgin/gtkdocklet.c
-pidgin/gtkexpander.c
 pidgin/gtkft.c
 pidgin/gtkimhtml.c
 pidgin/gtkimhtmltoolbar.c
@@ -242,7 +240,6 @@
 pidgin/gtkutils.c
 pidgin/gtkwhiteboard.c
 pidgin/pidgin.h
-pidgin/pidgincombobox.c
 pidgin/pidginstock.c
 pidgin/pidgintooltip.c
 pidgin/pixmaps/emotes/default/24/default.theme.in
--- a/po/ca.po	Mon Mar 08 22:51:42 2010 +0000
+++ b/po/ca.po	Mon Mar 08 22:53:02 2010 +0000
@@ -33,8 +33,8 @@
 msgstr ""
 "Project-Id-Version: Pidgin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-11-29 22:28+0100\n"
-"PO-Revision-Date: 2009-11-29 22:39+0100\n"
+"POT-Creation-Date: 2010-02-16 09:18+0100\n"
+"PO-Revision-Date: 2010-02-16 23:08+0100\n"
 "Last-Translator: Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>\n"
 "Language-Team: Catalan <tradgnome@softcatala.net>\n"
 "MIME-Version: 1.0\n"
@@ -1987,6 +1987,9 @@
 msgid "%s is not a regular file. Cowardly refusing to overwrite it.\n"
 msgstr "%s no és un fitxer normal, no se sobreescriurà.\n"
 
+msgid "File is not readable."
+msgstr "No ha estat possible llegir el fitxer."
+
 #, c-format
 msgid "%s wants to send you %s (%s)"
 msgstr "%s us vol enviar %s (%s)"
@@ -3845,6 +3848,13 @@
 msgid "Server requires plaintext authentication over an unencrypted stream"
 msgstr "El servidor requereix autenticació de text sobre un flux no xifrat"
 
+#. This should never happen!
+msgid "Invalid response from server"
+msgstr "La resposta del servidor no és vàlida"
+
+msgid "Server does not use any supported authentication method"
+msgstr "No hi ha cap mètode d'autenticació compatible amb aquest servidor"
+
 #, c-format
 msgid ""
 "%s requires plaintext authentication over an unencrypted connection.  Allow "
@@ -3856,25 +3866,35 @@
 msgid "Plaintext Authentication"
 msgstr "Autenticació de text"
 
-msgid "SASL authentication failed"
-msgstr "Ha fallat l'autenticació SASL"
-
-msgid "Invalid response from server"
-msgstr "La resposta del servidor no és vàlida"
-
-msgid "Server does not use any supported authentication method"
-msgstr "No hi ha cap mètode d'autenticació compatible amb aquest servidor"
-
 msgid "You require encryption, but it is not available on this server."
 msgstr "Requeriu xifratge, però no està disponible en aquest servidor."
 
 msgid "Invalid challenge from server"
 msgstr "Repte del servidor invàlid"
 
+msgid "Server thinks authentication is complete, but client does not"
+msgstr "El servidor creu que s'ha completat l'autenticació, però el client no"
+
+msgid "SASL authentication failed"
+msgstr "Ha fallat l'autenticació SASL"
+
 #, c-format
 msgid "SASL error: %s"
 msgstr "Error SASL: %s"
 
+# FIXME: canonicalize -> normalitzar (josep)
+msgid "Unable to canonicalize username"
+msgstr "No s'ha pogut normalitzar el nom d'usuari"
+
+msgid "Unable to canonicalize password"
+msgstr "No s'ha pogut normalitzar la contrasenya"
+
+msgid "Malicious challenge from server"
+msgstr "Desafiament malició del servidor"
+
+msgid "Unexpected response from server"
+msgstr "S'ha rebut una resposta inesperada del servidor"
+
 msgid "The BOSH connection manager terminated your session."
 msgstr "El gestor de connexions BOSH ha tancat la connexió."
 
@@ -3975,13 +3995,17 @@
 msgid "Resource"
 msgstr "Recurs"
 
+# FIXME
+msgid "Uptime"
+msgstr "Temps connectat"
+
+msgid "Logged Off"
+msgstr "Desconnectat"
+
 #, c-format
 msgid "%s ago"
 msgstr "fa %s"
 
-msgid "Logged Off"
-msgstr "Desconnectat"
-
 # Segons la viquipèdia
 msgid "Middle Name"
 msgstr "Nom del mig"
@@ -4172,13 +4196,6 @@
 msgid "Ping timed out"
 msgstr "S'ha esgitat el temps d'espera (ping)"
 
-msgid ""
-"Unable to find alternative XMPP connection methods after failing to connect "
-"directly."
-msgstr ""
-"No s'ha pogut trobar cap mètode alternatiu de connexió XMPP després de no "
-"haver pogut connectar directament."
-
 msgid "Invalid XMPP ID"
 msgstr "ID de l'XMPP invàlid"
 
@@ -4734,6 +4751,11 @@
 msgid "(Code %s)"
 msgstr "(Codi %s)"
 
+msgid "A custom smiley in the message is too large to send."
+msgstr ""
+"No es pot enviar una de les emoticones personalitzades del missatge atès que "
+"és massa llarga."
+
 msgid "XML Parse error"
 msgstr "Error en l'anàlisi de l'XML"
 
@@ -4776,13 +4798,13 @@
 msgstr "Us han fet fora (%s)"
 
 msgid "An error occurred on the in-band bytestream transfer\n"
-msgstr ""
+msgstr "S'ha produit un error en el fluxe de transferència de dades en banda\n"
 
 msgid "Transfer was closed."
 msgstr "La transferència s'ha tancat."
 
 msgid "Failed to open in-band bytestream"
-msgstr ""
+msgstr "No s'ha pogut obrir el fluxe de transferència de dades en banda"
 
 #, c-format
 msgid "Unable to send file to %s, user does not support file transfers"
@@ -5149,6 +5171,10 @@
 msgid "Your new MSN friendly name is too long."
 msgstr "El vostre nom amistós nou d'MSN és massa llarg."
 
+#, c-format
+msgid "Set friendly name for %s."
+msgstr "Establiu el nom amistós de %s."
+
 msgid "Set your friendly name."
 msgstr "Establiu el vostre nom amistós."
 
@@ -6771,7 +6797,10 @@
 msgid "Server port"
 msgstr "Port en el servidor"
 
-#. Note to translators: %s in this string is a URL
+#, c-format
+msgid "Received unexpected response from %s: %s"
+msgstr "S'ha rebut una resposta inesperada de %s: %s"
+
 #, c-format
 msgid "Received unexpected response from %s"
 msgstr "S'ha rebut una resposta inesperada de %s"
@@ -6790,6 +6819,13 @@
 msgid "Error requesting %s: %s"
 msgstr "S'ha produït un error en sol·licitar %s: %s"
 
+msgid ""
+"Server requested that you fill out a CAPTCHA in order to sign in, but this "
+"client does not currently support CAPTCHAs."
+msgstr ""
+"El servidor requereix que ompliu el CAPTCHA per poder entrar, però aquest "
+"client encara no permet l'ús de CAPTCHA."
+
 msgid "AOL does not allow your screen name to authenticate here"
 msgstr "AOL no permet que us autentiqueu amb aquest nom d'usuari aquí"
 
@@ -7352,13 +7388,6 @@
 "[No s'ha pogut mostrar el missatge d'aquest usuari perquè contenia caràcters "
 "invàlids.]"
 
-msgid ""
-"The last action you attempted could not be performed because you are over "
-"the rate limit. Please wait 10 seconds and try again.\n"
-msgstr ""
-"No s'ha pogut realitzar la darrera acció que havíeu intentat perquè esteu "
-"per sobre del límit. Espereu 10 segons i torneu-ho a provar.\n"
-
 #, c-format
 msgid "You have been disconnected from chat room %s."
 msgstr "Se us ha desconnectat de la conversa %s."
@@ -12040,15 +12069,15 @@
 msgid "Lao"
 msgstr "Lasià"
 
-msgid "Lithuanian"
-msgstr "Lituà"
-
 msgid "Macedonian"
 msgstr "Macedoni"
 
 msgid "Mongolian"
 msgstr "Mongol"
 
+msgid "Marathi"
+msgstr "Marathi"
+
 msgid "Malay"
 msgstr "Malai"
 
@@ -12067,6 +12096,9 @@
 msgid "Occitan"
 msgstr "Occità"
 
+msgid "Oriya"
+msgstr "Oriya"
+
 msgid "Punjabi"
 msgstr "Punjabi"
 
@@ -12148,6 +12180,9 @@
 msgid "Amharic"
 msgstr "Amhàric"
 
+msgid "Lithuanian"
+msgstr "Lituà"
+
 #, c-format
 msgid "About %s"
 msgstr "Quant al %s"
@@ -13816,6 +13851,10 @@
 msgid "_Save File"
 msgstr "_Desa el fitxer"
 
+# Nota: com que és una pissarra, fem servir esborrar.
+msgid "Do you really want to clear?"
+msgstr "Esteu segur que voleu esborrar-ho?"
+
 msgid "Select color"
 msgstr "Seleccioneu un color"
 
@@ -15036,6 +15075,20 @@
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr "Aquest connector és útil per a depurar servidors i clients XMPP."
 
+#~ msgid ""
+#~ "Unable to find alternative XMPP connection methods after failing to "
+#~ "connect directly."
+#~ msgstr ""
+#~ "No s'ha pogut trobar cap mètode alternatiu de connexió XMPP després de no "
+#~ "haver pogut connectar directament."
+
+#~ msgid ""
+#~ "The last action you attempted could not be performed because you are over "
+#~ "the rate limit. Please wait 10 seconds and try again.\n"
+#~ msgstr ""
+#~ "No s'ha pogut realitzar la darrera acció que havíeu intentat perquè esteu "
+#~ "per sobre del límit. Espereu 10 segons i torneu-ho a provar.\n"
+
 #~ msgid "(Default)"
 #~ msgstr "(Predeterminat)"
 
--- a/po/ca@valencia.po	Mon Mar 08 22:51:42 2010 +0000
+++ b/po/ca@valencia.po	Mon Mar 08 22:53:02 2010 +0000
@@ -3,7 +3,7 @@
 # Copyright (C) unknown, Robert Millan <zeratul2@wanadoo.es>
 # Copyright (C) December 2003 (from 2003-12-12 until 2003-12-18),
 #               January (2004-01-07,12), Xan <dxpublica@telefonica.net>
-# Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009
+# Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009, 2010
 #               Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>
 #
 # This file is distributed under the same license as the Pidgin package.
@@ -33,8 +33,8 @@
 msgstr ""
 "Project-Id-Version: Pidgin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-11-28 18:48+0100\n"
-"PO-Revision-Date: 2009-11-28 21:57+0100\n"
+"POT-Creation-Date: 2010-02-16 09:18+0100\n"
+"PO-Revision-Date: 2010-02-16 23:08+0100\n"
 "Last-Translator: Josep Puigdemont i Casamajó <josep.puigdemont@gmail.com>\n"
 "Language-Team: Catalan <tradgnome@softcatala.net>\n"
 "MIME-Version: 1.0\n"
@@ -66,7 +66,7 @@
 "Forma d'ús: %s [OPCIÓ]...\n"
 "\n"
 "  -c, --config=DIR    utilitza DIR per als fitxers de configuració\n"
-"  -d, --debug         mostra missatges de depuració a la eixida d'error\n"
+"  -d, --debug         mostra missatges de depuració a l'eixida d'error\n"
 "                      estàndard \n"
 "  -h, --help          mostra esta ajuda i ix\n"
 "  -n, --nologin       no entra automàticament\n"
@@ -1987,6 +1987,9 @@
 msgid "%s is not a regular file. Cowardly refusing to overwrite it.\n"
 msgstr "%s no és un fitxer normal, no se sobreescriurà.\n"
 
+msgid "File is not readable."
+msgstr "No ha estat possible llegir el fitxer."
+
 #, c-format
 msgid "%s wants to send you %s (%s)"
 msgstr "%s vos vol enviar %s (%s)"
@@ -2337,7 +2340,7 @@
 msgstr "Estableix el paràmetre d'acceptació automàtica"
 
 msgid "_Save"
-msgstr "De_sa"
+msgstr "Al_ça"
 
 msgid "_Cancel"
 msgstr "_Cancel·la"
@@ -2680,11 +2683,11 @@
 "WARNING: This plugin is still alpha code and may crash frequently.  Use it "
 "at your own risk!"
 msgstr ""
-"Quan es visualitzin els registres, este connector inclourà registres "
+"Quan es visualitzen els registres, este connector inclourà registres "
 "d'altres clients de MI. De moment, es poden incloure els d'Adium, MSN "
 "Messenger, aMSN, i Trillian.\n"
 "\n"
-"Avís: este connector encara està en desenvolupament i pot ser que es pengi."
+"Avís: este connector encara està en desenvolupament i pot ser que es penge."
 
 msgid "Mono Plugin Loader"
 msgstr "Carregador de connectors Mono"
@@ -3845,6 +3848,13 @@
 msgid "Server requires plaintext authentication over an unencrypted stream"
 msgstr "El servidor requereix autenticació de text sobre un flux no xifrat"
 
+#. This should never happen!
+msgid "Invalid response from server"
+msgstr "La resposta del servidor no és vàlida"
+
+msgid "Server does not use any supported authentication method"
+msgstr "No hi ha cap mètode d'autenticació compatible amb este servidor"
+
 #, c-format
 msgid ""
 "%s requires plaintext authentication over an unencrypted connection.  Allow "
@@ -3856,25 +3866,35 @@
 msgid "Plaintext Authentication"
 msgstr "Autenticació de text"
 
-msgid "SASL authentication failed"
-msgstr "Ha fallat l'autenticació SASL"
-
-msgid "Invalid response from server"
-msgstr "La resposta del servidor no és vàlida"
-
-msgid "Server does not use any supported authentication method"
-msgstr "No hi ha cap mètode d'autenticació compatible amb este servidor"
-
 msgid "You require encryption, but it is not available on this server."
 msgstr "Requeriu xifratge, però no està disponible en este servidor."
 
 msgid "Invalid challenge from server"
 msgstr "Repte del servidor invàlid"
 
+msgid "Server thinks authentication is complete, but client does not"
+msgstr "El servidor creu que s'ha completat l'autenticació, però el client no"
+
+msgid "SASL authentication failed"
+msgstr "Ha fallat l'autenticació SASL"
+
 #, c-format
 msgid "SASL error: %s"
 msgstr "Error SASL: %s"
 
+# FIXME: canonicalize -> normalitzar (josep)
+msgid "Unable to canonicalize username"
+msgstr "No s'ha pogut normalitzar el nom d'usuari"
+
+msgid "Unable to canonicalize password"
+msgstr "No s'ha pogut normalitzar la contrasenya"
+
+msgid "Malicious challenge from server"
+msgstr "Desafiament malició del servidor"
+
+msgid "Unexpected response from server"
+msgstr "S'ha rebut una resposta inesperada del servidor"
+
 msgid "The BOSH connection manager terminated your session."
 msgstr "El gestor de connexions BOSH ha tancat la connexió."
 
@@ -3975,13 +3995,17 @@
 msgid "Resource"
 msgstr "Recurs"
 
+# FIXME
+msgid "Uptime"
+msgstr "Temps connectat"
+
+msgid "Logged Off"
+msgstr "Desconnectat"
+
 #, c-format
 msgid "%s ago"
 msgstr "fa %s"
 
-msgid "Logged Off"
-msgstr "Desconnectat"
-
 # Segons la viquipèdia
 msgid "Middle Name"
 msgstr "Nom del mig"
@@ -4172,13 +4196,6 @@
 msgid "Ping timed out"
 msgstr "S'ha esgitat el temps d'espera (ping)"
 
-msgid ""
-"Unable to find alternative XMPP connection methods after failing to connect "
-"directly."
-msgstr ""
-"No s'ha pogut trobar cap mètode alternatiu de connexió XMPP després de no "
-"haver pogut connectar directament."
-
 msgid "Invalid XMPP ID"
 msgstr "ID de l'XMPP invàlid"
 
@@ -4734,6 +4751,11 @@
 msgid "(Code %s)"
 msgstr "(Codi %s)"
 
+msgid "A custom smiley in the message is too large to send."
+msgstr ""
+"No es pot enviar una de les emoticones personalitzades del missatge atès que "
+"és massa llarga."
+
 msgid "XML Parse error"
 msgstr "Error en l'anàlisi de l'XML"
 
@@ -4776,13 +4798,13 @@
 msgstr "Vos han fet fora (%s)"
 
 msgid "An error occurred on the in-band bytestream transfer\n"
-msgstr ""
+msgstr "S'ha produit un error en el fluxe de transferència de dades en banda\n"
 
 msgid "Transfer was closed."
 msgstr "La transferència s'ha tancat."
 
 msgid "Failed to open in-band bytestream"
-msgstr ""
+msgstr "No s'ha pogut obrir el fluxe de transferència de dades en banda"
 
 #, c-format
 msgid "Unable to send file to %s, user does not support file transfers"
@@ -5133,15 +5155,15 @@
 "no està implementat."
 
 msgid "Nudge"
-msgstr "Donar un colp de colze"
+msgstr "Donar un cop de colze"
 
 #, c-format
 msgid "%s has nudged you!"
-msgstr "%s vos ha donat un colp de colze!"
+msgstr "%s vos ha donat un cop de colze!"
 
 #, c-format
 msgid "Nudging %s..."
-msgstr "S'està donant un colp de colze a %s..."
+msgstr "S'està donant un cop de colze a %s..."
 
 msgid "Email Address..."
 msgstr "Correu electrònic..."
@@ -5149,6 +5171,10 @@
 msgid "Your new MSN friendly name is too long."
 msgstr "El vostre nom amistós nou d'MSN és massa llarg."
 
+#, c-format
+msgid "Set friendly name for %s."
+msgstr "Establiu el nom amistós de %s."
+
 msgid "Set your friendly name."
 msgstr "Establiu el vostre nom amistós."
 
@@ -5483,7 +5509,7 @@
 msgstr "Mostra emoticones personalitzades"
 
 msgid "nudge: nudge a user to get their attention"
-msgstr "nudge: doneu un colp de colze a un usuari perquè vos pare atenció"
+msgstr "nudge: doneu un cop de colze a un usuari perquè vos pare atenció"
 
 msgid "Windows Live ID authentication:Unable to connect"
 msgstr "Autenticació amb el Windows Live ID: no s'ha pogut connectar"
@@ -5493,7 +5519,7 @@
 
 #, c-format
 msgid "%s just sent you a Nudge!"
-msgstr "%s vos ha donat un colp de colze!"
+msgstr "%s vos ha donat un cop de colze!"
 
 msgid "The following users are missing from your addressbook"
 msgstr "Manquen estos usuaris a la vostra llista d'amics"
@@ -6346,7 +6372,7 @@
 msgstr "Este nom d'usuari està disponible. Voleu fer-lo servir?"
 
 msgid "ONCE SET, THIS CANNOT BE CHANGED!"
-msgstr "Una vegada l'hagueu establit no el podreu canviar!"
+msgstr "Un cop l'hagueu establit no el podreu canviar!"
 
 msgid "MySpaceIM - Please Set a Username"
 msgstr "MySpaceIM - Establiu un nom d'usuari"
@@ -6771,7 +6797,10 @@
 msgid "Server port"
 msgstr "Port en el servidor"
 
-#. Note to translators: %s in this string is a URL
+#, c-format
+msgid "Received unexpected response from %s: %s"
+msgstr "S'ha rebut una resposta inesperada de %s: %s"
+
 #, c-format
 msgid "Received unexpected response from %s"
 msgstr "S'ha rebut una resposta inesperada de %s"
@@ -6790,6 +6819,13 @@
 msgid "Error requesting %s: %s"
 msgstr "S'ha produït un error en sol·licitar %s: %s"
 
+msgid ""
+"Server requested that you fill out a CAPTCHA in order to sign in, but this "
+"client does not currently support CAPTCHAs."
+msgstr ""
+"El servidor requereix que ompliu el CAPTCHA per poder entrar, però este "
+"client encara no permet l'ús de CAPTCHA."
+
 msgid "AOL does not allow your screen name to authenticate here"
 msgstr "AOL no permet que vos autentiqueu amb este nom d'usuari ací"
 
@@ -7352,13 +7388,6 @@
 "[No s'ha pogut mostrar el missatge d'este usuari perquè contenia caràcters "
 "invàlids.]"
 
-msgid ""
-"The last action you attempted could not be performed because you are over "
-"the rate limit. Please wait 10 seconds and try again.\n"
-msgstr ""
-"No s'ha pogut realitzar la darrera acció que havíeu intentat perquè esteu "
-"per sobre del límit. Espereu 10 segons i torneu-ho a provar.\n"
-
 #, c-format
 msgid "You have been disconnected from chat room %s."
 msgstr "Se vos ha desconnectat de la conversa %s."
@@ -9495,10 +9524,10 @@
 msgstr "Serveis en línia"
 
 msgid "Let others see what services you are using"
-msgstr "Permet que els altres vegin quins serveis feu servir"
+msgstr "Permet que els altres vegen quins serveis feu servir"
 
 msgid "Let others see what computer you are using"
-msgstr "Permet que els altres vegin quin ordinador feu servir"
+msgstr "Permet que els altres vegen quin ordinador feu servir"
 
 msgid "Your VCard File"
 msgstr "El fitxer de la vostra VCard"
@@ -9514,7 +9543,7 @@
 "information. Please fill the information you would like other users to see "
 "about yourself."
 msgstr ""
-"Podeu permetre que altres usuaris vegin informació sobre el vostre estat en "
+"Podeu permetre que altres usuaris vegen informació sobre el vostre estat en "
 "línia, així com informació personal. Empleneu la informació que vulgueu que "
 "altres usuaris puguen veure."
 
@@ -10877,7 +10906,7 @@
 msgstr "_Bàsic"
 
 msgid "Create _this new account on the server"
-msgstr "Crea _aquest compte nou al servidor"
+msgstr "Crea _este compte nou al servidor"
 
 msgid "P_roxy"
 msgstr "Servidor _intermediari"
@@ -12040,15 +12069,15 @@
 msgid "Lao"
 msgstr "Lasià"
 
-msgid "Lithuanian"
-msgstr "Lituà"
-
 msgid "Macedonian"
 msgstr "Macedoni"
 
 msgid "Mongolian"
 msgstr "Mongol"
 
+msgid "Marathi"
+msgstr "Marathi"
+
 msgid "Malay"
 msgstr "Malai"
 
@@ -12067,6 +12096,9 @@
 msgid "Occitan"
 msgstr "Occità"
 
+msgid "Oriya"
+msgstr "Oriya"
+
 msgid "Punjabi"
 msgstr "Punjabi"
 
@@ -12148,6 +12180,9 @@
 msgid "Amharic"
 msgstr "Amhàric"
 
+msgid "Lithuanian"
+msgstr "Lituà"
+
 #, c-format
 msgid "About %s"
 msgstr "Quant al %s"
@@ -12780,7 +12815,7 @@
 msgstr "empra DIR per a fitxers de configuració"
 
 msgid "print debugging messages to stdout"
-msgstr "escriu missatges de depuració a la eixida estàndard"
+msgstr "escriu missatges de depuració a l'eixida estàndard"
 
 msgid "force online, regardless of network status"
 msgstr "força estar en línia, independentment de l'estat de la xarxa"
@@ -13816,6 +13851,10 @@
 msgid "_Save File"
 msgstr "Al_ça el fitxer"
 
+# Nota: com que és una pissarra, fem servir esborrar.
+msgid "Do you really want to clear?"
+msgstr "Esteu segur que voleu esborrar-ho?"
+
 msgid "Select color"
 msgstr "Seleccioneu un color"
 
@@ -15036,6 +15075,20 @@
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr "Este connector és útil per a depurar servidors i clients XMPP."
 
+#~ msgid ""
+#~ "Unable to find alternative XMPP connection methods after failing to "
+#~ "connect directly."
+#~ msgstr ""
+#~ "No s'ha pogut trobar cap mètode alternatiu de connexió XMPP després de no "
+#~ "haver pogut connectar directament."
+
+#~ msgid ""
+#~ "The last action you attempted could not be performed because you are over "
+#~ "the rate limit. Please wait 10 seconds and try again.\n"
+#~ msgstr ""
+#~ "No s'ha pogut realitzar la darrera acció que havíeu intentat perquè esteu "
+#~ "per sobre del límit. Espereu 10 segons i torneu-ho a provar.\n"
+
 #~ msgid "(Default)"
 #~ msgstr "(Predeterminat)"
 
--- a/po/fi.po	Mon Mar 08 22:51:42 2010 +0000
+++ b/po/fi.po	Mon Mar 08 22:53:02 2010 +0000
@@ -1,7 +1,7 @@
 # Piidgin Finnish translation
 # Copyright (C) 2002 Tero Kuusela <teroajk@subdimension.com>
 # Copyright (C) 2003-2005 Arto Alakulju <arto@alakulju.net>
-# Copyright (C) 2005-2009 Timo Jyrinki <timo.jyrinki@iki.fi>
+# Copyright (C) 2005-2010 Timo Jyrinki <timo.jyrinki@iki.fi>
 #
 # This file is distributed under the same license as the Pidgin package.
 #
@@ -10,8 +10,8 @@
 msgstr ""
 "Project-Id-Version: Pidgin\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-11-14 20:34-0500\n"
-"PO-Revision-Date: 2009-08-30 16:48+0300\n"
+"POT-Creation-Date: 2010-02-18 15:01+0200\n"
+"PO-Revision-Date: 2010-02-18 15:01+0200\n"
 "Last-Translator: Timo Jyrinki <timo.jyrinki@iki.fi>\n"
 "Language-Team: \n"
 "MIME-Version: 1.0\n"
@@ -630,9 +630,8 @@
 msgid "Enable Sounds"
 msgstr "Ota äänet käyttöön"
 
-#, fuzzy
 msgid "You are not connected."
-msgstr "Yhdistäminen ei onnistu"
+msgstr "Et ole yhdistettynä."
 
 msgid "<AUTO-REPLY> "
 msgstr "<AUTOMAATTIVASTAUS> "
@@ -1555,6 +1554,7 @@
 msgid "Online"
 msgstr "Linjoilla"
 
+#. primative,						no,							id,			name
 msgid "Offline"
 msgstr "Poissa linjoilta"
 
@@ -1942,6 +1942,9 @@
 msgid "%s is not a regular file. Cowardly refusing to overwrite it.\n"
 msgstr "%s ei ole tavallinen tiedosto. Ei suostuta ylikirjoittamaan sitä.\n"
 
+msgid "File is not readable."
+msgstr "Tiedosto ei ole luettavissa."
+
 #, c-format
 msgid "%s wants to send you %s (%s)"
 msgstr "%s on lähettämässä sinulle %s (%s)"
@@ -2213,25 +2216,19 @@
 msgid "A non-recoverable Farsight2 error has occurred."
 msgstr "Korjaamaton Farsight2-virhe."
 
-#, fuzzy
+msgid "Error with your microphone"
+msgstr "Virhe mikrofonin kanssa"
+
+msgid "Error with your webcam"
+msgstr "Virhe web-kameran kanssa"
+
 msgid "Conference error"
-msgstr "Virhe neuvottelussa."
-
-#, fuzzy
-msgid "Error with your microphone"
-msgstr "Virhe mikrofonin kanssa."
-
-#, fuzzy
-msgid "Error with your webcam"
-msgstr "Virhe web-kameran kanssa."
+msgstr "Virhe neuvottelussa"
 
 #, c-format
 msgid "Error creating session: %s"
 msgstr "Virhe luotaessa istuntoa: %s"
 
-msgid "Error creating conference."
-msgstr "Virhe luotaessa konferenssia."
-
 #, c-format
 msgid "You are using %s, but this plugin requires %s."
 msgstr "Käytät: %s, mutta tämä liitännäinen vaatii: %s."
@@ -3169,10 +3166,12 @@
 msgid "Add to chat..."
 msgstr "Lisää ryhmäkeskusteluun..."
 
+#. 0
 #. Global
 msgid "Available"
 msgstr "Tavoitettavissa"
 
+#. 1
 #. get_yahoo_status_from_purple_status() returns YAHOO_STATUS_CUSTOM for
 #. * the generic away state (YAHOO_STATUS_TYPE_AWAY) with no message
 #. Away stuff
@@ -3797,6 +3796,13 @@
 msgstr ""
 "Palvelin vaatii salaamattoman tunnistautumisen salaamattoman yhteyden yli"
 
+#. This should never happen!
+msgid "Invalid response from server"
+msgstr "Kelvoton vastaus palvelimelta"
+
+msgid "Server does not use any supported authentication method"
+msgstr "Palvelin ei käytä mitään tuetuista tunnistautumismenetelmistä"
+
 #, c-format
 msgid ""
 "%s requires plaintext authentication over an unencrypted connection.  Allow "
@@ -3808,25 +3814,38 @@
 msgid "Plaintext Authentication"
 msgstr "Tekstipohjainen tunnistus"
 
-msgid "SASL authentication failed"
-msgstr "SASL-todennus epäonnistui"
-
-msgid "Invalid response from server"
-msgstr "Kelvoton vastaus palvelimelta"
-
-msgid "Server does not use any supported authentication method"
-msgstr "Palvelin ei käytä mitään tuetuista tunnistautumismenetelmistä"
-
 msgid "You require encryption, but it is not available on this server."
 msgstr "Salausta vaadittu, mutta sitä ei tueta tällä palvelimella."
 
 msgid "Invalid challenge from server"
 msgstr "Virheellinen tunnistushaaste palvelimelta"
 
+msgid "Server thinks authentication is complete, but client does not"
+msgstr ""
+
+msgid "SASL authentication failed"
+msgstr "SASL-todennus epäonnistui"
+
 #, c-format
 msgid "SASL error: %s"
 msgstr "SASL-virhe: %s"
 
+#, fuzzy
+msgid "Unable to canonicalize username"
+msgstr "Asetusten tekeminen ei onnistu"
+
+#, fuzzy
+msgid "Unable to canonicalize password"
+msgstr "Kuuntelevaa porttia ei voi avata."
+
+#, fuzzy
+msgid "Malicious challenge from server"
+msgstr "Virheellinen tunnistushaaste palvelimelta"
+
+#, fuzzy
+msgid "Unexpected response from server"
+msgstr "Odottamaton HTTP-vastaus palvelimelta"
+
 msgid "The BOSH connection manager terminated your session."
 msgstr "BOSH-yhteyshallinta lopetti istunnon."
 
@@ -3927,13 +3946,16 @@
 msgid "Resource"
 msgstr "Sijainti"
 
+msgid "Uptime"
+msgstr "Ylhäällä"
+
+msgid "Logged Off"
+msgstr "Kirjautunut ulos"
+
 #, c-format
 msgid "%s ago"
 msgstr "%s sitten"
 
-msgid "Logged Off"
-msgstr "Kirjautunut ulos"
-
 msgid "Middle Name"
 msgstr "Muut etunimet"
 
@@ -3981,15 +4003,6 @@
 msgid "Log Out"
 msgstr "Kirjaudu ulos"
 
-msgid "Chatty"
-msgstr "Juttelutuulella"
-
-msgid "Extended Away"
-msgstr "Pidennetty poissaolo"
-
-msgid "Do Not Disturb"
-msgstr "Älä häiritse"
-
 msgid "JID"
 msgstr "JID"
 
@@ -4120,13 +4133,6 @@
 msgid "Ping timed out"
 msgstr "Pingin aikakatkaisu"
 
-msgid ""
-"Unable to find alternative XMPP connection methods after failing to connect "
-"directly."
-msgstr ""
-"Vaihtoehtoisia XMPP-yhteystapoja ei löytynyt suoran yhdistämisen "
-"epäonnistumisen jälkeen."
-
 msgid "Invalid XMPP ID"
 msgstr "Epäkelpo XMPP-ID"
 
@@ -4248,6 +4254,7 @@
 msgid "None (To pending)"
 msgstr "Ei mitään (odottaa lupaa)"
 
+#. 0
 msgid "None"
 msgstr "Ei mitään"
 
@@ -4261,6 +4268,14 @@
 msgid "Allow Buzz"
 msgstr "Salli äänimerkki"
 
+#. 2
+msgid "Chatty"
+msgstr "Juttelutuulella"
+
+#. 3
+msgid "Do Not Disturb"
+msgstr "Älä häiritse"
+
 msgid "Tune Artist"
 msgstr "Kappaleen esittäjä"
 
@@ -4506,10 +4521,6 @@
 "Äänimerkkiä ei voi lähettää, koska käyttäjä %s ei tue sitä tai ei halua "
 "vastaanottaa äänimerkkejä tällä hetkellä."
 
-#, c-format
-msgid "Buzzing %s..."
-msgstr "Töötätään tuttavalle %s..."
-
 #. Yahoo only supports one attention command: the 'buzz'.
 #. This is index number YAHOO_BUZZ.
 msgid "Buzz"
@@ -4520,6 +4531,10 @@
 msgstr "%s on töötännyt sinulle."
 
 #, c-format
+msgid "Buzzing %s..."
+msgstr "Töötätään tuttavalle %s..."
+
+#, c-format
 msgid "Unable to initiate media with %s: invalid JID"
 msgstr "Mediaa ei voi alustaa käyttäjän %s kanssa: virheellinen JID."
 
@@ -4554,9 +4569,8 @@
 msgid "configure:  Configure a chat room."
 msgstr "configure: Konfiguroi ryhmäkeskusteluhuone."
 
-#, fuzzy
 msgid "part [message]:  Leave the room."
-msgstr "part [huone]: Poistu huoneesta."
+msgstr "part [viesti]: Poistu huoneesta."
 
 msgid "register:  Register with a chat room."
 msgstr "register: Rekisteröidy ryhmäkeskusteluhuoneeseen."
@@ -4675,6 +4689,10 @@
 msgid "(Code %s)"
 msgstr "(Koodi %s)"
 
+#, fuzzy
+msgid "A custom smiley in the message is too large to send."
+msgstr "Viestiä ei voi lähettää: viesti on liian suuri."
+
 msgid "XML Parse error"
 msgstr "Virhe XML-jäsennyksessä"
 
@@ -5086,6 +5104,10 @@
 msgid "Your new MSN friendly name is too long."
 msgstr "Uusi MSN-tuttavanimesi on liian pitkä."
 
+#, c-format
+msgid "Set friendly name for %s."
+msgstr "Aseta tuttavanimi tilille %s."
+
 msgid "Set your friendly name."
 msgstr "Aseta tuttavanimesi."
 
@@ -5428,10 +5450,6 @@
 msgid "Windows Live ID authentication:Invalid response"
 msgstr "Windows Live ID -tunnistautuminen:Virheellinen vastaus"
 
-#, c-format
-msgid "%s just sent you a Nudge!"
-msgstr "Käyttäjä %s lähetti sinulle juuri tönäisyn!"
-
 msgid "The following users are missing from your addressbook"
 msgstr "Seuraavat käyttäjät puuttuvat osoitekirjastasi"
 
@@ -5447,9 +5465,8 @@
 msgid "Unknown error (%d)"
 msgstr "Tuntematon virhe (%d)"
 
-#, fuzzy
 msgid "Unable to remove user"
-msgstr "Käyttäjää ei voi lisätä"
+msgstr "Käyttäjää ei voi poistaa"
 
 msgid "Mobile message was not sent because it was too long."
 msgstr "Mobiiliviestiä ei lähetetty, koska se on liian pitkä."
@@ -5685,28 +5702,86 @@
 msgid "%s has removed you from his or her buddy list."
 msgstr "%s on poistanut sinut tuttavistaan."
 
+#. 1
+msgid "Angry"
+msgstr "Vihainen"
+
+#. 2
+msgid "Excited"
+msgstr "Innostunut"
+
+#. 3
+msgid "Grumpy"
+msgstr "Äreä"
+
+#. 4
+msgid "Happy"
+msgstr "Onnellinen"
+
+#. 5
+msgid "In Love"
+msgstr "Rakastunut"
+
+#. 6
+msgid "Invincible"
+msgstr "Haavoittumaton"
+
+#. 7
+msgid "Sad"
+msgstr "Surullinen"
+
+#. 8
+msgid "Hot"
+msgstr "Tulinen"
+
+#. 9
+msgid "Sick"
+msgstr "Kipeä"
+
+#. 10
+msgid "Sleepy"
+msgstr "Unelias"
+
 #. show current mood
-#, fuzzy
 msgid "Current Mood"
-msgstr "Tämänhetkinen mielialasi"
+msgstr "Tämänhetkinen mieliala"
 
 #. add all moods to list
-#, fuzzy
 msgid "New Mood"
-msgstr "Käyttäjän mieliala"
-
-#, fuzzy
+msgstr "Uusi mieliala"
+
 msgid "Change your Mood"
-msgstr "Vaihda salasana"
-
-#, fuzzy
+msgstr "Vaihda mielialaa"
+
 msgid "How do you feel right now?"
-msgstr "En ole täällä juuri nyt"
+msgstr "Kuinka voit nyt?"
+
+#, fuzzy
+msgid "The PIN you entered is invalid."
+msgstr "Syötetty SecurID-avain on virheellinen"
+
+#, fuzzy
+msgid "The PIN you entered has an invalid length [4-10]."
+msgstr "Syötetty SecurID-avain on virheellinen"
+
+msgid "The PIN is invalid. It should only consist of digits [0-9]."
+msgstr ""
+
+#, fuzzy
+msgid "The two PINs you entered do not match."
+msgstr "Uudet salasanat eivät täsmää."
+
+#, fuzzy
+msgid "The name you entered is invalid."
+msgstr "Syötetty SecurID-avain on virheellinen"
+
+msgid ""
+"The birthday you entered is invalid. The correct format is: 'YYYY-MM-DD'."
+msgstr ""
 
 #. show error to user
-#, fuzzy
 msgid "Profile Update Error"
-msgstr "Virhe kirjoituksessa"
+msgstr "Virhe päivitettäessä profiilia"
 
 #. no profile information yet, so we cannot update
 #. (reference: "libpurple/request.h")
@@ -5717,30 +5792,26 @@
 msgstr ""
 
 #. pin
-#, fuzzy
 msgid "PIN"
-msgstr "UIN"
+msgstr "PIN"
 
 msgid "Verify PIN"
-msgstr ""
+msgstr "Varmista PIN"
 
 #. display name
-#, fuzzy
 msgid "Display Name"
-msgstr "Näyttö"
+msgstr "Näyttönimi"
 
 #. hidden
 msgid "Hide my number"
-msgstr ""
+msgstr "Piilota numeroni"
 
 #. mobile number
-#, fuzzy
 msgid "Mobile Number"
 msgstr "Matkapuhelinnumero"
 
-#, fuzzy
 msgid "Update your Profile"
-msgstr "Käyttäjän profiili"
+msgstr "Päivitä profiilia"
 
 msgid "Here you can update your MXit profile"
 msgstr ""
@@ -5757,14 +5828,12 @@
 msgstr "Omat tiedot"
 
 #. display / change mood
-#, fuzzy
 msgid "Change Mood..."
-msgstr "Vaihda salasana..."
+msgstr "Vaihda mielialaa..."
 
 #. display / change profile
-#, fuzzy
 msgid "Change Profile..."
-msgstr "Vaihda salasana..."
+msgstr "Vaihda profiilia..."
 
 #. display splash-screen
 #, fuzzy
@@ -5772,49 +5841,53 @@
 msgstr "Näytä loki..."
 
 #. display plugin version
-#, fuzzy
 msgid "About..."
-msgstr "Omat tiedot"
+msgstr "Tietoja..."
 
 #. the file is too big
 #, fuzzy
 msgid "The file you are trying to send is too large!"
 msgstr "Viesti on liian suuri."
 
-msgid ""
-"Unable to connect to the mxit HTTP server. Please check your server server "
-"settings."
-msgstr ""
-
-#, fuzzy
-msgid "Logging In..."
-msgstr "Kirjaudutaan sisään"
-
-#, fuzzy
-msgid ""
-"Unable to connect to the mxit server. Please check your server server "
-"settings."
+#, fuzzy
+msgid ""
+"Unable to connect to the MXit HTTP server. Please check your server settings."
 msgstr ""
 "Palvelimeen ei voi yhdistää. Ole hyvä, syötä palvelimen osoite jolle haluat "
 "yhdistää."
 
-#, fuzzy
+msgid "Logging In..."
+msgstr "Kirjaudutaan sisään..."
+
+#, fuzzy
+msgid ""
+"Unable to connect to the MXit server. Please check your server settings."
+msgstr ""
+"Palvelimeen ei voi yhdistää. Ole hyvä, syötä palvelimen osoite jolle haluat "
+"yhdistää."
+
 msgid "Connecting..."
-msgstr "Yhdistetään"
+msgstr "Yhdistetään..."
+
+#, fuzzy
+msgid "The nick name you entered is invalid."
+msgstr "Syötetty SecurID-avain on virheellinen"
+
+#, fuzzy
+msgid "The PIN you entered has an invalid length [7-10]."
+msgstr "Syötetty SecurID-avain on virheellinen"
 
 #. mxit login name
 msgid "MXit Login Name"
 msgstr ""
 
 #. nick name
-#, fuzzy
 msgid "Nick Name"
 msgstr "Kutsumanimi"
 
 #. show the form to the user to complete
-#, fuzzy
 msgid "Register New MXit Account"
-msgstr "Rekisteröi uusi XMPP-tili"
+msgstr "Rekisteröi uusi MXit-tili"
 
 #, fuzzy
 msgid "Please fill in the following fields:"
@@ -5892,6 +5965,10 @@
 msgstr "Palvelimen tiedot"
 
 #, fuzzy
+msgid "Loading menu..."
+msgstr "Kirjaudutaan sisään"
+
+#, fuzzy
 msgid "Status Message"
 msgstr "Tilaviesti"
 
@@ -5937,6 +6014,11 @@
 msgid "Successfully Logged In..."
 msgstr "Liityttiin onnistuneesti Quniin"
 
+#, fuzzy, c-format
+msgid ""
+"%s sent you an encrypted message, but it is not supported on this client."
+msgstr "%s on lähettänyt puhekeskustelukutsun, mitä ei vielä tueta."
+
 #, fuzzy
 msgid "Message Error"
 msgstr "XMPP-viestivirhe"
@@ -5945,6 +6027,18 @@
 msgstr ""
 
 #, fuzzy
+msgid "An internal MXit server error occurred."
+msgstr "Tuntematon varmennevirhe."
+
+#, fuzzy, c-format
+msgid "Login error: %s (%i)"
+msgstr "SASL-virhe: %s"
+
+#, fuzzy, c-format
+msgid "Logout error: %s (%i)"
+msgstr "SASL-virhe: %s"
+
+#, fuzzy
 msgid "Contact Error"
 msgstr "Yhteysvirhe"
 
@@ -6019,39 +6113,6 @@
 msgid "A connection error occurred to MXit. (read stage 0x06)"
 msgstr ""
 
-msgid "Angry"
-msgstr "Vihainen"
-
-msgid "Excited"
-msgstr "Innostunut"
-
-#, fuzzy
-msgid "Grumpy"
-msgstr "Ryhmä"
-
-msgid "Happy"
-msgstr "Onnellinen"
-
-msgid "In Love"
-msgstr "Rakastunut"
-
-msgid "Invincible"
-msgstr "Haavoittumaton"
-
-msgid "Sad"
-msgstr "Surullinen"
-
-#, fuzzy
-msgid "Hot"
-msgstr "P_alvelin:"
-
-#, fuzzy
-msgid "Sick"
-msgstr "Kutsumanimi"
-
-msgid "Sleepy"
-msgstr "Unelias"
-
 #, fuzzy
 msgid "Pending"
 msgstr "Lähetetään"
@@ -6064,16 +6125,14 @@
 msgid "Rejected"
 msgstr "Kieltäydy"
 
-#, fuzzy
 msgid "Deleted"
-msgstr "Poista"
+msgstr "Poistettu"
 
 msgid "MXit Advertising"
-msgstr ""
-
-#, fuzzy
+msgstr "MXit-mainostus"
+
 msgid "More Information"
-msgstr "Työtiedot"
+msgstr "Lisätiedot"
 
 #, c-format
 msgid "No such user: %s"
@@ -6680,7 +6739,10 @@
 msgid "Server port"
 msgstr "Palvelimen portti"
 
-#. Note to translators: %s in this string is a URL
+#, fuzzy, c-format
+msgid "Received unexpected response from %s: %s"
+msgstr "Odottamaton vastaus osoitteesta %s"
+
 #, c-format
 msgid "Received unexpected response from %s"
 msgstr "Odottamaton vastaus osoitteesta %s"
@@ -6699,6 +6761,11 @@
 msgid "Error requesting %s: %s"
 msgstr "Virhe pyydettäessä %s: %s"
 
+msgid ""
+"Server requested that you fill out a CAPTCHA in order to sign in, but this "
+"client does not currently support CAPTCHAs."
+msgstr ""
+
 msgid "AOL does not allow your screen name to authenticate here"
 msgstr "AOL ei salli näyttönimen todentamista tätä kautta"
 
@@ -6853,9 +6920,8 @@
 msgid "Cannot send SMS without accepting terms"
 msgstr ""
 
-#, fuzzy
 msgid "Cannot send SMS"
-msgstr "Tiedoston lähetys ei onnistu"
+msgstr "Tekstiviestin (SMS) lähetys ei onnistu"
 
 #. SMS_WITHOUT_DISCLAIMER is weird
 #, fuzzy
@@ -7216,21 +7282,21 @@
 msgstr[0] "Et saanut %hu viestiä %s:lta tuntemattomasta syystä."
 msgstr[1] "Et saanut %hu viestiä %s:lta tuntemattomasta syystä."
 
-#, fuzzy, c-format
+#, c-format
 msgid "Unable to send message: %s (%s)"
-msgstr "Viestiä ei voi lähettää (%s)."
+msgstr "Viestiä ei voi lähettää: %s (%s)"
 
 #, c-format
 msgid "Unable to send message: %s"
 msgstr "Viestiä ei voi lähettää: %s"
 
-#, fuzzy, c-format
+#, c-format
 msgid "Unable to send message to %s: %s (%s)"
-msgstr "Viestiä ei voi lähettää käyttäjälle %s."
-
-#, fuzzy, c-format
+msgstr "Viestiä ei voi lähettää käyttäjälle %s: %s (%s)."
+
+#, c-format
 msgid "Unable to send message to %s: %s"
-msgstr "Viestiä ei voi lähettää käyttäjälle %s."
+msgstr "Viestiä ei voi lähettää käyttäjälle %s: %s"
 
 #, c-format
 msgid "User information not available: %s"
@@ -7259,14 +7325,6 @@
 "[Viestiä tältä käyttäjältä ei voi näyttää koska se sisälsi epäkelpoja "
 "merkkejä.]"
 
-#, fuzzy
-msgid ""
-"The last action you attempted could not be performed because you are over "
-"the rate limit. Please wait 10 seconds and try again.\n"
-msgstr ""
-"Viimeistä viestiä ei lähetetty koska olet ylittänyt taajuusrajan. Odota 10 "
-"sekuntia ja yritä uudelleen."
-
 #, c-format
 msgid "You have been disconnected from chat room %s."
 msgstr "Yhteytesi keskusteluhuoneeseen %s on katkennut."
@@ -11121,9 +11179,18 @@
 msgid "/Help/Online _Help"
 msgstr "/Ohje/O_hjeita verkossa"
 
+msgid "/Help/_Build Information"
+msgstr "/Ohje/Ohjelman _rakennustiedot"
+
 msgid "/Help/_Debug Window"
 msgstr "/Ohje/_Virheenjäljitysikkuna"
 
+msgid "/Help/De_veloper Information"
+msgstr "/Ohje/_Kehittäjätiedot"
+
+msgid "/Help/_Translator Information"
+msgstr "/Help/Kääntäjä_tiedot"
+
 msgid "/Help/_About"
 msgstr "/Ohje/Tietoj_a"
 
@@ -11400,12 +11467,6 @@
 msgid "Save Conversation"
 msgstr "Tallenna keskustelu"
 
-msgid "Find"
-msgstr "Etsi"
-
-msgid "_Search for:"
-msgstr "_Haettava termi:"
-
 msgid "Un-Ignore"
 msgstr "Huomioi"
 
@@ -11479,6 +11540,9 @@
 msgid "/Conversation/Se_nd File..."
 msgstr "/Keskustelu/_Lähetä tiedosto..."
 
+msgid "/Conversation/Get _Attention"
+msgstr "/Keskustelu/Hae huomiot_a"
+
 msgid "/Conversation/Add Buddy _Pounce..."
 msgstr "/Keskustelu/Lisää tuttava_ilmoitin..."
 
@@ -11560,6 +11624,9 @@
 msgid "/Conversation/Send File..."
 msgstr "/Keskustelu/Lähetä tiedosto..."
 
+msgid "/Conversation/Get Attention"
+msgstr "/Keskustelu/Hae huomiota"
+
 msgid "/Conversation/Add Buddy Pounce..."
 msgstr "/Keskustelu/Lisää tuttavailmoitin..."
 
@@ -11624,6 +11691,12 @@
 msgid "0 people in room"
 msgstr "0 ihmistä huoneessa"
 
+msgid "Close Find bar"
+msgstr "Sulje hakupalkki"
+
+msgid "Find:"
+msgstr "Etsi:"
+
 #, c-format
 msgid "%d person in room"
 msgid_plural "%d people in room"
@@ -11685,6 +11758,12 @@
 msgid "By account"
 msgstr "Tilin mukaan"
 
+msgid "Find"
+msgstr "Etsi"
+
+msgid "_Search for:"
+msgstr "_Haettava termi:"
+
 msgid "Save Debug Log"
 msgstr "Tallenna virheenjäljitysloki"
 
@@ -11903,15 +11982,20 @@
 msgid "Lao"
 msgstr "lao"
 
-msgid "Lithuanian"
-msgstr "liettua"
-
 msgid "Macedonian"
 msgstr "makedonia"
 
 msgid "Mongolian"
 msgstr "mongolia"
 
+#, fuzzy
+msgid "Marathi"
+msgstr "gudžarati"
+
+#, fuzzy
+msgid "Malay"
+msgstr "Mies"
+
 msgid "Bokmål Norwegian"
 msgstr "kirjanorja"
 
@@ -11927,6 +12011,10 @@
 msgid "Occitan"
 msgstr "oksitaani"
 
+#, fuzzy
+msgid "Oriya"
+msgstr "Opera"
+
 msgid "Punjabi"
 msgstr "punjabi"
 
@@ -11981,6 +12069,10 @@
 msgid "Turkish"
 msgstr "turkki"
 
+#, fuzzy
+msgid "Ukranian"
+msgstr "ukraina"
+
 msgid "Urdu"
 msgstr "urdu"
 
@@ -12002,69 +12094,64 @@
 msgid "Amharic"
 msgstr "amhara"
 
+msgid "Lithuanian"
+msgstr "liettua"
+
+#, c-format
+msgid ""
+"%s is a messaging client based on libpurple which is capable of connecting "
+"to multiple messaging services at once.  %s is written in C using GTK+.  %s "
+"is released, and may be modified and redistributed,  under the terms of the "
+"GPL version 2 (or later).  A copy of the GPL is distributed with %s.  %s is "
+"copyrighted by its contributors, a list of whom is also distributed with %"
+"s.  There is no warranty for %s.<BR><BR>"
+msgstr ""
+"%s on graafinen libpurple-kirjastoon perustuva pikaviestinsovellus, joka "
+"kykenee samanaikaisesti yhdistämään useisiin eri viestinpalveluihin. %s on "
+"ohjelmoitu C-ohjelmointikielellä, käyttäen Gtk+-kirjastoa. Voit muokata ja "
+"jakaa %s-ohjelmistoa GPL-lisenssin (versio 2 tai myöhempi) ehdoilla. Kopio "
+"GPL:stä on sisällytetty %s-ohjelmistojakeluun. %sin tekijänoikeudet ovat sen "
+"tekemiseen osallistuneilla, joista täydellinen luettelo on niinikään "
+"sisällytetty %s-jakelupakettiin. %sille ei anneta minkäänlaista takuuta."
+"<BR><BR>"
+
+#, c-format
+msgid ""
+"<FONT SIZE=\"4\"><B>Helpful Resources</B></FONT><BR>\t<A HREF=\"%s"
+"\">Website</A><BR>\t<A HREF=\"%s\">Frequently Asked Questions</A><BR>\tIRC "
+"Channel: #pidgin on irc.freenode.net<BR>\tXMPP MUC: devel@conference.pidgin."
+"im<BR><BR>"
+msgstr ""
+
+#, c-format
+msgid ""
+"<font size=\"4\"><b>Help from other Pidgin users</b></font> is available by "
+"e-mailing <a href=\"mailto:support@pidgin.im\">support@pidgin.im</a><br/"
+">This is a <b>public</b> mailing list! (<a href=\"http://pidgin.im/pipermail/"
+"support/\">archive</a>)<br/>We can't help with third-party protocols or "
+"plugins!<br/>This list's primary language is <b>English</b>.  You are "
+"welcome to post in another language, but the responses may be less helpful."
+"<br/>"
+msgstr ""
+"<font size=\"4\"><b>Apua muilta Pidgin-käyttäjiltä:</b></font> <a href="
+"\"mailto:support@pidgin.im\">support@pidgin.im</a><br/>Tämä on <b>julkinen</"
+"b> sähköpostilista! (<a href=\"http://pidgin.im/pipermail/support/"
+"\">arkisto</a>)<br/>Emme voi auttaa kolmannen osapuolen yhteyskäytännöissä "
+"tai liitännäisissä.<br/>Tämän listan pääkieli on <b>englanti</b>. Voit "
+"käyttää myös muuta kieltä, mutta auttavan vastauksen saaminen voi tällöin "
+"olla hankalampaa.<br/>"
+
 #, c-format
 msgid "About %s"
 msgstr "Tietoja %sistä"
 
-#, c-format
-msgid ""
-"%s is a graphical modular messaging client based on libpurple which is "
-"capable of connecting to AIM, MSN, Yahoo!, XMPP, ICQ, IRC, SILC, SIP/SIMPLE, "
-"Novell GroupWise, Lotus Sametime, Bonjour, Zephyr, MySpaceIM, Gadu-Gadu, and "
-"QQ all at once.  It is written using GTK+.<BR><BR>You may modify and "
-"redistribute the program under the terms of the GPL (version 2 or later).  A "
-"copy of the GPL is contained in the 'COPYING' file distributed with %s.  %s "
-"is copyrighted by its contributors.  See the 'COPYRIGHT' file for the "
-"complete list of contributors.  We provide no warranty for this program."
-"<BR><BR>"
-msgstr ""
-"%s on graafinen ja modulaarinen libpurple-kirjastoon perustuva "
-"pikaviestinsovellus, joka kykenee käyttämään AIM-, MSN-, Yahoo!-, XMPP-, "
-"ICQ-, IRC-, SILC-, SIP/SIMPLE-, Novell GroupWise-, Lotus Sametime-, "
-"Bonjour-, Zephyr-, MySpaceIM-, Gadu-Gadu- ja QQ-yhteyskäytäntöjä "
-"samanaikaisesti. Se on ohjelmoitu käyttäen Gtk+-kirjastoa.<BR><BR>Voit "
-"muokata ja jakaa ohjelmaa GPL-lisenssin (versio 2 tai myöhempi) ehdoilla. "
-"Kopio GPL:stä on sisällytetty \"COPYING\"-tiedostoon, joka tulee %sin "
-"mukana. %sin tekijänoikeudet on sen tekemiseen osallistuneilla. Täydellinen "
-"luettelo osallistuneista löytyy \"COPYRIGHT\"-tiedostosta. Tekijät eivät "
-"anna ohjelmalle minkäänlaista takuuta.<BR><BR>"
-
-#, c-format
-msgid ""
-"<FONT SIZE=\"4\">FAQ:</FONT> <A HREF=\"http://developer.pidgin.im/wiki/FAQ"
-"\">http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>"
-msgstr ""
-"<FONT SIZE=\"4\">UKK:</FONT> <A HREF=\"http://developer.pidgin.im/wiki/FAQ"
-"\">http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>"
-
-#, c-format
-msgid ""
-"<font size=\"4\">Help from other Pidgin users:</font> <a href=\"mailto:"
-"support@pidgin.im\">support@pidgin.im</a><br/>This is a <b>public</b> "
-"mailing list! (<a href=\"http://pidgin.im/pipermail/support/\">archive</a>)"
-"<br/>We can't help with 3rd party protocols or plugins!<br/>This list's "
-"primary language is <b>English</b>.  You are welcome to post in another "
-"language, but the responses may be less helpful.<br/><br/>"
-msgstr ""
-"<font size=\"4\">Apua muilta Pidgin-käyttäjiltä:</font> <a href=\"mailto:"
-"support@pidgin.im\">support@pidgin.im</a><br/>Tämä on <b>julkinen</b> "
-"sähköpostilista! (<a href=\"http://pidgin.im/pipermail/support/\">archive</"
-"a>)<br/>Emme voi auttaa kolmannen osapuolen yhteyskäytännöissä tai "
-"liitännäisissä.<br/>Tämän listan pääkieli on <b>englanti</b>. Voit käyttää "
-"myös muuta kieltä, mutta auttavan vastauksen saaminen voi tällöin olla "
-"hankalampaa.<br/><br/>"
-
-#, c-format
-msgid ""
-"<FONT SIZE=\"4\">IRC Channel:</FONT> #pidgin on irc.freenode.net<BR><BR>"
-msgstr ""
-"<FONT SIZE=\"4\">IRC-kanava:</FONT> #pidgin palvelimella irc.freenode."
-"net<BR><BR>"
-
-#, c-format
-msgid "<FONT SIZE=\"4\">XMPP MUC:</FONT> devel@conference.pidgin.im<BR><BR>"
-msgstr ""
-"<FONT SIZE=\"4\">XMPP-keskustelu:</FONT> devel@conference.pidgin.im<BR><BR>"
+msgid "Build Information"
+msgstr "Rakennustiedot"
+
+#. End of not to be translated section
+#, c-format
+msgid "%s Build Information"
+msgstr "%s – rakennustiedot"
 
 msgid "Current Developers"
 msgstr "Nykyiset kehittäjät"
@@ -12078,14 +12165,19 @@
 msgid "Retired Crazy Patch Writers"
 msgstr "Lopettaneet korjauspäivitysten kirjoittajat"
 
+#, c-format
+msgid "%s Developer Information"
+msgstr "%s – kehittäjätiedot"
+
 msgid "Current Translators"
 msgstr "Nykyiset kielenkääntäjät"
 
 msgid "Past Translators"
 msgstr "Aikaisemmat kielenkääntäjät"
 
-msgid "Debugging Information"
-msgstr "Virheenjäljitystietoja"
+#, c-format
+msgid "%s Translator Information"
+msgstr "%s – kääntäjätiedot"
 
 msgid "_Name"
 msgstr "_Nimi"
@@ -12371,15 +12463,6 @@
 "\n"
 "Oletetaan PNG-kuvaksi."
 
-msgid ""
-"Unrecognized file type\n"
-"\n"
-"Defaulting to PNG."
-msgstr ""
-"Tunnistamaton tiedostotyyppi\n"
-"\n"
-"Oletetaan PNG-kuvaksi."
-
 #, c-format
 msgid ""
 "<span size='larger' weight='bold'>Error saving image</span>\n"
@@ -12390,16 +12473,6 @@
 "\n"
 "%s"
 
-#, c-format
-msgid ""
-"Error saving image\n"
-"\n"
-"%s"
-msgstr ""
-"Virhe tallennettaessa kuvaa\n"
-"\n"
-"%s"
-
 msgid "Save Image"
 msgstr "Tallenna kuva"
 
@@ -12506,6 +12579,9 @@
 msgid "Insert Smiley"
 msgstr "Lisää hymiö"
 
+msgid "Send Attention"
+msgstr "Lähetä huomio"
+
 msgid "<b>_Bold</b>"
 msgstr "<b>_Lihavoi</b>"
 
@@ -12551,6 +12627,9 @@
 msgid "_Smile!"
 msgstr "_Hymyile!"
 
+msgid "_Attention!"
+msgstr "_Huomio!"
+
 msgid "Log Deletion Failed"
 msgstr "Lokin poistaminen epäonnistui"
 
@@ -12609,12 +12688,11 @@
 "Käyttö: %s [VALITSIN]...\n"
 "\n"
 
-#, fuzzy
 msgid "DIR"
-msgstr "IRC"
+msgstr "HAK"
 
 msgid "use DIR for config files"
-msgstr "käytä hakemistoa DIR asetustiedostoille"
+msgstr "käytä hakemistoa HAK asetustiedostoille"
 
 msgid "print debugging messages to stdout"
 msgstr "tulosta virheenjäljitysviestit standardiulostuloon"
@@ -12904,21 +12982,19 @@
 msgstr "Tuntematon... Raportoi tästä!"
 
 msgid "(Custom)"
-msgstr ""
-
-#, fuzzy
-msgid "(Default)"
-msgstr "(oletus)"
+msgstr "(oma)"
+
+msgid "Penguin Pimps"
+msgstr "Penguin Pimps"
 
 msgid "The default Pidgin sound theme"
-msgstr ""
-
-#, fuzzy
+msgstr "Pidginin oletusääniteema"
+
 msgid "The default Pidgin buddy list theme"
-msgstr "Pidginin tuttavaluettelon teemaeditori"
+msgstr "Pidginin oletustuttavaluetteloteema"
 
 msgid "The default Pidgin status icon theme"
-msgstr ""
+msgstr "Pidginin oletustilakuvaketeema"
 
 msgid "Theme failed to unpack."
 msgstr "Teeman purkaminen epäonnistui."
@@ -12929,18 +13005,30 @@
 msgid "Theme failed to copy."
 msgstr "Teeman kopioiminen epäonnistui."
 
-msgid "Install Theme"
-msgstr "Asenna teema"
-
-msgid ""
-"Select a smiley theme that you would like to use from the list below. New "
-"themes can be installed by dragging and dropping them onto the theme list."
-msgstr ""
-"Valitse haluamasi hymiöteema alla olevasta listasta. Uudet teemat voi "
-"asentaa vedä&pudota-menetelmällä pudottamalla ne teemalistaan."
-
-msgid "Icon"
-msgstr "Kuvake"
+msgid "Theme Selections"
+msgstr "Teemavalinnat"
+
+#. Instructions
+msgid ""
+"Select a theme that you would like to use from the lists below.\n"
+"New themes can be installed by dragging and dropping them onto the theme "
+"list."
+msgstr ""
+"Valitse haluamasi teema alla olevasta luettelosta.\n"
+"Uusia teemoja voi asentaa vedä&pudota-menetelmällä pudottamalla ne "
+"teemaluetteloon."
+
+msgid "Buddy List Theme:"
+msgstr "Tuttavaluettelon teema:"
+
+msgid "Status Icon Theme:"
+msgstr "Tilakuvaketeema:"
+
+msgid "Sound Theme:"
+msgstr "Ääniteema:"
+
+msgid "Smiley Theme:"
+msgstr "Hymiöteema:"
 
 msgid "Keyboard Shortcuts"
 msgstr "Pikanäppäimet"
@@ -12948,10 +13036,6 @@
 msgid "Cl_ose conversations with the Escape key"
 msgstr "S_ulje keskustelut Escape-näppäimellä"
 
-#. Buddy List Themes
-msgid "Buddy List Theme"
-msgstr "Tuttavaluettelon teema"
-
 #. System Tray
 msgid "System Tray Icon"
 msgstr "Ilmoitusalueen kuvake"
@@ -13038,9 +13122,6 @@
 msgid "Font"
 msgstr "Kirjasin"
 
-msgid "Use document font from _theme"
-msgstr "Käytä asiakirjojen kirjasinta _teemasta"
-
 msgid "Use font from _theme"
 msgstr "Käytä kirjasinta _teemasta"
 
@@ -13063,15 +13144,13 @@
 msgid "Cannot start browser configuration program."
 msgstr "Selaimen asetusohjelmaa ei voi käynnistää."
 
-#, fuzzy
 msgid "Disabled"
-msgstr "_Poista käytöstä"
+msgstr "Poissa käytöstä"
 
 #, c-format
 msgid "Use _automatically detected IP address: %s"
 msgstr "Käytä _automaattisesti tunnistettua IP-osoitetta: %s"
 
-#, fuzzy
 msgid "ST_UN server:"
 msgstr "ST_UN-palvelin:"
 
@@ -13087,78 +13166,27 @@
 msgid "_Enable automatic router port forwarding"
 msgstr "_Ota käyttöön automaattinen reitittimen porttien uudelleenohjaus"
 
-#, fuzzy
 msgid "_Manually specify range of ports to listen on:"
-msgstr "_Aseta kuunneltava porttialue"
-
-#, fuzzy
+msgstr "Aseta kuunneltava porttialue _käsin:"
+
 msgid "_Start:"
-msgstr "_Käynnistä "
-
-#, fuzzy
+msgstr "_Alku:"
+
 msgid "_End:"
-msgstr "_Laajenna"
+msgstr "_Loppu:"
 
 #. TURN server
 msgid "Relay Server (TURN)"
 msgstr "Edelleenvälityspalvelin (TURN)"
 
-#, fuzzy
 msgid "_TURN server:"
-msgstr "ST_UN-palvelin:"
-
-#, fuzzy
+msgstr "_TURN-palvelin:"
+
 msgid "Use_rname:"
-msgstr "Käyttäjänimi:"
-
-#, fuzzy
+msgstr "Käyttäjäni_mi:"
+
 msgid "Pass_word:"
-msgstr "Salasana:"
-
-msgid "Proxy Server &amp; Browser"
-msgstr "Välipalvelin &amp; selain"
-
-msgid "<b>Proxy configuration program was not found.</b>"
-msgstr "<b>Välipalvelimen asetusohjelmaa ei löydy.</b>"
-
-msgid "<b>Browser configuration program was not found.</b>"
-msgstr "<b>Selaimen asetusohjelmaa ei löydy.</b>"
-
-msgid ""
-"Proxy & Browser preferences are configured\n"
-"in GNOME Preferences"
-msgstr ""
-"Välipalvelimen & selaimen asetukset määritetään \n"
-"Gnomen asetuksissa"
-
-msgid "Configure _Proxy"
-msgstr "Aseta _välipalvelin"
-
-msgid "Configure _Browser"
-msgstr "Aseta _selain"
-
-msgid "Proxy Server"
-msgstr "Välipalvelin"
-
-#. This is a global option that affects SOCKS4 usage even with account-specific proxy settings
-#, fuzzy
-msgid "Use remote _DNS with SOCKS4 proxies"
-msgstr "Käytä etä-DNS:ää SOCKS4-välipalvelimien kanssa"
-
-#, fuzzy
-msgid "Proxy t_ype:"
-msgstr "Välipalvelimen _tyyppi:"
-
-msgid "No proxy"
-msgstr "Ei välipalvelinta"
-
-#, fuzzy
-msgid "P_ort:"
-msgstr "_Portti:"
-
-#, fuzzy
-msgid "User_name:"
-msgstr "Käyttäjänimi:"
+msgstr "Sala_sana:"
 
 msgid "Seamonkey"
 msgstr "Seamonkey"
@@ -13199,6 +13227,18 @@
 msgid "Browser Selection"
 msgstr "Selaimen valinta"
 
+#, fuzzy
+msgid "Browser preferences are configured in GNOME preferences"
+msgstr ""
+"Välipalvelimen & selaimen asetukset määritetään \n"
+"Gnomen asetuksissa"
+
+msgid "<b>Browser configuration program was not found.</b>"
+msgstr "<b>Selaimen asetusohjelmaa ei löydy.</b>"
+
+msgid "Configure _Browser"
+msgstr "Aseta _selain"
+
 msgid "_Browser:"
 msgstr "_Selain:"
 
@@ -13222,6 +13262,40 @@
 "_Komento:\n"
 "(URL:ksi %s)"
 
+msgid "Proxy Server"
+msgstr "Välipalvelin"
+
+#, fuzzy
+msgid "Proxy preferences are configured in GNOME preferences"
+msgstr ""
+"Välipalvelimen & selaimen asetukset määritetään \n"
+"Gnomen asetuksissa"
+
+msgid "<b>Proxy configuration program was not found.</b>"
+msgstr "<b>Välipalvelimen asetusohjelmaa ei löydy.</b>"
+
+msgid "Configure _Proxy"
+msgstr "Aseta _välipalvelin"
+
+#. This is a global option that affects SOCKS4 usage even with
+#. * account-specific proxy settings
+#, fuzzy
+msgid "Use remote _DNS with SOCKS4 proxies"
+msgstr "Käytä etä-DNS:ää SOCKS4-välipalvelimien kanssa"
+
+#, fuzzy
+msgid "Proxy t_ype:"
+msgstr "Välipalvelimen _tyyppi:"
+
+msgid "No proxy"
+msgstr "Ei välipalvelinta"
+
+msgid "P_ort:"
+msgstr "P_ortti:"
+
+msgid "User_name:"
+msgstr "Käyttäjä_nimi:"
+
 msgid "Log _format:"
 msgstr "Lokin _muoto:"
 
@@ -13305,25 +13379,18 @@
 msgid "Based on keyboard or mouse use"
 msgstr "Perustuen näppäimistön tai hiiren käyttöön"
 
+msgid "_Minutes before becoming idle:"
+msgstr "_Minuutteja ennen jouten olevaksi asettamista:"
+
+msgid "Change to this status when _idle:"
+msgstr "Tila johon vaihdetaan _jouten ollessa:"
+
 msgid "_Auto-reply:"
 msgstr "_Automaattivastaus:"
 
 msgid "When both away and idle"
 msgstr "Poissa ja jouten ollessa"
 
-#. Auto-away stuff
-msgid "Auto-away"
-msgstr "Automaattinen poissaoloasetus"
-
-msgid "_Minutes before becoming idle:"
-msgstr "_Minuutteja ennen jouten olevaksi asettamista:"
-
-msgid "Change status when _idle"
-msgstr "Vaihda tila, kun ollaan _jouten"
-
-msgid "Change _status to:"
-msgstr "Vaihda tila seuraavaksi:"
-
 #. Signon status stuff
 msgid "Status at Startup"
 msgstr "Tila käynnistettäessä"
@@ -13337,15 +13404,15 @@
 msgid "Interface"
 msgstr "Käyttöliittymä"
 
-msgid "Smiley Themes"
-msgstr "Hymiöteemat"
-
 msgid "Browser"
 msgstr "Selain"
 
 msgid "Status / Idle"
 msgstr "Tila / jouten"
 
+msgid "Themes"
+msgstr "Teemat"
+
 msgid "Allow all users to contact me"
 msgstr "Salli kaikkien käyttäjien ottaa minuun yhteyttä"
 
@@ -13501,6 +13568,9 @@
 msgid "Custom Smiley Manager"
 msgstr "Omien hymiöiden hallinta"
 
+msgid "Attention received"
+msgstr "Huomio vastanotettu"
+
 msgid "Select Buddy Icon"
 msgstr "Valitse tuttavakuvake"
 
@@ -13580,10 +13650,13 @@
 "Voit sisällyttää kuvan tähän viestiin tai käyttää sitä tuttavakuvakkeena "
 "tälle käyttäjälle"
 
-#. I don't know if we really want to do anything here.  Most of the desktop item types are crap like
-#. * "MIME Type" (I have no clue how that would be a desktop item) and "Comment"... nothing we can really
-#. * send.  The only logical one is "Application," but do we really want to send a binary and nothing else?
-#. * Probably not.  I'll just give an error and return.
+#. I don't know if we really want to do anything here.  Most of
+#. * the desktop item types are crap like "MIME Type" (I have no
+#. * clue how that would be a desktop item) and "Comment"...
+#. * nothing we can really send.  The only logical one is
+#. * "Application," but do we really want to send a binary and
+#. * nothing else? Probably not.  I'll just give an error and
+#. * return.
 #. The original patch sent the icon used by the launcher.  That's probably wrong
 msgid "Cannot send launcher"
 msgstr "Käynnistintä ei voi lähettää"
@@ -13615,17 +13688,6 @@
 msgid "Could not set icon"
 msgstr "Kuvaketta ei voi asettaa"
 
-#, c-format
-msgid "Failed to open file '%s': %s"
-msgstr "Tiedostoa \"%s\" ei voi avata: %s"
-
-#, c-format
-msgid ""
-"Failed to load image '%s': reason not known, probably a corrupt image file"
-msgstr ""
-"Kuvaa \"%s\" ei voi ladata: syy ei ole tiedossa, mahdollisesti vioittunut "
-"kuvatiedosto"
-
 msgid "_Open Link"
 msgstr "_Avaa linkki"
 
@@ -13650,6 +13712,10 @@
 msgid "_Save File"
 msgstr "_Tallenna tiedosto"
 
+#, fuzzy
+msgid "Do you really want to clear?"
+msgstr "Haluatko varmasti poistaa %s:n?"
+
 msgid "Select color"
 msgstr "Valitse väri"
 
@@ -13688,9 +13754,6 @@
 msgid "Pidgin smileys"
 msgstr "Pidgin-hymiöt"
 
-msgid "Penguin Pimps"
-msgstr "Penguin Pimps"
-
 msgid "Selecting this disables graphical emoticons."
 msgstr "Tämän valitseminen ottaa graafiset hymiöt pois käytöstä"
 
@@ -14324,6 +14387,9 @@
 msgid "Conversation Entry"
 msgstr "Keskustelumerkintä"
 
+msgid "Conversation History"
+msgstr "Keskusteluhistoria"
+
 msgid "Request Dialog"
 msgstr "Pyyntövalintaikkuna"
 
@@ -14505,6 +14571,9 @@
 msgid "Replaces text in outgoing messages according to user-defined rules."
 msgstr "Korvaa lähetettävän tekstin käyttäjän määritelmän mukaan."
 
+msgid "Extended Away"
+msgstr "Pidennetty poissaolo"
+
 msgid "Just logged in"
 msgstr "Juuri kirjautunut sisään"
 
@@ -14840,15 +14909,6 @@
 msgid "<font color='#777777'>Not connected to XMPP</font>"
 msgstr "<font color='#777777'>Ei yhdistetty XMPP:hen</font>"
 
-msgid "Insert an <iq/> stanza."
-msgstr "Lisää <iq/>-lohko."
-
-msgid "Insert a <presence/> stanza."
-msgstr "Lisää <presence/>-lohko."
-
-msgid "Insert a <message/> stanza."
-msgstr "Lisää <message/>-lohko."
-
 #. *< name
 #. *< version
 #. *  summary
@@ -14861,6 +14921,106 @@
 "Tätä liitännäistä voidaan käyttää XMPP-palvelimien tai -asiakasohjelmien "
 "virheenjäljitykseen."
 
+#~ msgid "Error creating conference."
+#~ msgstr "Virhe luotaessa konferenssia."
+
+#~ msgid ""
+#~ "Unable to find alternative XMPP connection methods after failing to "
+#~ "connect directly."
+#~ msgstr ""
+#~ "Vaihtoehtoisia XMPP-yhteystapoja ei löytynyt suoran yhdistämisen "
+#~ "epäonnistumisen jälkeen."
+
+#~ msgid "%s just sent you a Nudge!"
+#~ msgstr "Käyttäjä %s lähetti sinulle juuri tönäisyn!"
+
+#, fuzzy
+#~ msgid ""
+#~ "The last action you attempted could not be performed because you are over "
+#~ "the rate limit. Please wait 10 seconds and try again.\n"
+#~ msgstr ""
+#~ "Viimeistä viestiä ei lähetetty koska olet ylittänyt taajuusrajan. Odota "
+#~ "10 sekuntia ja yritä uudelleen."
+
+#~ msgid ""
+#~ "<FONT SIZE=\"4\">FAQ:</FONT> <A HREF=\"http://developer.pidgin.im/wiki/FAQ"
+#~ "\">http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>"
+#~ msgstr ""
+#~ "<FONT SIZE=\"4\">UKK:</FONT> <A HREF=\"http://developer.pidgin.im/wiki/FAQ"
+#~ "\">http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>"
+
+#~ msgid ""
+#~ "<FONT SIZE=\"4\">IRC Channel:</FONT> #pidgin on irc.freenode.net<BR><BR>"
+#~ msgstr ""
+#~ "<FONT SIZE=\"4\">IRC-kanava:</FONT> #pidgin palvelimella irc.freenode."
+#~ "net<BR><BR>"
+
+#~ msgid "<FONT SIZE=\"4\">XMPP MUC:</FONT> devel@conference.pidgin.im<BR><BR>"
+#~ msgstr ""
+#~ "<FONT SIZE=\"4\">XMPP-keskustelu:</FONT> devel@conference.pidgin."
+#~ "im<BR><BR>"
+
+#~ msgid "Debugging Information"
+#~ msgstr "Virheenjäljitystietoja"
+
+#~ msgid ""
+#~ "Unrecognized file type\n"
+#~ "\n"
+#~ "Defaulting to PNG."
+#~ msgstr ""
+#~ "Tunnistamaton tiedostotyyppi\n"
+#~ "\n"
+#~ "Oletetaan PNG-kuvaksi."
+
+#~ msgid ""
+#~ "Error saving image\n"
+#~ "\n"
+#~ "%s"
+#~ msgstr ""
+#~ "Virhe tallennettaessa kuvaa\n"
+#~ "\n"
+#~ "%s"
+
+#, fuzzy
+#~ msgid "(Default)"
+#~ msgstr "(oletus)"
+
+#~ msgid "Install Theme"
+#~ msgstr "Asenna teema"
+
+#~ msgid "Icon"
+#~ msgstr "Kuvake"
+
+#~ msgid "Use document font from _theme"
+#~ msgstr "Käytä asiakirjojen kirjasinta _teemasta"
+
+#~ msgid "Proxy Server &amp; Browser"
+#~ msgstr "Välipalvelin &amp; selain"
+
+#~ msgid "Auto-away"
+#~ msgstr "Automaattinen poissaoloasetus"
+
+#~ msgid "Change _status to:"
+#~ msgstr "Vaihda tila seuraavaksi:"
+
+#~ msgid "Failed to open file '%s': %s"
+#~ msgstr "Tiedostoa \"%s\" ei voi avata: %s"
+
+#~ msgid ""
+#~ "Failed to load image '%s': reason not known, probably a corrupt image file"
+#~ msgstr ""
+#~ "Kuvaa \"%s\" ei voi ladata: syy ei ole tiedossa, mahdollisesti vioittunut "
+#~ "kuvatiedosto"
+
+#~ msgid "Insert an <iq/> stanza."
+#~ msgstr "Lisää <iq/>-lohko."
+
+#~ msgid "Insert a <presence/> stanza."
+#~ msgstr "Lisää <presence/>-lohko."
+
+#~ msgid "Insert a <message/> stanza."
+#~ msgstr "Lisää <message/>-lohko."
+
 #, fuzzy
 #~ msgid "The root certificate this one claims to be issued by is unknown."
 #~ msgstr "Tämän varmenteen myöntäjän juurivarmenne on tuntematon Pidginille."
@@ -16013,9 +16173,6 @@
 #~ msgid "_Send To"
 #~ msgstr "_Lähetä käyttäjälle"
 
-#~ msgid "Conversation History"
-#~ msgstr "Keskusteluhistoria"
-
 #~ msgid "Log Viewer"
 #~ msgstr "Lokikatselin"
 
@@ -16040,9 +16197,6 @@
 #~ msgid "Attention! %s %s."
 #~ msgstr "Huomio. %s %s."
 
-#~ msgid "Attention!"
-#~ msgstr "Huomio"
-
 #~ msgid "Attention! You have been %s."
 #~ msgstr "Huomio, sinut on: %s."
 
@@ -17593,9 +17747,6 @@
 #~ msgid "Burmese"
 #~ msgstr "burma"
 
-#~ msgid "Ukrainian"
-#~ msgstr "ukraina"
-
 #~ msgid "Xhosa"
 #~ msgstr "xhosa"