changeset 26198:cb3c6ec9291e

merge of '6034e4df300b7da5e8fc3095e128f6215af903ab' and '6f6a810b0411a1e2b7a1bba79dae2b02ea200d19'
author Mike Ruprecht <maiku@soc.pidgin.im>
date Sat, 21 Feb 2009 05:15:28 +0000
parents 1830d339f9b9 (current diff) 9b0761b77218 (diff)
children 6cf36f68033c
files pidgin/gtkmedia.c
diffstat 77 files changed, 1021 insertions(+), 548 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sat Feb 21 05:15:14 2009 +0000
+++ b/COPYRIGHT	Sat Feb 21 05:15:28 2009 +0000
@@ -14,6 +14,7 @@
 Patrick Aussems
 Anibal Avelar
 Ali Albazaz
+Kosta Arvanitis
 Christopher Ayoup
 Alex Badea
 John Bailey
@@ -66,6 +67,7 @@
 Ludovico Cavedon
 Steve Cavilia
 Julien Cegarra
+Matěj Cepl
 Cerulean Studios, LLC
 Jonathan Champ
 Christophe Chapuis
@@ -204,6 +206,7 @@
 Intel Corporation
 Scott Jackson
 Hans Petter Jansson
+David Jedelsky
 Henry Jen
 Benjamin Kahn
 Anders Kaseorg
@@ -212,6 +215,7 @@
 John Kelm
 Jochen Kemnade
 Yann Kerherve
+Gordian Klein
 Akuke Kok
 Kir Kolyshkin
 Konstantin Korikov
@@ -418,6 +422,7 @@
 Andreas Stührk
 Oleg Sukhodolsky
 Sun Microsystems
+Marcus Sundberg
 Mårten Svantesson (fursten)
 Amir Szekely (kichik)
 Robert T.
--- a/ChangeLog	Sat Feb 21 05:15:14 2009 +0000
+++ b/ChangeLog	Sat Feb 21 05:15:28 2009 +0000
@@ -7,6 +7,9 @@
 	  enable, check the "Use SSL" option from the Advanced tab when
 	  editing your AIM or ICQ account. (Paul Aurich)
 	* Fix a memory leak in SILC. (Luke Petre)
+	* Fix some string handling in the SIMPLE prpl, which fixes some buddy name
+	  handling and other issues. (Paul Aurich, Marcus Sundberg)
+	* Implement support for resolving DNS via the SOCKS4 proxy (SOCKS4a).
 
 	ICQ:
 	* Fix retrieval of status messages from users of ICQ 6.x, Miranda, and
@@ -15,6 +18,7 @@
 	  of buddy icons and available messages.
 	* Properly publish status messages for statuses other than Available.
 	  ICQ 6.x users can now see these status messages. (Daniel Ljungborg)
+	* Fix recipt of messages from the mobile client Slick. (David Jedelsky)
 
 	MSN:
 	* Fix transfer of buddy icons, custom smileys, and files from the
@@ -23,8 +27,45 @@
 	* Large (multi-part) messages are now correctly re-combined.
 	* Federated/Yahoo! buddies should now stop creating sync issues at
 	  every signin.  You may need to remove duplicates in the Address
-	  Book.  See the FAQ for more information.
+	  Book.  See the FAQ for more information.  Thanks to Jason Lingohr
+	  for lots of debugging and testing.
 	* Messages from Yahoo! buddies are no longer silently dropped.
+	* We now save and use the CacheKey for ABCH SOAP requests.
+	* Don't try to parse Personal Status Messages or Current Media if they
+	  don't exist.
+	* Convert from ISO-8859-1 encoding to UTF-8 when no charset is specified
+	  on incoming messages.  This should fix some issues with messages from
+	  older clients.
+	* Force sending the font "Segoe UI" if outgoing formatting doesn't specify
+	  a font already.
+	* Queue callbacks when token updates are in progress to prevent two token
+	  update attempts from trampling each other.
+	* Fixed a crash on Windows when removing a buddy's alias.
+	* Update the Address Book when buddies' friendly names change.  This
+	  prevents seeing an outdated alias or not seeing an alias at all for
+	  buddies who are offline when you sign in.
+	* Update tokens for FindMembership and ABFindAll SOAP requests.
+	* We no longer try to send empty messages.  This could happen when a
+	  message contained only formatting and that formatting was not supported
+	  on MSN.
+	* Buddies on both the Allow and Block list are now automatically
+	  removed from the Allow list.  Users with this problem will now no
+	  longer receive an ADL 241 error.  The problematic buddy should now
+	  appear on the buddy list and can be removed or unblocked as desired.
+
+	XMPP:
+	* Resources using __HOSTNAME__ substitution will now grab only the short
+	  hostname instead of the FQDN on systems which put the FQDN in the
+	  hostname. (Matěj Cepl)
+	* No longer send a 'to' attribute on an outgoing stanza when we haven't
+	  received one.  This fixes a registration bug as described in ticket
+	  #6635.
+
+	Pidgin:
+	* Tooltip windows now appear below the mouse cursor. (Kosta Arvanitis)
+	* Tooltip windows now disappear on keypress events. (Kosta Arvanitis)
+	* Tooltip windows no longer linger when scrolling the buddy list. (Kosta
+	  Arvanitis)
 
 	Finch:
 	* Allow rebinding keys to change the focused widget (details in the
--- a/Makefile.am	Sat Feb 21 05:15:14 2009 +0000
+++ b/Makefile.am	Sat Feb 21 05:15:28 2009 +0000
@@ -99,7 +99,7 @@
 	@echo "Not generating devhelp index: xsltproc was not found by configure"
 endif
 else
-	@echo "doxygen was not found during configure.  Aborting."
+	@echo "doxygen was not found during configure.  Unable to build documentation."
 	@echo;
 endif
 
--- a/finch/finch.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/finch/finch.c	Sat Feb 21 05:15:28 2009 +0000
@@ -210,7 +210,7 @@
 		text = g_strdup_printf(_("%s\n"
 		       "Usage: %s [OPTION]...\n\n"
 		       "  -c, --config=DIR    use DIR for config files\n"
-		       "  -d, --debug         print debugging messages to stdout\n"
+		       "  -d, --debug         print debugging messages to stderr\n"
 		       "  -h, --help          display this help and exit\n"
 		       "  -n, --nologin       don't automatically login\n"
 		       "  -v, --version       display the current version and exit\n"), DISPLAY_VERSION, name);
--- a/finch/libgnt/gntkeys.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/finch/libgnt/gntkeys.c	Sat Feb 21 05:15:28 2009 +0000
@@ -163,7 +163,8 @@
 			(*(text + 2) >= 'A' && *(text + 2) <= 'D')) {
 		/* Apparently this is necessary for urxvt and screen and xterm */
 		if (strstr(term, "screen") == term || strcmp(term, "rxvt-unicode") == 0 ||
-				strstr(term, "xterm") == term)
+				strstr(term, "xterm") == term ||
+				strstr(term, "vt100") == term)
 			*(text + 1) = 'O';
 	} else if (*(unsigned char*)text == 195) {
 		if (*(text + 2) == 0 && strstr(term, "xterm") == term) {
--- a/finch/libgnt/gntwm.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/finch/libgnt/gntwm.c	Sat Feb 21 05:15:28 2009 +0000
@@ -353,7 +353,8 @@
 				p->y = y;
 				g_hash_table_replace(wm->positions, g_strdup(title + 1), p);
 			} else {
-				gnt_warning("Invalid number of arguments (%d) for positioning a window.", l);
+				gnt_warning("Invalid number of arguments (%" G_GSIZE_FORMAT
+						") for positioning a window.", l);
 			}
 			g_strfreev(coords);
 		}
--- a/finch/plugins/gntclipboard.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/finch/plugins/gntclipboard.c	Sat Feb 21 05:15:28 2009 +0000
@@ -22,7 +22,7 @@
 #include "internal.h"
 #include <glib.h>
 
-#define PLUGIN_STATIC_NAME	"GntClipboard"
+#define PLUGIN_STATIC_NAME	GntClipboard
 
 #ifdef HAVE_X11
 #include <X11/Xlib.h>
--- a/finch/plugins/gntgf.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/finch/plugins/gntgf.c	Sat Feb 21 05:15:28 2009 +0000
@@ -21,7 +21,7 @@
 
 #include "internal.h"
 
-#define PLUGIN_STATIC_NAME	"GntGf"
+#define PLUGIN_STATIC_NAME	GntGf
 
 #define PREFS_PREFIX          "/plugins/gnt/gntgf"
 #define PREFS_EVENT           PREFS_PREFIX "/events"
--- a/finch/plugins/grouping.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/finch/plugins/grouping.c	Sat Feb 21 05:15:28 2009 +0000
@@ -383,6 +383,6 @@
 {
 }
 
-PURPLE_INIT_PLUGIN(ignore, init_plugin, info)
+PURPLE_INIT_PLUGIN(grouping, init_plugin, info)
 
 
--- a/finch/plugins/lastlog.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/finch/plugins/lastlog.c	Sat Feb 21 05:15:28 2009 +0000
@@ -19,7 +19,7 @@
  */
 
 
-#define PLUGIN_STATIC_NAME	"GntLastlog"
+#define PLUGIN_STATIC_NAME	GntLastlog
 
 #include "internal.h"
 
--- a/libpurple/account.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/account.h	Sat Feb 21 05:15:28 2009 +0000
@@ -132,6 +132,14 @@
 								/*   to NULL when the account inherits      */
 								/*   proxy settings from global prefs.      */
 
+	/*
+	 * TODO: Supplementing the next two linked lists with hash tables
+	 * should help performance a lot when these lists are long.  This
+	 * matters quite a bit for protocols like MSN, where all your
+	 * buddies are added to your permit list.  Currently we have to
+	 * iterate through the entire list if we want to check if someone
+	 * is permitted or denied.  We should do this for 3.0.0.
+	 */
 	GSList *permit;             /**< Permit list.                           */
 	GSList *deny;               /**< Deny list.                             */
 	int perm_deny;              /**< The permit/deny setting.               */
--- a/libpurple/media.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/media.c	Sat Feb 21 05:15:28 2009 +0000
@@ -52,6 +52,7 @@
 	gchar *id;
 	PurpleMedia *media;
 	GstElement *src;
+	GstElement *tee;
 	FsSession *session;
 
 	PurpleMediaSessionType type;
@@ -69,20 +70,23 @@
 	gchar *participant;
 	FsStream *stream;
 	GstElement *sink;
+	GstElement *src;
+	GstElement *tee;
 
 	GList *local_candidates;
 	GList *remote_candidates;
 
 	gboolean candidates_prepared;
 
-	FsCandidate *local_candidate;
-	FsCandidate *remote_candidate;
+	GList *active_local_candidates;
+	GList *active_remote_candidates;
 
 	gulong window_id;
 };
 
 struct _PurpleMediaPrivate
 {
+	PurpleMediaManager *manager;
 	FsConference *conference;
 	gboolean initiator;
 
@@ -91,7 +95,6 @@
 
 	GList *streams;		/* PurpleMediaStream table */
 
-	GstElement *pipeline;
 	GstElement *confbin;
 };
 
@@ -111,6 +114,8 @@
 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 GObjectClass *parent_class = NULL;
 
@@ -129,6 +134,7 @@
 
 enum {
 	PROP_0,
+	PROP_MANAGER,
 	PROP_CONFERENCE,
 	PROP_INITIATOR,
 };
@@ -183,6 +189,13 @@
 	gobject_class->set_property = purple_media_set_property;
 	gobject_class->get_property = purple_media_get_property;
 
+	g_object_class_install_property(gobject_class, PROP_MANAGER,
+			g_param_spec_object("manager",
+			"Purple Media Manager",
+			"The media manager that contains this media session.",
+			PURPLE_TYPE_MEDIA_MANAGER,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
 	g_object_class_install_property(gobject_class, PROP_CONFERENCE,
 			g_param_spec_object("conference",
 			"Farsight conference",
@@ -250,10 +263,10 @@
 	if (stream->remote_candidates)
 		fs_candidate_list_destroy(stream->remote_candidates);
 
-	if (stream->local_candidate)
-		fs_candidate_destroy(stream->local_candidate);
-	if (stream->remote_candidate)
-		fs_candidate_destroy(stream->remote_candidate);
+	if (stream->active_local_candidates)
+		fs_candidate_list_destroy(stream->active_local_candidates);
+	if (stream->active_remote_candidates)
+		fs_candidate_list_destroy(stream->active_remote_candidates);
 
 	g_free(stream);
 }
@@ -276,13 +289,13 @@
 
 	purple_debug_info("media","purple_media_dispose\n");
 
-	purple_media_manager_remove_media(purple_media_manager_get(),
-			PURPLE_MEDIA(media));
+	purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media));
 
 	if (priv->confbin) {
 		gst_element_set_state(GST_ELEMENT(priv->confbin),
 				GST_STATE_NULL);
-		gst_bin_remove(GST_BIN(priv->pipeline), priv->confbin);
+		gst_bin_remove(GST_BIN(purple_media_manager_get_pipeline(
+				priv->manager)), priv->confbin);
 		priv->confbin = NULL;
 		priv->conference = NULL;
 	}
@@ -312,6 +325,19 @@
 			g_object_unref(participants->data);
 	}
 
+	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;
+	}
+
 	G_OBJECT_CLASS(parent_class)->finalize(media);
 }
 
@@ -336,6 +362,36 @@
 }
 
 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;
@@ -344,26 +400,19 @@
 	media = PURPLE_MEDIA(object);
 
 	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);
+			break;
 		case PROP_CONFERENCE: {
-			gchar *name;
-
 			if (media->priv->conference)
 				gst_object_unref(media->priv->conference);
 			media->priv->conference = g_value_get_object(value);
 			gst_object_ref(media->priv->conference);
 
-			name = g_strdup_printf("conf_%p",
-					media->priv->conference);
-			media->priv->confbin = gst_bin_new(name);
-			g_free(name);
-			gst_bin_add(GST_BIN(purple_media_get_pipeline(media)),
-					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);
+			purple_media_setup_pipeline(media);
 			break;
 		}
 		case PROP_INITIATOR:
@@ -384,6 +433,9 @@
 	media = PURPLE_MEDIA(object);
 
 	switch (prop_id) {
+		case PROP_MANAGER:
+			g_value_set_object(value, media->priv->manager);
+			break;
 		case PROP_CONFERENCE:
 			g_value_set_object(value, media->priv->conference);
 			break;
@@ -1055,8 +1107,13 @@
 	gst_bin_add(GST_BIN(session->media->priv->confbin),
 		    session->src);
 
+	session->tee = gst_element_factory_make("tee", NULL);
+	gst_bin_add(GST_BIN(session->media->priv->confbin), session->tee);
+	gst_element_link(session->src, session->tee);
+	gst_element_set_state(session->tee, GST_STATE_PLAYING);
+
 	g_object_get(session->session, "sink-pad", &sinkpad, NULL);
-	srcpad = gst_element_get_static_pad(src, "ghostsrc");
+	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");
@@ -1181,40 +1238,15 @@
 }
 
 static gboolean
-media_bus_call(GstBus *bus, GstMessage *msg, gpointer dummy)
+media_bus_call(GstBus *bus, GstMessage *msg, PurpleMedia *media)
 {
 	switch(GST_MESSAGE_TYPE(msg)) {
-		case GST_MESSAGE_EOS:
-			purple_debug_info("media", "End of Stream\n");
-			break;
-		case GST_MESSAGE_ERROR: {
-			gchar *debug = NULL;
-			GError *err = NULL;
-
-			gst_message_parse_error(msg, &err, &debug);
-
-			purple_debug_error("media", "gst pipeline error: %s\n", err->message);
-			g_error_free(err);
-
-			if (debug) {
-				purple_debug_error("media", "Debug details: %s\n", debug);
-				g_free (debug);
-			}
-			break;
-		}
 		case GST_MESSAGE_ELEMENT: {
-			PurpleMedia *media = NULL;
-			if (FS_IS_CONFERENCE(GST_MESSAGE_SRC(msg))) {
-				GList *iter = purple_media_manager_get_media(
-						purple_media_manager_get());
-				for (; iter; iter = g_list_next(iter)) {
-					if (PURPLE_MEDIA(iter->data)->priv->conference
-							== FS_CONFERENCE(GST_MESSAGE_SRC(msg))) {
-						media = iter->data;
-						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;
@@ -1322,24 +1354,9 @@
 GstElement *
 purple_media_get_pipeline(PurpleMedia *media)
 {
-	static GstElement *pipeline = NULL;
-
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
 
-	if (!pipeline) {
-		GstBus *bus;
-		media->priv->pipeline = pipeline = gst_pipeline_new(NULL);
-		bus = gst_pipeline_get_bus(GST_PIPELINE(media->priv->pipeline));
-		gst_bus_add_signal_watch(GST_BUS(bus));
-		g_signal_connect(G_OBJECT(bus), "message",
-				G_CALLBACK(media_bus_call), NULL);
-		gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL);
-		gst_object_unref(bus);
-		gst_element_set_state(pipeline, GST_STATE_PLAYING);
-	}
-
-	media->priv->pipeline = pipeline;
-	return media->priv->pipeline;
+	return purple_media_manager_get_pipeline(media->priv->manager);
 }
 
 void
@@ -1534,7 +1551,7 @@
 void
 purple_media_video_init_src(GstElement **sendbin)
 {
-	GstElement *src, *tee, *queue;
+	GstElement *src;
 	GstPad *pad;
 	GstPad *ghost;
 	const gchar *video_plugin = purple_prefs_get_string(
@@ -1548,20 +1565,12 @@
 	src = gst_element_factory_make(video_plugin, "purplevideosource");
 	gst_bin_add(GST_BIN(*sendbin), src);
 
-	tee = gst_element_factory_make("tee", "purplevideosrctee");
-	gst_bin_add(GST_BIN(*sendbin), tee);
-	gst_element_link(src, tee);
-
-	queue = gst_element_factory_make("queue", NULL);
-	gst_bin_add(GST_BIN(*sendbin), queue);
-	gst_element_link(tee, queue);
-
 	if (!strcmp(video_plugin, "videotestsrc")) {
 		/* unless is-live is set to true it doesn't throttle videotestsrc */
 		g_object_set (G_OBJECT(src), "is-live", TRUE, NULL);
 	}
 
-	pad = gst_element_get_static_pad(queue, "src");
+	pad = gst_element_get_static_pad(src, "src");
 	ghost = gst_ghost_pad_new("ghostsrc", pad);
 	gst_object_unref(pad);
 	gst_element_add_pad(*sendbin, ghost);
@@ -1573,7 +1582,7 @@
 void
 purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel)
 {
-	GstElement *sink, *volume;
+	GstElement *sink, *volume, *queue;
 	GstPad *pad, *ghost;
 	double output_volume = purple_prefs_get_int(
 			"/purple/media/audio/volume/output")/10.0;
@@ -1586,10 +1595,13 @@
 	volume = gst_element_factory_make("volume", "purpleaudiooutputvolume");
 	g_object_set(volume, "volume", output_volume, NULL);
 	*recvlevel = gst_element_factory_make("level", "recvlevel");
-	gst_bin_add_many(GST_BIN(*recvbin), sink, volume, *recvlevel, NULL);
+	queue = gst_element_factory_make("queue", NULL);
+	gst_bin_add_many(GST_BIN(*recvbin), sink, volume,
+			*recvlevel, queue, NULL);
 	gst_element_link(*recvlevel, sink);
 	gst_element_link(volume, *recvlevel);
-	pad = gst_element_get_pad(volume, "sink");
+	gst_element_link(queue, volume);
+	pad = gst_element_get_pad(queue, "sink");
 	ghost = gst_ghost_pad_new("ghostsink", pad);
 	gst_element_add_pad(*recvbin, ghost);
 	g_object_set(G_OBJECT(*recvlevel), "message", TRUE, NULL);
@@ -1670,6 +1682,7 @@
 	gchar *name;
 	FsParticipant *participant;
 	PurpleMediaStream *stream;
+	GList *iter;
 
 	g_return_if_fail(FS_IS_STREAM(fsstream));
 	g_return_if_fail(session != NULL);
@@ -1680,8 +1693,41 @@
 
 	stream = purple_media_get_stream(session->media, session->id, name);
 
-	stream->local_candidate = fs_candidate_copy(native_candidate);
-	stream->remote_candidate = fs_candidate_copy(remote_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);
+			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));
+			break;
+		}
+	}
+	if (iter == NULL)
+		stream->active_local_candidates = g_list_prepend(
+				stream->active_local_candidates,
+				fs_candidate_copy(native_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);
+			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));
+			break;
+		}
+	}
+	if (iter == NULL)
+		stream->active_remote_candidates = g_list_prepend(
+				stream->active_remote_candidates,
+				fs_candidate_copy(remote_candidate));
 
 	purple_debug_info("media", "candidate pair established\n");
 }
@@ -1701,21 +1747,53 @@
 purple_media_src_pad_added_cb(FsStream *fsstream, GstPad *srcpad,
 			      FsCodec *codec, PurpleMediaStream *stream)
 {
-	PurpleMediaSessionType type = purple_media_from_fs(codec->media_type, FS_DIRECTION_RECV);
-	GstPad *sinkpad = NULL;
+	PurpleMediaPrivate *priv;
+	GstPad *sinkpad;
 
 	g_return_if_fail(FS_IS_STREAM(fsstream));
 	g_return_if_fail(stream != NULL);
 
-	if (stream->sink == NULL)
-		stream->sink = purple_media_manager_get_element(
-			purple_media_manager_get(), type);
-
-	gst_bin_add(GST_BIN(stream->session->media->priv->confbin),
-		    stream->sink);
-	sinkpad = gst_element_get_static_pad(stream->sink, "ghostsink");
+	priv = stream->session->media->priv;
+
+	if (stream->src == NULL) {
+		GstElement *sink;
+
+		if (codec->media_type == FS_MEDIA_TYPE_AUDIO) {
+			/*
+			 * Should this instead be:
+			 *  audioconvert ! audioresample ! liveadder !
+			 *   audioresample ! audioconvert ! realsink
+			 */
+			stream->src = gst_element_factory_make(
+					"liveadder", NULL);
+			sink = purple_media_manager_get_element(priv->manager,
+					PURPLE_MEDIA_RECV_AUDIO);
+		} else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) {
+			stream->src = gst_element_factory_make(
+					"fsfunnel", NULL);
+			sink = gst_element_factory_make(
+					"fakesink", NULL);
+			g_object_set(G_OBJECT(sink), "async", FALSE, NULL);
+		}
+		stream->tee = gst_element_factory_make("tee", NULL);
+		gst_bin_add_many(GST_BIN(priv->confbin),
+				stream->src, stream->tee, sink, NULL);
+		gst_element_sync_state_with_parent(sink);
+		gst_element_sync_state_with_parent(stream->tee);
+		gst_element_sync_state_with_parent(stream->src);
+		gst_element_link_many(stream->src, stream->tee, sink, NULL);
+	}
+
+	sinkpad = gst_element_get_request_pad(stream->src, "sink%d");
 	gst_pad_link(srcpad, sinkpad);
-	gst_element_set_state(stream->sink, GST_STATE_PLAYING);
+	gst_object_unref(sinkpad);
+
+	if (codec->media_type == FS_MEDIA_TYPE_VIDEO &&
+			stream->sink != NULL) {
+		gst_bin_add(GST_BIN(priv->confbin), stream->sink);
+		gst_element_set_state(stream->sink, GST_STATE_PLAYING);
+		gst_element_link(stream->tee, stream->sink);
+	}
 
 	g_timeout_add_full(G_PRIORITY_HIGH, 0,
 			(GSourceFunc)purple_media_connected_cb, stream, NULL);
@@ -1804,7 +1882,7 @@
 		session_type = purple_media_from_fs(type, FS_DIRECTION_SEND);
 		purple_media_set_src(media, session->id,
 				purple_media_manager_get_element(
-				purple_media_manager_get(), session_type));
+				media->priv->manager, session_type));
 		gst_element_set_state(session->src, GST_STATE_PLAYING);
 	}
 
@@ -2023,22 +2101,26 @@
 	}
 }
 
-PurpleMediaCandidate *
-purple_media_get_local_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name)
+GList *
+purple_media_get_active_local_candidates(PurpleMedia *media,
+		const gchar *sess_id, const gchar *name)
 {
 	PurpleMediaStream *stream;
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
 	stream = purple_media_get_stream(media, sess_id, name);
-	return purple_media_candidate_from_fs(stream->local_candidate);
+	return purple_media_candidate_list_from_fs(
+			stream->active_local_candidates);
 }
 
-PurpleMediaCandidate *
-purple_media_get_remote_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name)
+GList *
+purple_media_get_active_remote_candidates(PurpleMedia *media,
+		const gchar *sess_id, const gchar *name)
 {
 	PurpleMediaStream *stream;
 	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
 	stream = purple_media_get_stream(media, sess_id, name);
-	return purple_media_candidate_from_fs(stream->remote_candidate);
+	return purple_media_candidate_list_from_fs(
+			stream->active_remote_candidates);
 }
 
 gboolean
@@ -2080,8 +2162,14 @@
 
 	for (; sessions; sessions = sessions->next) {
 		const gchar *session = sessions->data;
-		if (!purple_media_get_local_candidate(media, session, name) ||
-				!purple_media_get_remote_candidate(media, session, name))
+		GList *local = purple_media_get_active_local_candidates(
+				media, session, name);
+		GList *remote = purple_media_get_active_remote_candidates(
+				media, session, name);
+		gboolean result = (local == NULL || remote == NULL);
+		purple_media_candidate_list_free(local);
+		purple_media_candidate_list_free(remote);
+		if (!result)
 			return FALSE;
 	}
 
@@ -2268,15 +2356,14 @@
 		if (session->sink == NULL) {
 			PurpleMediaXOverlayData *data;
 			GstBus *bus;
-			GstElement *tee, *bin, *queue, *sink;
+			GstElement *bin, *queue, *sink;
 			GstPad *request_pad, *sinkpad, *ghostpad;
 			gchar *name;
 
 			/* Create sink */
-			tee = gst_bin_get_by_name(GST_BIN(session->src),
-					"purplevideosrctee");
 			bin = gst_bin_new(NULL);
-			gst_bin_add(GST_BIN(GST_ELEMENT_PARENT(tee)), bin);
+			gst_bin_add(GST_BIN(GST_ELEMENT_PARENT(
+					session->tee)), bin);
 
 			queue = gst_element_factory_make("queue", NULL);
 			name = g_strdup_printf(
@@ -2298,7 +2385,8 @@
 			data->window_id = window_id;
 
 			bus = gst_pipeline_get_bus(GST_PIPELINE(
-					purple_media_get_pipeline(media)));
+					purple_media_manager_get_pipeline(
+					media->priv->manager)));
 			data->handler_id = g_signal_connect(bus,
 					"sync-message::element",
 					G_CALLBACK(window_id_cb), data);
@@ -2307,7 +2395,7 @@
 			gst_element_set_state(bin, GST_STATE_PLAYING);
 
 			request_pad = gst_element_get_request_pad(
-					tee, "src%d");
+					session->tee, "src%d");
 			gst_pad_link(request_pad, ghostpad);
 			gst_object_unref(request_pad);
 
@@ -2329,7 +2417,7 @@
 		PurpleMediaStream *stream = purple_media_get_stream(
 				media, session_id, participant);
 		GstBus *bus;
-		GstElement *bin, *sink;
+		GstElement *bin, *queue, *sink;
 		GstPad *pad, *peer = NULL, *ghostpad;
 		PurpleMediaXOverlayData *data;
 		gchar *name;
@@ -2374,16 +2462,14 @@
 
 		bin = gst_bin_new(NULL);
 
-		if (stream->sink != NULL)
-			gst_bin_add(GST_BIN(GST_ELEMENT_PARENT(
-					stream->sink)), bin);
-
 		name = g_strdup_printf("stream-sink_%s_%s",
 				session_id, participant);
+		queue = gst_element_factory_make("queue", NULL);
 		sink = gst_element_factory_make("autovideosink", name);
 
-		gst_bin_add(GST_BIN(bin), sink);
-		pad = gst_element_get_static_pad(sink, "sink");
+		gst_bin_add_many(GST_BIN(bin), queue, sink, NULL);
+		gst_element_link(queue, sink);
+		pad = gst_element_get_static_pad(queue, "sink");
 		ghostpad = gst_ghost_pad_new("ghostsink", pad);
 		gst_element_add_pad(bin, ghostpad);
 
@@ -2393,15 +2479,18 @@
 		data->window_id = window_id;
 
 		bus = gst_pipeline_get_bus(GST_PIPELINE(
-				purple_media_get_pipeline(media)));
+				purple_media_manager_get_pipeline(
+				media->priv->manager)));
 		data->handler_id = g_signal_connect(bus,
 				"sync-message::element",
 				G_CALLBACK(window_id_cb), data);
 		gst_object_unref(bus);
 
-		if (stream->sink != NULL) {
+		if (stream->tee != NULL) {
+			gst_bin_add(GST_BIN(GST_ELEMENT_PARENT(
+					stream->tee)), bin);
 			gst_element_set_state(bin, GST_STATE_PLAYING);
-			gst_pad_link(peer, ghostpad);
+			gst_element_link(stream->tee, bin);
 		}
 
 		stream->sink = bin;
@@ -2445,6 +2534,7 @@
 		gst_element_release_request_pad(GST_ELEMENT_PARENT(peer), peer);
 		gst_object_unref(peer);
 
+		gst_element_set_locked_state(sink, TRUE);
 		gst_element_set_state(sink, GST_STATE_NULL);
 
 		gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(sink)), sink);
@@ -2485,6 +2575,7 @@
 	peer = gst_pad_get_peer(pad);
 	gst_object_unref(pad);
 	gst_pad_set_blocked_async(peer, TRUE, dummy_block_cb, NULL);
+	gst_element_set_locked_state(sink, TRUE);
 	gst_element_set_state(sink, GST_STATE_NULL);
 	gst_bin_remove(GST_BIN(parent), sink);
 
--- a/libpurple/media.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/media.h	Sat Feb 21 05:15:28 2009 +0000
@@ -65,6 +65,18 @@
 
 #endif /* USE_VV */
 
+/** 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,
@@ -533,27 +545,27 @@
 					 const gchar *name);
 
 /**
- * Gets the active local candidate for the stream.
+ * Gets the active local candidates for the stream.
  *
  * @param media The media object to find the session in.
  * @param sess_id The session id of the session to find the stream in.
  * @param name The name of the remote user to get the active candidate from.
  *
- * @return The active candidate retrieved.
+ * @return The active candidates retrieved.
  */
-PurpleMediaCandidate *purple_media_get_local_candidate(PurpleMedia *media,
+GList *purple_media_get_active_local_candidates(PurpleMedia *media,
 		const gchar *sess_id, const gchar *name);
 
 /**
- * Gets the active remote candidate for the stream.
+ * Gets the active remote candidates for the stream.
  *
  * @param media The media object to find the session in.
  * @param sess_id The session id of the session to find the stream in.
  * @param name The name of the remote user to get the remote candidate from.
  *
- * @return The remote candidate retrieved.
+ * @return The remote candidates retrieved.
  */
-PurpleMediaCandidate *purple_media_get_remote_candidate(PurpleMedia *media,
+GList *purple_media_get_active_remote_candidates(PurpleMedia *media,
 		const gchar *sess_id, const gchar *name);
 
 /**
--- a/libpurple/mediamanager.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/mediamanager.c	Sat Feb 21 05:15:28 2009 +0000
@@ -38,6 +38,7 @@
 
 struct _PurpleMediaManagerPrivate
 {
+	GstElement *pipeline;
 	GList *medias;
 	GList *elements;
 
@@ -144,6 +145,62 @@
 	return manager;
 }
 
+static gboolean
+pipeline_bus_call(GstBus *bus, GstMessage *msg, PurpleMediaManager *manager)
+{
+	switch(GST_MESSAGE_TYPE(msg)) {
+		case GST_MESSAGE_EOS:
+			purple_debug_info("mediamanager", "End of Stream\n");
+			break;
+		case GST_MESSAGE_ERROR: {
+			gchar *debug = NULL;
+			GError *err = NULL;
+
+			gst_message_parse_error(msg, &err, &debug);
+
+			purple_debug_error("mediamanager",
+					"gst pipeline error: %s\n",
+					err->message);
+			g_error_free(err);
+
+			if (debug) {
+				purple_debug_error("mediamanager",
+						"Debug details: %s\n", debug);
+				g_free (debug);
+			}
+			break;
+		}
+		default:
+			break;
+	}
+	return TRUE;
+}
+
+GstElement *
+purple_media_manager_get_pipeline(PurpleMediaManager *manager)
+{
+	g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
+
+	if (manager->priv->pipeline == NULL) {
+		GstBus *bus;
+		manager->priv->pipeline = gst_pipeline_new(NULL);
+
+		bus = gst_pipeline_get_bus(
+				GST_PIPELINE(manager->priv->pipeline));
+		gst_bus_add_signal_watch(GST_BUS(bus));
+		g_signal_connect(G_OBJECT(bus), "message",
+				G_CALLBACK(pipeline_bus_call), manager);
+		gst_bus_set_sync_handler(bus,
+				gst_bus_sync_signal_handler, NULL);
+		gst_object_unref(bus);
+
+		gst_element_set_state(manager->priv->pipeline,
+				GST_STATE_PLAYING);
+	}
+
+	return manager->priv->pipeline;
+}
+
 PurpleMedia *
 purple_media_manager_create_media(PurpleMediaManager *manager,
 				  PurpleConnection *gc,
@@ -165,6 +222,7 @@
 	}
 
 	media = PURPLE_MEDIA(g_object_new(purple_media_get_type(),
+			     "manager", manager,
 			     "conference", conference,
 			     "initiator", initiator,
 			     NULL));
--- a/libpurple/mediamanager.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/mediamanager.h	Sat Feb 21 05:15:28 2009 +0000
@@ -121,6 +121,16 @@
 PurpleMediaManager *purple_media_manager_get(void);
 
 /**
+ * Gets the pipeline from the media manager.
+ *
+ * @param manager The media manager to get the pipeline from.
+ *
+ * @return The pipeline.
+ */
+GstElement *
+purple_media_manager_get_pipeline(PurpleMediaManager *manager);
+
+/**
  * Creates a media session.
  *
  * @param manager The media manager to create the session under.
--- a/libpurple/plugin.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/plugin.h	Sat Feb 21 05:15:28 2009 +0000
@@ -34,11 +34,16 @@
 #include "signals.h"
 #include "value.h"
 
+/** @copydoc _PurplePlugin */
 typedef struct _PurplePlugin           PurplePlugin;
+/** @copydoc _PurplePluginInfo */
 typedef struct _PurplePluginInfo       PurplePluginInfo;
+/** @copydoc _PurplePluginUiInfo */
 typedef struct _PurplePluginUiInfo     PurplePluginUiInfo;
+/** @copydoc _PurplePluginLoaderInfo */
 typedef struct _PurplePluginLoaderInfo PurplePluginLoaderInfo;
 
+/** @copydoc _PurplePluginAction */
 typedef struct _PurplePluginAction     PurplePluginAction;
 
 typedef int PurplePluginPriority; /**< Plugin priority. */
--- a/libpurple/plugins/autoaccept.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/plugins/autoaccept.c	Sat Feb 21 05:15:28 2009 +0000
@@ -21,7 +21,7 @@
 
 #define PLUGIN_ID			"core-plugin_pack-autoaccept"
 #define PLUGIN_NAME			N_("Autoaccept")
-#define PLUGIN_STATIC_NAME	"Autoaccept"
+#define PLUGIN_STATIC_NAME	Autoaccept
 #define PLUGIN_SUMMARY		N_("Auto-accept file transfer requests from selected users.")
 #define PLUGIN_DESCRIPTION	N_("Auto-accept file transfer requests from selected users.")
 #define PLUGIN_AUTHOR		"Sadrul H Chowdhury <sadrul@users.sourceforge.net>"
--- a/libpurple/plugins/codeinline.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/plugins/codeinline.c	Sat Feb 21 05:15:28 2009 +0000
@@ -95,4 +95,4 @@
  {
  }
 
-PURPLE_INIT_PLUGIN(urlcatcher, init_plugin, info)
+PURPLE_INIT_PLUGIN(codeinline, init_plugin, info)
--- a/libpurple/plugins/newline.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/plugins/newline.c	Sat Feb 21 05:15:28 2009 +0000
@@ -133,4 +133,4 @@
 	purple_prefs_add_bool("/plugins/core/newline/chat", TRUE);
 }
 
-PURPLE_INIT_PLUGIN(lastseen, init_plugin, info)
+PURPLE_INIT_PLUGIN(newline, init_plugin, info)
--- a/libpurple/plugins/offlinemsg.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/plugins/offlinemsg.c	Sat Feb 21 05:15:28 2009 +0000
@@ -21,7 +21,7 @@
 
 #define PLUGIN_ID			"core-plugin_pack-offlinemsg"
 #define PLUGIN_NAME			N_("Offline Message Emulation")
-#define PLUGIN_STATIC_NAME	"offlinemsg"
+#define PLUGIN_STATIC_NAME	offlinemsg
 #define PLUGIN_SUMMARY		N_("Save messages sent to an offline user as pounce.")
 #define PLUGIN_DESCRIPTION	N_("Save messages sent to an offline user as pounce.")
 #define PLUGIN_AUTHOR		"Sadrul H Chowdhury <sadrul@users.sourceforge.net>"
--- a/libpurple/plugins/perl/common/Server.xs	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/plugins/perl/common/Server.xs	Sat Feb 21 05:15:28 2009 +0000
@@ -122,29 +122,28 @@
 	Purple::Connection gc
 	const char *name
 
-void 
-serv_join_chat(con, components)
-	Purple::Connection con 
-	SV * components
-INIT:
-	HV * t_HV;
-	HE * t_HE;
-	SV * t_SV;
-	GHashTable * t_GHash;
+void
+serv_join_chat(conn, components)
+	Purple::Connection conn
+	HV * components
+PREINIT:
+	HE *t_HE;
+	SV *t_SV;
 	I32 len;
+	GHashTable *t_GHash;
 	char *t_key, *t_value;
 CODE:
-	t_HV =  (HV *)SvRV(components);
 	t_GHash = g_hash_table_new(g_str_hash, g_str_equal);
 
-	for (t_HE = hv_iternext(t_HV); t_HE != NULL; t_HE = hv_iternext(t_HV) ) {
+	for (t_HE = hv_iternext(components); t_HE != NULL;
+	     t_HE = hv_iternext(components)) {
 		t_key = hv_iterkey(t_HE, &len);
-		t_SV = *hv_fetch(t_HV, t_key, len, 0);
- 		t_value = SvPVutf8_nolen(t_SV);
+		t_SV = *hv_fetch(components, t_key, len, 0);
+		t_value = SvPVutf8_nolen(t_SV);
 
 		g_hash_table_insert(t_GHash, t_key, t_value);
 	}
-	serv_join_chat(con, t_GHash);
+	serv_join_chat(conn, t_GHash);
 
 void 
 serv_move_buddy(buddy, group1, group2)
--- a/libpurple/prefs.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/prefs.h	Sat Feb 21 05:15:28 2009 +0000
@@ -30,17 +30,17 @@
 #include <glib.h>
 
 /**
- * Pref data types.
+ * Preference data types.
  */
 typedef enum _PurplePrefType
 {
-	PURPLE_PREF_NONE,
-	PURPLE_PREF_BOOLEAN,
-	PURPLE_PREF_INT,
-	PURPLE_PREF_STRING,
-	PURPLE_PREF_STRING_LIST,
-	PURPLE_PREF_PATH,
-	PURPLE_PREF_PATH_LIST
+	PURPLE_PREF_NONE,        /**< No type.         */
+	PURPLE_PREF_BOOLEAN,     /**< Boolean.         */
+	PURPLE_PREF_INT,         /**< Integer.         */
+	PURPLE_PREF_STRING,      /**< String.          */
+	PURPLE_PREF_STRING_LIST, /**< List of strings. */
+	PURPLE_PREF_PATH,        /**< Path.            */
+	PURPLE_PREF_PATH_LIST    /**< List of paths.   */
 
 } PurplePrefType;
 
--- a/libpurple/privacy.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/privacy.c	Sat Feb 21 05:15:28 2009 +0000
@@ -42,12 +42,14 @@
 	name = g_strdup(purple_normalize(account, who));
 
 	for (l = account->permit; l != NULL; l = l->next) {
-		if (!purple_utf8_strcasecmp(name, (char *)l->data))
+		if (g_str_equal(name, l->data))
+			/* This buddy already exists */
 			break;
 	}
 
 	if (l != NULL)
 	{
+		/* This buddy already exists, so bail out */
 		g_free(name);
 		return FALSE;
 	}
@@ -86,11 +88,13 @@
 	name = purple_normalize(account, who);
 
 	for (l = account->permit; l != NULL; l = l->next) {
-		if (!purple_utf8_strcasecmp(name, (char *)l->data))
+		if (g_str_equal(name, l->data))
+			/* We found the buddy we were looking for */
 			break;
 	}
 
 	if (l == NULL)
+		/* We didn't find the buddy we were looking for, so bail out */
 		return FALSE;
 
 	/* We should not free l->data just yet. There can be occasions where
@@ -130,12 +134,14 @@
 	name = g_strdup(purple_normalize(account, who));
 
 	for (l = account->deny; l != NULL; l = l->next) {
-		if (!purple_utf8_strcasecmp(name, purple_normalize(account, (char *)l->data)))
+		if (g_str_equal(name, l->data))
+			/* This buddy already exists */
 			break;
 	}
 
 	if (l != NULL)
 	{
+		/* This buddy already exists, so bail out */
 		g_free(name);
 		return FALSE;
 	}
@@ -173,14 +179,16 @@
 	normalized = purple_normalize(account, who);
 
 	for (l = account->deny; l != NULL; l = l->next) {
-		if (!purple_utf8_strcasecmp(normalized, (char *)l->data))
+		if (g_str_equal(normalized, l->data))
+			/* We found the buddy we were looking for */
 			break;
 	}
 
-	buddy = purple_find_buddy(account, normalized);
+	if (l == NULL)
+		/* We didn't find the buddy we were looking for, so bail out */
+		return FALSE;
 
-	if (l == NULL)
-		return FALSE;
+	buddy = purple_find_buddy(account, normalized);
 
 	name = l->data;
 	account->deny = g_slist_delete_link(account->deny, l);
@@ -349,7 +357,7 @@
 		case PURPLE_PRIVACY_ALLOW_USERS:
 			who = purple_normalize(account, who);
 			for (list=account->permit; list!=NULL; list=list->next) {
-				if (!purple_utf8_strcasecmp(who, (char *)list->data))
+				if (g_str_equal(who, list->data))
 					return TRUE;
 			}
 			return FALSE;
@@ -357,7 +365,7 @@
 		case PURPLE_PRIVACY_DENY_USERS:
 			who = purple_normalize(account, who);
 			for (list=account->deny; list!=NULL; list=list->next) {
-				if (!purple_utf8_strcasecmp(who, (char *)list->data ))
+				if (g_str_equal(who, list->data))
 					return FALSE;
 			}
 			return TRUE;
--- a/libpurple/protocols/jabber/adhoccommands.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/adhoccommands.c	Sat Feb 21 05:15:28 2009 +0000
@@ -229,7 +229,7 @@
 		JabberAdHocCommands *cmd = js->commands->data;
 		g_free(cmd->jid);
 		g_free(cmd->node);
-		g_free(cmd->node);
+		g_free(cmd->name);
 		g_free(cmd);
 		js->commands = g_list_delete_link(js->commands, js->commands);
 	}
--- a/libpurple/protocols/jabber/buddy.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Sat Feb 21 05:15:28 2009 +0000
@@ -1786,7 +1786,7 @@
 	if (!jid)
 		return;
 
-	if (jabber_chat_find(js, jid->node, jid->domain)) {
+	if (jid->node && jabber_chat_find(js, jid->node, jid->domain)) {
 		/* For a conversation, include the resource (indicates the user). */
 		jabber_buddy_get_info_for_jid(js, who);
 	} else {
--- a/libpurple/protocols/jabber/jabber.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sat Feb 21 05:15:28 2009 +0000
@@ -157,7 +157,8 @@
 }
 
 static char *jabber_prep_resource(char *input) {
-	char hostname[256]; /* current hostname */
+	char hostname[256], /* current hostname */
+		 *dot = NULL;
 
 	/* Empty resource == don't send any */
 	if (input == NULL || *input == '\0')
@@ -179,6 +180,12 @@
 	}
 	hostname[sizeof(hostname) - 1] = '\0';
 
+	/* We want only the short hostname, not the FQDN - this will prevent the
+	 * resource string from being unreasonably long on systems which stuff the
+	 * whole FQDN in the hostname */
+	if((dot = strchr(hostname, '.')))
+			dot = '\0';
+
 	return purple_strreplace(input, "__HOSTNAME__", hostname);
 }
 
@@ -818,10 +825,11 @@
 				js->user->node, js->user->domain);
 			if(account->registration_cb)
 				(account->registration_cb)(account, TRUE, account->registration_cb_user_data);
-		}
-		else
+		} else {
+			g_return_if_fail(to != NULL);
 			buf = g_strdup_printf(_("Registration to %s successful"),
 				to);
+		}
 		purple_notify_info(NULL, _("Registration Successful"),
 				_("Registration Successful"), buf);
 		g_free(buf);
@@ -848,7 +856,11 @@
 	const char *type = xmlnode_get_attrib(packet, "type");
 	char *buf;
 	char *to = data;
-	
+
+	/* This function is never called for unregistering our XMPP account from
+	 * the server, so there should always be a 'to' address. */
+	g_return_if_fail(to != NULL);
+
 	if(!strcmp(type, "result")) {
 		buf = g_strdup_printf(_("Registration from %s successfully removed"),
 							  to);
@@ -883,7 +895,8 @@
 
 	iq = jabber_iq_new_query(cbdata->js, JABBER_IQ_SET, "jabber:iq:register");
 	query = xmlnode_get_child(iq->node, "query");
-	xmlnode_set_attrib(iq->node, "to", cbdata->who);
+	if (cbdata->who)
+		xmlnode_set_attrib(iq->node, "to", cbdata->who);
 
 	for(groups = purple_request_fields_get_groups(fields); groups;
 			groups = groups->next) {
@@ -899,7 +912,8 @@
 					jabber_iq_free(iq);
 					iq = jabber_iq_new_query(cbdata->js, JABBER_IQ_SET, "jabber:iq:register");
 					query = xmlnode_get_child(iq->node, "query");
-					xmlnode_set_attrib(iq->node,"to",cbdata->who);
+					if (cbdata->who)
+						xmlnode_set_attrib(iq->node,"to",cbdata->who);
 					xmlnode_new_child(query, "remove");
 					
 					jabber_iq_set_callback(iq, jabber_unregistration_result_cb, cbdata->who);
@@ -944,8 +958,7 @@
 			}
 			xmlnode_insert_data(y, value, -1);
 				if(cbdata->js->registration && !strcmp(id, "username")) {
-					if(cbdata->js->user->node)
-						g_free(cbdata->js->user->node);
+					g_free(cbdata->js->user->node);
 					cbdata->js->user->node = g_strdup(value);
 			}
 				if(cbdata->js->registration && !strcmp(id, "password"))
@@ -988,7 +1001,8 @@
 
 	iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:register");
 	query = xmlnode_get_child(iq->node, "query");
-	xmlnode_set_attrib(iq->node,"to",to);
+	if (to)
+		xmlnode_set_attrib(iq->node,"to",to);
 
 	xmlnode_insert_child(query, result);
 
@@ -1013,10 +1027,7 @@
 		return;
 
 	from = xmlnode_get_attrib(packet, "from");
-	if (!from)
-		from = js->serverFQDN;
-	g_return_if_fail(from != NULL);
-	
+
 	if(js->registration) {
 		/* get rid of the login thingy */
 		purple_connection_set_state(js->gc, PURPLE_CONNECTED);
@@ -1037,11 +1048,11 @@
 		}
 	}
 	
-	if((x = xmlnode_get_child_with_namespace(packet, "x", "jabber:x:data"))) {
+	if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:data"))) {
 		jabber_x_data_request(js, x, jabber_register_x_data_cb, g_strdup(from));
 		return;
 
-	} else if((x = xmlnode_get_child_with_namespace(packet, "x", "jabber:x:oob"))) {
+	} else if((x = xmlnode_get_child_with_namespace(query, "x", "jabber:x:oob"))) {
 		xmlnode *url;
 
 		if((url = xmlnode_get_child(x, "url"))) {
@@ -1161,7 +1172,9 @@
 				purple_connection_get_account(js->gc), NULL, NULL,
 				cbdata);
 	else {
-		char *title = registered?g_strdup_printf(_("Change Account Registration at %s"), from)
+		char *title;
+		g_return_if_fail(from != NULL);
+		title = registered ? g_strdup_printf(_("Change Account Registration at %s"), from)
 								:g_strdup_printf(_("Register New Account at %s"), from);
 		purple_request_fields(js->gc, title,
 			  title, instructions, fields,
@@ -2591,11 +2604,11 @@
 		return jingle_rtp_initiate_media(gc->proto_data, who, type);
 }
 
-gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, 
-                             PurpleMediaSessionType type)
+PurpleMediaCaps jabber_get_media_caps(PurpleConnection *gc, const char *who)
 {
 	JabberStream *js = (JabberStream *) gc->proto_data;
 	JabberBuddy *jb;
+	PurpleMediaCaps caps = PURPLE_MEDIA_CAPS_NONE;
 
 	if (!js) {
 		purple_debug_error("jabber", "jabber_can_do_media: NULL stream\n");
@@ -2609,34 +2622,30 @@
 		return FALSE;
 	}
 
-	if (!jabber_buddy_has_capability(jb, JINGLE_TRANSPORT_ICEUDP) &&
-			!jabber_buddy_has_capability(jb,
-			JINGLE_TRANSPORT_RAWUDP) &&
-			!jabber_buddy_has_capability(jb, GTALK_CAP)) {
-		purple_debug_info("jingle-rtp", "Buddy doesn't support "
-				"the same transport types\n");
-		return FALSE;
+	if (jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_AUDIO))
+		caps |= PURPLE_MEDIA_CAPS_AUDIO |
+				PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION;
+	if (jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_VIDEO))
+		caps |= PURPLE_MEDIA_CAPS_VIDEO |
+				PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION;
+	if (caps & PURPLE_MEDIA_CAPS_AUDIO && caps & PURPLE_MEDIA_CAPS_VIDEO)
+		caps |= PURPLE_MEDIA_CAPS_AUDIO_VIDEO;
+	if (caps != PURPLE_MEDIA_CAPS_NONE) {
+		if (!jabber_buddy_has_capability(jb,
+				JINGLE_TRANSPORT_ICEUDP) &&
+				!jabber_buddy_has_capability(jb,
+				JINGLE_TRANSPORT_RAWUDP)) {
+			purple_debug_info("jingle-rtp", "Buddy doesn't "
+					"support the same transport types\n");
+			caps = PURPLE_MEDIA_CAPS_NONE;
+		} else
+			caps |= PURPLE_MEDIA_CAPS_MODIFY_SESSION |
+					PURPLE_MEDIA_CAPS_CHANGE_DIRECTION;
 	}
-
-	/* XMPP will only support two-way media, AFAIK... */
-	if (type == (PURPLE_MEDIA_AUDIO | PURPLE_MEDIA_VIDEO)) {
-		purple_debug_info("jabber", 
-				  "Checking audio/video XEP support for %s\n", who);
-		return (jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_AUDIO) ||
-				jabber_buddy_has_capability(jb, GTALK_CAP)) && 
-				jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_VIDEO);
-	} else if (type == (PURPLE_MEDIA_AUDIO)) {
-		purple_debug_info("jabber", 
-				  "Checking audio XEP support for %s\n", who);
-		return jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_AUDIO) ||
-				jabber_buddy_has_capability(jb, GTALK_CAP);
-	} else if (type == (PURPLE_MEDIA_VIDEO)) {
-		purple_debug_info("jabber", 
-				  "Checking video XEP support for %s\n", who);
-		return jabber_buddy_has_capability(jb, JINGLE_APP_RTP_SUPPORT_VIDEO);
-	}
-
-	return FALSE;
+	if (jabber_buddy_has_capability(jb, GTALK_CAP))
+		caps |= PURPLE_MEDIA_CAPS_AUDIO;
+
+	return caps;
 }
 
 #endif
--- a/libpurple/protocols/jabber/jabber.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Sat Feb 21 05:15:28 2009 +0000
@@ -329,7 +329,7 @@
 
 #ifdef USE_VV
 PurpleMedia *jabber_initiate_media(PurpleConnection *gc, const char *who, PurpleMediaSessionType type);
-gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, PurpleMediaSessionType type);
+PurpleMediaCaps jabber_get_media_caps(PurpleConnection *gc, const char *who);
 #endif
 
 #endif /* _PURPLE_JABBER_H_ */
--- a/libpurple/protocols/jabber/jingle/iceudp.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/jingle/iceudp.c	Sat Feb 21 05:15:28 2009 +0000
@@ -57,6 +57,7 @@
 	new_candidate->component = candidate->component;
 	new_candidate->foundation = g_strdup(candidate->foundation);
 	new_candidate->generation = candidate->generation;
+	new_candidate->id = g_strdup(candidate->id);
 	new_candidate->ip = g_strdup(candidate->ip);
 	new_candidate->network = candidate->network;
 	new_candidate->port = candidate->port;
@@ -74,6 +75,7 @@
 jingle_iceudp_candidate_free(JingleIceUdpCandidate *candidate)
 {
 	g_free(candidate->foundation);
+	g_free(candidate->id);
 	g_free(candidate->ip);
 	g_free(candidate->protocol);
 	g_free(candidate->type);
@@ -97,14 +99,16 @@
 
 JingleIceUdpCandidate *
 jingle_iceudp_candidate_new(guint component, const gchar *foundation,
-		guint generation, const gchar *ip, guint network,
-		guint port, guint priority, const gchar *protocol,
-		const gchar *type, const gchar *username, const gchar *password)
+		guint generation, const gchar *id, const gchar *ip,
+		guint network, guint port, guint priority,
+		const gchar *protocol, const gchar *type,
+		const gchar *username, const gchar *password)
 {
 	JingleIceUdpCandidate *candidate = g_new0(JingleIceUdpCandidate, 1);
 	candidate->component = component;
 	candidate->foundation = g_strdup(foundation);
 	candidate->generation = generation;
+	candidate->id = g_strdup(id);
 	candidate->ip = g_strdup(ip);
 	candidate->network = network;
 	candidate->port = port;
@@ -233,8 +237,7 @@
 
 	for (; iter; iter = g_list_next(iter)) {
 		JingleIceUdpCandidate *c = iter->data;
-		if ((c->component == candidate->component) &&
-				!strcmp(c->foundation, candidate->foundation)) {
+		if (!strcmp(c->id, candidate->id)) {
 			guint generation = c->generation + 1;
 
 			g_boxed_free(JINGLE_TYPE_ICEUDP_CANDIDATE, c);
@@ -261,13 +264,12 @@
 
 static JingleIceUdpCandidate *
 jingle_iceudp_get_remote_candidate_by_id(JingleIceUdp *iceudp,
-		guint component, const gchar *foundation)
+		const gchar *id)
 {
 	GList *iter = iceudp->priv->remote_candidates;
 	for (; iter; iter = g_list_next(iter)) {
 		JingleIceUdpCandidate *candidate = iter->data;
-		if ((candidate->component == component) &&
-				!strcmp(candidate->foundation, foundation)) {
+		if (!strcmp(candidate->id, id)) {
 			return candidate;
 		}
 	}
@@ -280,7 +282,7 @@
 	JingleIceUdpPrivate *priv = JINGLE_ICEUDP_GET_PRIVATE(iceudp);
 	JingleIceUdpCandidate *iceudp_candidate =
 			jingle_iceudp_get_remote_candidate_by_id(iceudp,
-					candidate->component, candidate->foundation);
+					candidate->id);
 	if (iceudp_candidate != NULL) {
 		priv->remote_candidates = g_list_remove(
 				priv->remote_candidates, iceudp_candidate);
@@ -304,6 +306,7 @@
 				atoi(xmlnode_get_attrib(candidate, "component")),
 				xmlnode_get_attrib(candidate, "foundation"),
 				atoi(xmlnode_get_attrib(candidate, "generation")),
+				xmlnode_get_attrib(candidate, "id"),
 				xmlnode_get_attrib(candidate, "ip"),
 				atoi(xmlnode_get_attrib(candidate, "network")),
 				atoi(xmlnode_get_attrib(candidate, "port")),
@@ -347,6 +350,7 @@
 			xmlnode_set_attrib(xmltransport, "component", component);
 			xmlnode_set_attrib(xmltransport, "foundation", candidate->foundation);
 			xmlnode_set_attrib(xmltransport, "generation", generation);
+			xmlnode_set_attrib(xmltransport, "id", candidate->id);
 			xmlnode_set_attrib(xmltransport, "ip", candidate->ip);
 			xmlnode_set_attrib(xmltransport, "network", network);
 			xmlnode_set_attrib(xmltransport, "port", port);
--- a/libpurple/protocols/jabber/jingle/iceudp.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/jingle/iceudp.h	Sat Feb 21 05:15:28 2009 +0000
@@ -66,6 +66,7 @@
 	guint component;
 	gchar *foundation;
 	guint generation;
+	gchar *id;
 	gchar *ip;
 	guint network;
 	guint port;
@@ -91,9 +92,10 @@
 GType jingle_iceudp_get_type(void);
 
 JingleIceUdpCandidate *jingle_iceudp_candidate_new(guint component,
-		const gchar *foundation, guint generation, const gchar *ip,
-		guint network, guint port, guint priority, const gchar *protocol,
-		const gchar *type, const gchar *username, const gchar *password);
+		const gchar *foundation, guint generation, const gchar *id,
+		const gchar *ip, guint network, guint port, guint priority,
+		const gchar *protocol, const gchar *type,
+		const gchar *username, const gchar *password);
 void jingle_iceudp_add_local_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *candidate);
 GList *jingle_iceudp_get_remote_candidates(JingleIceUdp *iceudp);
 
--- a/libpurple/protocols/jabber/jingle/jingle.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/jingle/jingle.h	Sat Feb 21 05:15:28 2009 +0000
@@ -32,7 +32,7 @@
 
 #define JINGLE "urn:xmpp:jingle:0"
 #define JINGLE_ERROR "urn:xmpp:jingle:errors:0"
-#define JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:0"
+#define JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:1"
 #define JINGLE_APP_RTP "urn:xmpp:jingle:apps:rtp:1"
 #define JINGLE_APP_RTP_ERROR "urn:xmpp:jingle:apps:rtp:errors:1"
 #define JINGLE_APP_RTP_INFO "urn:xmpp:jingle:apps:rtp:info:1"
@@ -40,7 +40,7 @@
 #define JINGLE_APP_RTP_SUPPORT_VIDEO "urn:xmpp:jingle:apps:rtp:video"
 #define JINGLE_APP_XML "urn:xmpp:tmp:jingle:apps:xmlstream"
 #define JINGLE_DTMF "urn:xmpp:jingle:dtmf:0"
-#define JINGLE_TRANSPORT_SOCKS "urn:xmpp:jingle:transports:bytestreams:0"
+#define JINGLE_TRANSPORT_S5B "urn:xmpp:jingle:transports:s5b:0"
 #define JINGLE_TRANSPORT_IBB "urn:xmpp:jingle:transports:ibb:0"
 #define JINGLE_TRANSPORT_ICEUDP "urn:xmpp:jingle:transports:ice-udp:0"
 #define JINGLE_TRANSPORT_RAWUDP "urn:xmpp:jingle:transports:raw-udp:1"
--- a/libpurple/protocols/jabber/jingle/rtp.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/jingle/rtp.c	Sat Feb 21 05:15:28 2009 +0000
@@ -189,26 +189,28 @@
 jingle_rtp_candidates_to_transport(JingleSession *session, GType type, guint generation, GList *candidates)
 {
 	if (type == JINGLE_TYPE_RAWUDP) {
-		gchar *id = jabber_get_next_id(jingle_session_get_js(session));
 		JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_RAWUDP);
 		JingleRawUdpCandidate *rawudp_candidate;
 		for (; candidates; candidates = g_list_next(candidates)) {
 			PurpleMediaCandidate *candidate = candidates->data;
-			id = jabber_get_next_id(jingle_session_get_js(session));
+			gchar *id = jabber_get_next_id(
+					jingle_session_get_js(session));
 			rawudp_candidate = jingle_rawudp_candidate_new(id,
 					generation, candidate->component_id,
 					candidate->ip, candidate->port);
 			jingle_rawudp_add_local_candidate(JINGLE_RAWUDP(transport), rawudp_candidate);
+			g_free(id);
 		}
-		g_free(id);
 		return transport;
 	} else if (type == JINGLE_TYPE_ICEUDP) {
 		JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_ICEUDP);
 		JingleIceUdpCandidate *iceudp_candidate;
 		for (; candidates; candidates = g_list_next(candidates)) {
 			PurpleMediaCandidate *candidate = candidates->data;
+			gchar *id = jabber_get_next_id(
+					jingle_session_get_js(session));
 			iceudp_candidate = jingle_iceudp_candidate_new(candidate->component_id,
-					candidate->foundation, generation, candidate->ip,
+					candidate->foundation, generation, id, candidate->ip,
 					0, candidate->port, candidate->priority, "udp",
 					candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" :
 					candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" :
@@ -216,6 +218,7 @@
 					candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" : "",
 					candidate->username, candidate->password);
 			jingle_iceudp_add_local_candidate(JINGLE_ICEUDP(transport), iceudp_candidate);
+			g_free(id);
 		}
 		return transport;
 	} else {
@@ -292,6 +295,26 @@
 }
 
 static void
+jingle_rtp_initiate_ack_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	JingleSession *session = data;
+
+	if (!strcmp(xmlnode_get_attrib(packet, "type"), "error") ||
+			xmlnode_get_child(packet, "error")) {
+		gchar *sid = jingle_session_get_sid(session);
+		purple_media_end(jingle_rtp_get_media(session), NULL, NULL);
+		g_hash_table_remove(jingle_session_get_js(
+				session)->medias, sid);
+		g_free(sid);
+		g_object_unref(session);
+		return;
+	}
+
+	jabber_iq_send(jingle_session_to_packet(session,
+			JINGLE_TRANSPORT_INFO));
+}
+
+static void
 jingle_rtp_ready_cb(PurpleMedia *media, gchar *sid, gchar *name, JingleSession *session)
 {
 	purple_debug_info("rtp", "ready-new: session: %s name: %s\n", sid, name);
@@ -299,16 +322,18 @@
 	if (sid == NULL && name == NULL) {
 		if (jingle_session_is_initiator(session) == TRUE) {
 			GList *contents = jingle_session_get_contents(session);
-
-			jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_INITIATE));
+			JabberIq *iq = jingle_session_to_packet(
+					session, JINGLE_SESSION_INITIATE);
 
-			for (; contents; contents = g_list_next(contents)) {
-				JingleContent *content = (JingleContent *)contents->data;
-				JingleTransport *transport = jingle_content_get_transport(content);
+			if (contents->data) {
+				JingleTransport *transport =
+						jingle_content_get_transport(contents->data);
 				if (JINGLE_IS_ICEUDP(transport))
-					jabber_iq_send(jingle_session_to_packet(session,
-							JINGLE_TRANSPORT_INFO));
+					jabber_iq_set_callback(iq,
+							jingle_rtp_initiate_ack_cb, session);
 			}
+
+			jabber_iq_send(iq);
 		} else {
 			jabber_iq_send(jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO));
 			jabber_iq_send(jingle_session_to_packet(session, JINGLE_SESSION_ACCEPT));
--- a/libpurple/protocols/jabber/libxmpp.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Sat Feb 21 05:15:28 2009 +0000
@@ -120,7 +120,7 @@
 	NULL, /* get_account_text_table */
 #ifdef USE_VV
 	jabber_initiate_media,          /* initiate_media */
-	jabber_can_do_media             /* can_do_media */
+	jabber_get_media_caps,                  /* get_media_caps */
 #else
 	NULL,					/* initiate_media */
 	NULL					/* can_do_media */
--- a/libpurple/protocols/jabber/parser.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/jabber/parser.c	Sat Feb 21 05:15:28 2009 +0000
@@ -86,15 +86,10 @@
 			}
 		}
 		for(i=0; i < nb_attributes * 5; i+=5) {
-			const char *prefix = (const char *)attributes[i + 1];
+			const char *attrib_ns = (const char *)attributes[i+2];
 			char *txt;
 			int attrib_len = attributes[i+4] - attributes[i+3];
 			char *attrib = g_malloc(attrib_len + 1);
-			char *attrib_ns = NULL;
-
-			if (attributes[i+2]) {
-				attrib_ns = g_strdup((char*)attributes[i+2]);
-			}
 
 			memcpy(attrib, attributes[i+3], attrib_len);
 			attrib[attrib_len] = '\0';
@@ -103,11 +98,7 @@
 			attrib = purple_unescape_html(txt);
 			g_free(txt);
 			xmlnode_set_attrib_with_namespace(node, (const char*) attributes[i], attrib_ns, attrib);
-			if (prefix && *prefix) {
-				node->prefix = g_strdup(prefix);
-			}
 			g_free(attrib);
-			g_free(attrib_ns);
 		}
 
 		js->current = node;
--- a/libpurple/protocols/msn/contact.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/contact.c	Sat Feb 21 05:15:28 2009 +0000
@@ -198,20 +198,38 @@
 	MsnCallbackState *state = data;
 	xmlnode *fault;
 	char *faultcode_str;
+	xmlnode *cachekey;
+	char *changed;
 
 	if (resp == NULL) {
 		purple_debug_error("msn",
 		                   "Operation {%s} failed. No response received from server.\n",
 		                   msn_contact_operation_str(state->action));
+		msn_session_set_error(state->session, MSN_ERROR_BAD_BLIST, NULL);
 		return;
 	}
 
+ 	/* Update CacheKey if necessary */
+ 	cachekey = xmlnode_get_child(resp->xml, "Header/ServiceHeader/CacheKeyChanged");
+ 	if (cachekey != NULL) {
+ 		changed = xmlnode_get_data(cachekey);
+ 		if (changed && !strcmp(changed, "true")) {
+ 			cachekey = xmlnode_get_child(resp->xml, "Header/ServiceHeader/CacheKey");
+ 			g_free(state->session->abch_cachekey);
+ 			state->session->abch_cachekey = xmlnode_get_data(cachekey);
+ 			purple_debug_info("msn", "Updated CacheKey for %s to '%s'.\n",
+ 			                  purple_account_get_username(state->session->account),
+ 			                  state->session->abch_cachekey);
+ 		}
+ 		g_free(changed);
+ 	}
+
 	fault = xmlnode_get_child(resp->xml, "Body/Fault");
 
 	if (fault == NULL) {
 		/* No errors */
 		if (state->cb)
-			((MsnSoapCallback)state->cb)(req, resp, data);
+			state->cb(req, resp, data);
 		msn_callback_state_free(state);
 		return;
 	}
@@ -230,7 +248,7 @@
 	else
 	{
 		if (state->cb) {
-			((MsnSoapCallback)state->cb)(req, resp, data);
+			state->cb(req, resp, data);
 		} else {
 			/* We don't know how to respond to this faultcode, so log it */
 			char *str = xmlnode_to_str(fault, NULL);
@@ -247,6 +265,14 @@
 static gboolean
 msn_contact_request(MsnCallbackState *state)
 {
+	xmlnode *cachekey = xmlnode_get_child(state->body,
+	                                      "Header/ABApplicationHeader/CacheKey");
+	if (cachekey != NULL)
+		xmlnode_free(cachekey);
+	if (state->session->abch_cachekey != NULL) {
+		cachekey = xmlnode_new_child(xmlnode_get_child(state->body, "Header/ABApplicationHeader"), "CacheKey");
+		xmlnode_insert_data(cachekey, state->session->abch_cachekey, -1);
+	}
 	if (state->token == NULL)
 		state->token = xmlnode_get_child(state->body,
 			"Header/ABAuthHeader/TicketToken");
@@ -891,8 +917,7 @@
 		/*
 		msn_get_address_book(session, NULL, NULL);
 		*/
-		msn_session_disconnect(session);
-		purple_connection_error_reason(session->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to retrieve MSN Address Book"));
+		msn_session_set_error(session, MSN_ERROR_BAD_BLIST, NULL);
 	}
 }
 
--- a/libpurple/protocols/msn/msg.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/msg.c	Sat Feb 21 05:15:28 2009 +0000
@@ -80,7 +80,7 @@
 	msg->ref_count++;
 
 #ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message ref (%p)[%d]\n", msg, msg->ref_count);
+	purple_debug_info("msn", "message ref (%p)[%" G_GSIZE_FORMAT "]\n", msg, msg->ref_count);
 #endif
 
 	return msg;
@@ -95,7 +95,7 @@
 	msg->ref_count--;
 
 #ifdef MSN_DEBUG_MSG
-	purple_debug_info("msn", "message unref (%p)[%d]\n", msg, msg->ref_count);
+	purple_debug_info("msn", "message unref (%p)[%" G_GSIZE_FORMAT "]\n", msg, msg->ref_count);
 #endif
 
 	if (msg->ref_count == 0)
@@ -352,6 +352,14 @@
 			memcpy(msg->body, tmp, msg->body_len);
 			msg->body[msg->body_len] = '\0';
 		}
+		
+		if (msg->charset == NULL) {
+			char *body = g_convert(msg->body, msg->body_len, "UTF-8",
+			                       "ISO-8859-1", NULL, &msg->body_len, NULL);
+			g_free(msg->body);
+			msg->body = body;
+			msg->charset = g_strdup("UTF-8");
+		}
 	}
 
 	g_free(tmp_base);
--- a/libpurple/protocols/msn/msnutils.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Sat Feb 21 05:15:28 2009 +0000
@@ -446,7 +446,7 @@
 	}
 
 	if (fontface == NULL)
-		fontface = g_strdup("MS Sans Serif");
+		fontface = g_strdup("Segoe UI");
 
 	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
 								  encode_spaces(fontface),
--- a/libpurple/protocols/msn/nexus.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/nexus.c	Sat Feb 21 05:15:28 2009 +0000
@@ -74,6 +74,7 @@
 	for (i = 0; i < nexus->token_len; i++) {
 		g_hash_table_destroy(nexus->tokens[i].token);
 		g_free(nexus->tokens[i].secret);
+		g_slist_free(nexus->tokens[i].updates);
 	}
 
 	g_free(nexus->tokens);
@@ -235,6 +236,10 @@
 struct _MsnNexusUpdateData {
 	MsnNexus *nexus;
 	int id;
+};
+
+typedef struct _MsnNexusUpdateCallback MsnNexusUpdateCallback;
+struct _MsnNexusUpdateCallback {
 	GSourceFunc cb;
 	gpointer data;
 };
@@ -428,6 +433,7 @@
 	char *nonce;
 	gsize len;
 	char *key;
+	GSList *updates;
 
 #if 0
 	char *decrypted_pp;
@@ -489,8 +495,15 @@
 		g_free(decrypted_data);
 	}
 
-	if (ud->cb)
-		purple_timeout_add(0, ud->cb, ud->data);
+	updates = nexus->tokens[ud->id].updates;
+	nexus->tokens[ud->id].updates = NULL;
+	while (updates != NULL) {
+		MsnNexusUpdateCallback *update = updates->data;
+		if (update->cb)
+			purple_timeout_add(0, update->cb, update->data);
+		g_free(update);
+		updates = g_slist_delete_link(updates, updates);
+	}
 
 	g_free(ud);
 }
@@ -500,6 +513,7 @@
 {
 	MsnSession *session = nexus->session;
 	MsnNexusUpdateData *ud;
+	MsnNexusUpdateCallback *update;
 	PurpleCipherContext *sha1;
 	PurpleCipherContext *hmac;
 
@@ -526,16 +540,31 @@
 	char *request;
 	MsnSoapMessage *soap;
 
-	purple_debug_info("msn",
-	                  "Updating ticket for user '%s' on domain '%s'\n",
-	                  purple_account_get_username(session->account),
-	                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+	update = g_new0(MsnNexusUpdateCallback, 1);
+	update->cb = cb;
+	update->data = data;
+
+	if (nexus->tokens[id].updates != NULL) {
+		/* Update already in progress. Just add to list and return. */
+		purple_debug_info("msn",
+		                  "Ticket update for user '%s' on domain '%s' in progress. Adding request to queue.\n",
+		                  purple_account_get_username(session->account),
+		                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+		nexus->tokens[id].updates = g_slist_prepend(nexus->tokens[id].updates,
+		                                            update);
+		return;
+	} else {
+		purple_debug_info("msn",
+		                  "Updating ticket for user '%s' on domain '%s'\n",
+		                  purple_account_get_username(session->account),
+		                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+		nexus->tokens[id].updates = g_slist_prepend(nexus->tokens[id].updates,
+		                                            update);
+	}
 
 	ud = g_new0(MsnNexusUpdateData, 1);
 	ud->nexus = nexus;
 	ud->id = id;
-	ud->cb = cb;
-	ud->data = data;
 
 	sha1 = purple_cipher_context_new_by_name("sha1", NULL);
 
--- a/libpurple/protocols/msn/nexus.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/nexus.h	Sat Feb 21 05:15:28 2009 +0000
@@ -204,6 +204,7 @@
 	GHashTable *token;
 	char *secret;
 	time_t expiry;
+	GSList *updates;
 };
 
 typedef struct _MsnNexus MsnNexus;
--- a/libpurple/protocols/msn/notification.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Sat Feb 21 05:15:28 2009 +0000
@@ -623,6 +623,18 @@
 		if (user->passport && !strcmp(user->passport, "messenger@microsoft.com"))
 			continue;
 
+		if ((user->list_op & MSN_LIST_OP_MASK) == (MSN_LIST_AL_OP | MSN_LIST_BL_OP)) {
+			/* The server will complain if we send it a user on both the
+			   Allow and Block lists. So assume they're on the Block list
+			   and remove them from the Allow list in the membership lists to
+			   stop this from happening again. */
+			purple_debug_warning("msn",
+			                     "User %s is on both Allow and Block list,"
+			                     "removing from Allow list.\n",
+			                     user->passport);
+			msn_userlist_rem_buddy_from_list(session->userlist, user->passport, MSN_LIST_AL);
+		}
+
 		msn_add_contact_xml(session, adl_node, user->passport,
 			user->list_op & MSN_LIST_OP_MASK, user->networkid);
 
@@ -1619,19 +1631,25 @@
 		return;
 	}
 
-	psm_str = msn_get_psm(cmd->payload,len);
-	msn_user_set_statusline(user, psm_str);
-	g_free(psm_str);
+	if (len != 0) {
+		psm_str = msn_get_psm(cmd->payload,len);
+		msn_user_set_statusline(user, psm_str);
+		g_free(psm_str);
 
-	str = msn_get_currentmedia(cmd->payload, len);
-	if (msn_parse_currentmedia(str, &media))
-		msn_user_set_currentmedia(user, &media);
-	else
+		str = msn_get_currentmedia(cmd->payload, len);
+		if (msn_parse_currentmedia(str, &media))
+			msn_user_set_currentmedia(user, &media);
+		else
+			msn_user_set_currentmedia(user, NULL);
+		g_free(media.title);
+		g_free(media.album);
+		g_free(media.artist);
+		g_free(str);
+
+	} else {
+		msn_user_set_statusline(user, NULL);
 		msn_user_set_currentmedia(user, NULL);
-	g_free(media.title);
-	g_free(media.album);
-	g_free(media.artist);
-	g_free(str);
+	}
 
 	msn_user_update(user);
 }
--- a/libpurple/protocols/msn/session.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/session.c	Sat Feb 21 05:15:28 2009 +0000
@@ -90,8 +90,10 @@
 	msn_userlist_destroy(session->userlist);
 
 	g_free(session->psm);
-
+	g_free(session->abch_cachekey);
+#if 0
 	g_free(session->blocked_text);
+#endif
 
 	g_free(session->passport_info.kv);
 	g_free(session->passport_info.sid);
--- a/libpurple/protocols/msn/session.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/session.h	Sat Feb 21 05:15:28 2009 +0000
@@ -94,11 +94,11 @@
 	gboolean http_method;
 
 	MsnNotification *notification;
-	MsnNexus *nexus;
-	MsnOim		*oim;
-	MsnSync *sync;
-
-	MsnUserList *userlist;
+	MsnNexus        *nexus;
+	MsnOim          *oim;
+	MsnSync         *sync;
+	MsnUserList     *userlist;
+	char            *abch_cachekey;
 
 	int servconns_count; /**< The count of server connections. */
 	GList *switches; /**< The list of all the switchboards. */
@@ -107,7 +107,9 @@
 	/*psm info*/
 	char *psm;
 
+#if 0
 	char *blocked_text;
+#endif
 
 	struct
 	{
--- a/libpurple/protocols/msn/slpcall.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Sat Feb 21 05:15:28 2009 +0000
@@ -47,6 +47,7 @@
 	if (!slpcall->pending && !slpcall->progress)
 	{
 		msn_slpcall_destroy(slpcall);
+		slpcall->timer = 0;
 		return FALSE;
 	}
 
@@ -222,8 +223,10 @@
 
 		if (slpcall != NULL)
 		{
-			if (slpcall->timer)
+			if (slpcall->timer) {
 				purple_timeout_remove(slpcall->timer);
+				slpcall->timer = 0;
+			}
 
 			slpcall->cb(slpcall, body, body_len);
 
--- a/libpurple/protocols/msn/state.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/state.c	Sat Feb 21 05:15:28 2009 +0000
@@ -189,12 +189,12 @@
 
 	purple_debug_info("msn", "msn get PSM\n");
 	payloadNode = xmlnode_from_str(xml_str, len);
-	if (!payloadNode){
+	if (!payloadNode) {
 		purple_debug_error("msn", "PSM XML parse Error!\n");
 		return NULL;
 	}
 	psmNode = xmlnode_get_child(payloadNode, "PSM");
-	if (psmNode == NULL){
+	if (psmNode == NULL) {
 		purple_debug_info("msn", "No PSM status Node");
 		xmlnode_free(payloadNode);
 		return NULL;
@@ -213,7 +213,7 @@
 	char *ret;
 	PurpleStatus *status = purple_presence_get_status(presence, "tune");
 	if (!status || !purple_status_is_active(status))
-		return g_strdup_printf("\\0Music\\00\\0\\0");
+		return NULL;
 
 	title = purple_status_get_attr_string(status, PURPLE_TUNE_TITLE);
 	game = purple_status_get_attr_string(status, "game");
@@ -234,7 +234,7 @@
 	else if (office && *office)
 		ret = g_strdup_printf("\\0Office\\01\\0Editing {0}\\0%s\\0", office);
 	else
-		ret = g_strdup_printf("\\0Music\\00\\0\\0");
+		ret = NULL;
 
 	return ret;
 }
--- a/libpurple/protocols/msn/transaction.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/msn/transaction.c	Sat Feb 21 05:15:28 2009 +0000
@@ -199,6 +199,7 @@
 	if (trans->timeout_cb != NULL)
 		trans->timeout_cb(trans->cmdproc, trans);
 
+	trans->timer = 0;
 	return FALSE;
 }
 
--- a/libpurple/protocols/myspace/myspace.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Sat Feb 21 05:15:28 2009 +0000
@@ -1146,7 +1146,11 @@
 			break;
 
 		case MSIM_CONTACT_LIST_INITIAL_FRIENDS:
-			/* Nothing */
+			/* The session is now set up, ready to be connected. This emits the
+			 * signedOn signal, so clients can now do anything with msimprpl, and
+			 * we're ready for it (session key, userid, username all setup). */
+			purple_connection_update_progress(session->gc, _("Connected"), 3, 4);
+			purple_connection_set_state(session->gc, PURPLE_CONNECTED);
 			break;
 	}
 
@@ -1185,12 +1189,6 @@
 	/* Set display name to username (otherwise will show email address) */
 	purple_connection_set_display_name(session->gc, session->username);
 
-	/* The session is now set up, ready to be connected. This emits the
-	 * signedOn signal, so clients can now do anything with msimprpl, and
-	 * we're ready for it (session key, userid, username all setup). */
-	purple_connection_update_progress(session->gc, _("Connected"), 3, 4);
-	purple_connection_set_state(session->gc, PURPLE_CONNECTED);
-
 	body = msim_msg_new(
 			"UserID", MSIM_TYPE_INTEGER, session->userid,
 			NULL);
--- a/libpurple/protocols/oscar/family_icbm.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Sat Feb 21 05:15:28 2009 +0000
@@ -1566,9 +1566,10 @@
 
 static int incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
 {
-	guint16 type, length;
+	guint16 type, length, magic1, msglen;
 	aim_rxcallback_t userfunc;
 	int ret = 0;
+	int rev = 0;
 	struct aim_incomingim_ch1_args args;
 	unsigned int endpos;
 
@@ -1603,10 +1604,30 @@
 			 *   - 0101 -- Unknown
 			 *   - Message
 			 *
+			 * Slick and possible others reverse 'Features' and 'Messages' section.
+			 * Thus, the TLV could have following layout:
+			 *   - 0101 -- Unknown (possibly magic for message section)
+			 *   - Message
+			 *   - 0501 -- Unknown (possibly magic for features section)
+			 *   - Features: Don't know how to interpret these
 			 */
 
-			byte_stream_get8(bs); /* 05 */
-			byte_stream_get8(bs); /* 01 */
+			magic1 = byte_stream_get16(bs); /* 0501 or 0101 */
+			if (magic1 == 0x101) /* Bad, message comes before attributes */
+			{
+				/* Jump to the features section */
+				msglen = byte_stream_get16(bs);
+				bs->offset += msglen;
+				rev = 1;
+
+				magic1 = byte_stream_get16(bs); /* 0501 */
+			}
+
+			if (magic1 != 0x501)
+			{
+				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				break;
+			}
 
 			args.featureslen = byte_stream_get16(bs);
 			if (args.featureslen > byte_stream_empty(bs))
@@ -1624,11 +1645,25 @@
 				args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
 			}
 
+			if (rev)
+			{
+				/* Fix buffer back to message */
+				bs->offset -= args.featureslen + 2 + 2 + msglen + 2 + 2;
+			}
+
+			magic1 = byte_stream_get16(bs); /* 01 01 */
+			if (magic1 != 0x101) /* Bad, message comes before attributes */
+			{
+				purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->sn);
+				break;
+			}
+			msglen = byte_stream_get16(bs);
+
 			/*
 			 * The rest of the TLV contains one or more message
 			 * blocks...
 			 */
-			incomingim_ch1_parsemsgs(od, userinfo, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args);
+			incomingim_ch1_parsemsgs(od, userinfo, bs->data + bs->offset - 2 - 2 /* XXX evil!!! */, msglen + 2 + 2, &args);
 
 		} else if (type == 0x0003) { /* Server Ack Requested */
 
--- a/libpurple/protocols/qq/ChangeLog	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/qq/ChangeLog	Sat Feb 21 05:15:28 2009 +0000
@@ -1,3 +1,6 @@
+2009.02.08 - flos <lonicerae(at)gmail.com>
+	* Fixed showing message of chat room when message comes in
+
 2008.12.28 - flos <lonicerae(at)gmail.com>
 	* Fixes #7908
 
--- a/libpurple/protocols/qq/buddy_info.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/qq/buddy_info.c	Sat Feb 21 05:15:28 2009 +0000
@@ -108,41 +108,41 @@
 } QQ_FIELD_INFO;
 
 static const QQ_FIELD_INFO field_infos[] = {
-	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "uid", 			N_("QQ Number"), NULL, 0 },
-	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "nick", 			N_("Nickname"), NULL, 0 },
-	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "country", 	N_("Country/Region"), NULL, 0 },
-	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "province", 	N_("Province/State"), NULL, 0 },
-	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "zipcode", 	N_("Zipcode"), NULL, 0 },
-	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "address", 	N_("Address"), NULL, 0 },
-	{ QQ_FIELD_CONTACT, QQ_FIELD_STRING, "tel", 				N_("Phone Number"), NULL, 0 },
-	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "age", 			N_("Age"), NULL, 0 },
-	{ QQ_FIELD_BASE, 		QQ_FIELD_CHOICE, "gender", 		N_("Gender"), genders, QQ_GENDER_SIZE },
-	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "name", 			N_("Name"), NULL, 0 },
-	{ QQ_FIELD_CONTACT, QQ_FIELD_STRING, "email", 			N_("Email"), NULL, 0 },
-	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_sn",		"Pager Serial Num", NULL, 0 },
+	{ QQ_FIELD_BASE, 	QQ_FIELD_STRING, "uid", 	N_("QQ Number"), NULL, 0 },
+	{ QQ_FIELD_BASE, 	QQ_FIELD_STRING, "nick", 	N_("Nickname"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 	QQ_FIELD_STRING, "country", 	N_("Country/Region"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 	QQ_FIELD_STRING, "province", 	N_("Province/State"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 	QQ_FIELD_STRING, "zipcode", 	N_("Zipcode"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 	QQ_FIELD_STRING, "address", 	N_("Address"), NULL, 0 },
+	{ QQ_FIELD_CONTACT, 	QQ_FIELD_STRING, "tel", 	N_("Phone Number"), NULL, 0 },
+	{ QQ_FIELD_BASE, 	QQ_FIELD_STRING, "age", 	N_("Age"), NULL, 0 },
+	{ QQ_FIELD_BASE, 	QQ_FIELD_CHOICE, "gender", 	N_("Gender"), genders, QQ_GENDER_SIZE },
+	{ QQ_FIELD_BASE, 	QQ_FIELD_STRING, "name", 	N_("Name"), NULL, 0 },
+	{ QQ_FIELD_CONTACT, 	QQ_FIELD_STRING, "email", 	N_("Email"), NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_sn",	"Pager Serial Num", NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_num",	"Pager Num", NULL, 0 },
-	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_sp",		"Pager Serivce Provider", NULL, 0 },
-	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_sta",		"Pager Station Num", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_sp",	"Pager Serivce Provider", NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_sta",	"Pager Station Num", NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "pg_type",	"Pager Type", NULL, 0 },
-	{ QQ_FIELD_BASE, 		QQ_FIELD_STRING, "occupation", 	N_("Occupation"), NULL, 0 },
-	{ QQ_FIELD_CONTACT, QQ_FIELD_STRING, "homepage", 		N_("Homepage"), NULL, 0 },
-	{ QQ_FIELD_BASE, 		QQ_FIELD_BOOL, 	"auth", 				N_("Authorize adding"), NULL, 0 },
+	{ QQ_FIELD_BASE, 	QQ_FIELD_STRING, "occupation", 	N_("Occupation"), NULL, 0 },
+	{ QQ_FIELD_CONTACT, 	QQ_FIELD_STRING, "homepage", 	N_("Homepage"), NULL, 0 },
+	{ QQ_FIELD_BASE, 	QQ_FIELD_BOOL, 	"auth", 	N_("Authorize adding"), NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow1",	"Unknow1", NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow2",	"Unknow2", NULL, 0 },
-	{ QQ_FIELD_UNUSED, 		QQ_FIELD_STRING, "face",				"Face", NULL, 0 },
-	{ QQ_FIELD_CONTACT, QQ_FIELD_STRING, "mobile",		N_("Cellphone Number"), NULL, 0 },
-	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "mobile_type","Cellphone Type", NULL, 0 },
-	{ QQ_FIELD_BASE, 		QQ_FIELD_MULTI, 	"intro", 		N_("Personal Introduction"), NULL, 0 },
-	{ QQ_FIELD_ADDR, 		QQ_FIELD_STRING, "city",			N_("City/Area"), NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "face",	"Face", NULL, 0 },
+	{ QQ_FIELD_CONTACT, 	QQ_FIELD_STRING, "mobile",	N_("Cellphone Number"), NULL, 0 },
+	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "mobile_type", "Cellphone Type", NULL, 0 },
+	{ QQ_FIELD_BASE, 	QQ_FIELD_MULTI,  "intro", 	N_("Personal Introduction"), NULL, 0 },
+	{ QQ_FIELD_ADDR, 	QQ_FIELD_STRING, "city",	N_("City/Area"), NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow3",	"Unknow3", NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow4",	"Unknow4", NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow5",	"Unknow5", NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_CHOICE, "pub_mobile",	N_("Publish Mobile"), publish_types, QQ_PUBLISH_SIZE },
-	{ QQ_FIELD_CONTACT, QQ_FIELD_CHOICE, "pub_contact",	N_("Publish Contact"), publish_types, QQ_PUBLISH_SIZE },
-	{ QQ_FIELD_EXT, 		QQ_FIELD_STRING, "college",			N_("College"), NULL, 0 },
-	{ QQ_FIELD_EXT, 		QQ_FIELD_CHOICE, "horoscope",	N_("Horoscope"), horoscope_names, QQ_HOROSCOPE_SIZE },
-	{ QQ_FIELD_EXT, 		QQ_FIELD_CHOICE, "zodiac",		N_("Zodiac"), zodiac_names, QQ_ZODIAC_SIZE },
-	{ QQ_FIELD_EXT, 		QQ_FIELD_CHOICE, "blood",			N_("Blood"), blood_types, QQ_BLOOD_SIZE },
+	{ QQ_FIELD_CONTACT, 	QQ_FIELD_CHOICE, "pub_contact",	N_("Publish Contact"), publish_types, QQ_PUBLISH_SIZE },
+	{ QQ_FIELD_EXT, 	QQ_FIELD_STRING, "college",	N_("College"), NULL, 0 },
+	{ QQ_FIELD_EXT, 	QQ_FIELD_CHOICE, "horoscope",	N_("Horoscope"), horoscope_names, QQ_HOROSCOPE_SIZE },
+	{ QQ_FIELD_EXT, 	QQ_FIELD_CHOICE, "zodiac",	N_("Zodiac"), zodiac_names, QQ_ZODIAC_SIZE },
+	{ QQ_FIELD_EXT, 	QQ_FIELD_CHOICE, "blood",	N_("Blood"), blood_types, QQ_BLOOD_SIZE },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "qq_show",	"QQ Show", NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "unknow6",	"Unknow6", NULL, 0 },
 	{ QQ_FIELD_UNUSED, 	QQ_FIELD_STRING, "LAST_2005",	"LAST_2005", NULL, 0 }
@@ -196,7 +196,9 @@
 				break;
 			case QQ_FIELD_CHOICE:
 				choice_num = strtol(segments[index], NULL, 10);
-				if (choice_num < 0 || choice_num >= field_infos[index].choice_size)	choice_num = 0;
+				if (choice_num < 0 || choice_num >= field_infos[index].choice_size) {
+					choice_num = 0;
+				}
 
 				purple_notify_user_info_add_pair(user_info, field_infos[index].text, field_infos[index].choice[choice_num]);
 				break;
--- a/libpurple/protocols/qq/buddy_info.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/qq/buddy_info.h	Sat Feb 21 05:15:28 2009 +0000
@@ -31,7 +31,7 @@
 #include "buddy_opt.h"
 #include "qq.h"
 
-/* use is openq2005
+/* use in qq2005
  * ext_flag: (0-7)
  *        bit1 => qq space
  * comm_flag: (0-7)
--- a/libpurple/protocols/qq/group_im.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/qq/group_im.c	Sat Feb 21 05:15:28 2009 +0000
@@ -165,6 +165,7 @@
 		guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time)
 {
 	PurpleConversation *conv;
+	qq_data *qd;
 	qq_buddy_data *bd;
 	qq_room_data *rmd;
 	gchar *from;
@@ -172,15 +173,17 @@
 	g_return_if_fail(gc != NULL && room_id != 0);
 	g_return_if_fail(msg != NULL);
 
+	qd = (qq_data *)gc->proto_data;
 	conv = purple_find_chat(gc, room_id);
 	rmd = qq_room_data_find(gc, room_id);
 	g_return_if_fail(rmd != NULL);
 
-	if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/auto_popup_conversation")) {
+	purple_debug_info("QQ", "is_show_chat:%d\n", qd->is_show_chat);
+	if (NULL == conv && qd->is_show_chat) {
 		conv = qq_room_conv_open(gc, rmd);
 	}
 
-	if (conv == NULL) {
+	if (NULL == conv) {
 		purple_debug_info("QQ", "Conversion of %u is not open, missing from %d:/n%s/v",
 				room_id, uid_from, msg);
 		return;
--- a/libpurple/protocols/qq/qq.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/qq/qq.c	Sat Feb 21 05:15:28 2009 +0000
@@ -173,6 +173,7 @@
 
 	qd->is_show_notice = purple_account_get_bool(account, "show_notice", TRUE);
 	qd->is_show_news = purple_account_get_bool(account, "show_news", TRUE);
+	qd->is_show_chat = purple_account_get_bool(account, "show_chat", TRUE);
 
 	qd->resend_times = purple_prefs_get_int("/plugins/prpl/qq/resend_times");
 	if (qd->resend_times <= 1) qd->itv_config.resend = 4;
@@ -1097,6 +1098,9 @@
 	option = purple_account_option_bool_new(_("Show server news"), "show_news", TRUE);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
+	option = purple_account_option_bool_new(_("Show chat room when msg comes"), "show_chat", TRUE);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
 	option = purple_account_option_int_new(_("Keep alive interval (seconds)"), "keep_alive_interval", 60);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
@@ -1106,7 +1110,6 @@
 	purple_prefs_add_none("/plugins/prpl/qq");
 	purple_prefs_add_bool("/plugins/prpl/qq/show_status_by_icon", TRUE);
 	purple_prefs_add_bool("/plugins/prpl/qq/show_fake_video", FALSE);
-	purple_prefs_add_bool("/plugins/prpl/qq/auto_popup_conversation", TRUE);
 	purple_prefs_add_bool("/plugins/prpl/qq/auto_get_authorize_info", TRUE);
 	purple_prefs_add_int("/plugins/prpl/qq/resend_interval", 3);
 	purple_prefs_add_int("/plugins/prpl/qq/resend_times", 10);
--- a/libpurple/protocols/qq/qq.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/qq/qq.h	Sat Feb 21 05:15:28 2009 +0000
@@ -182,6 +182,7 @@
 
 	gboolean is_show_notice;
 	gboolean is_show_news;
+	gboolean is_show_chat;
 
 	guint16 send_im_id;		/* send IM sequence number */
 };
--- a/libpurple/protocols/qq/qq_network.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Sat Feb 21 05:15:28 2009 +0000
@@ -214,7 +214,7 @@
 		qd->connect_retry = QQ_CONNECT_MAX;
 	}
 
-	segments = g_strsplit(qd->curr_server, ":", 0);
+	segments = g_strsplit_set(qd->curr_server, ":", 0);
 	tmp_server = g_strdup(segments[0]);
 	if (NULL != segments[1]) {
 		port = atoi(segments[1]);
--- a/libpurple/protocols/silc10/silc.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/silc10/silc.c	Sat Feb 21 05:15:28 2009 +0000
@@ -1957,4 +1957,4 @@
 #endif
 }
 
-PURPLE_INIT_PLUGIN(silc, init_plugin, info);
+PURPLE_INIT_PLUGIN(silc10, init_plugin, info);
--- a/libpurple/protocols/simple/simple.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/protocols/simple/simple.c	Sat Feb 21 05:15:28 2009 +0000
@@ -196,7 +196,7 @@
 {
 	struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
 	struct simple_buddy *b;
-	if(strcmp("sip:", buddy->name)) {
+	if(strncmp(buddy->name, "sip:", 4)) {
 		gchar *buf = g_strdup_printf("sip:%s", buddy->name);
 		purple_blist_rename_buddy(buddy, buf);
 		g_free(buf);
@@ -834,10 +834,10 @@
 		"Event: presence\r\n",
 		expiration);
 
-	if(strstr(buddy->name, "sip:"))
+	if(strncmp(buddy->name, "sip:", 4))
+		to = g_strdup_printf("sip:%s", buddy->name);
+	else
 		to = g_strdup(buddy->name);
-	else
-		to = g_strdup_printf("sip:%s", buddy->name);
 
 	tmp = get_contact(sip);
 	contact = g_strdup_printf("%sContact: %s\r\n", tmp2, tmp);
@@ -881,7 +881,7 @@
 
 
 	tmp = sipmsg_find_header(msg, "Event");
-	if(tmp && !strcmp(tmp, "vnd-microsoft-roaming-contacts")){
+	if(tmp && !strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)){
 
 		purple_debug_info("simple", "simple_add_lcs_contacts->%s-%d\n", msg->body, len);
 		/*Convert the contact from XML to Purple Buddies*/
@@ -1013,11 +1013,11 @@
 static void simple_send_message(struct simple_account_data *sip, const char *to, const char *msg, const char *type) {
 	gchar *hdr;
 	gchar *fullto;
-	if(strcmp("sip:", to)) {
+	if(strncmp(to, "sip:", 4))
 		fullto = g_strdup_printf("sip:%s", to);
-	} else {
+	else
 		fullto = g_strdup(to);
-	}
+
 	if(type) {
 		hdr = g_strdup_printf("Content-Type: %s\r\n", type);
 	} else {
@@ -1050,12 +1050,12 @@
 	purple_debug(PURPLE_DEBUG_MISC, "simple", "got message from %s: %s\n", from, msg->body);
 
 	contenttype = sipmsg_find_header(msg, "Content-Type");
-	if(!contenttype || !strcmp(contenttype, "text/plain") || !strcmp(contenttype, "text/html")) {
+	if(!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
 		serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
 		send_sip_response(sip->gc, msg, 200, "OK", NULL);
 		found = TRUE;
 	}
-	else if(!strcmp(contenttype, "application/im-iscomposing+xml")) {
+	else if(!strncmp(contenttype, "application/im-iscomposing+xml", 30)) {
 		xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
 		xmlnode *state;
 		gchar *statedata;
--- a/libpurple/proxy.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/proxy.c	Sat Feb 21 05:15:28 2009 +0000
@@ -1177,56 +1177,47 @@
 }
 
 static void
-s4_canwrite(gpointer data, gint source, PurpleInputCondition cond)
+s4_host_resolved(GSList *hosts, gpointer data, const char *error_message)
 {
-	unsigned char packet[9];
-	struct hostent *hp;
 	PurpleProxyConnectData *connect_data = data;
-	int error = ETIMEDOUT;
-	int ret;
-
-	purple_debug_info("socks4 proxy", "Connected.\n");
+	unsigned char packet[9];
+	struct sockaddr *addr;
 
-	if (connect_data->inpa > 0)
-	{
-		purple_input_remove(connect_data->inpa);
-		connect_data->inpa = 0;
-	}
+	connect_data->query_data = NULL;
 
-	ret = purple_input_get_error(connect_data->fd, &error);
-	if ((ret != 0) || (error != 0))
-	{
-		if (ret != 0)
-			error = errno;
-		purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
+	if (error_message != NULL) {
+		purple_proxy_connect_data_disconnect(connect_data, error_message);
 		return;
 	}
 
-	/*
-	 * The socks4 spec doesn't include support for doing host name
-	 * lookups by the proxy.  Some socks4 servers do this via
-	 * extensions to the protocol.  Since we don't know if a
-	 * server supports this, it would need to be implemented
-	 * with an option, or some detection mechanism - in the
-	 * meantime, stick with plain old SOCKS4.
-	 */
-	/* TODO: Use purple_dnsquery_a() */
-	hp = gethostbyname(connect_data->host);
-	if (hp == NULL) {
+	if (hosts == NULL) {
 		purple_proxy_connect_data_disconnect_formatted(connect_data,
 				_("Error resolving %s"), connect_data->host);
 		return;
 	}
 
-	packet[0] = 4;
-	packet[1] = 1;
+	/* Discard the length... */
+	hosts = g_slist_delete_link(hosts, hosts);
+	addr = hosts->data;
+	hosts = g_slist_delete_link(hosts, hosts);
+
+	packet[0] = 0x04;
+	packet[1] = 0x01;
 	packet[2] = connect_data->port >> 8;
 	packet[3] = connect_data->port & 0xff;
-	packet[4] = (unsigned char)(hp->h_addr_list[0])[0];
-	packet[5] = (unsigned char)(hp->h_addr_list[0])[1];
-	packet[6] = (unsigned char)(hp->h_addr_list[0])[2];
-	packet[7] = (unsigned char)(hp->h_addr_list[0])[3];
-	packet[8] = 0;
+	memcpy(packet + 4, &((struct sockaddr_in *)addr)->sin_addr.s_addr, 4);
+	packet[8] = 0x00;
+
+	g_free(addr);
+
+	/* We could try the other hosts, but hopefully that shouldn't be necessary */
+	while (hosts != NULL) {
+		/* Discard the length... */
+		hosts = g_slist_delete_link(hosts, hosts);
+		/* Free the address... */
+		g_free(hosts->data);
+		hosts = g_slist_delete_link(hosts, hosts);
+	}
 
 	connect_data->write_buffer = g_memdup(packet, sizeof(packet));
 	connect_data->write_buf_len = sizeof(packet);
@@ -1235,7 +1226,74 @@
 
 	connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
 
-	proxy_do_write(connect_data, connect_data->fd, cond);
+	proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
+}
+
+static void
+s4_canwrite(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleProxyConnectData *connect_data = data;
+	int error = ETIMEDOUT;
+	int ret;
+
+	purple_debug_info("socks4 proxy", "Connected.\n");
+
+	if (connect_data->inpa > 0) {
+		purple_input_remove(connect_data->inpa);
+		connect_data->inpa = 0;
+	}
+
+	ret = purple_input_get_error(connect_data->fd, &error);
+	if ((ret != 0) || (error != 0)) {
+		if (ret != 0)
+			error = errno;
+		purple_proxy_connect_data_disconnect(connect_data, g_strerror(error));
+		return;
+	}
+
+	/*
+	 * The socks4 spec doesn't include support for doing host name lookups by
+	 * the proxy.  Many socks4 servers do this via the "socks4a" extension to
+	 * the protocol.  There doesn't appear to be a way to detect if a server
+	 * supports this, so we require that the user set a global option.
+	 */
+	if (purple_prefs_get_bool("/purple/proxy/socks4_remotedns")) {
+		unsigned char packet[9];
+		int len;
+
+		purple_debug_info("socks4 proxy", "Attempting to use remote DNS.\n");
+
+		packet[0] = 0x04;
+		packet[1] = 0x01;
+		packet[2] = connect_data->port >> 8;
+		packet[3] = connect_data->port & 0xff;
+		packet[4] = 0x00;
+		packet[5] = 0x00;
+		packet[6] = 0x00;
+		packet[7] = 0x01;
+		packet[8] = 0x00;
+
+		len = sizeof(packet) + strlen(connect_data->host) + 1;
+
+		connect_data->write_buffer = g_malloc0(len);
+		memcpy(connect_data->write_buffer, packet, sizeof(packet));
+		memcpy(connect_data->write_buffer + sizeof(packet), connect_data->host, strlen(connect_data->host));
+		connect_data->write_buf_len = len;
+		connect_data->written_len = 0;
+		connect_data->read_cb = s4_canread;
+
+		connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data);
+
+		proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE);
+	} else {
+		connect_data->query_data = purple_dnsquery_a(connect_data->host,
+				connect_data->port, s4_host_resolved, connect_data);
+
+		if (connect_data->query_data == NULL) {
+			purple_debug_error("proxy", "dns query failed unexpectedly.\n");
+			purple_proxy_connect_data_destroy(connect_data);
+		}
+	}
 }
 
 static void
@@ -1396,7 +1454,7 @@
 s5_sendconnect(gpointer data, int source)
 {
 	PurpleProxyConnectData *connect_data = data;
-	int hlen = strlen(connect_data->host);
+	size_t hlen = strlen(connect_data->host);
 	connect_data->write_buf_len = 5 + hlen + 2;
 	connect_data->write_buffer = g_malloc(connect_data->write_buf_len);
 	connect_data->written_len = 0;
@@ -1479,7 +1537,7 @@
 	int i;
 	unsigned char Kxoripad[65];
 	unsigned char Kxoropad[65];
-	int pwlen;
+	size_t pwlen;
 
 	cipher = purple_ciphers_find_cipher("md5");
 	ctx = purple_cipher_context_new(cipher, NULL);
@@ -1697,7 +1755,7 @@
 			return;
 
 		msg_ret = s5_parse_chap_msg(connect_data);
-	
+
 		if (msg_ret < 0)
 			return;
 
@@ -1777,7 +1835,7 @@
 	}
 
 	if (connect_data->read_buffer[1] == 0x02) {
-		gsize i, j;
+		size_t i, j;
 		const char *u, *p;
 
 		u = purple_proxy_info_get_username(connect_data->gpi);
@@ -1810,7 +1868,7 @@
 
 		return;
 	} else if (connect_data->read_buffer[1] == 0x03) {
-		gsize userlen;
+		size_t userlen;
 		userlen = strlen(purple_proxy_info_get_username(connect_data->gpi));
 
 		connect_data->write_buf_len = 7 + userlen;
@@ -1957,7 +2015,7 @@
 
 static void try_connect(PurpleProxyConnectData *connect_data)
 {
-	size_t addrlen;
+	socklen_t addrlen;
 	struct sockaddr *addr;
 	char ipaddr[INET6_ADDRSTRLEN];
 
@@ -1969,7 +2027,7 @@
 	inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr,
 			ipaddr, sizeof(ipaddr));
 #else
-	memcpy(ipaddr,inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
+	memcpy(ipaddr, inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
 			sizeof(ipaddr));
 #endif
 	purple_debug_info("proxy", "Attempting connection to %s\n", ipaddr);
@@ -2293,6 +2351,7 @@
 	purple_prefs_add_int("/purple/proxy/port", 0);
 	purple_prefs_add_string("/purple/proxy/username", "");
 	purple_prefs_add_string("/purple/proxy/password", "");
+	purple_prefs_add_bool("/purple/proxy/socks4_remotedns", FALSE);
 
 	/* Setup callbacks for the preferences. */
 	handle = purple_proxy_get_handle();
--- a/libpurple/prpl.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/prpl.c	Sat Feb 21 05:15:28 2009 +0000
@@ -524,10 +524,8 @@
 #endif
 }
 
-gboolean
-purple_prpl_can_do_media(PurpleAccount *account,
-			 const char *who, 
-			 PurpleMediaSessionType type)
+PurpleMediaCaps
+purple_prpl_get_media_caps(PurpleAccount *account, const char *who)
 {
 #ifdef USE_VV
 	PurpleConnection *gc = NULL;
@@ -541,14 +539,12 @@
 	if (prpl)
 		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
 	
-	if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, can_do_media)) {
-		return prpl_info->can_do_media(gc, who, type);
-	} else {
-		return FALSE;
+	if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info,
+			get_media_caps)) {
+		return prpl_info->get_media_caps(gc, who);
 	}
-#else
-	return FALSE;
 #endif
+	return PURPLE_MEDIA_CAPS_NONE;
 }
 
 /**************************************************************************
--- a/libpurple/prpl.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/prpl.h	Sat Feb 21 05:15:28 2009 +0000
@@ -466,11 +466,10 @@
 	 *
 	 * @param conn The connection the contact is on.
 	 * @param who The remote user to check for media capability with.
-	 * @param type The type of media session to check for.
-	 * @return @c TRUE The contact supports the given media type, or @c FALSE otherwise.
+	 * @return The media caps the contact supports.
 	 */
-	gboolean (*can_do_media)(PurpleConnection *gc, const char *who,
-				 PurpleMediaSessionType type);
+	PurpleMediaCaps (*get_media_caps)(PurpleConnection *gc,
+					  const char *who);
 };
 
 #define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \
@@ -765,13 +764,11 @@
  *
  * @param account The account the user is on.
  * @param who The name of the contact to check capabilities for.
- * @param type The type of media session to check for.
  *
- * @return @c TRUE if the contact supports the session type, else @c FALSE.
+ * @return The media caps the contact supports.
  */
-gboolean purple_prpl_can_do_media(PurpleAccount *account,
-				  const char *who, 
-				  PurpleMediaSessionType type);
+PurpleMediaCaps purple_prpl_get_media_caps(PurpleAccount *account,
+				  const char *who);
 
 /**
  * Initiates a media session with the given contact.
--- a/libpurple/savedstatuses.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/savedstatuses.c	Sat Feb 21 05:15:28 2009 +0000
@@ -148,11 +148,11 @@
 	/* Avoid using 0 because it's an invalid hash key */
 	status->creation_time = creation_time != 0 ? creation_time : 1;
 
-	while (g_hash_table_lookup(creation_times, &status->creation_time) != NULL)
+	while (g_hash_table_lookup(creation_times, (gconstpointer)status->creation_time) != NULL)
 		status->creation_time++;
 
 	g_hash_table_insert(creation_times,
-						&status->creation_time,
+						(gpointer)status->creation_time,
 						status);
 }
 
@@ -217,7 +217,7 @@
 				{
 					saved_statuses = g_list_remove(saved_statuses, saved_status);
 					creation_time = purple_savedstatus_get_creation_time(saved_status);
-					g_hash_table_remove(creation_times, &creation_time);
+					g_hash_table_remove(creation_times, (gconstpointer)creation_time);
 					free_saved_status(saved_status);
 				}
 			}
@@ -713,7 +713,7 @@
 
 	saved_statuses = g_list_remove(saved_statuses, status);
 	creation_time = purple_savedstatus_get_creation_time(status);
-	g_hash_table_remove(creation_times, &creation_time);
+	g_hash_table_remove(creation_times, (gconstpointer)creation_time);
 	free_saved_status(status);
 
 	schedule_save();
@@ -801,13 +801,13 @@
 PurpleSavedStatus *
 purple_savedstatus_get_default()
 {
-	int creation_time;
+	time_t creation_time;
 	PurpleSavedStatus *saved_status = NULL;
 
 	creation_time = purple_prefs_get_int("/purple/savedstatus/default");
 
 	if (creation_time != 0)
-		saved_status = g_hash_table_lookup(creation_times, &creation_time);
+		saved_status = g_hash_table_lookup(creation_times, (gconstpointer)creation_time);
 
 	if (saved_status == NULL)
 	{
@@ -828,13 +828,13 @@
 PurpleSavedStatus *
 purple_savedstatus_get_idleaway()
 {
-	int creation_time;
+	time_t creation_time;
 	PurpleSavedStatus *saved_status = NULL;
 
 	creation_time = purple_prefs_get_int("/purple/savedstatus/idleaway");
 
 	if (creation_time != 0)
-		saved_status = g_hash_table_lookup(creation_times, &creation_time);
+		saved_status = g_hash_table_lookup(creation_times, (gconstpointer)creation_time);
 
 	if (saved_status == NULL)
 	{
@@ -907,13 +907,13 @@
 PurpleSavedStatus *
 purple_savedstatus_get_startup()
 {
-	int creation_time;
+	time_t creation_time;
 	PurpleSavedStatus *saved_status = NULL;
 
 	creation_time = purple_prefs_get_int("/purple/savedstatus/startup");
 
 	if (creation_time != 0)
-		saved_status = g_hash_table_lookup(creation_times, &creation_time);
+		saved_status = g_hash_table_lookup(creation_times, (gconstpointer)creation_time);
 
 	if (saved_status == NULL)
 	{
@@ -1187,7 +1187,7 @@
 {
 	void *handle = purple_savedstatuses_get_handle();
 
-	creation_times = g_hash_table_new(g_int_hash, g_int_equal);
+	creation_times = g_hash_table_new(g_direct_hash, g_direct_equal);
 
 	/*
 	 * Using 0 as the creation_time is a special case.
--- a/libpurple/sslconn.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/sslconn.h	Sat Feb 21 05:15:28 2009 +0000
@@ -39,6 +39,7 @@
 
 #define PURPLE_SSL_DEFAULT_PORT 443
 
+/** @copydoc _PurpleSslConnection */
 typedef struct _PurpleSslConnection PurpleSslConnection;
 
 typedef void (*PurpleSslInputFunction)(gpointer, PurpleSslConnection *,
--- a/libpurple/upnp.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/upnp.c	Sat Feb 21 05:15:28 2009 +0000
@@ -567,7 +567,7 @@
 purple_upnp_discover_send_broadcast(UPnPDiscoveryData *dd)
 {
 	gchar *sendMessage = NULL;
-	gsize totalSize;
+	size_t totalSize;
 	gboolean sentSuccess;
 
 	/* because we are sending over UDP, if there is a failure
@@ -693,6 +693,7 @@
 		/* XXX: This should probably be async */
 		if(cb)
 			cb(NULL, cb_data, NULL, 0, NULL);
+		return NULL;
 	}
 	if(port == 0 || port == -1) {
 		port = DEFAULT_HTTP_PORT;
@@ -711,11 +712,11 @@
 	g_free(soapMessage);
 
 	gfud = purple_util_fetch_url_request_len(control_info.control_url, FALSE, NULL, TRUE,
-				totalSendMessage,  TRUE, MAX_UPNP_DOWNLOAD, cb, cb_data);
+				totalSendMessage, TRUE, MAX_UPNP_DOWNLOAD, cb, cb_data);
 
 	g_free(totalSendMessage);
 	g_free(addressOfControl);
-	
+
 	return gfud;
 }
 
--- a/libpurple/util.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/util.h	Sat Feb 21 05:15:28 2009 +0000
@@ -1207,7 +1207,7 @@
 G_CONST_RETURN gchar *purple_gai_strerror(gint errnum);
 
 /**
- * Compares two UTF-8 strings case-insensitively.  This string is
+ * Compares two UTF-8 strings case-insensitively.  This comparison is
  * more expensive than a simple g_utf8_collate() comparison because
  * it calls g_utf8_casefold() on each string, which allocates new
  * strings.
--- a/libpurple/version.h.in	Sat Feb 21 05:15:14 2009 +0000
+++ b/libpurple/version.h.in	Sat Feb 21 05:15:28 2009 +0000
@@ -24,8 +24,11 @@
 #ifndef _PURPLE_VERSION_H_
 #define _PURPLE_VERSION_H_
 
+/** The major version of the running libpurple. */
 #define PURPLE_MAJOR_VERSION (@PURPLE_MAJOR_VERSION@)
+/** The minor version of the running libpurple. */
 #define PURPLE_MINOR_VERSION (@PURPLE_MINOR_VERSION@)
+/** The micro version of the running libpurple. */
 #define PURPLE_MICRO_VERSION (@PURPLE_MICRO_VERSION@)
 
 #define PURPLE_VERSION_CHECK(x,y,z) ((x) == PURPLE_MAJOR_VERSION && \
--- a/pidgin/gtkblist.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/gtkblist.c	Sat Feb 21 05:15:28 2009 +0000
@@ -66,7 +66,7 @@
 #include <gtk/gtk.h>
 #include <gdk/gdk.h>
 
-#define HEADLINE_CLOSE_SIZE 12
+#define HEADLINE_CLOSE_SIZE 11
 
 typedef struct
 {
@@ -348,8 +348,9 @@
 {
 	/* if the buddy supports both audio and video, start a combined call,
 	 otherwise start a pure video session */
-	if (purple_prpl_can_do_media(purple_buddy_get_account(b),
-		purple_buddy_get_name(b), PURPLE_MEDIA_AUDIO)) {
+	if (purple_prpl_get_media_caps(purple_buddy_get_account(b),
+			purple_buddy_get_name(b)) &
+			PURPLE_MEDIA_CAPS_AUDIO_VIDEO) {
 		purple_prpl_initiate_media(purple_buddy_get_account(b),
 			purple_buddy_get_name(b), PURPLE_MEDIA_AUDIO | PURPLE_MEDIA_VIDEO);
 	} else {
@@ -1463,20 +1464,20 @@
 			G_CALLBACK(gtk_blist_menu_im_cb), buddy, 0, 0, NULL);
 	
 #ifdef USE_VV
-	if (prpl_info && prpl_info->can_do_media) {
-		PurpleConnection *gc = 
-			purple_account_get_connection(purple_buddy_get_account(buddy));
+	if (prpl_info && prpl_info->get_media_caps) {
+		PurpleAccount *account = purple_buddy_get_account(buddy);
 		const gchar *who = purple_buddy_get_name(buddy);
-		if (prpl_info->can_do_media(gc, who, PURPLE_MEDIA_AUDIO)) {
+		PurpleMediaCaps caps = purple_prpl_get_media_caps(account, who);
+		if (caps & PURPLE_MEDIA_CAPS_AUDIO) {
 			pidgin_new_item_from_stock(menu, _("_Audio Call"),
 				PIDGIN_STOCK_TOOLBAR_AUDIO_CALL,
 				G_CALLBACK(gtk_blist_menu_audio_call_cb), buddy, 0, 0, NULL);
 		}
-		if (prpl_info->can_do_media(gc, who, PURPLE_MEDIA_VIDEO | PURPLE_MEDIA_AUDIO)) {
+		if (caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO) {
 			pidgin_new_item_from_stock(menu, _("Audio/_Video Call"),
 				PIDGIN_STOCK_TOOLBAR_VIDEO_CALL,
 				G_CALLBACK(gtk_blist_menu_video_call_cb), buddy, 0, 0, NULL);
-		} else if (prpl_info->can_do_media(gc, who, PURPLE_MEDIA_VIDEO)) {
+		} else if (caps & PURPLE_MEDIA_CAPS_VIDEO) {
 			pidgin_new_item_from_stock(menu, _("_Video Call"),
 				PIDGIN_STOCK_TOOLBAR_VIDEO_CALL,
 				G_CALLBACK(gtk_blist_menu_video_call_cb), buddy, 0, 0, NULL);
@@ -4601,6 +4602,9 @@
 	if (!gtkblist)
 		return FALSE;
 
+	/* clear any tooltips */
+	pidgin_blist_tooltip_destroy();
+
 	widget = gtk_window_get_focus(GTK_WINDOW(gtkblist->window));
 
 	if (GTK_IS_IMHTML(widget) || GTK_IS_ENTRY(widget)) {
@@ -5434,7 +5438,8 @@
 			  NULL);
 	gtk_widget_set_name(gtkblist->headline_hbox, "gtk-tooltips");
 
-	gtkblist->headline_close = gtk_widget_render_icon(ebox, GTK_STOCK_CLOSE, HEADLINE_CLOSE_SIZE, NULL);
+	gtkblist->headline_close = gtk_widget_render_icon(ebox, GTK_STOCK_CLOSE,
+		gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC), NULL);
 	gtkblist->hand_cursor = gdk_cursor_new (GDK_HAND2);
 	gtkblist->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
 
@@ -7723,13 +7728,11 @@
 			if (!disabled_accounts) {
 				menuitem = gtk_menu_item_new_with_label(_("Enable Account"));
 				gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
-				gtk_widget_show(menuitem);
 
 				submenu = gtk_menu_new();
 				gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
 				gtk_menu_set_accel_path(GTK_MENU(submenu), N_("<PurpleMain>/Accounts/Enable Account"));
 				gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-				gtk_widget_show(submenu);
 
 				disabled_accounts = TRUE;
 			}
@@ -7751,7 +7754,6 @@
 			g_signal_connect(G_OBJECT(menuitem), "activate",
 				G_CALLBACK(enable_account_cb), account);
 			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-			gtk_widget_show(menuitem);
 		} else {
 			enabled_accounts = TRUE;
 		}
@@ -7793,21 +7795,18 @@
 			gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
 		}
 		gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
-		gtk_widget_show(menuitem);
 
 		submenu = gtk_menu_new();
 		gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
 		gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path_buf);
 		g_free(accel_path_buf);
 		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-		gtk_widget_show(submenu);
 
 
 		menuitem = gtk_menu_item_new_with_mnemonic(_("_Edit Account"));
 		g_signal_connect(G_OBJECT(menuitem), "activate",
 				G_CALLBACK(modify_account_cb), account);
 		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-		gtk_widget_show(menuitem);
 
 		pidgin_separator(submenu);
 
@@ -7819,7 +7818,6 @@
 			menuitem = gtk_menu_item_new_with_label(_("No actions available"));
 			gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
 			gtk_widget_set_sensitive(menuitem, FALSE);
-			gtk_widget_show(menuitem);
 		}
 
 		pidgin_separator(submenu);
@@ -7828,8 +7826,8 @@
 		g_signal_connect(G_OBJECT(menuitem), "activate",
 				G_CALLBACK(disable_account_cb), account);
 		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
-		gtk_widget_show(menuitem);
-	}
+	}
+	gtk_widget_show_all(accountmenu);
 }
 
 static GList *plugin_submenus = NULL;
@@ -7868,13 +7866,11 @@
 
 		menuitem = gtk_image_menu_item_new_with_label(_(plugin->info->name));
 		gtk_menu_shell_append(GTK_MENU_SHELL(pluginmenu), menuitem);
-		gtk_widget_show(menuitem);
 
 		plugin_submenus = g_list_append(plugin_submenus, menuitem);
 
 		submenu = gtk_menu_new();
 		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-		gtk_widget_show(submenu);
 
 		gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
 		path = g_strdup_printf("%s/Tools/%s", gtkblist->ift->path, plugin->info->name);
@@ -7883,6 +7879,7 @@
 
 		build_plugin_actions(submenu, plugin, NULL);
 	}
+	gtk_widget_show_all(pluginmenu);
 }
 
 static void
--- a/pidgin/gtkblist.h	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/gtkblist.h	Sat Feb 21 05:15:28 2009 +0000
@@ -27,6 +27,7 @@
 #ifndef _PIDGINBLIST_H_
 #define _PIDGINBLIST_H_
 
+/** @copydoc _PidginBuddyList */
 typedef struct _PidginBuddyList PidginBuddyList;
 
 enum {
--- a/pidgin/gtkconv.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/gtkconv.c	Sat Feb 21 05:15:28 2009 +0000
@@ -1976,6 +1976,9 @@
 	win      = gtkconv->win;
 	curconv = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook));
 
+	/* clear any tooltips */
+	pidgin_tooltip_destroy();
+
 	/* If CTRL was held down... */
 	if (event->state & GDK_CONTROL_MASK) {
 		switch (event->keyval) {
@@ -6446,19 +6449,19 @@
 			supports it */
 		if (account != NULL && purple_conversation_get_type(conv)
 					== PURPLE_CONV_TYPE_IM) {
-			gboolean audio = purple_prpl_can_do_media(account,
-					purple_conversation_get_name(conv),
-					PURPLE_MEDIA_AUDIO);
-			gboolean video = purple_prpl_can_do_media(account,
-					purple_conversation_get_name(conv),
-					PURPLE_MEDIA_VIDEO);
-			gboolean av = purple_prpl_can_do_media(account,
-					purple_conversation_get_name(conv),
-					PURPLE_MEDIA_AUDIO | PURPLE_MEDIA_VIDEO);
-
-			gtk_widget_set_sensitive(win->menu.audio_call, audio ? TRUE : FALSE);
-			gtk_widget_set_sensitive(win->menu.video_call, video ? TRUE : FALSE);
-			gtk_widget_set_sensitive(win->menu.audio_video_call, av ? TRUE : FALSE);
+			PurpleMediaCaps caps =
+					purple_prpl_get_media_caps(account,
+					purple_conversation_get_name(conv));
+
+			gtk_widget_set_sensitive(win->menu.audio_call,
+					caps & PURPLE_MEDIA_CAPS_AUDIO
+					? TRUE : FALSE);
+			gtk_widget_set_sensitive(win->menu.video_call,
+					caps & PURPLE_MEDIA_CAPS_VIDEO
+					? TRUE : FALSE);
+			gtk_widget_set_sensitive(win->menu.audio_video_call, 
+					caps & PURPLE_MEDIA_CAPS_AUDIO_VIDEO
+					? TRUE : FALSE);
 		} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
 			/* for now, don't care about chats... */
 			gtk_widget_set_sensitive(win->menu.audio_call, FALSE);
--- a/pidgin/gtkdialogs.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/gtkdialogs.c	Sat Feb 21 05:15:28 2009 +0000
@@ -457,12 +457,17 @@
 		  "warranty for this program.<BR><BR>"), PIDGIN_NAME, PIDGIN_NAME, PIDGIN_NAME);
 
 	g_string_append(str, "<FONT SIZE=\"4\">URL:</FONT> <A HREF=\""
-					PURPLE_WEBSITE "\">" PURPLE_WEBSITE "</A><BR/><BR/>");
-	g_string_append(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\">IRC:</FONT> "
-						   "#pidgin on irc.freenode.net<BR><BR>"));
+				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 via e-mail:</FONT>"
+				" <A HREF=\"mailto:support@pidgin.im\">support@pidgin.im</A>"
+				"<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>"));
 
 	/* Current Developers */
 	g_string_append_printf(str, "<FONT SIZE=\"4\">%s:</FONT><BR/>",
--- a/pidgin/gtkdocklet.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/gtkdocklet.c	Sat Feb 21 05:15:28 2009 +0000
@@ -88,13 +88,16 @@
 static GList *
 get_pending_list(guint max)
 {
-	GList *l_im = NULL;
-	GList *l_chat = NULL;
+	GList *l_im, *l_chat;
 
 	l_im = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM,
 						       PIDGIN_UNSEEN_TEXT,
 						       FALSE, max);
 
+	/* Short circuit if we have our information already */
+	if (max == 1 && l_im != NULL)
+		return l_im;
+
 	l_chat = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT,
 		 					 PIDGIN_UNSEEN_NICK,
 							 FALSE, max);
@@ -344,7 +347,9 @@
 docklet_menu_leave_enter(GtkWidget *menu, GdkEventCrossing *event, void *data)
 {
 	static guint hide_docklet_timer = 0;
-	if (event->type == GDK_LEAVE_NOTIFY && event->detail == GDK_NOTIFY_ANCESTOR) {
+
+	if (event->type == GDK_LEAVE_NOTIFY && (event->detail == GDK_NOTIFY_ANCESTOR ||
+			event->detail == GDK_NOTIFY_UNKNOWN)) {
 		purple_debug(PURPLE_DEBUG_INFO, "docklet", "menu leave-notify-event\n");
 		/* Add some slop so that the menu doesn't annoyingly disappear when mousing around */
 		if (hide_docklet_timer == 0) {
@@ -652,11 +657,9 @@
 
 		menuitem = gtk_image_menu_item_new_with_label(_(plugin->info->name));
 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
-		gtk_widget_show(menuitem);
 
 		submenu = gtk_menu_new();
 		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
-		gtk_widget_show(submenu);
 
 		build_plugin_actions(submenu, plugin, NULL);
 
--- a/pidgin/gtkmedia.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/gtkmedia.c	Sat Feb 21 05:15:28 2009 +0000
@@ -62,10 +62,6 @@
 	GtkWidget *menubar;
 	GtkWidget *statusbar;
 
-	GtkWidget *calling;
-	GtkWidget *accept;
-	GtkWidget *reject;
-	GtkWidget *hangup;
 	GtkWidget *mute;
 
 	GtkWidget *send_progress;
@@ -287,7 +283,7 @@
 	gtk_box_pack_end(GTK_BOX(vbox), media->priv->statusbar,
 			FALSE, FALSE, 0);
 	gtk_statusbar_push(GTK_STATUSBAR(media->priv->statusbar),
-			0, _("Connecting..."));
+			0, _("Calling..."));
 	gtk_widget_show(media->priv->statusbar);
 
 	media->priv->menubar = setup_menubar(media);
@@ -298,23 +294,12 @@
 	gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
 	gtk_widget_show(GTK_WIDGET(hbox));
 
-	media->priv->calling = gtk_label_new("Calling...");
-	media->priv->hangup = gtk_button_new_with_mnemonic("_Hangup");
-	media->priv->accept = gtk_button_new_with_mnemonic("_Accept");
-	media->priv->reject = gtk_button_new_with_mnemonic("_Reject");
 	media->priv->mute = gtk_toggle_button_new_with_mnemonic("_Mute");
 
 	g_signal_connect(media->priv->mute, "toggled",
 			G_CALLBACK(pidgin_media_mute_toggled), media);
 
-	gtk_box_pack_end(GTK_BOX(hbox), media->priv->reject, FALSE, FALSE, 0);
-	gtk_box_pack_end(GTK_BOX(hbox), media->priv->accept, FALSE, FALSE, 0);
-	gtk_box_pack_end(GTK_BOX(hbox), media->priv->hangup, FALSE, FALSE, 0);
 	gtk_box_pack_end(GTK_BOX(hbox), media->priv->mute, FALSE, FALSE, 0);
-	gtk_box_pack_end(GTK_BOX(hbox), media->priv->calling, FALSE, FALSE, 0);
-
-	gtk_widget_show_all(media->priv->accept);
-	gtk_widget_show_all(media->priv->reject);
 
 	media->priv->display = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
 	gtk_box_pack_start(GTK_BOX(vbox), media->priv->display,
@@ -386,6 +371,7 @@
 	purple_debug_info("gtkmedia", "pidgin_media_dispose\n");
 
 	if (gtkmedia->priv->media) {
+		purple_request_close_with_handle(gtkmedia);
 		purple_media_remove_output_windows(gtkmedia->priv->media);
 		pidgin_media_disconnect_levels(gtkmedia->priv->media, gtkmedia);
 		g_object_unref(gtkmedia->priv->media);
@@ -620,14 +606,14 @@
 
 	if (type & PURPLE_MEDIA_RECV_AUDIO) {
 		gtkmedia->priv->recv_progress = gtk_progress_bar_new();
-		gtk_widget_set_size_request(gtkmedia->priv->recv_progress, 70, 10);
+		gtk_widget_set_size_request(gtkmedia->priv->recv_progress, 320, 10);
 		gtk_box_pack_end(GTK_BOX(recv_widget),
 				   gtkmedia->priv->recv_progress, FALSE, FALSE, 0);
 		gtk_widget_show(gtkmedia->priv->recv_progress);
 	}
 	if (type & PURPLE_MEDIA_SEND_AUDIO) {
 		gtkmedia->priv->send_progress = gtk_progress_bar_new();
-		gtk_widget_set_size_request(gtkmedia->priv->send_progress, 70, 10);
+		gtk_widget_set_size_request(gtkmedia->priv->send_progress, 320, 10);
 		gtk_box_pack_end(GTK_BOX(send_widget),
 				   gtkmedia->priv->send_progress, FALSE, FALSE, 0);
 		gtk_widget_show(gtkmedia->priv->send_progress);
@@ -715,13 +701,6 @@
 			else
 				pidgin_media_set_state(media, PIDGIN_MEDIA_REQUESTED);
 
-			g_signal_connect_swapped(G_OBJECT(media->priv->accept), "clicked", 
-				 G_CALLBACK(purple_media_accept), media->priv->media);
-			g_signal_connect_swapped(G_OBJECT(media->priv->reject), "clicked",
-				 G_CALLBACK(purple_media_reject), media->priv->media);
-			g_signal_connect_swapped(G_OBJECT(media->priv->hangup), "clicked",
-				 G_CALLBACK(purple_media_hangup), media->priv->media);
-
 			g_signal_connect(G_OBJECT(media->priv->media), "error",
 				G_CALLBACK(pidgin_media_error_cb), media);
 			g_signal_connect(G_OBJECT(media->priv->media), "accepted",
@@ -793,28 +772,6 @@
 pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state)
 {
 	gtkmedia->priv->state = state;
-	switch (state) {
-		case PIDGIN_MEDIA_WAITING:
-			gtk_widget_show(gtkmedia->priv->calling);
-			gtk_widget_hide(gtkmedia->priv->accept);
-			gtk_widget_hide(gtkmedia->priv->reject);
-			gtk_widget_show(gtkmedia->priv->hangup);
-			break;
-		case PIDGIN_MEDIA_REQUESTED:
-			gtk_widget_hide(gtkmedia->priv->calling);
-			gtk_widget_show(gtkmedia->priv->accept);
-			gtk_widget_show(gtkmedia->priv->reject);
-			gtk_widget_hide(gtkmedia->priv->hangup);
-			break;
-		case PIDGIN_MEDIA_ACCEPTED:
-			gtk_widget_show(gtkmedia->priv->hangup);
-			gtk_widget_hide(gtkmedia->priv->calling);
-			gtk_widget_hide(gtkmedia->priv->accept);
-			gtk_widget_hide(gtkmedia->priv->reject);
-			break;
-		default:
-			break;
-	}
 }
 
 static gboolean
@@ -835,10 +792,10 @@
 	if (initiator == FALSE) {
 		gchar *message = g_strdup_printf("%s wishes to start a "
 				"media session with you\n", alias);
-		purple_request_accept_cancel(media, "Media invitation",
-				message, NULL, 1, (void*)pc, screenname,
-				NULL, media, purple_media_accept,
-				purple_media_reject);
+		purple_request_accept_cancel(gtkmedia, "Media invitation",
+				message, NULL, PURPLE_DEFAULT_ACTION_NONE,
+				(void*)pc, screenname, NULL, media,
+				purple_media_accept, purple_media_reject);
 		g_free(message);
 	} else
 		gtk_widget_show(GTK_WIDGET(gtkmedia));
--- a/pidgin/gtkprefs.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/gtkprefs.c	Sat Feb 21 05:15:28 2009 +0000
@@ -1439,6 +1439,10 @@
 		purple_prefs_connect_callback(prefs, "/purple/proxy/type",
 					    proxy_changed_cb, prefs_proxy_frame);
 
+		/* This is a global option that affects SOCKS4 usage even with account-specific proxy settings */
+		pidgin_prefs_checkbox(_("Use remote DNS with SOCKS4 proxies"),
+							  "/purple/proxy/socks4_remotedns", prefs_proxy_frame);
+
 		table = gtk_table_new(4, 2, FALSE);
 		gtk_container_set_border_width(GTK_CONTAINER(table), 0);
 		gtk_table_set_col_spacings(GTK_TABLE(table), 5);
--- a/pidgin/pidgintooltip.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/pidgintooltip.c	Sat Feb 21 05:15:28 2009 +0000
@@ -135,14 +135,14 @@
 setup_tooltip_window_position(gpointer data, int w, int h)
 {
 	int sig;
-	int scr_w, scr_h, x, y;
+	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);
@@ -158,6 +158,12 @@
 	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;
@@ -168,9 +174,9 @@
 	x -= ((w >> 1) + 4);
 
 	if ((y + h + 4) > scr_h)
-		y = y - h - 5;
+		y = y - h - dy - 5;
 	else
-		y = y + 6;
+		y = y + dy + 6;
 
 	if (y < mon_size.y)
 		y = mon_size.y;
@@ -353,6 +359,7 @@
 
 	g_signal_connect(G_OBJECT(tree), "motion-notify-event", G_CALLBACK(row_motion_cb), tdata);
 	g_signal_connect(G_OBJECT(tree), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL);
+	g_signal_connect(G_OBJECT(tree), "scroll-event", G_CALLBACK(widget_leave_cb), NULL);
 	g_signal_connect_swapped(G_OBJECT(tree), "destroy", G_CALLBACK(destroy_tooltip_data), tdata);
 	return TRUE;
 }
@@ -381,6 +388,7 @@
 
 	g_signal_connect(G_OBJECT(widget), "motion-notify-event", G_CALLBACK(widget_motion_cb), wdata);
 	g_signal_connect(G_OBJECT(widget), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL);
+	g_signal_connect(G_OBJECT(widget), "scroll-event", G_CALLBACK(widget_leave_cb), NULL);
 	g_signal_connect_swapped(G_OBJECT(widget), "destroy", G_CALLBACK(destroy_tooltip_data), wdata);
 	return TRUE;
 }
--- a/pidgin/plugins/convcolors.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/plugins/convcolors.c	Sat Feb 21 05:15:28 2009 +0000
@@ -21,7 +21,7 @@
 
 #define PLUGIN_ID			"gtk-plugin_pack-convcolors"
 #define PLUGIN_NAME			N_("Conversation Colors")
-#define PLUGIN_STATIC_NAME	"Conversation Colors"
+#define PLUGIN_STATIC_NAME	ConversationColors
 #define PLUGIN_SUMMARY		N_("Customize colors in the conversation window")
 #define PLUGIN_DESCRIPTION	N_("Customize colors in the conversation window")
 #define PLUGIN_AUTHOR		"Sadrul H Chowdhury <sadrul@users.sourceforge.net>"
--- a/pidgin/plugins/markerline.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/plugins/markerline.c	Sat Feb 21 05:15:28 2009 +0000
@@ -21,7 +21,7 @@
 
 #define PLUGIN_ID			"gtk-plugin_pack-markerline"
 #define PLUGIN_NAME			N_("Markerline")
-#define PLUGIN_STATIC_NAME	"Markerline"
+#define PLUGIN_STATIC_NAME	Markerline
 #define PLUGIN_SUMMARY		N_("Draw a line to indicate new messages in a conversation.")
 #define PLUGIN_DESCRIPTION	N_("Draw a line to indicate new messages in a conversation.")
 #define PLUGIN_AUTHOR		"Sadrul H Chowdhury <sadrul@users.sourceforge.net>"
--- a/pidgin/plugins/pidgininc.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/plugins/pidgininc.c	Sat Feb 21 05:15:28 2009 +0000
@@ -6,7 +6,7 @@
 #include "conversation.h"
 #include "version.h"
 
-/* include UI for pidgindialogs_about() */
+/* include UI for pidgin_dialogs_about() */
 #include "gtkplugin.h"
 #include "gtkdialogs.h"
 
@@ -16,7 +16,7 @@
 echo_hi(PurpleConnection *gc)
 {
 	/* this doesn't do much, just lets you know who we are :) */
-	pidgindialogs_about();
+	pidgin_dialogs_about();
 }
 
 static gboolean
@@ -108,7 +108,12 @@
 	NULL,                                             /**< ui_info        */
 	NULL,                                             /**< extra_info     */
 	NULL,                                             /**< prefs_info     */
-	NULL                                              /**< actions        */
+	NULL,                                             /**< actions        */
+	/* padding */
+	NULL,
+	NULL,
+	NULL,
+	NULL
 };
 
 static void
--- a/pidgin/plugins/timestamp.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/plugins/timestamp.c	Sat Feb 21 05:15:28 2009 +0000
@@ -229,4 +229,4 @@
 	purple_prefs_add_int("/plugins/gtk/timestamp/interval", interval * 1000);
 }
 
-PURPLE_INIT_PLUGIN(interval, init_plugin, info)
+PURPLE_INIT_PLUGIN(timestamp, init_plugin, info)
--- a/pidgin/plugins/xmppconsole.c	Sat Feb 21 05:15:14 2009 +0000
+++ b/pidgin/plugins/xmppconsole.c	Sat Feb 21 05:15:28 2009 +0000
@@ -890,4 +890,4 @@
 {
 }
 
-PURPLE_INIT_PLUGIN(interval, init_plugin, info)
+PURPLE_INIT_PLUGIN(xmppconsole, init_plugin, info)
--- a/po/de.po	Sat Feb 21 05:15:14 2009 +0000
+++ b/po/de.po	Sat Feb 21 05:15:28 2009 +0000
@@ -11,15 +11,15 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-01-09 00:39+0100\n"
-"PO-Revision-Date: 2009-01-09 00:38+0100\n"
-"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n"
-"Language-Team: Deutsch <de@li.org>\n"
+"POT-Creation-Date: 2009-02-18 21:09+0100\n"
+"PO-Revision-Date: 2009-02-18 21:09+0100\n"
+"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
+"Language-Team: German <de@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: KBabel 1.11.4\n"
+"X-Generator: Lokalize 0.2\n"
 
 #. Translators may want to transliterate the name.
 #. It is not to be translated.
@@ -36,7 +36,7 @@
 "Usage: %s [OPTION]...\n"
 "\n"
 "  -c, --config=DIR    use DIR for config files\n"
-"  -d, --debug         print debugging messages to stdout\n"
+"  -d, --debug         print debugging messages to stderr\n"
 "  -h, --help          display this help and exit\n"
 "  -n, --nologin       don't automatically login\n"
 "  -v, --version       display the current version and exit\n"
@@ -45,7 +45,7 @@
 "Benutzung: %s [OPTION]...\n"
 "\n"
 "  -c, --config=VERZ   benutze VERZ als Konfigurationsverzeichnis\n"
-"  -d, --debug         drucke Debugging-Meldungen nach stdout\n"
+"  -d, --debug         drucke Debugging-Meldungen nach stderr\n"
 "  -h, --help          zeigt diese Hilfe und beendet das Programm\n"
 "  -n, --nologin       nicht automatisch anmelden\n"
 "  -v, --version       zeigt aktuelle Version und beendet das Programm\n"
@@ -1172,7 +1172,6 @@
 msgid "Change status to"
 msgstr "Ändere Status zu"
 
-#. Conversations
 msgid "Conversations"
 msgstr "Unterhaltungen"
 
@@ -4033,7 +4032,7 @@
 
 msgid "Nick changing not supported in non-MUC chatrooms"
 msgstr ""
-"Die Änderung des Nick-Namens wird in nicht-MUC Chaträumen nicht unterstützt"
+"Die Änderung des Nick-Namens wird in nicht-MUC-Chaträumen nicht unterstützt"
 
 msgid "Error retrieving room list"
 msgstr "Fehler beim Empfangen der Raumliste"
@@ -6975,6 +6974,7 @@
 msgid "Get AIM Info"
 msgstr "AIM-Info"
 
+#. We only do this if the user is in our buddy list
 msgid "Edit Buddy Comment"
 msgstr "Buddy-Kommentar bearbeiten"
 
@@ -7626,6 +7626,9 @@
 msgid "Show server news"
 msgstr "Server-News anzeigen"
 
+msgid "Show chat room when msg comes"
+msgstr "Chatraum zeigen, wenn Nachricht empfangen wird"
+
 msgid "Keep alive interval (seconds)"
 msgstr "Intervall zum Aufrechterhalten der Verbindung (Sekunden)"
 
@@ -11365,6 +11368,9 @@
 msgid "Macedonian"
 msgstr "Makedonisch"
 
+msgid "Mongolian"
+msgstr "Mongolisch"
+
 msgid "Bokmål Norwegian"
 msgstr "Bokmål Norwegisch"
 
@@ -11478,8 +11484,27 @@
 "geschützt.  Die Datei 'COPYRIGHT' enthält die komplette Liste der "
 "Mitwirkenden.  Wir übernehmen keine Haftung für dieses Programm.<BR><BR>"
 
-msgid "<FONT SIZE=\"4\">IRC:</FONT> #pidgin on irc.freenode.net<BR><BR>"
-msgstr "<FONT SIZE=\"4\">IRC:</FONT> #pidgin auf irc.freenode.net<BR><BR>"
+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\">FAQ:</FONT> <A HREF=\"http://developer.pidgin.im/wiki/FAQ"
+"\">http://developer.pidgin.im/wiki/FAQ</A><BR/><BR/>"
+
+msgid ""
+"<FONT SIZE=\"4\">Help via e-mail:</FONT> <A HREF=\"mailto:support@pidgin.im"
+"\">support@pidgin.im</A><BR/><BR/>"
+msgstr ""
+"<FONT SIZE=\"4\">Hilfe per E-Mail:</FONT> <A HREF=\"mailto:support@pidgin.im"
+"\">support@pidgin.im</A><BR/><BR/>"
+
+msgid ""
+"<FONT SIZE=\"4\">IRC Channel:</FONT> #pidgin on irc.freenode.net<BR><BR>"
+msgstr ""
+"<FONT SIZE=\"4\">IRC-Kanal:</FONT> #pidgin auf irc.freenode.net<BR><BR>"
+
+msgid "<FONT SIZE=\"4\">XMPP MUC:</FONT> devel@conference.pidgin.im<BR><BR>"
+msgstr "<FONT SIZE=\"4\">XMPP-MUC:</FONT> devel@conference.pidgin.im<BR><BR>"
 
 msgid "Current Developers"
 msgstr "Aktuelle Entwickler"
@@ -11908,7 +11933,7 @@
 msgstr "Dieses Thema verfügt über keine Smileys."
 
 msgid "_Font"
-msgstr "_Schrift"
+msgstr "S_chrift"
 
 msgid "Group Items"
 msgstr "Elemente gruppieren"
@@ -12143,6 +12168,9 @@
 msgid "Pidgin"
 msgstr "Pidgin"
 
+msgid "Exiting because another libpurple client is already running.\n"
+msgstr "Wird geschlossen, da bereits ein anderer libpurple-Client läuft\n"
+
 msgid "Open All Messages"
 msgstr "Alle Nachrichten öffnen"
 
@@ -12482,6 +12510,10 @@
 msgid "No proxy"
 msgstr "Kein Proxy"
 
+#. This is a global option that affects SOCKS4 usage even with account-specific proxy settings
+msgid "Use remote DNS with SOCKS4 proxies"
+msgstr "Remote-DNS mit SOCKS4-Proxys benuten"
+
 msgid "_User:"
 msgstr "_Benutzer:"
 
@@ -13938,9 +13970,6 @@
 msgid "Only when docked"
 msgstr "Nur wenn angedockt"
 
-msgid "_Flash window when chat messages are received"
-msgstr "_Fenster blinkt, wenn Chat-Nachrichten empfangen werden"
-
 msgid "Windows Pidgin Options"
 msgstr "Windows-Pidgin-Optionen"