changeset 26430:d7393eebf1f4

merge of '85389d6ca8dc05827fa7e57e17906b2f73e2c676' and 'd1fcdfed72472e989b6233008b7293bba744532c'
author Mike Ruprecht <maiku@soc.pidgin.im>
date Mon, 23 Mar 2009 01:43:25 +0000
parents 1c73d2ef9ddc (diff) c0499eb4dd4d (current diff)
children 1ae3af12095a
files libpurple/media.c libpurple/media.h
diffstat 37 files changed, 1017 insertions(+), 779 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Mon Mar 23 01:43:21 2009 +0000
+++ b/COPYRIGHT	Mon Mar 23 01:43:25 2009 +0000
@@ -8,6 +8,7 @@
 Dave Ahlswede
 Manuel Amador
 Matt Amato
+Josef Andrysek
 Geoffrey Antos
 Daniel Atallah
 Paul Aurich
--- a/ChangeLog	Mon Mar 23 01:43:21 2009 +0000
+++ b/ChangeLog	Mon Mar 23 01:43:25 2009 +0000
@@ -6,8 +6,9 @@
 	  project.  With some minor additions and clean ups from Paul Aurich.
 
 	XMPP:
-	* Add support for in-band bytestreams (XEP-0047).
-	* Add support for attention (XEP-0224).
+	* Add support for in-band bytestreams for file transfers (XEP-0047).
+	* Add support for sending attentions (equivalent to "buzz" and "nudge")
+	  using the command /buzz (XEP-0224).
 
 	Pidgin:
 	* Added -f command line option to tell Pidgin to ignore NetworkManager
@@ -18,6 +19,9 @@
 	* Pressing the Enter key in the message entry box of the New Status
 	  dialog and various other dialogs now causes the cursor to move to
 	  the next line.
+	* Created a unified Buddy Pounce notification window for all pounces
+	  where "Pop up a notification" is selected, which avoids having a
+	  new dialog box every time a pounce is triggered. (Jorge VillaseƱor) 
 
 version 2.5.5 (03/01/2009):
 	libpurple:
--- a/ChangeLog.API	Mon Mar 23 01:43:21 2009 +0000
+++ b/ChangeLog.API	Mon Mar 23 01:43:25 2009 +0000
@@ -68,6 +68,7 @@
 		* pidgin_blist_get_theme
 		* pidgin_sound_is_customized
 		* pidgin_utils_init, pidgin_utils_uninit
+		* pidgin_notify_pounce_add
 
 	perl:
 		Changed:
--- a/finch/gntaccount.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntaccount.c	Mon Mar 23 01:43:25 2009 +0000
@@ -1099,4 +1099,3 @@
 	return &ui_ops;
 }
 
-
--- a/finch/gntdebug.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntdebug.c	Mon Mar 23 01:43:25 2009 +0000
@@ -23,8 +23,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-#include "util.h"
-
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -38,6 +36,7 @@
 #include "gntdebug.h"
 #include "finch.h"
 #include "notify.h"
+#include "util.h"
 
 #include <stdio.h>
 #include <string.h>
--- a/finch/gntft.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntft.c	Mon Mar 23 01:43:25 2009 +0000
@@ -25,12 +25,6 @@
  */
 #include "finch.h"
 
-#include "debug.h"
-#include "notify.h"
-#include "ft.h"
-#include "prpl.h"
-#include "util.h"
-
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -38,6 +32,12 @@
 #include <gntlabel.h>
 #include <gnttree.h>
 
+#include "debug.h"
+#include "notify.h"
+#include "ft.h"
+#include "prpl.h"
+#include "util.h"
+
 #include "gntft.h"
 #include "prefs.h"
 
--- a/finch/gntmedia.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntmedia.c	Mon Mar 23 01:43:25 2009 +0000
@@ -159,25 +159,9 @@
 }
 
 static void
-finch_media_ready_cb(PurpleMedia *media, FinchMedia *gntmedia)
-{
-	GstElement *sendbin, *sendlevel;
-
-	GList *sessions = purple_media_get_session_names(media);
-
-	purple_media_audio_init_src(&sendbin, &sendlevel);
-
-	for (; sessions; sessions = sessions->next) {
-		purple_media_set_src(media, sessions->data, sendbin);
-	}
-	g_list_free(sessions);
-}
-
-static void
 finch_media_accept_cb(PurpleMedia *media, FinchMedia *gntmedia)
 {
 	GntWidget *parent;
-	GstElement *sendbin = NULL;
 
 	finch_media_emit_message(gntmedia, _("Call in progress."));
 
@@ -200,9 +184,6 @@
 		parent = parent->parent;
 	gnt_box_readjust(GNT_BOX(parent));
 	gnt_widget_draw(parent);
-
-	purple_media_get_elements(media, &sendbin, NULL, NULL, NULL);
-	gst_element_set_state(GST_ELEMENT(sendbin), GST_STATE_PLAYING);
 }
 
 static void
@@ -226,14 +207,13 @@
 }
 
 static void
-finch_media_state_changed_cb(PurpleMedia *media,
-		PurpleMediaStateChangedType type,
+finch_media_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
 		gchar *sid, gchar *name, FinchMedia *gntmedia)
 {
-	purple_debug_info("gntmedia", "type: %d sid: %s name: %s\n",
-			type, sid, name);
+	purple_debug_info("gntmedia", "state: %d sid: %s name: %s\n",
+			state, sid, name);
 	if (sid == NULL && name == NULL) {
-		if (type == PURPLE_MEDIA_STATE_CHANGED_END) {
+		if (state == PURPLE_MEDIA_STATE_END) {
 			finch_media_emit_message(gntmedia,
 					_("The call has been terminated."));
 			finch_conversation_set_info_widget(
@@ -244,19 +224,23 @@
 			 * to free the FinchMedia widget.
 			 */
 			g_object_unref(gntmedia);
-		} else if (type == PURPLE_MEDIA_STATE_CHANGED_REJECTED) {
-			finch_media_emit_message(gntmedia,
-					_("You have rejected the call."));
 		}
-	} else if (type == PURPLE_MEDIA_STATE_CHANGED_NEW
-			&& sid != NULL && name != NULL) {
-		finch_media_ready_cb(media, gntmedia);
-	} else if (type == PURPLE_MEDIA_STATE_CHANGED_CONNECTED) {
+	} else if (state == PURPLE_MEDIA_STATE_CONNECTED) {
 		finch_media_accept_cb(media, gntmedia);
 	}
 }
 
 static void
+finch_media_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
+		gchar *sid, gchar *name, FinchMedia *gntmedia)
+{
+	if (type == PURPLE_MEDIA_INFO_REJECT) {
+		finch_media_emit_message(gntmedia,
+				_("You have rejected the call."));
+	}
+}
+
+static void
 finch_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
 	FinchMedia *media;
@@ -285,6 +269,8 @@
 			}
 			g_signal_connect(G_OBJECT(media->priv->media), "state-changed",
 				G_CALLBACK(finch_media_state_changed_cb), media);
+			g_signal_connect(G_OBJECT(media->priv->media), "stream-info",
+				G_CALLBACK(finch_media_stream_info_cb), media);
 			break;
 		}
 		default:
@@ -362,6 +348,75 @@
 	return PURPLE_CMD_STATUS_OK;
 }
 
+static GstElement *
+create_default_audio_src(void)
+{
+	GstElement *bin, *src, *volume;
+	GstPad *pad, *ghost;
+	const gchar *audio_device = purple_prefs_get_string(
+			"/purple/media/audio/device");
+	double input_volume = purple_prefs_get_int(
+			"/purple/media/audio/volume/input")/10.0;
+
+	bin = gst_bin_new("purplesendaudiobin");
+	src = gst_element_factory_make("alsasrc", "asrc");
+	volume = gst_element_factory_make("volume", "purpleaudioinputvolume");
+	g_object_set(volume, "volume", input_volume, NULL);
+	gst_bin_add_many(GST_BIN(bin), src, volume, NULL);
+	gst_element_link(src, volume);
+	pad = gst_element_get_pad(volume, "src");
+	ghost = gst_ghost_pad_new("ghostsrc", pad);
+	gst_element_add_pad(bin, ghost);
+
+	if (audio_device != NULL && strcmp(audio_device, ""))
+		g_object_set(G_OBJECT(src), "device", audio_device, NULL);
+
+	return bin;
+}
+
+static GstElement *
+create_default_audio_sink(void)
+{
+	GstElement *bin, *sink, *volume, *queue;
+	GstPad *pad, *ghost;
+	double output_volume = purple_prefs_get_int(
+			"/purple/media/audio/volume/output")/10.0;
+
+	bin = gst_bin_new("pidginrecvaudiobin");
+	sink = gst_element_factory_make("alsasink", "asink");
+	g_object_set(G_OBJECT(sink), "async", FALSE, "sync", FALSE, NULL);
+	volume = gst_element_factory_make("volume", "purpleaudiooutputvolume");
+	g_object_set(volume, "volume", output_volume, NULL);
+	queue = gst_element_factory_make("queue", NULL);
+	gst_bin_add_many(GST_BIN(bin), sink, volume, queue, NULL);
+	gst_element_link(volume, sink);
+	gst_element_link(queue, volume);
+	pad = gst_element_get_pad(queue, "sink");
+	ghost = gst_ghost_pad_new("ghostsink", pad);
+	gst_element_add_pad(bin, ghost);
+
+	return bin;
+}
+
+static PurpleMediaElementInfo default_audio_src =
+{
+	"finchdefaultaudiosrc",		/* id */
+	PURPLE_MEDIA_ELEMENT_AUDIO	/* type */
+			| PURPLE_MEDIA_ELEMENT_SRC
+			| PURPLE_MEDIA_ELEMENT_ONE_SRC
+			| PURPLE_MEDIA_ELEMENT_UNIQUE,
+	create_default_audio_src,	/* create */
+};
+
+static PurpleMediaElementInfo default_audio_sink =
+{
+	"finchdefaultaudiosink",	/* id */
+	PURPLE_MEDIA_ELEMENT_AUDIO	/* type */
+			| PURPLE_MEDIA_ELEMENT_SINK
+			| PURPLE_MEDIA_ELEMENT_ONE_SINK,
+	create_default_audio_sink,	/* create */
+};
+
 void finch_media_manager_init(void)
 {
 	PurpleMediaManager *manager = purple_media_manager_get();
@@ -369,6 +424,10 @@
 	purple_cmd_register("call", "", PURPLE_CMD_P_DEFAULT,
 			PURPLE_CMD_FLAG_IM, NULL,
 			call_cmd_cb, _("call: Make an audio call."), NULL);
+
+	purple_debug_info("gntmedia", "Registering media element types\n");
+	purple_media_manager_set_active_element(manager, &default_audio_src);
+	purple_media_manager_set_active_element(manager, &default_audio_sink);
 }
 
 void finch_media_manager_uninit(void)
--- a/finch/gntnotify.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntnotify.c	Mon Mar 23 01:43:25 2009 +0000
@@ -23,8 +23,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-#include <util.h>
-
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -35,6 +33,7 @@
 
 #include "finch.h"
 
+#include <util.h>
 
 #include "gntnotify.h"
 #include "debug.h"
--- a/finch/gntplugin.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntplugin.c	Mon Mar 23 01:43:25 2009 +0000
@@ -23,9 +23,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-#include "notify.h"
-#include "request.h"
-
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -37,6 +34,8 @@
 #include "finch.h"
 
 #include "debug.h"
+#include "notify.h"
+#include "request.h"
 
 #include "gntplugin.h"
 #include "gntrequest.h"
--- a/finch/gntpounce.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntpounce.c	Mon Mar 23 01:43:25 2009 +0000
@@ -24,16 +24,6 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  *
  */
-#include "internal.h"
-#include "account.h"
-#include "conversation.h"
-#include "debug.h"
-#include "notify.h"
-#include "prpl.h"
-#include "request.h"
-#include "server.h"
-#include "util.h"
-
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -47,6 +37,15 @@
 
 #include "finch.h"
 
+#include "account.h"
+#include "conversation.h"
+#include "debug.h"
+#include "notify.h"
+#include "prpl.h"
+#include "request.h"
+#include "server.h"
+#include "util.h"
+
 #include "gntpounce.h"
 
 
--- a/finch/gntrequest.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntrequest.c	Mon Mar 23 01:43:25 2009 +0000
@@ -23,9 +23,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-
-#include "util.h"
-
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
--- a/finch/gntstatus.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntstatus.c	Mon Mar 23 01:43:25 2009 +0000
@@ -23,10 +23,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-
-#include <notify.h>
-#include <request.h>
-
 #include <gnt.h>
 #include <gntbox.h>
 #include <gntbutton.h>
@@ -39,6 +35,9 @@
 
 #include "finch.h"
 
+#include <notify.h>
+#include <request.h>
+
 #include "gntstatus.h"
 
 static struct
--- a/finch/gntui.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/gntui.c	Mon Mar 23 01:43:25 2009 +0000
@@ -19,9 +19,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-#include <prefs.h>
 #include "finch.h"
 
+#include "gntui.h"
 
 #include "gntaccount.h"
 #include "gntblist.h"
@@ -41,7 +41,7 @@
 #include "gntstatus.h"
 #include "gntsound.h"
 
-#include "gntui.h"
+#include <prefs.h>
 
 void gnt_ui_init()
 {
--- a/finch/libgnt/wms/s.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/finch/libgnt/wms/s.c	Mon Mar 23 01:43:25 2009 +0000
@@ -2,7 +2,6 @@
 #include <sys/types.h>
 
 #include "internal.h"
-#include "blist.h"
 
 #include "gnt.h"
 #include "gntbox.h"
@@ -12,6 +11,7 @@
 #include "gntwindow.h"
 #include "gntlabel.h"
 
+#include "blist.h"
 
 #define TYPE_S				(s_get_gtype())
 
--- a/libpurple/blist.h	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/blist.h	Mon Mar 23 01:43:25 2009 +0000
@@ -595,7 +595,7 @@
  * @param contact The optional contact to place the buddy in.
  * @param group   The group to add the new buddy to.
  * @param node    The insertion point.  Pass in NULL to add the node as
- *                the last child in the given group.
+ *                the first child in the given group.
  */
 void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node);
 
--- a/libpurple/circbuffer.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/circbuffer.c	Mon Mar 23 01:43:25 2009 +0000
@@ -68,7 +68,8 @@
 
 	/* If the fill pointer is wrapped to before the remove
 	 * pointer, we need to shift the data */
-	if (in_offset < out_offset) {
+	if (in_offset < out_offset
+			|| (in_offset == out_offset && buf->bufused > 0)) {
 		int shift_n = MIN(buf->buflen - start_buflen,
 			in_offset);
 		memcpy(buf->buffer + start_buflen, buf->buffer,
--- a/libpurple/dnssrv.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/dnssrv.c	Mon Mar 23 01:43:25 2009 +0000
@@ -175,9 +175,11 @@
 
 end:
 	size = g_list_length(ret);
+	/* TODO: Check return value */
 	write(out, &size, sizeof(int));
 	while (ret != NULL)
 	{
+		/* TODO: Check return value */
 		write(out, ret->data, sizeof(PurpleSrvResponse));
 		g_free(ret->data);
 		ret = g_list_remove(ret, ret->data);
--- a/libpurple/media.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/media.c	Mon Mar 23 01:43:25 2009 +0000
@@ -38,7 +38,6 @@
 
 #ifdef USE_VV
 
-#include <gst/interfaces/propertyprobe.h>
 #include <gst/farsight/fs-conference-iface.h>
 
 /** @copydoc _PurpleMediaSession */
@@ -56,9 +55,6 @@
 
 	PurpleMediaSessionType type;
 
-	gboolean codecs_ready;
-
-	GstElement *sink;
 	gulong window_id;
 };
 
@@ -67,7 +63,6 @@
 	PurpleMediaSession *session;
 	gchar *participant;
 	FsStream *stream;
-	GstElement *sink;
 	GstElement *src;
 	GstElement *tee;
 
@@ -129,8 +124,8 @@
 	CANDIDATES_PREPARED,
 	CODECS_CHANGED,
 	NEW_CANDIDATE,
-	READY_NEW,
 	STATE_CHANGED,
+	STREAM_INFO,
 	LAST_SIGNAL
 };
 static guint purple_media_signals[LAST_SIGNAL] = {0};
@@ -173,12 +168,36 @@
 	static GType type = 0;
 	if (type == 0) {
 		static const GEnumValue values[] = {
-			{ PURPLE_MEDIA_STATE_CHANGED_NEW, "PURPLE_MEDIA_STATE_CHANGED_NEW", "new" },
-			{ PURPLE_MEDIA_STATE_CHANGED_CONNECTED, "PURPLE_MEDIA_STATE_CHANGED_CONNECTED", "connected" },
-			{ PURPLE_MEDIA_STATE_CHANGED_END, "PURPLE_MEDIA_STATE_CHANGED_END", "end" },
+			{ PURPLE_MEDIA_STATE_NEW,
+				"PURPLE_MEDIA_STATE_NEW", "new" },
+			{ PURPLE_MEDIA_STATE_CONNECTED,
+				"PURPLE_MEDIA_STATE_CONNECTED", "connected" },
+			{ PURPLE_MEDIA_STATE_END,
+				"PURPLE_MEDIA_STATE_END", "end" },
 			{ 0, NULL, NULL }
 		};
-		type = g_enum_register_static("PurpleMediaStateChangedType", values);
+		type = g_enum_register_static("PurpleMediaState", values);
+	}
+	return type;
+}
+
+GType
+purple_media_info_type_get_type()
+{
+	static GType type = 0;
+	if (type == 0) {
+		static const GEnumValue values[] = {
+			{ PURPLE_MEDIA_INFO_HANGUP,
+					"PURPLE_MEDIA_INFO_HANGUP", "hangup" },
+			{ PURPLE_MEDIA_INFO_REJECT,
+					"PURPLE_MEDIA_INFO_REJECT", "reject" },
+			{ PURPLE_MEDIA_INFO_MUTE,
+					"PURPLE_MEDIA_INFO_MUTE", "mute" },
+			{ PURPLE_MEDIA_INFO_HOLD,
+					"PURPLE_MEDIA_INFO_HOLD", "hold" },
+			{ 0, NULL, NULL }
+		};
+		type = g_enum_register_static("PurpleMediaInfoType", values);
 	}
 	return type;
 }
@@ -249,14 +268,15 @@
 					 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
 					 G_TYPE_NONE, 3, G_TYPE_POINTER,
 					 G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE);
-	purple_media_signals[READY_NEW] = g_signal_new("ready-new", G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-					 purple_smarshal_VOID__STRING_STRING,
-					 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
 	purple_media_signals[STATE_CHANGED] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 purple_smarshal_VOID__ENUM_STRING_STRING,
-					 G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_STATE_CHANGED,
+					 G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_STATE,
+					 G_TYPE_STRING, G_TYPE_STRING);
+	purple_media_signals[STREAM_INFO] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 purple_smarshal_VOID__ENUM_STRING_STRING,
+					 G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_INFO_TYPE,
 					 G_TYPE_STRING, G_TYPE_STRING);
 	g_type_class_add_private(klass, sizeof(PurpleMediaPrivate));
 }
@@ -921,26 +941,6 @@
 	return type;
 }
 
-
-
-PurpleMediaSessionType
-purple_media_get_overall_type(PurpleMedia *media)
-{
-	GList *values;
-	PurpleMediaSessionType type = PURPLE_MEDIA_NONE;
-
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), type);
-
-	values = g_hash_table_get_values(media->priv->sessions);
-
-	for (; values; values = g_list_delete_link(values, values)) {
-		PurpleMediaSession *session = values->data;
-		type |= session->type;
-	}
-
-	return type;
-}
-
 static PurpleMediaSession*
 purple_media_get_session(PurpleMedia *media, const gchar *sess_id)
 {
@@ -1091,37 +1091,7 @@
 			g_hash_table_get_keys(media->priv->sessions) : NULL;
 }
 
-void 
-purple_media_get_elements(PurpleMedia *media, GstElement **audio_src, GstElement **audio_sink,
-                                                  GstElement **video_src, GstElement **video_sink)
-{
-	GList *values;
-
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	values = g_hash_table_get_values(media->priv->sessions);
-
-	for (; values; values = g_list_delete_link(values, values)) {
-		PurpleMediaSession *session = (PurpleMediaSession*)values->data;
-
-		if (session->type & PURPLE_MEDIA_SEND_AUDIO && audio_src)
-			*audio_src = session->src;
-		if (session->type & PURPLE_MEDIA_SEND_VIDEO && video_src)
-			*video_src = session->src;
-	}
-
-	values = media->priv->streams;
-	for (; values; values = g_list_next(values)) {
-		PurpleMediaStream *stream = (PurpleMediaStream*)values->data;
-
-		if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO && audio_sink)
-			*audio_sink = stream->sink;
-		if (stream->session->type & PURPLE_MEDIA_RECV_VIDEO && video_sink)
-			*video_sink = stream->sink;
-	}
-}
-
-void 
+static void 
 purple_media_set_src(PurpleMedia *media, const gchar *sess_id, GstElement *src)
 {
 	PurpleMediaSession *session;
@@ -1158,7 +1128,8 @@
 	gst_element_set_locked_state(session->src, FALSE);
 }
 
-void 
+#if 0
+static void 
 purple_media_set_sink(PurpleMedia *media, const gchar *sess_id,
 		const gchar *participant, GstElement *sink)
 {
@@ -1180,6 +1151,7 @@
 	gst_bin_add(GST_BIN(stream->session->media->priv->confbin),
 		    stream->sink);
 }
+#endif
 
 GstElement *
 purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
@@ -1190,15 +1162,6 @@
 	return (session != NULL) ? session->src : NULL;
 }
 
-GstElement *
-purple_media_get_sink(PurpleMedia *media, const gchar *sess_id, const gchar *participant)
-{
-	PurpleMediaStream *stream;
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	stream = purple_media_get_stream(media, sess_id, participant);
-	return (stream != NULL) ? stream->sink : NULL;
-}
-
 static PurpleMediaSession *
 purple_media_session_from_fs_stream(PurpleMedia *media, FsStream *stream)
 {
@@ -1226,57 +1189,6 @@
 	return NULL;
 }
 
-/* This could also emit when participants are ready */
-static void
-purple_media_emit_ready(PurpleMedia *media, PurpleMediaSession *session, const gchar *participant)
-{
-	GList *sessions;
-	gboolean conf_ready = TRUE;
-
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	if ((session != NULL) && ((media->priv->initiator == FALSE &&
-			purple_media_accepted(media, session->id, NULL) == FALSE) ||
-			(purple_media_codecs_ready(media, session->id) == FALSE)))
-		return;
-
-	sessions = g_hash_table_get_values(media->priv->sessions);
-
-	for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
-		PurpleMediaSession *session_data = sessions->data;
-		GList *streams = purple_media_get_streams(media,
-				session_data->id, NULL);
-		gboolean session_ready = TRUE;
-
-		if ((media->priv->initiator == FALSE &&
-				purple_media_accepted(media,
-				session->id, NULL) == FALSE) ||
-				(purple_media_codecs_ready(
-				media, session_data->id) == FALSE))
-			conf_ready = FALSE;
-
-		for (; streams; streams = g_list_delete_link(streams, streams)) {
-			PurpleMediaStream *stream = streams->data;
-			if (stream->candidates_prepared == FALSE) {
-				session_ready = FALSE;
-				conf_ready = FALSE;
-			} else if (session_data == session)
-				g_signal_emit(media, purple_media_signals[READY_NEW],
-						0, session_data->id, stream->participant);
-		}
-
-		if (session_ready == TRUE &&
-				(session == session_data || session == NULL))
-			g_signal_emit(media, purple_media_signals[READY_NEW],
-					0, session_data->id, NULL);
-	}
-
-	if (conf_ready == TRUE) {
-		g_signal_emit(media, purple_media_signals[READY_NEW],
-				0, NULL, NULL);
-	}
-}
-
 static gboolean
 media_bus_call(GstBus *bus, GstMessage *msg, PurpleMedia *media)
 {
@@ -1365,16 +1277,7 @@
 				for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
 					PurpleMediaSession *session = sessions->data;
 					if (session->session == fssession) {
-						gboolean ready;
-						gchar *session_id;
-
-						g_object_get(session->session, "codecs-ready", &ready, NULL);
-						if (session->codecs_ready == FALSE && ready == TRUE) {
-							session->codecs_ready = ready;
-							purple_media_emit_ready(media, session, NULL);
-						}
-
-						session_id = g_strdup(session->id);
+						gchar *session_id = g_strdup(session->id);
 						g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, session_id);
 						g_free(session_id);
 						g_list_free(sessions);
@@ -1445,7 +1348,6 @@
 void
 purple_media_accept(PurpleMedia *media)
 {
-	GList *sessions;
 	GList *streams;
 
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
@@ -1462,24 +1364,14 @@
 
 	g_signal_emit(media, purple_media_signals[ACCEPTED],
 			0, NULL, NULL);
-
-	sessions = g_hash_table_get_values(media->priv->sessions);
-
-	for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
-		PurpleMediaSession *session = sessions->data;
-
-		if (media->priv->initiator == FALSE)
-			purple_media_emit_ready(media, session, NULL);
-	}
-
 }
 
 void
 purple_media_hangup(PurpleMedia *media)
 {
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
-	g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-			0, PURPLE_MEDIA_STATE_CHANGED_HANGUP,
+	g_signal_emit(media, purple_media_signals[STREAM_INFO],
+			0, PURPLE_MEDIA_INFO_HANGUP,
 			NULL, NULL);
 	purple_media_end(media, NULL, NULL);
 }
@@ -1488,8 +1380,8 @@
 purple_media_reject(PurpleMedia *media)
 {
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
-	g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-			0, PURPLE_MEDIA_STATE_CHANGED_REJECTED,
+	g_signal_emit(media, purple_media_signals[STREAM_INFO],
+			0, PURPLE_MEDIA_INFO_REJECT,
 			NULL, NULL);
 	purple_media_end(media, NULL, NULL);
 }
@@ -1501,197 +1393,12 @@
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
 	if (session_id == NULL && participant == NULL) {
 		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_CHANGED_END,
+				0, PURPLE_MEDIA_STATE_END,
 				NULL, NULL);
 		g_object_unref(media);
 	}
 }
 
-GList*
-purple_media_get_devices(const gchar *plugin)
-{
-	GObjectClass *klass;
-	GstPropertyProbe *probe;
-	const GParamSpec *pspec;
-	GstElement *element = gst_element_factory_make(plugin, NULL);
-	GstElementFactory *factory;
-	const gchar *longname = NULL;
-	GList *ret = NULL;
-
-	if (element == NULL)
-		return NULL;
-
-	factory = gst_element_get_factory(element);
-
-	longname = gst_element_factory_get_longname(factory);
-	klass = G_OBJECT_GET_CLASS(element);
-
-	if (!g_object_class_find_property (klass, "device") ||
-			!GST_IS_PROPERTY_PROBE (element) ||
-			!(probe = GST_PROPERTY_PROBE (element)) ||
-			!(pspec = gst_property_probe_get_property (probe, "device"))) {
-		purple_debug_info("media", "Found source '%s' (%s) - no device\n",
-				longname, GST_PLUGIN_FEATURE (factory)->name);
-	} else {
-		gint n;
-		gchar *name;
-		GValueArray *array;
-
-		purple_debug_info("media", "Found devices\n");
-
-		/* Set autoprobe[-fps] to FALSE to avoid delays when probing. */
-		if (g_object_class_find_property (klass, "autoprobe")) {
-			g_object_set (G_OBJECT (element), "autoprobe", FALSE, NULL);
-			if (g_object_class_find_property (klass, "autoprobe-fps")) {
-				g_object_set (G_OBJECT (element), "autoprobe-fps", FALSE, NULL);
-			}
-		}
-
-		array = gst_property_probe_probe_and_get_values (probe, pspec);
-		if (array != NULL) {
-			for (n = 0 ; n < array->n_values ; n++) {
-				GValue *device = g_value_array_get_nth (array, n);
-				
-				ret = g_list_append(ret, g_value_dup_string(device));
-
-				g_object_set(G_OBJECT(element), "device",
-						g_value_get_string(device), NULL);
-				g_object_get(G_OBJECT(element), "device-name", &name, NULL);
-				purple_debug_info("media", "Found source '%s' (%s) - device '%s' (%s)\n",
-						  longname, GST_PLUGIN_FEATURE (factory)->name,
-						  name, g_value_get_string(device));
-				g_free(name);
-			}
-			g_value_array_free(array);
-		}
-
-		/* Restore autoprobe[-fps] to TRUE. */
-		if (g_object_class_find_property (klass, "autoprobe")) {
-			g_object_set (G_OBJECT (element), "autoprobe", TRUE, NULL);
-			if (g_object_class_find_property (klass, "autoprobe-fps")) {
-				g_object_set (G_OBJECT (element), "autoprobe-fps", TRUE, NULL);
-			}
-		}
-	}
-
-	gst_object_unref(element);
-	return ret;
-}
-
-gchar *
-purple_media_element_get_device(GstElement *element)
-{
-	gchar *device;
-	g_object_get(G_OBJECT(element), "device", &device, NULL);
-	return device;
-}
-
-void
-purple_media_audio_init_src(GstElement **sendbin, GstElement **sendlevel)
-{
-	GstElement *src;
-	GstElement *volume;
-	GstPad *pad;
-	GstPad *ghost;
-	const gchar *audio_device = purple_prefs_get_string("/purple/media/audio/device");
-	double input_volume = purple_prefs_get_int("/purple/media/audio/volume/input")/10.0;
-
-	g_return_if_fail(sendbin != NULL && sendlevel != NULL);
-
-	*sendbin = gst_bin_new("purplesendaudiobin");
-	src = gst_element_factory_make("alsasrc", "asrc");
-	volume = gst_element_factory_make("volume", "purpleaudioinputvolume");
-	g_object_set(volume, "volume", input_volume, NULL);
-	*sendlevel = gst_element_factory_make("level", "sendlevel");
-	gst_bin_add_many(GST_BIN(*sendbin), src, volume, *sendlevel, NULL);
-	gst_element_link(src, volume);
-	gst_element_link(volume, *sendlevel);
-	pad = gst_element_get_pad(*sendlevel, "src");
-	ghost = gst_ghost_pad_new("ghostsrc", pad);
-	gst_element_add_pad(*sendbin, ghost);
-	g_object_set(G_OBJECT(*sendlevel), "message", TRUE, NULL);
-
-	if (audio_device != NULL && strcmp(audio_device, ""))
-		g_object_set(G_OBJECT(src), "device", audio_device, NULL);
-}
-
-void
-purple_media_video_init_src(GstElement **sendbin)
-{
-	GstElement *src, *videoscale, *capsfilter;
-	GstPad *pad;
-	GstPad *ghost;
-	GstCaps *caps;
-	const gchar *video_plugin = purple_prefs_get_string(
-			"/purple/media/video/plugin");
-	const gchar *video_device = purple_prefs_get_string(
-			"/purple/media/video/device");
-
-	g_return_if_fail(sendbin != NULL);
-
-	*sendbin = gst_bin_new("purplesendvideobin");
-	src = gst_element_factory_make(video_plugin, "purplevideosource");
-	videoscale = gst_element_factory_make("videoscale", NULL);
-	capsfilter = gst_element_factory_make("capsfilter", NULL);
-
-	/* It was recommended to set the size < 352x288 and framerate < 20 */
-	caps = gst_caps_from_string("video/x-raw-yuv , width=[250,350] , "
-			"height=[200,275] , framerate=[10/1,20/1]");
-	g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
-
-	gst_bin_add_many(GST_BIN(*sendbin), src, videoscale, capsfilter, NULL);
-	gst_element_link_many(src, videoscale, capsfilter, NULL);
-
-	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(capsfilter, "src");
-	ghost = gst_ghost_pad_new("ghostsrc", pad);
-	gst_object_unref(pad);
-	gst_element_add_pad(*sendbin, ghost);
-
-	if (video_device != NULL && strcmp(video_device, ""))
-		g_object_set(G_OBJECT(src), "device", video_device, NULL);
-}
-
-void
-purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel)
-{
-	GstElement *sink, *volume, *queue;
-	GstPad *pad, *ghost;
-	double output_volume = purple_prefs_get_int(
-			"/purple/media/audio/volume/output")/10.0;
-
-	g_return_if_fail(recvbin != NULL && recvlevel != NULL);
-
-	*recvbin = gst_bin_new("pidginrecvaudiobin");
-	sink = gst_element_factory_make("alsasink", "asink");
-	g_object_set(G_OBJECT(sink), "async", FALSE, "sync", FALSE, NULL);
-	volume = gst_element_factory_make("volume", "purpleaudiooutputvolume");
-	g_object_set(volume, "volume", output_volume, NULL);
-	*recvlevel = gst_element_factory_make("level", "recvlevel");
-	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);
-	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);
-}
-
-void
-purple_media_video_init_recv(GstElement **recvbin)
-{
-	g_return_if_fail(recvbin != NULL);
-
-	*recvbin = gst_element_factory_make("autovideosink", NULL);
-}
-
 static void
 purple_media_new_local_candidate_cb(FsStream *stream,
 				    FsCandidate *local_candidate,
@@ -1740,7 +1447,6 @@
 			purple_media_signals[CANDIDATES_PREPARED],
 			0, session->id, name);
 
-	purple_media_emit_ready(session->media, session, name);
 	g_free(name);
 }
 
@@ -1819,7 +1525,7 @@
 
 	g_signal_emit(stream->session->media,
 			purple_media_signals[STATE_CHANGED],
-			0, PURPLE_MEDIA_STATE_CHANGED_CONNECTED,
+			0, PURPLE_MEDIA_STATE_CONNECTED,
 			stream->session->id, stream->participant);
 	return FALSE;
 }
@@ -1869,13 +1575,6 @@
 	gst_pad_link(srcpad, sinkpad);
 	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);
-	}
-
 	stream->connected_cb_id = purple_timeout_add(0,
 			(GSourceFunc)purple_media_connected_cb, stream);
 }
@@ -1958,7 +1657,7 @@
 
 		purple_media_add_session(media, session);
 		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_CHANGED_NEW,
+				0, PURPLE_MEDIA_STATE_NEW,
 				session->id, NULL);
 
 		session_type = purple_media_from_fs(type, FS_DIRECTION_SEND);
@@ -1979,7 +1678,7 @@
 		return FALSE;
 	} else {
 		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_CHANGED_NEW,
+				0, PURPLE_MEDIA_STATE_NEW,
 				NULL, who);
 	}
 
@@ -2080,7 +1779,7 @@
 				 "src-pad-added", G_CALLBACK(purple_media_src_pad_added_cb), stream);
 
 		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_CHANGED_NEW,
+				0, PURPLE_MEDIA_STATE_NEW,
 				session->id, who);
 	} else if (*direction != type_direction) {	
 		/* change direction */
@@ -2418,10 +2117,27 @@
 		PurpleMediaStream *stream = streams->data;
 
 		if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO) {
-			GstElement *volume = gst_bin_get_by_name(
-					GST_BIN(stream->sink),
-					"purpleaudiooutputvolume");
-			g_object_set(volume, "volume", level, NULL);
+			GstElement *tee = stream->tee;
+			GstIterator *iter = gst_element_iterate_src_pads(tee);
+			GstPad *sinkpad;
+			while (gst_iterator_next(iter, (gpointer)&sinkpad)
+					 == GST_ITERATOR_OK) {
+				GstPad *peer = gst_pad_get_peer(sinkpad);
+				GstElement *volume;
+
+				if (peer == NULL) {
+					gst_object_unref(sinkpad);
+					continue;
+				}
+					
+				volume = gst_bin_get_by_name(GST_BIN(
+						GST_OBJECT_PARENT(peer)),
+						"purpleaudiooutputvolume");
+				g_object_set(volume, "volume", level, NULL);
+				gst_object_unref(peer);
+				gst_object_unref(sinkpad);
+			}
+			gst_iterator_free(iter);
 		}
 	}
 }
--- a/libpurple/media.h	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/media.h	Mon Mar 23 01:43:25 2009 +0000
@@ -44,7 +44,8 @@
 #define PURPLE_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA))
 #define PURPLE_MEDIA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA, PurpleMediaClass))
 
-#define PURPLE_MEDIA_TYPE_STATE_CHANGED	(purple_media_state_changed_get_type())
+#define PURPLE_MEDIA_TYPE_STATE      (purple_media_state_changed_get_type())
+#define PURPLE_MEDIA_TYPE_INFO_TYPE	(purple_media_info_type_get_type())
 
 /** @copydoc _PurpleMedia */
 typedef struct _PurpleMedia PurpleMedia;
@@ -90,12 +91,18 @@
 
 /** Media state-changed types */
 typedef enum {
-	PURPLE_MEDIA_STATE_CHANGED_NEW = 0,
-	PURPLE_MEDIA_STATE_CHANGED_CONNECTED,
-	PURPLE_MEDIA_STATE_CHANGED_REJECTED,	/** Local user rejected the stream. */
-	PURPLE_MEDIA_STATE_CHANGED_HANGUP,	/** Local user hung up the stream */
-	PURPLE_MEDIA_STATE_CHANGED_END,
-} PurpleMediaStateChangedType;
+	PURPLE_MEDIA_STATE_NEW = 0,
+	PURPLE_MEDIA_STATE_CONNECTED,
+	PURPLE_MEDIA_STATE_END,
+} PurpleMediaState;
+
+/** Media info types */
+typedef enum {
+	PURPLE_MEDIA_INFO_HANGUP = 0,
+	PURPLE_MEDIA_INFO_REJECT,
+	PURPLE_MEDIA_INFO_MUTE,
+	PURPLE_MEDIA_INFO_HOLD,
+} PurpleMediaInfoType;
 
 typedef enum {
 	PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
@@ -182,6 +189,13 @@
 GType purple_media_state_changed_get_type(void);
 
 /**
+ * Gets the type of the info type enum
+ *
+ * @return The info type enum's GType
+ */
+GType purple_media_info_type_get_type(void);
+
+/**
  * Gets the type of the media candidate structure.
  *
  * @return The media canditate's GType
@@ -300,15 +314,6 @@
 void purple_media_codec_list_free(GList *codecs);
 
 /**
- * Combines all the separate session types into a single PurpleMediaSessionType.
- *
- * @param media The media session to retrieve session types from.
- *
- * @return Combined type.
- */
-PurpleMediaSessionType purple_media_get_overall_type(PurpleMedia *media);
-
-/**
  * Gets a list of session names.
  *
  * @param media The media session to retrieve session names from.
@@ -318,41 +323,6 @@
 GList *purple_media_get_session_names(PurpleMedia *media);
 
 /**
- * Gets an audio and video source and sink from the media session.
- *
- * Retrieves the first of each element in the media session.
- *
- * @param media The media session to retreive the sources and sinks from.
- * @param audio_src Set to the audio source.
- * @param audio_sink Set to the audio sink.
- * @param video_src Set to the video source.
- * @param video_sink Set to the video sink.
- */
-void purple_media_get_elements(PurpleMedia *media,
-			       GstElement **audio_src, GstElement **audio_sink,
-			       GstElement **video_src, GstElement **video_sink);
-
-/**
- * Sets the source on a session.
- *
- * @param media The media object the session is in.
- * @param sess_id The session id of the session to set the source on.
- * @param src The source to set the session source to.
- */
-void purple_media_set_src(PurpleMedia *media, const gchar *sess_id, GstElement *src);
-
-/**
- * Sets the sink on a stream.
- *
- * @param media The media object the session is in.
- * @param sess_id The session id the stream belongs to.
- * @param sess_id The participant the stream is associated with.
- * @param sink The source to set the session sink to.
- */
-void purple_media_set_sink(PurpleMedia *media, const gchar *sess_id,
-		const gchar *participant, GstElement *sink);
-
-/**
  * Gets the source from a session
  *
  * @param media The media object the session is in.
@@ -363,17 +333,6 @@
 GstElement *purple_media_get_src(PurpleMedia *media, const gchar *sess_id);
 
 /**
- * Gets the sink from a stream
- *
- * @param media The media object the session is in.
- * @param sess_id The session id the stream belongs to.
- * @param participant The participant the stream is associated with.
- *
- * @return The sink retrieved.
- */
-GstElement *purple_media_get_sink(PurpleMedia *media, const gchar *sess_id, const gchar *participant);
-
-/**
  * Gets the pipeline from the media session.
  *
  * @param media The media session to retrieve the pipeline from.
@@ -449,55 +408,6 @@
 		const gchar *participant);
 
 /**
- * Enumerates a list of devices.
- *
- * @param plugin The name of the GStreamer plugin from which to enumerate devices.
- *
- * @return The list of enumerated devices.
- */
-GList *purple_media_get_devices(const gchar *plugin);
-
-/**
- * Gets the device the plugin is currently set to.
- *
- * @param element The plugin to retrieve the device from.
- *
- * @return The device retrieved.
- */
-gchar *purple_media_element_get_device(GstElement *element);
-
-/**
- * Creates a default audio source.
- *
- * @param sendbin Set to the newly created audio source.
- * @param sendlevel Set to the newly created level within the audio source.
- */
-void purple_media_audio_init_src(GstElement **sendbin,
-                                 GstElement **sendlevel);
-
-/**
- * Creates a default video source.
- *
- * @param sendbin Set to the newly created video source.
- */
-void purple_media_video_init_src(GstElement **sendbin);
-
-/**
- * Creates a default audio sink.
- *
- * @param recvbin Set to the newly created audio sink.
- * @param recvlevel Set to the newly created level within the audio sink.
- */
-void purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel);
-
-/**
- * Creates a default video sink.
- *
- * @param sendbin Set to the newly created video sink.
- */
-void purple_media_video_init_recv(GstElement **sendbin);
-
-/**
  * Adds a stream to a session.
  *
  * It only adds a stream to one audio session or video session as
@@ -679,12 +589,36 @@
 void purple_media_set_output_volume(PurpleMedia *media, const gchar *session_id,
 		const gchar *participant, double level);
 
+/**
+ * Sets a video output window for the given session/stream.
+ *
+ * @param media The media instance to set the output window on.
+ * @param session_id The session to set the output window on.
+ * @param participant Optionally, the participant to set the output window on.
+ * @param window_id The window id use for embedding the video in.
+ *
+ * @return An id to reference the output window.
+ */
 gulong purple_media_set_output_window(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant,
 		gulong window_id);
 
+/**
+ * Removes all output windows from a given media session.
+ *
+ * @param media The instance to remove all output windows from.
+ */
 void purple_media_remove_output_windows(PurpleMedia *media);
 
+/**
+ * Gets the tee from a given session/stream.
+ *
+ * @param media The instance to get the tee from.
+ * @param session_id The id of the session to get the tee from.
+ * @param participant Optionally, the participant of the stream to get the tee from.
+ *
+ * @return The GstTee element from the chosen session/stream.
+ */
 GstElement *purple_media_get_tee(PurpleMedia *media,
 		const gchar *session_id, const gchar *participant);
 
--- a/libpurple/protocols/jabber/google.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Mon Mar 23 01:43:25 2009 +0000
@@ -210,37 +210,45 @@
 
 		google_session_send_candidates(session->media,
 				"google-voice", session->remote_jid, session);
+
+		g_signal_handlers_disconnect_by_func(G_OBJECT(session->media),
+				G_CALLBACK(google_session_ready), session);
+	}
+}
+
+static void
+google_session_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
+		gchar *sid, gchar *name, GoogleSession *session)
+{
+	if (sid == NULL && name == NULL) {
+		if (state == PURPLE_MEDIA_STATE_END) {
+			google_session_destroy(session);
+		}
 	}
 }
 
 static void
-google_session_state_changed_cb(PurpleMedia *media,
-		PurpleMediaStateChangedType type,
+google_session_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
 		gchar *sid, gchar *name, GoogleSession *session)
 {
-	if (sid == NULL && name == NULL) {
-		if (type == PURPLE_MEDIA_STATE_CHANGED_END) {
-			google_session_destroy(session);
-		} else if (type == PURPLE_MEDIA_STATE_CHANGED_HANGUP) {
-			xmlnode *sess;
-			JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
+	if (type == PURPLE_MEDIA_INFO_HANGUP) {
+		xmlnode *sess;
+		JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
+
+		xmlnode_set_attrib(iq->node, "to", session->remote_jid);
+		sess = google_session_create_xmlnode(session, "terminate");
+		xmlnode_insert_child(iq->node, sess);
 
-			xmlnode_set_attrib(iq->node, "to", session->remote_jid);
-			sess = google_session_create_xmlnode(session, "terminate");
-			xmlnode_insert_child(iq->node, sess);
-	
-			jabber_iq_send(iq);
-		} else if (type == PURPLE_MEDIA_STATE_CHANGED_REJECTED) {
-			xmlnode *sess;
-			JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
+		jabber_iq_send(iq);
+	} else if (type == PURPLE_MEDIA_INFO_REJECT) {
+		xmlnode *sess;
+		JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
 
-			xmlnode_set_attrib(iq->node, "to", session->remote_jid);
-			sess = google_session_create_xmlnode(session, "reject");
-			xmlnode_insert_child(iq->node, sess);
-	
-			jabber_iq_send(iq);
-		}
-		
+		xmlnode_set_attrib(iq->node, "to", session->remote_jid);
+		sess = google_session_create_xmlnode(session, "reject");
+		xmlnode_insert_child(iq->node, sess);
+
+		jabber_iq_send(iq);
 	}
 }
 
@@ -326,6 +334,8 @@
 			G_CALLBACK(google_session_ready), session);
 	g_signal_connect(G_OBJECT(session->media), "state-changed",
 			G_CALLBACK(google_session_state_changed_cb), session);
+	g_signal_connect(G_OBJECT(session->media), "stream-info",
+			G_CALLBACK(google_session_stream_info_cb), session);
 
 	g_free(params);
 
@@ -391,6 +401,8 @@
 			G_CALLBACK(google_session_ready), session);
 	g_signal_connect(G_OBJECT(session->media), "state-changed",
 			G_CALLBACK(google_session_state_changed_cb), session);
+	g_signal_connect(G_OBJECT(session->media), "stream-info",
+			G_CALLBACK(google_session_stream_info_cb), session);
 
 	purple_media_codec_list_free(codecs);
 
@@ -498,12 +510,10 @@
 		google_session_handle_candidates(js, session, packet, sess);
 	}
 }
-#endif /* USE_VV */
 
 void
 jabber_google_session_parse(JabberStream *js, xmlnode *packet)
 {
-#ifdef USE_VV
 	GoogleSession *session = NULL;
 	GoogleSessionId id;
 
@@ -560,10 +570,8 @@
 	session->remote_jid = g_strdup(session->id.initiator);
 
 	google_session_parse_iq(js, session, packet);
-#else
-	/* TODO: send proper error response */
+}
 #endif /* USE_VV */
-}
 
 static void
 jabber_gmail_parse(JabberStream *js, xmlnode *packet, gpointer nul)
--- a/libpurple/protocols/jabber/google.h	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/jabber/google.h	Mon Mar 23 01:43:25 2009 +0000
@@ -27,6 +27,7 @@
 #include "jabber.h"
 #include "media.h"
 
+#define GOOGLE_VOICE_CAP "http://www.google.com/xmpp/protocol/voice/v1"
 #define GOOGLE_JINGLE_INFO_NAMESPACE "google:jingleinfo"
 
 void jabber_gmail_init(JabberStream *js);
--- a/libpurple/protocols/jabber/iq.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/jabber/iq.c	Mon Mar 23 01:43:25 2009 +0000
@@ -372,11 +372,13 @@
 			return;
 		}
 	}
-	
+
+#ifdef USE_VV
 	if (xmlnode_get_child_with_namespace(packet, "session", "http://www.google.com/session")) {
 		jabber_google_session_parse(js, packet);
 		return;
 	}
+#endif
 
 	if(xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si")) {
 		jabber_si_parse(js, packet);
@@ -406,13 +408,11 @@
 		jabber_ibb_parse(js, packet);
 		return;
 	}
-	
-#ifdef USE_VV
+
 	if (xmlnode_get_child_with_namespace(packet, "jingle", JINGLE)) {
 		jingle_parse(js, packet);
 		return;
 	}
-#endif
 
 	/* If we get here, send the default error reply mandated by XMPP-CORE */
 	if(!strcmp(type, "set") || !strcmp(type, "get")) {
@@ -453,9 +453,8 @@
 	jabber_iq_register_handler("http://jabber.org/protocol/disco#items", jabber_disco_items_parse);
 	jabber_iq_register_handler("jabber:iq:register", jabber_register_parse);
 	jabber_iq_register_handler("urn:xmpp:ping", urn_xmpp_ping_parse);
-#ifdef USE_VV
 	jabber_iq_register_handler(JINGLE, jingle_parse);
-#endif
+
 	/* handle Google jingleinfo */
 	jabber_iq_register_handler(GOOGLE_JINGLE_INFO_NAMESPACE, 
 		jabber_google_handle_jingle_info);
--- a/libpurple/protocols/jabber/jabber.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Mon Mar 23 01:43:25 2009 +0000
@@ -62,13 +62,6 @@
 #include "jingle/jingle.h"
 #include "jingle/rtp.h"
 
-#ifdef USE_VV
-#include <gst/farsight/fs-conference-iface.h>
-
-#define GTALK_CAP "http://www.google.com/xmpp/protocol/voice/v1"
-
-#endif
-
 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5)
 
 static PurplePlugin *my_protocol = NULL;
@@ -738,10 +731,7 @@
 	js->old_length = 0;
 	js->keepalive_timeout = -1;
 	js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user ? js->user->domain : NULL);
-#ifdef USE_VV
 	js->sessions = NULL;
-#endif
-
 	js->stun_ip = NULL;
 	js->stun_port = 0;
 	js->stun_query = NULL;
@@ -1340,10 +1330,8 @@
 {
 	JabberStream *js = gc->proto_data;
 
-#ifdef USE_VV
 	/* Close all of the open Jingle sessions on this stream */
 	jingle_terminate_sessions(js);
-#endif
 
 	/* Don't perform any actions on the ssl connection
 	 * if we were forcibly disconnected because it will crash
@@ -2623,12 +2611,12 @@
 {
 	return TRUE;
 }
-#ifdef USE_VV
 
 PurpleMedia *
 jabber_initiate_media(PurpleConnection *gc, const char *who, 
 		      PurpleMediaSessionType type)
 {
+#ifdef USE_VV
 	JabberStream *js = (JabberStream *) gc->proto_data;
 	JabberBuddy *jb;
 
@@ -2648,14 +2636,18 @@
 	if (type & PURPLE_MEDIA_AUDIO &&
 			!jabber_buddy_has_capability(jb,
 			JINGLE_APP_RTP_SUPPORT_AUDIO) &&
-			jabber_buddy_has_capability(jb, GTALK_CAP))
+			jabber_buddy_has_capability(jb, GOOGLE_VOICE_CAP))
 		return jabber_google_session_initiate(gc->proto_data, who, type);
 	else
 		return jingle_rtp_initiate_media(gc->proto_data, who, type);
+#else
+	return NULL;
+#endif
 }
 
 PurpleMediaCaps jabber_get_media_caps(PurpleConnection *gc, const char *who)
 {
+#ifdef USE_VV
 	JabberStream *js = (JabberStream *) gc->proto_data;
 	JabberBuddy *jb;
 	PurpleMediaCaps caps = PURPLE_MEDIA_CAPS_NONE;
@@ -2692,14 +2684,15 @@
 			caps |= PURPLE_MEDIA_CAPS_MODIFY_SESSION |
 					PURPLE_MEDIA_CAPS_CHANGE_DIRECTION;
 	}
-	if (jabber_buddy_has_capability(jb, GTALK_CAP))
+	if (jabber_buddy_has_capability(jb, GOOGLE_VOICE_CAP))
 		caps |= PURPLE_MEDIA_CAPS_AUDIO;
 
 	return caps;
+#else
+	return PURPLE_MEDIA_CAPS_NONE;
+#endif
 }
 
-#endif
-
 void jabber_register_commands(void)
 {
 	purple_cmd_register("config", "", PURPLE_CMD_P_PRPL,
--- a/libpurple/protocols/jabber/jabber.h	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Mon Mar 23 01:43:25 2009 +0000
@@ -321,12 +321,9 @@
 gboolean jabber_offline_message(const PurpleBuddy *buddy);
 int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len);
 GList *jabber_actions(PurplePlugin *plugin, gpointer context);
+PurpleMedia *jabber_initiate_media(PurpleConnection *gc, const char *who,
+		PurpleMediaSessionType type);
+PurpleMediaCaps jabber_get_media_caps(PurpleConnection *gc, const char *who);
 void jabber_register_commands(void);
 void jabber_init_plugin(PurplePlugin *plugin);
-
-#ifdef USE_VV
-PurpleMedia *jabber_initiate_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/rtp.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/jabber/jingle/rtp.c	Mon Mar 23 01:43:25 2009 +0000
@@ -335,15 +335,14 @@
 	}
 }
 
-static void jingle_rtp_send_initiate(JingleSession *session);
-static void jingle_rtp_send_accept(JingleSession *session);
+static void jingle_rtp_ready(JingleSession *session);
 
 static void
 jingle_rtp_accepted_cb(PurpleMedia *media, gchar *sid, gchar *name,
 		JingleSession *session)
 {
 	purple_debug_info("jingle-rtp", "jingle_rtp_accepted_cb\n");
-	jingle_rtp_send_accept(session);
+	jingle_rtp_ready(session);
 }
 
 static void
@@ -377,8 +376,7 @@
 	jingle_content_set_pending_transport(content, transport);
 	jingle_content_accept_transport(content);
 
-	jingle_rtp_send_initiate(session);
-	jingle_rtp_send_accept(session);
+	jingle_rtp_ready(session);
 }
 
 static void
@@ -387,8 +385,7 @@
 {
 	purple_debug_info("jingle-rtp", "jingle_rtp_codecs_changed_cb: "
 			"session_id: %s jingle_session: %p\n", sid, session);
-	jingle_rtp_send_initiate(session);
-	jingle_rtp_send_accept(session);
+	jingle_rtp_ready(session);
 }
 
 static void
@@ -438,50 +435,58 @@
 }
 
 static void
-jingle_rtp_state_changed_cb(PurpleMedia *media, PurpleMediaStateChangedType type,
+jingle_rtp_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
 		gchar *sid, gchar *name, JingleSession *session)
 {
-	purple_debug_info("jingle-rtp", "state-changed: type %d id: %s name: %s\n", type, sid, name);
+	purple_debug_info("jingle-rtp", "state-changed: state %d "
+			"id: %s name: %s\n", state, sid, name);
+}
 
-	if ((type == PURPLE_MEDIA_STATE_CHANGED_REJECTED ||
-			type == PURPLE_MEDIA_STATE_CHANGED_HANGUP) &&
-			sid == NULL && name == NULL) {
+static void
+jingle_rtp_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
+		gchar *sid, gchar *name, JingleSession *session)
+{
+	purple_debug_info("jingle-rtp", "stream-info: type %d "
+			"id: %s name: %s\n", type, sid, name);
+	if (type == PURPLE_MEDIA_INFO_HANGUP) {
 		jabber_iq_send(jingle_session_terminate_packet(
 				session, "success"));
 		g_object_unref(session);
+	} else if (type == PURPLE_MEDIA_INFO_REJECT) {
+		jabber_iq_send(jingle_session_terminate_packet(
+				session, "decline"));
+		g_object_unref(session);
 	}
 }
 
 static void
-jingle_rtp_send_initiate(JingleSession *session)
+jingle_rtp_ready(JingleSession *session)
 {
 	PurpleMedia *media = jingle_rtp_get_media(session);
 
-	if (jingle_session_is_initiator(session) == TRUE &&
+	if (purple_media_candidates_prepared(media, NULL, NULL) &&
 			purple_media_codecs_ready(media, NULL) &&
-			purple_media_candidates_prepared(media, NULL, NULL)) {
-		JabberIq *iq = jingle_session_to_packet(
-				session, JINGLE_SESSION_INITIATE);
-		jabber_iq_set_callback(iq,
-				jingle_rtp_initiate_ack_cb, session);
-		jabber_iq_send(iq);
-		g_signal_connect(G_OBJECT(media), "new-candidate",
-				G_CALLBACK(jingle_rtp_new_candidate_cb),
+			(jingle_session_is_initiator(session) == TRUE ||
+			purple_media_accepted(media, NULL, NULL))) {
+		if (jingle_session_is_initiator(session)) {
+			JabberIq *iq = jingle_session_to_packet(
+					session, JINGLE_SESSION_INITIATE);
+			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_SESSION_ACCEPT));
+		}
+
+		g_signal_handlers_disconnect_by_func(G_OBJECT(media),
+				G_CALLBACK(jingle_rtp_accepted_cb), session);
+		g_signal_handlers_disconnect_by_func(G_OBJECT(media),
+				G_CALLBACK(jingle_rtp_candidates_prepared_cb),
 				session);
-	}
-}
-
-static void
-jingle_rtp_send_accept(JingleSession *session)
-{
-	PurpleMedia *media = jingle_rtp_get_media(session);
-
-	if (jingle_session_is_initiator(session) == FALSE &&
-			purple_media_codecs_ready(media, NULL) &&
-			purple_media_accepted(media, NULL, NULL) &&
-			purple_media_candidates_prepared(media, NULL, NULL)) {
-		jabber_iq_send(jingle_session_to_packet(session,
-				JINGLE_SESSION_ACCEPT));
+		g_signal_handlers_disconnect_by_func(G_OBJECT(media),
+				G_CALLBACK(jingle_rtp_codecs_changed_cb),
+				session);
 		g_signal_connect(G_OBJECT(media), "new-candidate",
 				G_CALLBACK(jingle_rtp_new_candidate_cb),
 				session);
@@ -508,14 +513,17 @@
 	purple_media_set_prpl_data(media, session);
 
 	/* connect callbacks */
-	g_signal_connect(G_OBJECT(media), "accepted",
-				 G_CALLBACK(jingle_rtp_accepted_cb), session);
+	if (jingle_session_is_initiator(session) == FALSE)
+		g_signal_connect(G_OBJECT(media), "accepted",
+				G_CALLBACK(jingle_rtp_accepted_cb), session);
 	g_signal_connect(G_OBJECT(media), "candidates-prepared",
 				 G_CALLBACK(jingle_rtp_candidates_prepared_cb), session);
 	g_signal_connect(G_OBJECT(media), "codecs-changed",
 				 G_CALLBACK(jingle_rtp_codecs_changed_cb), session);
 	g_signal_connect(G_OBJECT(media), "state-changed",
 				 G_CALLBACK(jingle_rtp_state_changed_cb), session);
+	g_signal_connect(G_OBJECT(media), "stream-info",
+			G_CALLBACK(jingle_rtp_stream_info_cb), session);
 
 	g_object_unref(session);
 	return media;
--- a/libpurple/protocols/jabber/libxmpp.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Mon Mar 23 01:43:25 2009 +0000
@@ -119,13 +119,8 @@
 	jabber_attention_types,			/* attention_types */
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
 	NULL, /* get_account_text_table */
-#ifdef USE_VV
 	jabber_initiate_media,          /* initiate_media */
 	jabber_get_media_caps,                  /* get_media_caps */
-#else
-	NULL,					/* initiate_media */
-	NULL					/* can_do_media */
-#endif
 };
 
 static gboolean load_plugin(PurplePlugin *plugin)
--- a/libpurple/protocols/msn/notification.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Mon Mar 23 01:43:25 2009 +0000
@@ -1609,7 +1609,7 @@
 
 	if ( (root = xmlnode_from_str(cmd->payload, cmd->payload_len)) == NULL)
 	{
-		purple_debug_error("msn", "Unable to parse GCF payload into a XML tree");
+		purple_debug_error("msn", "Unable to parse GCF payload into a XML tree\n");
 		return;
 	}
 
@@ -1682,7 +1682,7 @@
 	user = msn_userlist_find_user(session->userlist, passport);
 	if (user == NULL) {
 		char *str = g_strndup(payload, len);
-		purple_debug_info("msn", "unknown user %s, payload is %s",
+		purple_debug_info("msn", "unknown user %s, payload is %s\n",
 			passport, str);
 		g_free(str);
 		return;
--- a/libpurple/protocols/msn/oim.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/msn/oim.c	Mon Mar 23 01:43:25 2009 +0000
@@ -174,7 +174,7 @@
 		gchar *faultcode_str = xmlnode_get_data(faultcode);
 
 		if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) {
-			purple_debug_warning("msn", "OIM Request Error, Updating token now.");
+			purple_debug_warning("msn", "OIM Request Error, Updating token now.\n");
 			msn_nexus_update_token(data->oim->session->nexus,
 				data->send ? MSN_AUTH_LIVE_SECURE : MSN_AUTH_MESSENGER_WEB,
 				(GSourceFunc)msn_oim_request_helper, data);
@@ -183,7 +183,7 @@
 
 		} else if (faultcode_str && g_str_equal(faultcode_str, "q0:AuthenticationFailed")) {
 			if (xmlnode_get_child(fault, "detail/RequiredAuthPolicy") != NULL) {
-				purple_debug_warning("msn", "OIM Request Error, Updating token now.");
+				purple_debug_warning("msn", "OIM Request Error, Updating token now.\n");
 				msn_nexus_update_token(data->oim->session->nexus,
 					data->send ? MSN_AUTH_LIVE_SECURE : MSN_AUTH_MESSENGER_WEB,
 					(GSourceFunc)msn_oim_request_helper, data);
--- a/libpurple/protocols/msn/state.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/msn/state.c	Mon Mar 23 01:43:25 2009 +0000
@@ -169,7 +169,7 @@
 	}
 	currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia");
 	if (currentmediaNode == NULL) {
-		purple_debug_info("msn", "No CurrentMedia Node");
+		purple_debug_info("msn", "No CurrentMedia Node\n");
 		xmlnode_free(payloadNode);
 		return NULL;
 	}
@@ -195,7 +195,7 @@
 	}
 	psmNode = xmlnode_get_child(payloadNode, "PSM");
 	if (psmNode == NULL) {
-		purple_debug_info("msn", "No PSM status Node");
+		purple_debug_info("msn", "No PSM status Node\n");
 		xmlnode_free(payloadNode);
 		return NULL;
 	}
--- a/libpurple/protocols/msn/switchboard.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Mon Mar 23 01:43:25 2009 +0000
@@ -590,7 +590,7 @@
 	payload = msn_message_gen_payload(msg, &payload_len);
 
 #ifdef MSN_DEBUG_SB
-	purple_debug_info("msn", "SB length:{%" G_GSIZE_FORMAT "}", payload_len);
+	purple_debug_info("msn", "SB length:{%" G_GSIZE_FORMAT "}\n", payload_len);
 	msn_message_show_readable(msg, "SB SEND", FALSE);
 #endif
 
--- a/libpurple/protocols/msn/userlist.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/protocols/msn/userlist.c	Mon Mar 23 01:43:25 2009 +0000
@@ -858,7 +858,7 @@
 	}
 
 	if ( (user = msn_userlist_find_user(userlist, who)) == NULL) {
-		purple_debug_error("msn", "User %s not found!", who);
+		purple_debug_error("msn", "User %s not found!\n", who);
 		return FALSE;
 	}
 
@@ -887,7 +887,7 @@
 	}
 
 	if ( (user = msn_userlist_find_user(userlist, who)) == NULL) {
-		purple_debug_error("msn", "User %s not found!", who);
+		purple_debug_error("msn", "User %s not found!\n", who);
 		return FALSE;
 	}
 
--- a/libpurple/util.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/libpurple/util.c	Mon Mar 23 01:43:25 2009 +0000
@@ -4044,6 +4044,13 @@
 				   &gfud->website.page, &gfud->website.user, &gfud->website.passwd);
 
 	if (purple_strcasestr(url, "https://") != NULL) {
+		if (!purple_ssl_is_supported()) {
+			purple_util_fetch_url_error(gfud,
+					_("Unable to connect to %s: Server requires TLS/SSL, but no TLS/SSL support was found."),
+					gfud->website.address);
+			return NULL;
+		}
+
 		gfud->is_ssl = TRUE;
 		gfud->ssl_connection = purple_ssl_connect(NULL,
 				gfud->website.address, gfud->website.port,
--- a/pidgin/gtkmedia.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/pidgin/gtkmedia.c	Mon Mar 23 01:43:25 2009 +0000
@@ -324,7 +324,7 @@
 	if (message->type != GST_MESSAGE_ELEMENT)
 		return TRUE;
 
-	if (gst_structure_has_name(
+	if (!gst_structure_has_name(
 			gst_message_get_structure(message), "level"))
 		return TRUE;
 
@@ -612,6 +612,10 @@
 		gtk_widget_show(gtkmedia->priv->recv_progress);
 	}
 	if (type & PURPLE_MEDIA_SEND_AUDIO) {
+		GstElement *media_src = purple_media_get_src(media, sid);
+		gtkmedia->priv->send_level = gst_bin_get_by_name(
+				GST_BIN(media_src), "sendlevel");
+
 		gtkmedia->priv->send_progress = gtk_progress_bar_new();
 		gtk_widget_set_size_request(gtkmedia->priv->send_progress, 320, 10);
 		gtk_box_pack_end(GTK_BOX(send_widget),
@@ -656,25 +660,49 @@
 }
 
 static void
-pidgin_media_state_changed_cb(PurpleMedia *media,
-		PurpleMediaStateChangedType type,
+pidgin_media_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
 		gchar *sid, gchar *name, PidginMedia *gtkmedia)
 {
-	purple_debug_info("gtkmedia", "type: %d sid: %s name: %s\n",
-			type, sid, name);
+	purple_debug_info("gtkmedia", "state: %d sid: %s name: %s\n",
+			state, sid, name);
 	if (sid == NULL && name == NULL) {
-		if (type == PURPLE_MEDIA_STATE_CHANGED_END) {
+		if (state == PURPLE_MEDIA_STATE_END) {
 			pidgin_media_emit_message(gtkmedia,
 					_("The call has been terminated."));
 			gtk_widget_destroy(GTK_WIDGET(gtkmedia));
-			
-		} else if (type == PURPLE_MEDIA_STATE_CHANGED_REJECTED) {
-			pidgin_media_emit_message(gtkmedia,
-					_("You have rejected the call."));
 		}
-	} else if (type == PURPLE_MEDIA_STATE_CHANGED_NEW &&
+	} else if (state == PURPLE_MEDIA_STATE_NEW &&
 			sid != NULL && name != NULL) {
 		pidgin_media_ready_cb(media, gtkmedia, sid);
+	} else if (state == PURPLE_MEDIA_STATE_CONNECTED &&
+			purple_media_get_session_type(media, sid) &
+			PURPLE_MEDIA_RECV_AUDIO) {
+		GstElement *tee = purple_media_get_tee(media, sid, name);
+		GstIterator *iter = gst_element_iterate_src_pads(tee);
+		GstPad *sinkpad;
+		if (gst_iterator_next(iter, (gpointer)&sinkpad)
+				 == GST_ITERATOR_OK) {
+			GstPad *peer = gst_pad_get_peer(sinkpad);
+			if (peer != NULL) {
+				gtkmedia->priv->recv_level =
+						gst_bin_get_by_name(
+						GST_BIN(GST_OBJECT_PARENT(
+						peer)), "recvlevel");
+				gst_object_unref(peer);
+			}
+			gst_object_unref(sinkpad);
+		}
+		gst_iterator_free(iter);
+	}
+}
+
+static void
+pidgin_media_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
+		gchar *sid, gchar *name, PidginMedia *gtkmedia)
+{
+	if (type == PURPLE_MEDIA_INFO_REJECT) {
+		pidgin_media_emit_message(gtkmedia,
+				_("You have rejected the call."));
 	}
 }
 
@@ -707,6 +735,8 @@
 				G_CALLBACK(pidgin_media_accepted_cb), media);
 			g_signal_connect(G_OBJECT(media->priv->media), "state-changed",
 				G_CALLBACK(pidgin_media_state_changed_cb), media);
+			g_signal_connect(G_OBJECT(media->priv->media), "stream-info",
+				G_CALLBACK(pidgin_media_stream_info_cb), media);
 			break;
 		}
 		case PROP_SCREENNAME:
@@ -806,33 +836,105 @@
 static GstElement *
 create_default_video_src(void)
 {
-	GstElement *ret = NULL;
-	purple_media_video_init_src(&ret);
-	return ret;
+	GstElement *sendbin, *src, *videoscale, *capsfilter;
+	GstPad *pad;
+	GstPad *ghost;
+	GstCaps *caps;
+	const gchar *video_plugin = purple_prefs_get_string(
+			"/purple/media/video/plugin");
+	const gchar *video_device = purple_prefs_get_string(
+			"/purple/media/video/device");
+
+	sendbin = gst_bin_new("purplesendvideobin");
+	src = gst_element_factory_make(video_plugin, "purplevideosource");
+	videoscale = gst_element_factory_make("videoscale", NULL);
+	capsfilter = gst_element_factory_make("capsfilter", NULL);
+
+	/* It was recommended to set the size < 352x288 and framerate < 20 */
+	caps = gst_caps_from_string("video/x-raw-yuv , width=[250,350] , "
+			"height=[200,275] , framerate=[10/1,20/1]");
+	g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
+
+	gst_bin_add_many(GST_BIN(sendbin), src,
+			videoscale, capsfilter, NULL);
+	gst_element_link_many(src, videoscale, capsfilter, NULL);
+
+	if (!strcmp(video_plugin, "videotestsrc")) {
+		/* Set is-live to true to throttle videotestsrc */
+		g_object_set (G_OBJECT(src), "is-live", TRUE, NULL);
+	}
+
+	pad = gst_element_get_static_pad(capsfilter, "src");
+	ghost = gst_ghost_pad_new("ghostsrc", pad);
+	gst_object_unref(pad);
+	gst_element_add_pad(sendbin, ghost);
+
+	if (video_device != NULL && strcmp(video_device, ""))
+		g_object_set(G_OBJECT(src), "device", video_device, NULL);
+
+	return sendbin;
 }
 
 static GstElement *
 create_default_video_sink(void)
 {
-	GstElement *ret = NULL;
-	purple_media_video_init_recv(&ret);
-	return ret;
+	return gst_element_factory_make("autovideosink", NULL);
 }
 
 static GstElement *
 create_default_audio_src(void)
 {
-	GstElement *ret = NULL, *level = NULL;
-	purple_media_audio_init_src(&ret, &level);
-	return ret;
+	GstElement *bin, *src, *volume, *level;
+	GstPad *pad, *ghost;
+	const gchar *audio_device = purple_prefs_get_string(
+			"/purple/media/audio/device");
+	double input_volume = purple_prefs_get_int(
+			"/purple/media/audio/volume/input")/10.0;
+
+	bin = gst_bin_new("purplesendaudiobin");
+	src = gst_element_factory_make("alsasrc", "asrc");
+	volume = gst_element_factory_make("volume", "purpleaudioinputvolume");
+	g_object_set(volume, "volume", input_volume, NULL);
+	level = gst_element_factory_make("level", "sendlevel");
+	gst_bin_add_many(GST_BIN(bin), src, volume, level, NULL);
+	gst_element_link(src, volume);
+	gst_element_link(volume, level);
+	pad = gst_element_get_pad(level, "src");
+	ghost = gst_ghost_pad_new("ghostsrc", pad);
+	gst_element_add_pad(bin, ghost);
+	g_object_set(G_OBJECT(level), "message", TRUE, NULL);
+
+	if (audio_device != NULL && strcmp(audio_device, ""))
+		g_object_set(G_OBJECT(src), "device", audio_device, NULL);
+
+	return bin;
 }
 
 static GstElement *
 create_default_audio_sink(void)
 {
-	GstElement *ret = NULL, *level = NULL;
-	purple_media_audio_init_recv(&ret, &level);
-	return ret;
+	GstElement *bin, *sink, *volume, *level, *queue;
+	GstPad *pad, *ghost;
+	double output_volume = purple_prefs_get_int(
+			"/purple/media/audio/volume/output")/10.0;
+
+	bin = gst_bin_new("pidginrecvaudiobin");
+	sink = gst_element_factory_make("alsasink", "asink");
+	g_object_set(G_OBJECT(sink), "async", FALSE, "sync", FALSE, NULL);
+	volume = gst_element_factory_make("volume", "purpleaudiooutputvolume");
+	g_object_set(volume, "volume", output_volume, NULL);
+	level = gst_element_factory_make("level", "recvlevel");
+	queue = gst_element_factory_make("queue", NULL);
+	gst_bin_add_many(GST_BIN(bin), sink, volume, level, queue, NULL);
+	gst_element_link(level, sink);
+	gst_element_link(volume, level);
+	gst_element_link(queue, volume);
+	pad = gst_element_get_pad(queue, "sink");
+	ghost = gst_ghost_pad_new("ghostsink", pad);
+	gst_element_add_pad(bin, ghost);
+	g_object_set(G_OBJECT(level), "message", TRUE, NULL);
+
+	return bin;
 }
 
 static PurpleMediaElementInfo default_video_src =
--- a/pidgin/gtknotify.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/pidgin/gtknotify.c	Mon Mar 23 01:43:25 2009 +0000
@@ -28,6 +28,7 @@
 
 #include <gdk/gdkkeysyms.h>
 
+#include "account.h"
 #include "connection.h"
 #include "debug.h"
 #include "prefs.h"
@@ -37,6 +38,7 @@
 #include "gtkblist.h"
 #include "gtkimhtml.h"
 #include "gtknotify.h"
+#include "gtkpounce.h"
 #include "gtkutils.h"
 
 typedef struct
@@ -57,6 +59,13 @@
 typedef struct
 {
 	PurpleAccount *account;
+	PurplePounce *pounce;
+} PidginNotifyPounceData;
+
+
+typedef struct
+{
+	PurpleAccount *account;
 	GtkListStore *model;
 	GtkWidget *treeview;
 	GtkWidget *window;
@@ -80,21 +89,44 @@
 	COLUMNS_PIDGIN_MAIL
 };
 
-typedef struct _PidginMailDialog PidginMailDialog;
+enum
+{
+	PIDGIN_POUNCE_ICON,
+	PIDGIN_POUNCE_ALIAS,
+	PIDGIN_POUNCE_EVENT,
+	PIDGIN_POUNCE_TEXT,
+	PIDGIN_POUNCE_DATE,
+	PIDGIN_POUNCE_DATA,
+	PIDGIN_POUNCE_COLUMNS
+};
 
-struct _PidginMailDialog
+typedef struct _PidginNotifyDialog PidginNotifyDialog;
+typedef PidginNotifyDialog PidginMailDialog;
+
+struct _PidginNotifyDialog
 {
 	GtkWidget *dialog;
 	GtkWidget *treeview;
 	GtkTreeStore *treemodel;
 	GtkLabel *label;
 	GtkWidget *open_button;
+	GtkWidget *dismiss_button;
+	GtkWidget *edit_button;
 	int total_count;
 	gboolean in_use;
 };
 
-static PidginMailDialog *mail_dialog = NULL;
+typedef enum
+{
+	PIDGIN_NOTIFY_MAIL,
+	PIDGIN_NOTIFY_POUNCE,
+        PIDGIN_NOTIFY_TYPES
+} PidginNotifyType;
 
+static PidginNotifyDialog *mail_dialog = NULL;
+static PidginNotifyDialog *pounce_dialog = NULL;
+
+static GtkWidget *pidgin_get_notification_dialog(PidginNotifyType type);
 static void *pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed,
 									const char **subjects,
 									const char **froms, const char **tos,
@@ -109,6 +141,159 @@
 }
 
 static void
+pounce_response_close(PidginNotifyDialog *dialog)
+{
+	GtkTreeIter iter;
+	PidginNotifyPounceData *pounce_data;
+
+	while (gtk_tree_model_get_iter_first(
+				GTK_TREE_MODEL(pounce_dialog->treemodel), &iter)) {
+		gtk_tree_model_get(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter,
+				PIDGIN_POUNCE_DATA, &pounce_data,
+				-1);
+		gtk_tree_store_remove(dialog->treemodel, &iter);
+
+		g_free(pounce_data);
+	}
+
+	gtk_widget_destroy(pounce_dialog->dialog);
+	g_free(pounce_dialog);
+	pounce_dialog = NULL;
+}
+
+static void
+delete_foreach(GtkTreeModel *model, GtkTreePath *path,
+		GtkTreeIter *iter, gpointer data)
+{
+	PidginNotifyPounceData *pounce_data;
+
+	gtk_tree_model_get(model, iter,
+			PIDGIN_POUNCE_DATA, &pounce_data,
+			-1);
+
+	if (pounce_data != NULL)
+		g_free(pounce_data);
+}
+
+static void
+append_to_list(GtkTreeModel *model, GtkTreePath *path,
+		GtkTreeIter *iter, gpointer data)
+{
+	GList **list = data;
+	*list = g_list_prepend(*list, gtk_tree_path_copy(path));
+}
+static void
+pounce_response_dismiss()
+{
+	GtkTreeSelection *selection;
+	GList *list = NULL;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog->treeview));
+	gtk_tree_selection_selected_foreach(selection, delete_foreach, pounce_dialog);
+	gtk_tree_selection_selected_foreach(selection, append_to_list, &list);
+
+	while (list) {
+		GtkTreeIter iter;
+		if (gtk_tree_model_get_iter(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter,
+					list->data)) {
+			gtk_tree_store_remove(GTK_TREE_STORE(pounce_dialog->treemodel), &iter);
+		}
+		gtk_tree_path_free(list->data);
+		list = g_list_delete_link(list, list);
+	}
+}
+
+static void
+pounce_response_edit_cb(GtkTreeModel *model, GtkTreePath *path,
+		GtkTreeIter *iter, gpointer data)
+{
+	PidginNotifyPounceData *pounce_data;
+	PidginNotifyDialog *dialog = (PidginNotifyDialog*)data;
+	PurplePounce *pounce;
+	GList *list;
+
+	list = purple_pounces_get_all();
+
+	gtk_tree_model_get(GTK_TREE_MODEL(dialog->treemodel), iter,
+			PIDGIN_POUNCE_DATA, &pounce_data,
+			-1);
+	
+	for (; list != NULL; list = list->next) {
+		pounce = list->data;
+		if (pounce == pounce_data->pounce) {
+			pidgin_pounce_editor_show(pounce_data->account, NULL, pounce_data->pounce);
+			return;
+		}
+	}
+
+	purple_debug_warning("gtknotify", "Pounce was destroyed.\n");
+}
+
+static void
+pounce_response_cb(GtkDialog *dlg, gint id, PidginNotifyDialog *dialog)
+{
+	GtkTreeSelection *selection = NULL;
+
+	switch (id) {
+		case GTK_RESPONSE_CLOSE:
+		case GTK_RESPONSE_DELETE_EVENT:
+			pounce_response_close(dialog);
+			break;
+		case GTK_RESPONSE_NO:
+			pounce_response_dismiss();
+			break;
+		case GTK_RESPONSE_APPLY:
+			selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview));
+			gtk_tree_selection_selected_foreach(selection, pounce_response_edit_cb,
+					dialog);
+			break;
+	}
+}
+
+static void
+pounce_row_selected_cb(GtkTreeView *tv, GtkTreePath *path,
+	GtkTreeViewColumn *col, gpointer data)
+{
+	GtkTreeIter iter;
+	GtkTreeSelection *selection;
+	gboolean selected;
+	GList *list;
+
+	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog->treeview));
+
+	selected = gtk_tree_selection_get_selected(selection,
+			NULL, &iter);
+
+	if (selected) {
+		PurplePounce *pounce;
+		PidginNotifyPounceData *pounce_data;
+
+		list = purple_pounces_get_all();
+
+		gtk_tree_model_get(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter,
+				PIDGIN_POUNCE_DATA, &pounce_data,
+				-1);
+
+		gtk_widget_set_sensitive(pounce_dialog->edit_button, FALSE);
+
+		for (; list != NULL; list = list->next) {
+			pounce = list->data;
+			if (pounce == pounce_data->pounce) {
+				gtk_widget_set_sensitive(pounce_dialog->edit_button, TRUE);
+				break;
+			}
+		}
+	
+		gtk_widget_set_sensitive(pounce_dialog->dismiss_button, TRUE);
+	} else {
+		gtk_widget_set_sensitive(pounce_dialog->edit_button, FALSE);
+		gtk_widget_set_sensitive(pounce_dialog->dismiss_button, FALSE);
+	}
+
+
+}
+
+static void
 email_response_cb(GtkDialog *dlg, gint id, PidginMailDialog *dialog)
 {
 	PidginNotifyMailData *data = NULL;
@@ -342,89 +527,7 @@
 static GtkWidget *
 pidgin_get_mail_dialog(void)
 {
-	if (mail_dialog == NULL) {
-		GtkWidget *dialog = NULL;
-		GtkWidget *label;
-		GtkWidget *sw;
-		GtkCellRenderer *rend;
-		GtkTreeViewColumn *column;
-		GtkWidget *button = NULL;
-		GtkWidget *vbox = NULL;
-
-		dialog = gtk_dialog_new_with_buttons(_("New Mail"), NULL, 0,
-						     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
-						     NULL);
-		gtk_window_set_role(GTK_WINDOW(dialog), "new_mail_detailed");
-		g_signal_connect(G_OBJECT(dialog), "focus-in-event",
-					G_CALLBACK(mail_window_focus_cb), NULL);
-
-		gtk_dialog_add_button(GTK_DIALOG(dialog),
-					 _("Open All Messages"), GTK_RESPONSE_ACCEPT);
-
-		button = gtk_dialog_add_button(GTK_DIALOG(dialog),
-						 PIDGIN_STOCK_OPEN_MAIL, GTK_RESPONSE_YES);
-
-		/* make "Open All Messages" the default response */
-		gtk_dialog_set_default_response(GTK_DIALOG(dialog),
-						GTK_RESPONSE_ACCEPT);
-
-		/* Setup the dialog */
-		gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BOX_SPACE);
-		gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BOX_SPACE);
-		gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
-		gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
-
-		/* Vertical box */
-		vbox = GTK_DIALOG(dialog)->vbox;
-
-		/* Golden ratio it up! */
-		gtk_widget_set_size_request(dialog, 550, 400);
-
-		sw = gtk_scrolled_window_new(NULL, NULL);
-		gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
-		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-
-		mail_dialog = g_new0(PidginMailDialog, 1);
-		mail_dialog->dialog = dialog;
-		mail_dialog->open_button = button;
-
-		mail_dialog->treemodel = gtk_tree_store_new(COLUMNS_PIDGIN_MAIL,
-						GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
-		mail_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(mail_dialog->treemodel));
-		g_object_unref(G_OBJECT(mail_dialog->treemodel));
-		gtk_tree_view_set_search_column(GTK_TREE_VIEW(mail_dialog->treeview), PIDGIN_MAIL_TEXT);
-		gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(mail_dialog->treeview),
-			             pidgin_tree_view_search_equal_func, NULL, NULL);
-
-		g_signal_connect(G_OBJECT(dialog), "response",
-						 G_CALLBACK(email_response_cb), mail_dialog);
-		g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(mail_dialog->treeview))),
-						 "changed", G_CALLBACK(selection_changed_cb), mail_dialog);
-		g_signal_connect(G_OBJECT(mail_dialog->treeview), "row-activated", G_CALLBACK(email_row_activated_cb), NULL);
-
-		gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(mail_dialog->treeview), FALSE);
-		gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(mail_dialog->treeview), TRUE);
-		gtk_container_add(GTK_CONTAINER(sw), mail_dialog->treeview);
-
-		column = gtk_tree_view_column_new();
-		gtk_tree_view_column_set_resizable(column, TRUE);
-		rend = gtk_cell_renderer_pixbuf_new();
-		gtk_tree_view_column_pack_start(column, rend, FALSE);
-		gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_MAIL_ICON, NULL);
-		rend = gtk_cell_renderer_text_new();
-		gtk_tree_view_column_pack_start(column, rend, TRUE);
-		gtk_tree_view_column_set_attributes(column, rend, "markup", PIDGIN_MAIL_TEXT, NULL);
-		gtk_tree_view_append_column(GTK_TREE_VIEW(mail_dialog->treeview), column);
-
-		label = gtk_label_new(NULL);
-		gtk_label_set_markup(GTK_LABEL(label), _("<span weight=\"bold\" size=\"larger\">You have mail!</span>"));
-		gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
-		gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-		gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-		gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
-	}
-
-	return mail_dialog->dialog;
+	return pidgin_get_notification_dialog(PIDGIN_NOTIFY_MAIL);
 }
 
 /* count == 0 means this is a detailed mail notification.
@@ -1001,8 +1104,10 @@
 	{
 		PidginNotifyMailData *data = (PidginNotifyMailData *)ui_handle;
 
-		g_free(data->url);
-		g_free(data);
+		if (data) {
+			g_free(data->url);
+			g_free(data);
+		}
 	}
 	else if (type == PURPLE_NOTIFY_SEARCHRESULTS)
 	{
@@ -1234,6 +1339,228 @@
 	return NULL;
 }
 
+static GtkWidget *
+pidgin_get_dialog(PidginNotifyType type, GtkTreeStore *treemodel)
+{
+	GtkWidget *dialog = NULL;
+	GtkWidget *label = NULL;
+	GtkWidget *sw;
+	GtkCellRenderer *rend;
+	GtkTreeViewColumn *column;
+	GtkWidget *button = NULL;
+	GtkWidget *vbox = NULL;
+	GtkTreeSelection *sel;
+	PidginNotifyDialog *spec_dialog = NULL;
+	
+	g_return_val_if_fail(type < PIDGIN_NOTIFY_TYPES, NULL);
+	
+	dialog = gtk_dialog_new_with_buttons(NULL, NULL, 0,
+			GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+			NULL);
+
+	/* Setup the dialog */
+	gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BOX_SPACE);
+	gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BOX_SPACE);
+	gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
+	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER);
+
+	/* Vertical box */
+	vbox = GTK_DIALOG(dialog)->vbox;
+
+	/* Golden ratio it up! */
+	gtk_widget_set_size_request(dialog, 550, 400);
+
+	sw = gtk_scrolled_window_new(NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
+	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+
+	spec_dialog = g_new0(PidginNotifyDialog, 1);
+	spec_dialog->dialog = dialog;
+	spec_dialog->open_button = button;
+
+	spec_dialog->treemodel = treemodel;
+	spec_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(spec_dialog->treemodel));
+	g_object_unref(G_OBJECT(spec_dialog->treemodel));
+	
+	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(spec_dialog->treeview), TRUE);
+	gtk_container_add(GTK_CONTAINER(sw), spec_dialog->treeview);
+
+	if (type == PIDGIN_NOTIFY_MAIL) {
+		gtk_window_set_title(GTK_WINDOW(dialog), _("New Mail"));
+		gtk_window_set_role(GTK_WINDOW(dialog), "new_mail_detailed");
+		g_signal_connect(G_OBJECT(dialog), "focus-in-event",
+					G_CALLBACK(mail_window_focus_cb), NULL);
+
+		gtk_dialog_add_button(GTK_DIALOG(dialog),
+					 _("Open All Messages"), GTK_RESPONSE_ACCEPT);
+
+		button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+						 PIDGIN_STOCK_OPEN_MAIL, GTK_RESPONSE_YES);
+
+		gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(spec_dialog->treeview), FALSE);
+
+		gtk_tree_view_set_search_column(GTK_TREE_VIEW(spec_dialog->treeview), PIDGIN_MAIL_TEXT);
+		gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(spec_dialog->treeview),
+			             pidgin_tree_view_search_equal_func, NULL, NULL);
+
+		g_signal_connect(G_OBJECT(dialog), "response",
+						 G_CALLBACK(email_response_cb), spec_dialog);
+		g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog->treeview))),
+						 "changed", G_CALLBACK(selection_changed_cb), spec_dialog);
+		g_signal_connect(G_OBJECT(spec_dialog->treeview), "row-activated", G_CALLBACK(email_row_activated_cb), NULL);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_pixbuf_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+
+		gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_MAIL_ICON, NULL);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, TRUE);
+		gtk_tree_view_column_set_attributes(column, rend, "markup", PIDGIN_MAIL_TEXT, NULL);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		label = gtk_label_new(NULL);
+		gtk_label_set_markup(GTK_LABEL(label), _("<span weight=\"bold\" size=\"larger\">You have mail!</span>"));
+
+	} else if (type == PIDGIN_NOTIFY_POUNCE) {
+		gtk_window_set_title(GTK_WINDOW(dialog), _("New Pounces"));
+
+		button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+						_("Dismiss"), GTK_RESPONSE_NO);
+		gtk_widget_set_sensitive(button, FALSE);
+		spec_dialog->dismiss_button = button;
+
+		button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+						PIDGIN_STOCK_EDIT, GTK_RESPONSE_APPLY);
+		gtk_widget_set_sensitive(button, FALSE);
+		spec_dialog->edit_button = button;
+
+		g_signal_connect(G_OBJECT(dialog), "response",
+						 G_CALLBACK(pounce_response_cb), spec_dialog);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_title(column, _("Buddy"));
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_pixbuf_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+
+		gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_POUNCE_ICON, NULL);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+		gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_ALIAS);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_title(column, _("Event"));
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+		gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_EVENT);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_title(column, _("Message"));
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+		gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_TEXT);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		column = gtk_tree_view_column_new();
+		gtk_tree_view_column_set_title(column, _("Date"));
+		gtk_tree_view_column_set_resizable(column, TRUE);
+		rend = gtk_cell_renderer_text_new();
+		gtk_tree_view_column_pack_start(column, rend, FALSE);
+		gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_DATE);
+		gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column);
+
+		label = gtk_label_new(NULL);
+		gtk_label_set_markup(GTK_LABEL(label), _("<span weight=\"bold\" size=\"larger\">You have pounced!</span>"));
+
+		sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog->treeview));
+		gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+		g_signal_connect(G_OBJECT(sel), "changed",
+			G_CALLBACK(pounce_row_selected_cb), NULL);
+	}
+
+	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 2);
+
+	if (type == PIDGIN_NOTIFY_MAIL)
+		mail_dialog = spec_dialog;
+	else if (type == PIDGIN_NOTIFY_POUNCE) {
+		pounce_dialog = spec_dialog;
+        }
+
+	return spec_dialog->dialog;
+
+}
+
+void
+pidgin_notify_pounce_add(PurpleAccount *account, PurplePounce *pounce,
+		const char *alias, const char *event, const char *message, const char *date)
+{
+	GtkWidget *dialog;
+	GdkPixbuf *icon;
+	GtkTreeIter iter;
+	PidginNotifyPounceData *pounce_data;
+
+	dialog = pidgin_get_notification_dialog(PIDGIN_NOTIFY_POUNCE);
+
+	icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+
+	pounce_data = g_new(PidginNotifyPounceData, 1);
+
+	pounce_data->account = account;
+	pounce_data->pounce = pounce;
+
+	gtk_tree_store_append(pounce_dialog->treemodel, &iter, NULL);
+
+	gtk_tree_store_set(pounce_dialog->treemodel, &iter,
+			PIDGIN_POUNCE_ICON, icon,
+			PIDGIN_POUNCE_ALIAS, alias,
+			PIDGIN_POUNCE_EVENT, event,
+			PIDGIN_POUNCE_TEXT, (message != NULL)? message : _("No message"),
+			PIDGIN_POUNCE_DATE, date,
+			PIDGIN_POUNCE_DATA, pounce_data,
+			-1);
+
+	if (icon)
+		g_object_unref(icon);
+
+	gtk_widget_show_all(dialog);
+
+	return;
+}
+
+static GtkWidget *
+pidgin_get_notification_dialog(PidginNotifyType type)
+{
+	GtkTreeStore *model = NULL;
+
+	if (type == PIDGIN_NOTIFY_MAIL) {
+		if (mail_dialog != NULL)
+			return mail_dialog->dialog;
+
+		model = gtk_tree_store_new(COLUMNS_PIDGIN_MAIL,
+						GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
+
+	} else if (type == PIDGIN_NOTIFY_POUNCE) {
+
+		if (pounce_dialog != NULL)
+			return pounce_dialog->dialog;
+
+		model = gtk_tree_store_new(PIDGIN_POUNCE_COLUMNS,
+				GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+				G_TYPE_STRING, G_TYPE_POINTER);
+	}
+
+	return pidgin_get_dialog(type, model);
+}
+
 static PurpleNotifyUiOps ops =
 {
 	pidgin_notify_message,
--- a/pidgin/gtknotify.h	Mon Mar 23 01:43:21 2009 +0000
+++ b/pidgin/gtknotify.h	Mon Mar 23 01:43:25 2009 +0000
@@ -27,6 +27,18 @@
 #define _PIDGINNOTIFY_H_
 
 #include "notify.h"
+#include "pounce.h"
+
+/**
+ * Adds a buddy pounce to the buddy pounce dialog
+ *
+ * @param alias		The buddy alias
+ * @param event 	Event description
+ * @param message	Pounce message
+ * @param date		Pounce date
+ */
+void pidgin_notify_pounce_add(PurpleAccount *account, PurplePounce *pounce,
+		const char *alias, const char *event, const char *message, const char *date);
 
 /**
  * Returns the UI operations structure for GTK+ notification functions.
--- a/pidgin/gtkpounce.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/pidgin/gtkpounce.c	Mon Mar 23 01:43:25 2009 +0000
@@ -30,7 +30,6 @@
 #include "account.h"
 #include "conversation.h"
 #include "debug.h"
-#include "notify.h"
 #include "prpl.h"
 #include "request.h"
 #include "server.h"
@@ -41,6 +40,7 @@
 #include "gtkdialogs.h"
 #include "gtkimhtml.h"
 #include "gtkpounce.h"
+#include "gtknotify.h"
 #include "pidginstock.h"
 #include "gtkutils.h"
 
@@ -1275,7 +1275,6 @@
 	/* Handle double-clicking */
 	g_signal_connect(G_OBJECT(treeview), "button_press_event",
 					 G_CALLBACK(pounce_double_click_cb), dialog);
-
 	gtk_container_add(GTK_CONTAINER(sw), treeview);
 	gtk_widget_show(treeview);
 
@@ -1458,27 +1457,27 @@
 		 */
 		tmp = g_strdup_printf(
 				   (events & PURPLE_POUNCE_TYPING) ?
-				   _("%s has started typing to you (%s)") :
+				   _("Started typing") :
 				   (events & PURPLE_POUNCE_TYPED) ?
-				   _("%s has paused while typing to you (%s)") :
+				   _("Paused while typing") :
 				   (events & PURPLE_POUNCE_SIGNON) ?
-				   _("%s has signed on (%s)") :
+				   _("Signed on") :
 				   (events & PURPLE_POUNCE_IDLE_RETURN) ?
-				   _("%s has returned from being idle (%s)") :
+				   _("Returned from being idle") :
 				   (events & PURPLE_POUNCE_AWAY_RETURN) ?
-				   _("%s has returned from being away (%s)") :
+				   _("Returned from being away") :
 				   (events & PURPLE_POUNCE_TYPING_STOPPED) ?
-				   _("%s has stopped typing to you (%s)") :
+				   _("Stopped typing") :
 				   (events & PURPLE_POUNCE_SIGNOFF) ?
-				   _("%s has signed off (%s)") :
+				   _("Signed off") :
 				   (events & PURPLE_POUNCE_IDLE) ?
-				   _("%s has become idle (%s)") :
+				   _("Became idle") :
 				   (events & PURPLE_POUNCE_AWAY) ?
-				   _("%s has gone away. (%s)") :
+				   _("Went away") :
 				   (events & PURPLE_POUNCE_MESSAGE_RECEIVED) ?
-				   _("%s has sent you a message. (%s)") :
-				   _("Unknown pounce event. Please report this!"),
-				   alias, purple_account_get_protocol_name(account));
+				   _("Sent a message") :
+				   _("Unknown.... Please report this!")
+				   );
 
 		/*
 		 * Ok here is where I change the second argument, title, from
@@ -1488,16 +1487,9 @@
 		if ((name_shown = purple_account_get_alias(account)) == NULL)
 			name_shown = purple_account_get_username(account);
 
-		if (reason == NULL)
-		{
-			purple_notify_info(NULL, name_shown, tmp, purple_date_format_full(NULL));
-		}
-		else
-		{
-			char *tmp2 = g_strdup_printf("%s\n\n%s", reason, purple_date_format_full(NULL));
-			purple_notify_info(NULL, name_shown, tmp, tmp2);
-			g_free(tmp2);
-		}
+		pidgin_notify_pounce_add(account, pounce, alias, tmp, reason,
+				purple_date_format_full(NULL));
+
 		g_free(tmp);
 	}
 
--- a/pidgin/gtkprefs.c	Mon Mar 23 01:43:21 2009 +0000
+++ b/pidgin/gtkprefs.c	Mon Mar 23 01:43:25 2009 +0000
@@ -57,6 +57,10 @@
 #include "gtkutils.h"
 #include "pidginstock.h"
 
+#ifdef USE_VV
+#include <gst/interfaces/propertyprobe.h>
+#endif
+
 #define PROXYHOST 0
 #define PROXYPORT 1
 #define PROXYUSER 2
@@ -2401,13 +2405,97 @@
 }
 
 #ifdef USE_VV
+static GList*
+get_devices(const gchar *plugin)
+{
+	GObjectClass *klass;
+	GstPropertyProbe *probe;
+	const GParamSpec *pspec;
+	GstElement *element = gst_element_factory_make(plugin, NULL);
+	GstElementFactory *factory;
+	const gchar *longname = NULL;
+	GList *ret = NULL;
+
+	if (element == NULL)
+		return NULL;
+
+	factory = gst_element_get_factory(element);
+
+	longname = gst_element_factory_get_longname(factory);
+	klass = G_OBJECT_GET_CLASS(element);
+
+	if (!g_object_class_find_property(klass, "device") ||
+			!GST_IS_PROPERTY_PROBE(element) ||
+			!(probe = GST_PROPERTY_PROBE(element)) ||
+			!(pspec = gst_property_probe_get_property(probe,
+					"device"))) {
+		purple_debug_info("media",
+				"Found source '%s' (%s) - no device\n",
+				longname, GST_PLUGIN_FEATURE (factory)->name);
+	} else {
+		gint n;
+		gchar *name;
+		GValueArray *array;
+
+		purple_debug_info("media", "Found devices\n");
+
+		/* Set autoprobe[-fps] to FALSE to avoid delays when probing. */
+		if (g_object_class_find_property (klass, "autoprobe")) {
+			g_object_set(G_OBJECT (element),
+					"autoprobe", FALSE, NULL);
+			if (g_object_class_find_property(klass,
+					"autoprobe-fps")) {
+				g_object_set(G_OBJECT(element),
+						"autoprobe-fps", FALSE, NULL);
+			}
+		}
+
+		array = gst_property_probe_probe_and_get_values(probe, pspec);
+		if (array != NULL) {
+			for (n = 0 ; n < array->n_values ; n++) {
+				GValue *device = g_value_array_get_nth(
+						array, n);
+				
+				ret = g_list_append(ret,
+						g_value_dup_string(device));
+
+				g_object_set(G_OBJECT(element), "device",
+						g_value_get_string(device),
+						NULL);
+				g_object_get(G_OBJECT(element),
+						"device-name", &name, NULL);
+				purple_debug_info("media", "Found source '%s'"
+						" (%s) - device '%s' (%s)\n",
+						longname, GST_PLUGIN_FEATURE(
+						factory)->name,	name,
+						g_value_get_string(device));
+				g_free(name);
+			}
+			g_value_array_free(array);
+		}
+
+		/* Restore autoprobe[-fps] to TRUE. */
+		if (g_object_class_find_property(klass, "autoprobe")) {
+			g_object_set(G_OBJECT(element),
+					"autoprobe", TRUE, NULL);
+			if (g_object_class_find_property(klass,
+					"autoprobe-fps")) {
+				g_object_set(G_OBJECT(element),
+						"autoprobe-fps", TRUE, NULL);
+			}
+		}
+	}
+
+	gst_object_unref(element);
+	return ret;
+}
 
 /* get a GList of pairs name / device */
 static GList *
 get_device_items(const gchar *plugin)
 {
 	GList *ret = NULL;
-	GList *devices = purple_media_get_devices(plugin);
+	GList *devices = get_devices(plugin);
 	GstElement *element = gst_element_factory_make(plugin, NULL);
 
 	if (element == NULL)