changeset 23787:92e71f6e10d4

Patch from Marcus Lundblad ('mlundblad') to improve audio support in xmpp. It's now possible to initiate an audio session, sometimes. It's somewhat buggy. Some other issues also need to be resolved: * Properly get rid of the compile warnings * Rename the serv_ functions with proper namespacing. * Possibly rename the purple_media_ functions that don't deal with a PurpleMedia (e.g. purple_media_audio_init_src) to something different, e.g. purple_media_util_, or even purple_gst_util etc.
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Sat, 22 Mar 2008 04:45:46 +0000
parents e4a4bc86c547
children 9f36ed35615e
files configure.ac libpurple/Makefile.am libpurple/media.c libpurple/media.h libpurple/plugins/Makefile.am libpurple/plugins/ssl/Makefile.am libpurple/protocols/bonjour/Makefile.am libpurple/protocols/bonjour/bonjour.c libpurple/protocols/gg/Makefile.am libpurple/protocols/gg/gg.c libpurple/protocols/irc/Makefile.am libpurple/protocols/irc/irc.c libpurple/protocols/jabber/Makefile.am libpurple/protocols/jabber/caps.c libpurple/protocols/jabber/disco.c libpurple/protocols/jabber/google.c libpurple/protocols/jabber/iq.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jabber.h libpurple/protocols/jabber/libxmpp.c libpurple/protocols/msn/Makefile.am libpurple/protocols/msn/msn.c libpurple/protocols/msnp9/Makefile.am libpurple/protocols/msnp9/msn.c libpurple/protocols/myspace/Makefile.am libpurple/protocols/myspace/myspace.c libpurple/protocols/novell/Makefile.am libpurple/protocols/novell/novell.c libpurple/protocols/null/nullprpl.c libpurple/protocols/oscar/Makefile.am libpurple/protocols/oscar/libaim.c libpurple/protocols/oscar/libicq.c libpurple/protocols/qq/Makefile.am libpurple/protocols/qq/qq.c libpurple/protocols/sametime/sametime.c libpurple/protocols/silc/Makefile.am libpurple/protocols/silc/silc.c libpurple/protocols/silc10/silc.c libpurple/protocols/simple/Makefile.am libpurple/protocols/simple/simple.c libpurple/protocols/yahoo/Makefile.am libpurple/protocols/yahoo/yahoo.c libpurple/protocols/zephyr/Makefile.am libpurple/protocols/zephyr/zephyr.c libpurple/prpl.h libpurple/server.c libpurple/server.h pidgin/Makefile.am pidgin/gtkconv.c pidgin/gtkconvwin.h pidgin/gtkimhtml.h pidgin/gtkimhtmltoolbar.c pidgin/gtkimhtmltoolbar.h pidgin/gtkmedia.c pidgin/gtkmedia.h pidgin/gtkprefs.c pidgin/gtkprefs.h pidgin/pidginstock.c pidgin/pidginstock.h pidgin/plugins/cap/Makefile.am pidgin/plugins/gevolution/Makefile.am
diffstat 61 files changed, 1351 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Tue Mar 18 05:40:29 2008 +0000
+++ b/configure.ac	Sat Mar 22 04:45:46 2008 +0000
@@ -47,7 +47,7 @@
 m4_define([purple_major_version], [2])
 m4_define([purple_minor_version], [4])
 m4_define([purple_micro_version], [1])
-m4_define([purple_version_suffix], [devel])
+m4_define([purple_version_suffix], [vv-devel])
 m4_define([purple_version],
           [purple_major_version.purple_minor_version.purple_micro_version])
 m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix]))
@@ -696,6 +696,29 @@
 	])
 fi
 
+
+dnl #######################################################################
+dnl # Check for GStreamer-properties
+dnl #######################################################################
+AC_ARG_ENABLE(gstprops,
+	[AC_HELP_STRING([--disable-gstprops], [compile without gstreamer props])],
+	enable_gstprops="$enableval", enable_gstprops="yes")
+if test "x$enable_gstprops" != "xno";
+then
+  dnl gstreamer-libs-$GST_MAJORMINOR
+  dnl gstreamer-gconf-$GST_MAJORMINOR
+  PKG_CHECK_MODULES(GSTPROPS, [gstreamer-0.10 gstreamer-plugins-base-0.10 libxml-2.0], [
+  		GSTPROPS_LIBS="$GSTPROPS_LIBS -lgstinterfaces-0.10"	   
+  		AC_DEFINE(USE_GSTPROPS, 1, [Use GStreamer property probe for finding devices])
+  		AC_SUBST(GSTPROPS_LIBS)
+  		AC_SUBST(GSTPROPS_CFLAGS)
+  ], [
+		AC_MSG_RESULT(no)
+		enable_gstprops="no"
+  ])
+fi
+
+
 dnl #######################################################################
 dnl # Check for Meanwhile headers (for Sametime)
 dnl #######################################################################
--- a/libpurple/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -251,6 +251,8 @@
 	$(LIBNM_LIBS) \
 	$(INTLLIBS) \
 	$(FARSIGHT_LIBS) \
+	$(GSTREAMER_LIBS) \
+	$(GSTPROPS_LIBS) \
 	-lm
 
 AM_CPPFLAGS = \
@@ -264,4 +266,6 @@
 	$(DBUS_CFLAGS) \
 	$(LIBXML_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(GSTPROPS_CFLAGS) \
 	$(LIBNM_CFLAGS)
--- a/libpurple/media.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/media.c	Sat Mar 22 04:45:46 2008 +0000
@@ -30,8 +30,12 @@
 #include "connection.h"
 #include "media.h"
 
+#include "debug.h"
+
 #ifdef USE_FARSIGHT
+#ifdef USE_GSTPROPS
 
+#include <gst/interfaces/propertyprobe.h>
 #include <farsight/farsight.h>
 
 struct _PurpleMediaPrivate
@@ -67,6 +71,7 @@
 	HANGUP,
 	REJECT,
 	GOT_HANGUP,
+	GOT_ACCEPT,
 	LAST_SIGNAL
 };
 static guint purple_media_signals[LAST_SIGNAL] = {0};
@@ -200,6 +205,10 @@
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
+	purple_media_signals[GOT_ACCEPT] = g_signal_new("got-accept", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 g_cclosure_marshal_VOID__VOID,
+					 G_TYPE_NONE, 0);
 
 	g_type_class_add_private(klass, sizeof(PurpleMediaPrivate));
 }
@@ -209,7 +218,7 @@
 purple_media_init (PurpleMedia *media)
 {
 	media->priv = PURPLE_MEDIA_GET_PRIVATE(media);
-	memset(media->priv, 0, sizeof(media->priv));	
+	memset(media->priv, 0, sizeof(media->priv));
 }
 
 static void
@@ -223,7 +232,7 @@
 {
 	PurpleMedia *media;
 	g_return_if_fail(PURPLE_IS_MEDIA(object));
-	
+
 	media = PURPLE_MEDIA(object);
 
 	switch (prop_id) {
@@ -455,4 +464,161 @@
 	g_signal_emit(media, purple_media_signals[GOT_HANGUP], 0);
 }
 
+void
+purple_media_got_accept(PurpleMedia *media)
+{
+    g_signal_emit(media, purple_media_signals[GOT_ACCEPT], 0);
+}
+
+gchar*
+purple_media_get_device_name(GstElement *element, GValue *device)
+{
+	gchar *name;
+
+	GstElementFactory *factory = gst_element_get_factory(element);
+	GstElement *temp = gst_element_factory_create(factory, "tmp_src");
+
+	g_object_set_property (G_OBJECT (temp), "device", device);
+	g_object_get (G_OBJECT (temp), "device-name", &name, NULL);
+	gst_object_unref(temp);
+
+	return name;
+}
+
+GList*
+purple_media_get_devices(GstElement *element)
+{
+	GObjectClass *klass;
+	GstPropertyProbe *probe;
+	const GParamSpec *pspec;
+
+	const gchar *longname = NULL;
+
+	GstElementFactory *factory =
+		gst_element_get_factory(element);
+
+	GList *ret = NULL;
+
+	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("Found source '%s' (%s) - no device",
+				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);
+				gst_element_set_state (element, GST_STATE_NULL);
+
+				ret = g_list_append(ret, device);
+			}
+		}
+	}
+
+	return ret;
+}
+
+void
+purple_media_element_set_device(GstElement *element, GValue *device)
+{
+	g_object_set_property(G_OBJECT(element), "device", device); 
+}
+
+GValue *
+purple_media_element_get_device(GstElement *element)
+{
+	GValue *device;
+	g_object_get(G_OBJECT(element), "device", &device, NULL);
+	return device;
+}
+
+GstElement *
+purple_media_get_element(const gchar *factory_name)
+{
+	GstElementFactory *factory = gst_element_factory_find(factory_name);
+	GstElement *element = gst_element_factory_create(factory, "video_src");
+	gst_object_unref(factory);
+	return element;
+}
+
+void
+purple_media_audio_init_src(GstElement **sendbin, GstElement **sendlevel)
+{
+	GstElement *src;
+	GstPad *pad;
+	GstPad *ghost;
+	const gchar *audio_device = purple_prefs_get_string("/purple/media/audio/device");
+
+	purple_debug_info("media", "purple_media_audio_init_src\n");
+
+	*sendbin = gst_bin_new("sendbin");
+	src = gst_element_factory_make("alsasrc", "asrc");
+	*sendlevel = gst_element_factory_make("level", "sendlevel");
+	gst_bin_add_many(GST_BIN(*sendbin), src, *sendlevel, NULL);
+	gst_element_link(src, *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);
+
+	/* set current audio device on "src"... */
+	if (audio_device) {
+		GList *devices = purple_media_get_devices(src);
+		GList *dev = devices;
+		purple_debug_info("media", "Setting device of GstElement src to %s\n",
+				audio_device);
+		for (; dev ; dev = dev->next) {
+			GValue *device = (GValue *) dev->data;
+			char *name = purple_media_get_device_name(src, device);
+			if (strcmp(name, audio_device) == 0) {
+				purple_media_element_set_device(src, device);
+			}
+			g_free(name);
+		}
+	}
+}
+
+void
+purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel)
+{
+	GstElement *sink;
+	GstPad *pad, *ghost;
+
+	purple_debug_info("media", "purple_media_audio_init_recv\n");
+
+	*recvbin = gst_bin_new("pidginrecvbin");
+	sink = gst_element_factory_make("alsasink", "asink");
+	g_object_set(G_OBJECT(sink), "sync", FALSE, NULL);
+	*recvlevel = gst_element_factory_make("level", "recvlevel");
+	gst_bin_add_many(GST_BIN(*recvbin), sink, *recvlevel, NULL);
+	gst_element_link(*recvlevel, sink);
+	pad = gst_element_get_pad(*recvlevel, "sink");
+	ghost = gst_ghost_pad_new("ghostsink", pad);
+	gst_element_add_pad(*recvbin, ghost);
+	g_object_set(G_OBJECT(*recvlevel), "message", TRUE, NULL);
+
+	purple_debug_info("media", "purple_media_audio_init_recv end\n");
+}
+
+#endif  /* USE_GSTPROPS */
 #endif  /* USE_FARSIGHT */
--- a/libpurple/media.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/media.h	Sat Mar 22 04:45:46 2008 +0000
@@ -27,6 +27,7 @@
 #define __MEDIA_H_
 
 #ifdef USE_FARSIGHT
+#ifdef USE_GSTPROPS
 
 #include <farsight/farsight.h>
 #include <glib.h>
@@ -89,8 +90,27 @@
 void purple_media_reject(PurpleMedia *media);
 void purple_media_hangup(PurpleMedia *media);
 void purple_media_got_hangup(PurpleMedia *media);
+void purple_media_got_accept(PurpleMedia *media);
+
+gchar *purple_media_get_device_name(GstElement *element, 
+										  GValue *device);
+
+GList *purple_media_get_devices(GstElement *element);
+void purple_media_element_set_device(GstElement *element, GValue *device);
+void purple_media_element_set_device_from_name(GstElement *element,
+											   const gchar *name);
+GValue *purple_media_element_get_device(GstElement *element);
+GstElement *purple_media_get_element(const gchar *factory_name);
+
+void purple_media_audio_init_src(GstElement **sendbin,
+                                 GstElement **sendlevel);
+void purple_media_video_init_src(GstElement **sendbin);
+
+void purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel);
+
 G_END_DECLS
 
+#endif  /* USE_GSTPROPS */
 #endif  /* USE_FARSIGHT */
 
 
--- a/libpurple/plugins/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/plugins/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -141,6 +141,8 @@
 	-I$(top_builddir)/libpurple \
 	$(DEBUG_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(PLUGIN_CFLAGS) \
 	$(DBUS_CFLAGS)
--- a/libpurple/plugins/ssl/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/plugins/ssl/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -32,6 +32,8 @@
 	$(DEBUG_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS) \
 	$(PLUGIN_CFLAGS)
 
 ssl_gnutls_la_CFLAGS = $(AM_CPPFLAGS) $(GNUTLS_CFLAGS)
--- a/libpurple/protocols/bonjour/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/bonjour/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -51,10 +51,12 @@
 	$(GLIB_CFLAGS) \
 	$(DEBUG_CFLAGS) \
 	$(LIBXML_CFLAGS) \
-	$(FARSIGHT_CFLAGS)
+	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
 
-if MDNS_AVAHI
-  AM_CPPFLAGS += $(AVAHI_CFLAGS)
-else
-endif
+#if MDNS_AVAHI
+#  AM_CPPFLAGS += $(AVAHI_CFLAGS)
+#else
+#endif
 
--- a/libpurple/protocols/bonjour/bonjour.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/bonjour/bonjour.c	Sat Mar 22 04:45:46 2008 +0000
@@ -486,7 +486,9 @@
 	NULL,                                                    /* send_attention */
 	NULL,                                                    /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),                        /* struct_size */
-	NULL                                                     /* initiate_media */
+	NULL,                                                     /* initiate_media */
+	NULL                                                     /* can_do_media */
+
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/gg/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/gg/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -76,5 +76,7 @@
 	$(INTGG_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS) \
 	$(DEBUG_CFLAGS)
 
--- a/libpurple/protocols/gg/gg.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/gg/gg.c	Sat Mar 22 04:45:46 2008 +0000
@@ -2151,7 +2151,8 @@
 	NULL,				/* send_attention */
 	NULL,				/* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL				/* initiate_media */
+	NULL,				/* initiate_media */
+	NULL                /* can_do_media */
 };
 /* }}} */
 
--- a/libpurple/protocols/irc/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/irc/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -33,4 +33,5 @@
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
 	$(DEBUG_CFLAGS)
--- a/libpurple/protocols/irc/irc.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/irc/irc.c	Sat Mar 22 04:45:46 2008 +0000
@@ -904,7 +904,8 @@
 	NULL,                   /* send_attention */
 	NULL,                   /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),    /* struct_size */
-	NULL                    /* initiate_media */
+	NULL,                    /* initiate_media */
+	NULL					 /* can_do_media */
 };
 
 static gboolean load_plugin (PurplePlugin *plugin) {
--- a/libpurple/protocols/jabber/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/jabber/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -19,6 +19,8 @@
 			  iq.h \
 			  jabber.c \
 			  jabber.h \
+			  jingle.c \
+			  jingle.h \
 			  jutil.c \
 			  jutil.h \
 			  message.c \
@@ -69,7 +71,7 @@
 pkg_LTLIBRARIES = libjabber.la libxmpp.la
 noinst_LIBRARIES =
 
-libjabber_la_SOURCES = $(JABBERSOURCES)
+libjabber_la_SOURCES = $(JABBERSOURCES)		
 libjabber_la_LIBADD = $(GLIB_LIBS) $(SASL_LIBS) $(LIBXML_LIBS)
 
 libxmpp_la_SOURCES = libxmpp.c
@@ -83,4 +85,5 @@
 	$(DEBUG_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
 	$(LIBXML_CFLAGS)
--- a/libpurple/protocols/jabber/caps.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/jabber/caps.c	Sat Mar 22 04:45:46 2008 +0000
@@ -27,6 +27,7 @@
 #include "util.h"
 #include "iq.h"
 
+
 #define JABBER_CAPS_FILENAME "xmpp-caps.xml"
 
 static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */
--- a/libpurple/protocols/jabber/disco.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/jabber/disco.c	Sat Mar 22 04:45:46 2008 +0000
@@ -79,7 +79,7 @@
 void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) {
 	const char *from = xmlnode_get_attrib(packet, "from");
 	const char *type = xmlnode_get_attrib(packet, "type");
-
+	
 	if(!from || !type)
 		return;
 
@@ -105,7 +105,7 @@
 
 		if(node)
 			xmlnode_set_attrib(query, "node", node);
-
+		
 		if(!node || !strcmp(node, CAPS0115_NODE "#" VERSION)) {
 			identity = xmlnode_new_child(query, "identity");
 			xmlnode_set_attrib(identity, "category", "client");
--- a/libpurple/protocols/jabber/google.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/jabber/google.c	Sat Mar 22 04:45:46 2008 +0000
@@ -214,8 +214,7 @@
 	FarsightCodec *codec;
 	const char *id, *encoding_name,  *clock_rate;
 	int res;
-	
-
+		
 	if (session->state != UNINIT) {
 		purple_debug_error("jabber", "Received initiate for active session.\n");
 		return FALSE;
@@ -226,6 +225,7 @@
 		purple_debug_error("jabber", "Farsight's rtp plugin not installed");
 		return FALSE;
 	}
+	
 	session->stream = farsight_session_create_stream(fs, FARSIGHT_MEDIA_TYPE_AUDIO, FARSIGHT_STREAM_DIRECTION_BOTH);
 
 	g_object_set(G_OBJECT(session->stream), "transmitter", "libjingle", NULL);
@@ -278,7 +278,7 @@
 	xmlnode *cand;
 	static int name = 0;
 	char n[4];	
-	
+		
 	for (cand = xmlnode_get_child(sess, "candidate"); cand; cand = xmlnode_get_next_twin(cand)) {
 		FarsightTransportInfo *info = g_new0(FarsightTransportInfo, 1);
 		g_snprintf(n, sizeof(n), "S%d", name++);
--- a/libpurple/protocols/jabber/iq.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/jabber/iq.c	Sat Mar 22 04:45:46 2008 +0000
@@ -313,7 +313,7 @@
 	const char *xmlns;
 	const char *type, *id, *from;
 	JabberIqHandler *jih;
-
+	
 	query = xmlnode_get_child(packet, "query");
 	type = xmlnode_get_attrib(packet, "type");
 	from = xmlnode_get_attrib(packet, "from");
@@ -353,12 +353,37 @@
 		return;
 	}
 	
-	purple_debug_info("jabber", "jabber_iq_parse\n");
-
 	if(xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) {
 		jabber_ping_parse(js, packet);
 		return;
 	}
+	
+#ifdef USE_FARSIGHT
+	/* handle session initiate XEP 0167 */
+	if (type && !strcmp(type, "set")) {
+		/* is this a Jingle package? */
+		xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+		if (jingle) {
+			const char *action = xmlnode_get_attrib(jingle, "action");
+			purple_debug_info("jabber", "got Jingle package action = %s\n",
+							  action);
+			if (!strcmp(action, "session-initiate")) {
+				jabber_handle_session_initiate(js, packet);
+			} else if (!strcmp(action, "session-accept")
+					   || !strcmp(action, "content-accept")) {
+				jabber_handle_session_accept(js, packet);
+			} else if (!strcmp(action, "session-terminate")) {
+				jabber_handle_session_terminate(js, packet);
+			} else if (!strcmp(action, "transport-info")) {
+				jabber_handle_session_candidates(js, packet);
+			} else if (!strcmp(action, "content-replace")) {
+				jabber_handle_session_content_replace(js, packet);
+			}
+
+			return;
+		}
+	}
+#endif
 
 	/* If we get here, send the default error reply mandated by XMPP-CORE */
 	if(type && (!strcmp(type, "set") || !strcmp(type, "get"))) {
--- a/libpurple/protocols/jabber/jabber.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Sat Mar 22 04:45:46 2008 +0000
@@ -56,6 +56,7 @@
 #include "xdata.h"
 #include "pep.h"
 #include "adhoccommands.h"
+#include "jingle.h"
 
 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5)
 
@@ -2343,10 +2344,561 @@
 }
 
 #ifdef USE_FARSIGHT
-PurpleMedia *jabber_media_initiate(PurpleConnection *gc, const char *who, PurpleMediaStreamType type)
+
+static void
+jabber_session_send_accept(JingleSession *session)
+{
+	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+									 JABBER_IQ_SET);
+	xmlnode *jingle = jabber_jingle_session_create_session_accept(session);
+	xmlnode_set_attrib(result->node, "to",
+					   jabber_jingle_session_get_remote_jid(session));
+
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+	purple_debug_info("jabber", "Sent session accept, starting stream\n");
+	farsight_stream_start(jabber_jingle_session_get_stream(session));
+	farsight_stream_set_remote_codecs(
+			jabber_jingle_session_get_stream(session),
+			jabber_jingle_session_get_remote_codecs(session));
+
+}
+
+static void
+jabber_session_send_content_accept(JingleSession *session)
+{
+	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+									 JABBER_IQ_SET);
+	xmlnode *jingle = jabber_jingle_session_create_content_accept(session);
+	xmlnode_set_attrib(result->node, "to",
+					   jabber_jingle_session_get_remote_jid(session));
+
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+}
+
+static void
+jabber_session_send_reject(JingleSession *session)
+{
+	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+									 JABBER_IQ_SET);
+	xmlnode *jingle = jabber_jingle_session_create_terminate(session,
+															 "decline", NULL);
+	xmlnode_set_attrib(result->node, "to", 
+					   jabber_jingle_session_get_remote_jid(session));
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+	farsight_stream_stop(jabber_jingle_session_get_stream(session));
+	jabber_jingle_session_destroy(session);
+}
+
+static void
+jabber_session_send_terminate(JingleSession *session)
+{
+	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+									 JABBER_IQ_SET);
+	xmlnode *jingle = jabber_jingle_session_create_terminate(session,
+															 "no-error", NULL);
+	xmlnode_set_attrib(result->node, "to",
+					   jabber_jingle_session_get_remote_jid(session));
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+	farsight_stream_stop(jabber_jingle_session_get_stream(session));
+	jabber_jingle_session_destroy(session);
+}
+
+/* callback called when new local transport candidate(s) are available on the
+	Farsight stream */
+static void
+jabber_session_candidates_prepared(FarsightStream *stream, gchar *candidate_id, 
+								   JingleSession *session)
+{
+	/* create transport-info package */
+	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+									 JABBER_IQ_SET);
+	xmlnode *jingle = jabber_jingle_session_create_transport_info(session,
+																  candidate_id);
+	purple_debug_info("jabber", "jabber_session_candidates_prepared called for candidate_id = %s\n",
+					  candidate_id);
+	xmlnode_set_attrib(result->node, "to",
+					   jabber_jingle_session_get_remote_jid(session));
+	
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+}
+
+/* callback called when a pair of transport candidates (local and remote)
+	has been established */
+static void
+jabber_session_candidate_pair_established(FarsightStream *stream,
+										  gchar *native_candidate_id,
+										  gchar *remote_candidate_id,
+										  JingleSession *session)
+{	
+	purple_debug_info("jabber", "jabber_candidate_pair_established called");
+	/* if we are the initiator, we should send a content-modify message */
+	if (jabber_jingle_session_is_initiator(session)) {
+		purple_debug_info("jabber", 
+						  "we are the initiator, let's send conten-modify\n");
+		JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+										 JABBER_IQ_SET);
+		/* shall change this to a "content-replace" */
+		xmlnode *jingle = 
+			jabber_jingle_session_create_content_replace(session,
+														 native_candidate_id,
+														 remote_candidate_id);
+		xmlnode_set_attrib(result->node, "to",
+						   jabber_jingle_session_get_remote_jid(session));
+		xmlnode_insert_child(result->node, jingle);
+		jabber_iq_send(result);
+	}
+	/*
+	farsight_stream_set_active_candidate_pair(stream, native_candidate_id,
+											  remote_candidate_id);
+	*/
+}
+
+
+PurpleMedia *jabber_initiate_media(PurpleConnection *gc, const char *who, 
+								   PurpleMediaStreamType type)
+{
+	/* create content negotiation */
+	JabberStream *js = gc->proto_data;
+	JabberIq *request = jabber_iq_new(js, JABBER_IQ_SET);
+	xmlnode *jingle, *content, *description, *payload_type, *transport;
+	FarsightSession *fs = NULL;
+	FarsightStream *audio = NULL; /* only audio for now... */
+	GList *codecs;
+	GList *codec_iter = NULL;
+	FarsightCodec *codec = NULL;
+	PurpleMedia *media = NULL;
+	JingleSession *session;
+	JabberBuddy *jb;
+	JabberBuddyResource *jbr;
+	
+	char id[10];
+	char clock_rate[10];
+	char channels[10];
+	char jid[256];
+	char me[256];
+	
+	/* for debug */
+	char *output;
+	int len;
+		
+	/* setup stream */
+	fs = farsight_session_factory_make("rtp");
+	if (fs == NULL) {
+		purple_debug_error("jabber", "Farsight's rtp plugin not installed");
+		return NULL;
+	}
+	
+	/* check media stream type, and so on... */
+	/* only do audio for now... */
+	
+	/* get stuff from Farsight... */
+	audio = farsight_session_create_stream(fs, FARSIGHT_MEDIA_TYPE_AUDIO, 
+										   FARSIGHT_STREAM_DIRECTION_BOTH);
+	g_object_set(G_OBJECT(audio), "transmitter", "libjingle", NULL);
+	
+	purple_debug_info("jabber", "Getting local codecs\n");
+	codecs = farsight_stream_get_local_codecs(audio);
+	purple_debug_info("jabber", "number of codecs: %d\n", g_list_length(codecs));
+	
+ 	if (audio == NULL) {
+		purple_debug_error("jabber", "Unable to create Farsight stream for audio");
+		/* destroy FarsightSession? */
+		return NULL;
+	}
+	
+	media = purple_media_manager_create_media(purple_media_manager_get(),
+											  gc, who, audio, NULL);
+	purple_debug_info("jabber", "After purple_media_manager_create_media\n");
+	/* construct JID to send to */
+	jb = jabber_buddy_find(js, who, FALSE);
+	if (!jb) {
+		purple_debug_error("jabber", "Could not find Jabber buddy\n");
+		return NULL;
+	}
+	jbr = jabber_buddy_find_resource(jb, NULL);
+	if (!jbr) {
+		purple_debug_error("jabber", "Could not find buddy's resource\n");
+	}
+	
+	g_snprintf(jid, 255, "%s/%s", who, jbr->name);
+	
+	session = jabber_jingle_session_create(js);
+	jabber_jingle_session_set_remote_jid(session, jid);
+	/* set ourselves as initiator */
+	g_snprintf(me, sizeof(me), "%s@%s/%s", js->user->node, js->user->domain,
+			   js->user->resource);
+	jabber_jingle_session_set_initiator(session, me);
+	
+	jabber_jingle_session_set_stream(session, audio);
+	jabber_jingle_session_set_media(session, media);
+	
+	g_signal_connect_swapped(G_OBJECT(media), "accepted", 
+							 G_CALLBACK(jabber_session_send_accept), session);
+	g_signal_connect_swapped(G_OBJECT(media), "reject", 
+							 G_CALLBACK(jabber_session_send_reject), session);
+	g_signal_connect_swapped(G_OBJECT(media), "hangup", 
+							 G_CALLBACK(jabber_session_send_terminate), session);
+	
+	GstElement *e = purple_media_get_audio_src(media);
+	farsight_stream_set_source(jabber_jingle_session_get_stream(session), e);	
+	e = purple_media_get_audio_sink(media);
+	farsight_stream_set_sink(jabber_jingle_session_get_stream(session), e);
+	
+	farsight_stream_prepare_transports(audio);
+	/* callback for new native (local) transport candidates for the stream */
+	g_signal_connect(G_OBJECT(audio), 
+					 "new-native-candidate", 
+					 G_CALLBACK(jabber_session_candidates_prepared), session);
+	/* callback for new active candidate pair (established connection) */
+	g_signal_connect(G_OBJECT(audio),
+					 "new-active-candidate-pair",
+					 G_CALLBACK(jabber_session_candidate_pair_established),
+					 session);
+	
+	/* create request */
+	
+	xmlnode_set_attrib(request->node, "to", 
+					   jabber_jingle_session_get_remote_jid(session));
+	jingle = xmlnode_new_child(request->node, "jingle");
+	xmlnode_set_namespace(jingle, "urn:xmpp:tmp:jingle");
+	xmlnode_set_attrib(jingle, "action", "session-initiate");
+	/* get our JID and a session id... */
+	xmlnode_set_attrib(jingle, "initiator", me);
+	xmlnode_set_attrib(jingle, "sid", jabber_jingle_session_get_id(session));
+	
+	content = xmlnode_new_child(jingle, "content");
+	xmlnode_set_attrib(content, "name", "audio-content");
+	xmlnode_set_attrib(content, "profile", "RTP/AVP");
+	
+	description = xmlnode_new_child(content, "description");
+	xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp");
+	
+	/* create payload-type nodes */
+	purple_debug_info("jabber", "Generating payload_type elements\n");
+	for (; codecs ; codecs = codecs->next) {
+		codec = (FarsightCodec *) codecs->data;
+		purple_debug_info("jabber", "Generating payload_type for (%d) %s\n",
+						  codec->id, codec->encoding_name);
+		sprintf(id, "%d", codec->id);
+		sprintf(clock_rate, "%d", codec->clock_rate);
+		sprintf(channels, "%d", codec->channels);
+		
+		payload_type = xmlnode_new_child(description, "payload-type");
+		xmlnode_set_attrib(payload_type, "id", id);
+		xmlnode_set_attrib(payload_type, "name", codec->encoding_name);
+		xmlnode_set_attrib(payload_type, "clockrate", clock_rate);
+		xmlnode_set_attrib(payload_type, "channels", channels);
+	}
+		
+	transport = xmlnode_new_child(content, "transport");
+	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp");
+	
+	/* send request to other part */	
+	jabber_iq_send(request);
+
+	return media;
+}
+
+gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, 
+                             PurpleMediaStreamType type)
+{
+	return TRUE;
+}
+
+
+void
+jabber_handle_session_accept(JabberStream *js, xmlnode *packet)
 {
-	return NULL;
+	JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	xmlnode *content = xmlnode_get_child(jingle, "content");
+	const char *sid = xmlnode_get_attrib(jingle, "sid");
+	const char *action = xmlnode_get_attrib(jingle, "action");
+	JingleSession *session = jabber_jingle_session_find_by_id(sid);
+	FarsightStream *stream = jabber_jingle_session_get_stream(session);
+	GList *remote_codecs = NULL;
+	GList *remote_transports = NULL;
+	GList *codec_intersection = NULL;
+	FarsightCodec *top = NULL;
+	xmlnode *description = NULL;
+	xmlnode *transport = NULL;
+
+	/* We should probably check validity of the incoming XML... */
+
+	xmlnode_set_attrib(result->node, "to",
+			jabber_jingle_session_get_remote_jid(session));
+	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+
+	description = xmlnode_get_child(jingle, "description");
+	transport = xmlnode_get_child(content, "transport");
+
+	/* fetch codecs from remote party */
+	purple_debug_info("jabber", "get codecs from session-accept\n");
+	remote_codecs = jabber_jingle_get_codecs(description);
+	purple_debug_info("jabber", "get transport candidates from session accept\n");
+	remote_transports = jabber_jingle_get_candidates(transport);
+
+	purple_debug_info("jabber", "Got %d codecs from responder\n",
+			g_list_length(remote_codecs));
+	purple_debug_info("jabber", "Got %d transport candidates from responder\n",
+			g_list_length(remote_transports));
+
+	purple_debug_info("jabber", "Setting remote codecs on stream\n");
+
+	farsight_stream_set_remote_codecs(stream, remote_codecs);
+	if (!strcmp(action, "session-accept")) {
+		jabber_jingle_session_set_remote_codecs(session, remote_codecs);
+	}
+
+	codec_intersection = farsight_stream_get_codec_intersection(stream);
+	purple_debug_info("jabber", "codec_intersection contains %d elems\n",
+			g_list_length(codec_intersection));
+	/* get the top codec */
+	if (g_list_length(codec_intersection) > 0) {
+		top = (FarsightCodec *) codec_intersection->data;
+		purple_debug_info("jabber", "setting active codec on stream = %d\n",
+				top->id);
+		farsight_stream_set_active_codec(stream, top->id);
+		/* we have found a suitable codec, but we will not start the stream
+		   just yet, wait for transport negotiation to complete... */
+	}
+	/* if we also got transport candidates, add them to our streams
+	   list of known remote candidates */
+	if (g_list_length(remote_transports) > 0) {
+		farsight_stream_set_remote_candidate_list(stream, remote_transports);
+	}
+	if (g_list_length(codec_intersection) == 0 &&
+			g_list_length(remote_transports)) {
+		/* we didn't get any candidates and the codec intersection is empty,
+		   this means this was not a content-accept message and we couldn't
+		   find any suitable codecs, should return error and hang up */
+
+	}
+
+	g_list_free(codec_intersection);
+
+	if (!strcmp(action, "session-accept")) {
+		purple_media_got_accept(jabber_jingle_session_get_media(session));
+		purple_debug_info("jabber", "Got session-accept, starting stream\n");
+		farsight_stream_start(jabber_jingle_session_get_stream(session));
+	}
+
+	jabber_iq_send(result);
+}
+
+void
+jabber_handle_session_terminate(JabberStream *js, xmlnode *packet)
+{
+	JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET);
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	const char *sid = xmlnode_get_attrib(jingle, "sid");
+	JingleSession *session = jabber_jingle_session_find_by_id(sid);
+
+	xmlnode_set_attrib(result->node, "to",
+			jabber_jingle_session_get_remote_jid(session));
+	xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+
+
+
+	/* maybe we should look at the reasoncode to determine if it was
+	   a hangup or a reject, and call different callbacks to purple_media */
+
+
+	purple_media_got_hangup(jabber_jingle_session_get_media(session));
+	jabber_iq_send(result);
+	farsight_stream_stop(jabber_jingle_session_get_stream(session));
+	jabber_jingle_session_destroy(session);
+}
+
+void
+jabber_handle_session_candidates(JabberStream *js, xmlnode *packet)
+{
+	JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	xmlnode *content = xmlnode_get_child(jingle, "content");
+	xmlnode *transport = xmlnode_get_child(content, "transport");
+	GList *remote_candidates = jabber_jingle_get_candidates(transport);
+	const char *sid = xmlnode_get_attrib(jingle, "sid");
+	JingleSession *session = jabber_jingle_session_find_by_id(sid);
+
+	/* send acknowledement */
+	xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
+	jabber_iq_send(result);
+
+	/* add candidates to our list of remote candidates */
+	if (g_list_length(remote_candidates) > 0) {
+		farsight_stream_add_remote_candidate(
+				jabber_jingle_session_get_stream(session),
+				remote_candidates);
+		jabber_jingle_session_add_remote_candidate(session, remote_candidates);
+	}
 }
+
+/* change this to content-replace */
+void
+jabber_handle_session_content_replace(JabberStream *js, xmlnode *packet)
+{
+	JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+	JabberIq *accept = jabber_iq_new(js, JABBER_IQ_SET);
+	xmlnode *content_accept = NULL;
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	const char *sid = xmlnode_get_attrib(jingle, "sid");
+	JingleSession *session = jabber_jingle_session_find_by_id(sid);
+
+	/* send acknowledement */
+	xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
+	jabber_iq_send(result);
+
+	/* send content-accept */
+	content_accept = jabber_jingle_session_create_content_accept(session);
+	xmlnode_set_attrib(accept->node, "id", xmlnode_get_attrib(packet, "id"));
+	xmlnode_set_attrib(accept->node, "to", xmlnode_get_attrib(packet, "from"));
+	xmlnode_insert_child(accept->node, content_accept);
+
+	jabber_iq_send(accept);
+}
+
+void 
+jabber_handle_session_initiate(JabberStream *js, xmlnode *packet)
+{
+	JingleSession *session = NULL;
+	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
+	xmlnode *content = NULL;
+	xmlnode *description = NULL;
+	char *sid = NULL;
+	char *initiator = NULL;
+	GList *codecs = NULL;
+	FarsightSession *fs = NULL;
+	FarsightStream *audio = NULL;
+	PurpleMedia *media = NULL;
+	JabberIq *result = NULL;
+	JabberIq *content_accept = NULL;
+	xmlnode *content_accept_jingle = NULL;
+	GList *codec_intersection = NULL;
+
+	int res;
+
+	if (!jingle) {
+		purple_debug_error("jabber", "Malformed request");
+		return;
+	}
+
+	sid = xmlnode_get_attrib(jingle, "sid");
+	//initiator = xmlnode_get_attrib(jingle, "initiator");
+	initiator = xmlnode_get_attrib(packet, "from");
+	session = jabber_jingle_session_create_by_id(js, sid);
+	jabber_jingle_session_set_remote_jid(session,
+			xmlnode_get_attrib(packet, "from"));
+	/* set "from" as iniator (we are responder) */
+	jabber_jingle_session_set_initiator(session,
+			xmlnode_get_attrib(packet, "from"));
+
+	/* init media */
+	content = xmlnode_get_child(jingle, "content");
+	if (!content) {
+		purple_debug_error("jabber", "jingle tag must contain content tag\n");
+		/* should send error here */
+		return;
+	}
+
+	description = xmlnode_get_child(content, "description");
+
+	if (!description) {
+		purple_debug_error("jabber", "content tag must contain description tag");
+		/* we should create an error iq here */
+		return;
+	}
+
+	codecs = jabber_jingle_get_codecs(description);
+
+	fs = farsight_session_factory_make("rtp");
+	if (!fs) {
+		purple_debug_error("jabber", 
+				"Could not initialize Farsight's RTP plugin");
+		return;
+	}
+
+	audio = farsight_session_create_stream(fs, 
+			FARSIGHT_MEDIA_TYPE_AUDIO, 
+			FARSIGHT_STREAM_DIRECTION_BOTH);
+
+	g_object_set(G_OBJECT(audio), "transmitter", "libjingle", NULL);
+
+	media = purple_media_manager_create_media(purple_media_manager_get(), 
+			js->gc, initiator, audio, NULL);
+	jabber_jingle_session_set_media(session, media);
+	jabber_jingle_session_set_stream(session, audio);
+
+
+	g_signal_connect_swapped(G_OBJECT(media), "accepted", 
+			G_CALLBACK(jabber_session_send_accept), session);
+	g_signal_connect_swapped(G_OBJECT(media), "reject", 
+			G_CALLBACK(jabber_session_send_reject), session);
+	g_signal_connect_swapped(G_OBJECT(media), "hangup", 
+			G_CALLBACK(jabber_session_send_terminate), session);
+
+
+	GstElement *e = purple_media_get_audio_src(media);
+	farsight_stream_set_source(jabber_jingle_session_get_stream(session), e);	
+
+	e = purple_media_get_audio_sink(media);
+	farsight_stream_set_sink(jabber_jingle_session_get_stream(session), e);
+
+	farsight_stream_prepare_transports(jabber_jingle_session_get_stream(session));
+	/* For some reason Farsight starts the stream immediatly when calling
+	   farsight_stream_set_remote_codecs, before having called farsight_stream_start
+	   As a "workaround" (maybe this gets fixed in FS2) I'll store the list in
+	   the session to call it later when accepting the call */
+	/*
+	   res = 
+	   farsight_stream_set_remote_codecs(jabber_jingle_session_get_stream(session), 
+	   codecs);
+	   */
+	jabber_jingle_session_set_remote_codecs(session, codecs);
+
+	purple_media_ready(media);
+
+	/* We store the remote candidates in the session object... */
+	/*
+	   farsight_codec_list_destroy(codecs);
+	   */
+
+	/* callback for new native (local) transport candidates for the stream */
+	g_signal_connect(G_OBJECT(jabber_jingle_session_get_stream(session)),
+			"new-native-candidate",
+			G_CALLBACK(jabber_session_candidates_prepared), session);
+	/* callback for new active candidate pair (established connection) */
+	g_signal_connect(G_OBJECT(jabber_jingle_session_get_stream(session)),
+			"new-active-candidate-pair",
+			G_CALLBACK(jabber_session_candidate_pair_established),
+			session);
+
+	result = jabber_iq_new(js, JABBER_IQ_RESULT);
+	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
+	jabber_iq_send(result);
+
+	/* should send a content-accept */
+	/* It crashes after this gets sent, also the id of this iq is set to
+	   "purple0", that seems odd... maybe I'm making some mistake here... */
+	/*
+	   content_accept = jabber_iq_new(jabber_jingle_session_get_stream(session),
+	   JABBER_IQ_SET);
+	   content_accept_jingle = jabber_jingle_session_create_content_accept(session);
+	   xmlnode_set_attrib(content_accept->node, "to", 
+	   jabber_jingle_session_get_remote_jid(session));
+	   xmlnode_insert_child(content_accept->node, content_accept_jingle);
+	   jabber_iq_send(content_accept);
+	   */
+}
+
 #endif
 
 void jabber_register_commands(void)
@@ -2436,5 +2988,5 @@
 void
 jabber_init_plugin(PurplePlugin *plugin)
 {
-        my_protocol = plugin;
+	my_protocol = plugin;
 }
--- a/libpurple/protocols/jabber/jabber.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Sat Mar 22 04:45:46 2008 +0000
@@ -56,6 +56,7 @@
 #include "roomlist.h"
 #include "sslconn.h"
 #include "media.h"
+#include "mediamanager.h"
 
 #include "jutil.h"
 #include "xmlnode.h"
@@ -265,7 +266,16 @@
 void jabber_init_plugin(PurplePlugin *plugin);
 
 #ifdef USE_FARSIGHT
-PurpleMedia *jabber_media_initiate(PurpleConnection *gc, const char *who, PurpleMediaStreamType type);
+PurpleMedia *jabber_initiate_media(PurpleConnection *gc, const char *who, PurpleMediaStreamType type);
+gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, PurpleMediaStreamType type);
+
+/* Jingle handle session messages */
+void jabber_handle_session_initiate(JabberStream *js, xmlnode *packet);
+void jabber_handle_session_accept(JabberStream *js, xmlnode *packet);
+void jabber_handle_session_terminate(JabberStream *js, xmlnode *packet);
+void jabber_handle_session_candidates(JabberStream *js, xmlnode *packet);
+void jabber_handle_session_content_replace(JabberStream *js, xmlnode *packet);
+
 #endif
 
 #endif /* _PURPLE_JABBER_H_ */
--- a/libpurple/protocols/jabber/libxmpp.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Sat Mar 22 04:45:46 2008 +0000
@@ -116,7 +116,8 @@
 	jabber_send_attention,			/* send_attention */
 	jabber_attention_types,			/* attention_types */
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
-	jabber_media_initiate                   /* initiate_media */
+	jabber_initiate_media,          /* initiate_media */
+	jabber_can_do_media             /* can_do_media */
 };
 
 static gboolean load_plugin(PurplePlugin *plugin)
--- a/libpurple/protocols/msn/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/msn/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -97,3 +97,5 @@
 	$(GLIB_CFLAGS) \
 	$(DEBUG_CFLAGS) \
 	$(FARSIGHT_CFLAGS)
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
--- a/libpurple/protocols/msn/msn.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Sat Mar 22 04:45:46 2008 +0000
@@ -2293,7 +2293,8 @@
 	msn_send_attention,                     /* send_attention */
 	msn_attention_types,                    /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
-	NULL                                    /* initiate_media */
+	NULL,                                    /* initiate_media */
+	NULL									 /* can_do_media */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/msnp9/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/msnp9/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -88,5 +88,7 @@
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(DEBUG_CFLAGS) \
-	$(FARSIGHT_CFLAGS)
+	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
 
--- a/libpurple/protocols/msnp9/msn.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/msnp9/msn.c	Sat Mar 22 04:45:46 2008 +0000
@@ -2148,7 +2148,8 @@
 	msn_send_attention,                     /* send_attention */
 	msn_attention_types,                    /* attention_types */
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
-	NULL                                    /* initiate_media */
+	NULL,                                    /* initiate_media */
+	NULL                                    /* can_do_media */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/myspace/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/myspace/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -41,4 +41,6 @@
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
-	$(DEBUG_CFLAGS)
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
--- a/libpurple/protocols/myspace/myspace.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Sat Mar 22 04:45:46 2008 +0000
@@ -3125,7 +3125,8 @@
 	msim_send_attention,   /* send_attention */
 	msim_attention_types,  /* attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL                   /* initiate_media */
+	NULL,                   /* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 
--- a/libpurple/protocols/novell/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/novell/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -55,4 +55,7 @@
 	-I$(top_builddir)/libpurple \
 	$(DEBUG_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
-	$(GLIB_CFLAGS)
+	$(GLIB_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
--- a/libpurple/protocols/novell/novell.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/novell/novell.c	Sat Mar 22 04:45:46 2008 +0000
@@ -3516,7 +3516,8 @@
 	NULL,						/* send_attention */
 	NULL,						/* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL						/* initiate_media */
+	NULL,						/* initiate_media */
+	NULL                        /* can_do_media */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/protocols/null/nullprpl.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/null/nullprpl.c	Sat Mar 22 04:45:46 2008 +0000
@@ -1126,7 +1126,8 @@
   NULL,                                /* send_attention */
   NULL,                                /* get_attention_types */
   sizeof(PurplePluginProtocolInfo),    /* struct_size */
-  NULL                                 /* initiate_media */
+  NULL,                                 /* initiate_media */
+  NULL                                  /* can_do_media */	
 };
 
 static void nullprpl_init(PurplePlugin *plugin)
--- a/libpurple/protocols/oscar/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/oscar/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -77,4 +77,7 @@
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
-	$(DEBUG_CFLAGS)
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
--- a/libpurple/protocols/oscar/libaim.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/oscar/libaim.c	Sat Mar 22 04:45:46 2008 +0000
@@ -96,7 +96,8 @@
 	NULL,                   /* send_attention */
 	NULL,                   /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),    /* struct_size */
-	NULL                    /* initiate_media */
+	NULL,                    /* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/oscar/libicq.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/oscar/libicq.c	Sat Mar 22 04:45:46 2008 +0000
@@ -96,7 +96,8 @@
 	NULL,                   /* send_attention */
 	NULL,                   /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),    /* struct_size */
-	NULL                    /* initiate_media */
+	NULL,                    /* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/qq/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/qq/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -95,4 +95,7 @@
 	-DQQ_BUDDY_ICON_DIR=\"$(datadir)/pixmaps/purple/buddy_icons/qq\" \
 	$(DEBUG_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
-	$(GLIB_CFLAGS)
+	$(GLIB_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
--- a/libpurple/protocols/qq/qq.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/qq/qq.c	Sat Mar 22 04:45:46 2008 +0000
@@ -705,7 +705,8 @@
 	NULL,							/* send_attention */
 	NULL,							/* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL							/* initiate_media */
+	NULL,							/* initiate_media */
+	NULL                            /* can_do_media */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/protocols/sametime/sametime.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/sametime/sametime.c	Sat Mar 22 04:45:46 2008 +0000
@@ -5188,7 +5188,7 @@
   .offline_message           = NULL,
   .whiteboard_prpl_ops       = NULL,
   .send_raw                  = NULL,
-  .struct_size               = sizeof(PurplePluginProtocolInfo)
+  .struct_size               = sizeof(PurplePluginProtocolInfo)		
 };
 
 
--- a/libpurple/protocols/silc/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/silc/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -34,4 +34,7 @@
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
-	$(DEBUG_CFLAGS)
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
--- a/libpurple/protocols/silc/silc.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/silc/silc.c	Sat Mar 22 04:45:46 2008 +0000
@@ -1997,7 +1997,8 @@
 	NULL,				        /* send_attention */
 	NULL,				        /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
-	NULL				        /* initiate_media */
+	NULL,				        /* initiate_media */
+	NULL                        /* can_do_media */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/silc10/silc.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/silc10/silc.c	Sat Mar 22 04:45:46 2008 +0000
@@ -1803,7 +1803,8 @@
 	NULL,                   /* send_attention */
 	NULL,                   /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL                    /* initiate_media */
+	NULL,                    /* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/simple/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/simple/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -34,4 +34,7 @@
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
-	$(DEBUG_CFLAGS)
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
--- a/libpurple/protocols/simple/simple.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/simple/simple.c	Sat Mar 22 04:45:46 2008 +0000
@@ -2051,7 +2051,8 @@
 	NULL,					/* send_attention */
 	NULL,					/* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL					/* initiate_media */
+	NULL,					/* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 
--- a/libpurple/protocols/yahoo/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/yahoo/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -54,4 +54,7 @@
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
-	$(DEBUG_CFLAGS)
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
--- a/libpurple/protocols/yahoo/yahoo.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/yahoo/yahoo.c	Sat Mar 22 04:45:46 2008 +0000
@@ -4378,7 +4378,8 @@
 	yahoo_send_attention,
 	yahoo_attention_types,
 	sizeof(PurplePluginProtocolInfo),
-	NULL
+	NULL,					/* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 static PurplePluginInfo info =
--- a/libpurple/protocols/zephyr/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/zephyr/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -108,4 +108,7 @@
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
 	$(KRB4_CFLAGS) \
-	$(DEBUG_CFLAGS)
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
--- a/libpurple/protocols/zephyr/zephyr.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/protocols/zephyr/zephyr.c	Sat Mar 22 04:45:46 2008 +0000
@@ -2911,7 +2911,9 @@
 	NULL,
 	NULL,
 	NULL,
-        sizeof(PurplePluginProtocolInfo)
+        sizeof(PurplePluginProtocolInfo),
+	NULL,					/* initate_media */
+	NULL					/* can_do_media */
 };
 
 static PurplePluginInfo info = {
--- a/libpurple/prpl.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/prpl.h	Sat Mar 22 04:45:46 2008 +0000
@@ -403,9 +403,21 @@
 	int struct_size;
 
 #ifdef USE_FARSIGHT
+	/* Initiate media with the given buddy */
 	PurpleMedia  *(*initiate_media)(PurpleConnection *conn, const char *who, PurpleMediaStreamType type);
+	
+	gboolean (*can_do_media)(PurpleConnection *conn, const char *who, PurpleMediaStreamType type);
+    
+    /*
+	gboolean (*can_receive_video)(PurpleConnection *conn, const char *who);
+	gboolean (*can_send_video)(PurpleConnection *conn, const char *who);
+	gboolean (*can_receive_audio)(PurpleConnection *conn, const char *who);
+	gboolean (*can_send_audio)(PurpleConnection *conn, const char *who);
+	*/
+    
 #else
 	void (*initiate_media)(void);
+	void (*can_do_media)(void);
 #endif
 };
 
--- a/libpurple/server.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/server.c	Sat Mar 22 04:45:46 2008 +0000
@@ -37,6 +37,9 @@
 #include "server.h"
 #include "status.h"
 #include "util.h"
+#ifdef USE_FARSIGHT
+#include "media.h"
+#endif
 
 #define SECS_BEFORE_RESENDING_AUTORESPONSE 600
 #define SEX_BEFORE_RESENDING_AUTORESPONSE "Only after you're married"
@@ -1040,3 +1043,45 @@
 		}
 	}
 }
+
+#ifdef USE_FARSIGHT
+PurpleMedia *serv_initiate_media(PurpleConnection *gc, const char *who,
+								 PurpleMediaStreamType type)
+{
+	PurplePlugin *prpl = NULL;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+	
+	if (gc)
+		prpl = purple_connection_get_prpl(gc);
+	if (prpl)
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	
+	if (prpl_info && prpl_info->initiate_media) {
+		/* should check that the protol supports this media type here.... */
+		return prpl_info->initiate_media(gc, who, type);
+	} else {
+		return NULL;
+	}
+}
+
+gboolean
+serv_can_do_media(PurpleConnection *gc, const char *who, 
+                  PurpleMediaStreamType type)
+{
+    PurplePlugin *prpl = NULL;
+	PurplePluginProtocolInfo *prpl_info = NULL;
+	
+	if (gc)
+		prpl = purple_connection_get_prpl(gc);
+	if (prpl)
+		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+	
+	if (prpl_info && prpl_info->can_do_media) {
+		/* should check that the protol supports this media type here.... */
+		return prpl_info->can_do_media(gc, who, type);
+	} else {
+		return FALSE;
+	}
+}
+    
+#endif
--- a/libpurple/server.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/libpurple/server.h	Sat Mar 22 04:45:46 2008 +0000
@@ -29,6 +29,7 @@
 #include "account.h"
 #include "conversation.h"
 #include "prpl.h"
+#include "media.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -171,6 +172,17 @@
 					  PurpleMessageFlags flags, const char *message, time_t mtime);
 void serv_send_file(PurpleConnection *gc, const char *who, const char *file);
 
+#ifdef USE_FARSIGHT
+PurpleMedia *serv_initiate_media(PurpleConnection *gc, const char *who,
+						  PurpleMediaStreamType type);
+gboolean serv_can_do_media(PurpleConnection *gc, const char *who, 
+                           PurpleMediaStreamType type);
+#else
+/* hmm, is this really nice? */
+void *serv_initiate_media(void*, void*, void*);
+void *serv_can_do_media(void *, void *, void *);
+#endif
+	
 #ifdef __cplusplus
 }
 #endif
--- a/pidgin/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -197,6 +197,7 @@
 	$(LIBXML_LIBS) \
 	$(GTK_LIBS) \
 	$(FARSIGHT_LIBS) \
+	$(GSTPROPS_LIBS) \
 	$(top_builddir)/libpurple/libpurple.la
 
 if USE_INTERNAL_LIBGADU
@@ -222,5 +223,6 @@
 	$(STARTUP_NOTIFICATION_CFLAGS) \
 	$(LIBXML_CFLAGS) \
 	$(INTGG_CFLAGS) \
-	$(FARSIGHT_CFLAGS) 
+	$(FARSIGHT_CFLAGS) \
+	$(GSTPROPS_CFLAGS)
 endif  # ENABLE_GTK
--- a/pidgin/gtkconv.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/gtkconv.c	Sat Mar 22 04:45:46 2008 +0000
@@ -1172,6 +1172,14 @@
 	gtk_widget_grab_focus(s->entry);
 }
 
+#ifdef USE_FARSIGHT
+/* Forward declare this here, because I want to keep VV-related stuff together
+for now */
+static void 
+menu_initiate_voice_call_cb(gpointer data, guint action, GtkWidget *widget);
+
+#endif
+
 static void
 menu_send_file_cb(gpointer data, guint action, GtkWidget *widget)
 {
@@ -2947,6 +2955,11 @@
 
 	{ "/Conversation/sep1", NULL, NULL, 0, "<Separator>", NULL },
 
+#ifdef USE_FARSIGHT
+	{ N_("/Conversation/_Voice Call..."), NULL, menu_initiate_voice_call_cb, 0,
+		"<StockItem>", PIDGIN_STOCK_TOOLBAR_CALL},
+#endif
+
 	{ N_("/Conversation/Se_nd File..."), NULL, menu_send_file_cb, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SEND_FILE },
 	{ N_("/Conversation/Add Buddy _Pounce..."), NULL, menu_add_pounce_cb,
 			0, "<Item>", NULL },
@@ -3248,6 +3261,12 @@
 		gtk_item_factory_get_widget(win->menu.item_factory,
 		                            N_("/Conversation/View Log"));
 
+#ifdef USE_FARSIGHT
+	win->menu.call =
+		gtk_item_factory_get_widget(win->menu.item_factory,
+									N_("/Conversation/Voice Call..."));
+#endif
+	
 	/* --- */
 
 	win->menu.send_file =
@@ -6319,6 +6338,28 @@
 		if(conv->features & PURPLE_CONNECTION_NO_IMAGES)
 			buttons &= ~GTK_IMHTML_IMAGE;
 
+#ifdef USE_FARSIGHT
+		/* check if account support voice calls, and if the current buddy
+			supports it */
+		if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) {
+			if (serv_can_do_media(gc, purple_conversation_get_name(conv), 
+						PURPLE_MEDIA_RECV_AUDIO & PURPLE_MEDIA_SEND_AUDIO)) {
+				buttons |= GTK_IMHTML_CALL;
+				gtk_widget_set_sensitive(win->menu.call, TRUE);
+			} else {
+				buttons &= ~GTK_IMHTML_CALL;
+				gtk_widget_set_sensitive(win->menu.call, FALSE);
+			}
+		} else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) {
+			/* for now, don't care about chats... */
+			buttons &= ~GTK_IMHTML_CALL;
+			gtk_widget_set_sensitive(win->menu.call, FALSE);
+		} else {
+			buttons &= ~GTK_IMHTML_CALL;
+			gtk_widget_set_sensitive(win->menu.call, FALSE);
+		}							
+#endif
+		
 		gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons);
 		if (account != NULL)
 			gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account));
@@ -6842,7 +6883,7 @@
 	gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE);
 #endif
 	gtk_widget_add_events(event,
-                              GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
+			GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
 	g_signal_connect(G_OBJECT(event), "button-press-event",
 					 G_CALLBACK(icon_menu), gtkconv);
 
@@ -7575,6 +7616,9 @@
 	return TRUE;
 }
 
+
+#ifdef USE_FARSIGHT
+
 static void
 pidgin_gtkmedia_message_cb(PidginMedia *media, const char *msg, PurpleConversation *conv)
 {
@@ -7582,8 +7626,43 @@
 }
 
 static void
+menu_initiate_voice_call_cb(gpointer data, guint action, GtkWidget *widget)
+{
+	PidginWindow *win = (PidginWindow *)data;
+	GtkWidget *gtkmedia = NULL;
+	PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win);
+	PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
+
+	PurpleConnection *gc = purple_conversation_get_gc(conv);
+	PurplePluginProtocolInfo *prpl = gc->prpl;
+	PurpleMediaManager *manager = purple_media_manager_get();
+
+	PurpleMedia *media =
+		serv_initiate_media(gc,
+							purple_conversation_get_name(conv),
+							PURPLE_MEDIA_RECV_AUDIO & PURPLE_MEDIA_SEND_AUDIO);
+	GstElement *sendbin, *src, *sendlevel;
+	GstElement *recvbin, *sink, *recvlevel;
+	GstPad *pad, *ghost;
+
+	purple_media_audio_init_src(&sendbin, &sendlevel);
+	purple_media_audio_init_recv(&recvbin, &recvlevel);
+
+	purple_media_set_audio_src(media, sendbin);
+	purple_media_set_audio_sink(media, recvbin);
+
+	gtkmedia = pidgin_media_new(media, PIDGIN_MEDIA_WAITING, sendlevel, recvlevel);
+
+	gtk_box_pack_start(GTK_BOX(gtkconv->topvbox), gtkmedia, FALSE, FALSE, 0);
+	gtk_widget_show(gtkmedia);
+	g_signal_connect(G_OBJECT(gtkmedia), "message",
+					 G_CALLBACK(pidgin_gtkmedia_message_cb), conv);
+	/* need to setup handler for accept, reject and if we hangup here... */
+}
+
+static void
 pidgin_conv_new_media_cb(PurpleMediaManager *manager, PurpleMedia *media, gpointer nul)
-{	
+{
 	GstElement *sendbin, *src, *sendlevel;
 	GstElement *recvbin, *sink, *recvlevel;
 	GstPad *pad, *ghost;
@@ -7592,41 +7671,24 @@
 	PurpleConversation *conv;
 	PidginConversation *gtkconv;
 
-	sendbin = gst_bin_new("sendbin");
-	src = gst_element_factory_make("alsasrc", "asrc");
-	sendlevel = gst_element_factory_make("level", "sendlevel");
-	gst_bin_add_many(GST_BIN(sendbin), src, sendlevel, NULL);
-	gst_element_link(src, sendlevel); //, gst_caps_new_simple("audio/x-raw-int", "rate", G_TYPE_INT, 8000, NULL));
-	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);
-
-	recvbin = gst_bin_new("pidginrecvbin");
-	sink = gst_element_factory_make("alsasink", "asink");
-	g_object_set(G_OBJECT(sink), "sync", FALSE, NULL);
-	recvlevel = gst_element_factory_make("level", "recvlevel");
-	gst_bin_add_many(GST_BIN(recvbin), sink, recvlevel, NULL);
-	gst_element_link(recvlevel, sink);
-	pad = gst_element_get_pad(recvlevel, "sink");
-	ghost = gst_ghost_pad_new("ghostsink", pad);
-	gst_element_add_pad(recvbin, ghost);
-	g_object_set(G_OBJECT(recvlevel), "message", TRUE, NULL);
+	purple_media_audio_init_src(&sendbin, &sendlevel);
+	purple_media_audio_init_recv(&recvbin, &recvlevel);
 
 	purple_media_set_audio_src(media, sendbin);
 	purple_media_set_audio_sink(media, recvbin);
 
-	gtkmedia = pidgin_media_new(media, sendlevel, recvlevel);
-	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, 
-				       purple_connection_get_account(purple_media_get_connection(media)), 
+	gtkmedia = pidgin_media_new(media, PIDGIN_MEDIA_REQUESTED, sendlevel, recvlevel);
+	conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
+				       purple_connection_get_account(purple_media_get_connection(media)),
 				       purple_media_get_screenname(media));
 	gtkconv = PIDGIN_CONVERSATION(conv);
 	gtk_box_pack_start(GTK_BOX(gtkconv->topvbox), gtkmedia, FALSE, FALSE, 0);
 	gtk_widget_show(gtkmedia);
-	g_signal_connect_swapped(G_OBJECT(media), "got-hangup", G_CALLBACK(gtk_widget_destroy), gtkmedia);
 	g_signal_connect(G_OBJECT(gtkmedia), "message", G_CALLBACK(pidgin_gtkmedia_message_cb), conv);
 }
 
+#endif
+
 void *
 pidgin_conversations_get_handle(void)
 {
--- a/pidgin/gtkconvwin.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/gtkconvwin.h	Sat Mar 22 04:45:46 2008 +0000
@@ -50,6 +50,7 @@
 
 		GtkWidget *view_log;
 
+		GtkWidget *call;
 		GtkWidget *send_file;
 		GtkWidget *add_pounce;
 		GtkWidget *get_info;
--- a/pidgin/gtkimhtml.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/gtkimhtml.h	Sat Mar 22 04:45:46 2008 +0000
@@ -76,6 +76,7 @@
 	GTK_IMHTML_SMILEY =     1 << 11,
 	GTK_IMHTML_LINKDESC =   1 << 12,
 	GTK_IMHTML_STRIKE =     1 << 13,
+	GTK_IMHTML_CALL =       1 << 14,
 	GTK_IMHTML_ALL =       -1
 } GtkIMHtmlButtons;
 
--- a/pidgin/gtkimhtmltoolbar.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/gtkimhtmltoolbar.c	Sat Mar 22 04:45:46 2008 +0000
@@ -39,6 +39,8 @@
 #include "gtkthemes.h"
 #include "gtkutils.h"
 
+#include "debug.h"
+
 #include <gdk/gdkkeysyms.h>
 
 static GtkHBoxClass *parent_class = NULL;
@@ -448,12 +450,12 @@
 
 static void insert_hr_cb(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
 {
-        GtkTextIter iter;
-        GtkTextMark *ins;
+	GtkTextIter iter;
+	GtkTextMark *ins;
 	GtkIMHtmlScalable *hr;
 
-        ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)));
-        gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)), &iter, ins);
+	ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)));
+	gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)), &iter, ins);
 	hr = gtk_imhtml_hr_new();
 	gtk_imhtml_hr_add_to(hr, GTK_IMHTML(toolbar->imhtml), &iter);
 }
@@ -788,6 +790,14 @@
 	gtk_widget_grab_focus(toolbar->imhtml);
 }
 
+#ifdef USE_FARSIGHT
+static void
+init_voice_call_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar)
+{
+	purple_debug_info("gtkimhtmltoolbar", "Call clicked!\n");
+}
+#endif
+
 static void update_buttons_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, GtkIMHtmlToolbar *toolbar)
 {
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bold), buttons & GTK_IMHTML_BOLD);
@@ -816,6 +826,9 @@
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->image), buttons & GTK_IMHTML_IMAGE);
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->link), buttons & GTK_IMHTML_LINK);
 	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->smiley), buttons & GTK_IMHTML_SMILEY);
+#ifdef USE_FARSIGHT
+	gtk_widget_set_sensitive(GTK_WIDGET(toolbar->call), buttons & GTK_IMHTML_CALL);
+#endif
 }
 
 /* we call this when we want to _set_active the toggle button, it'll
@@ -1104,6 +1117,10 @@
 		{PIDGIN_STOCK_TOOLBAR_INSERT_LINK, insert_link_cb, &toolbar->link, _("Insert Link")},
 		{PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, insert_image_cb, &toolbar->image, _("Insert IM Image")},
 		{PIDGIN_STOCK_TOOLBAR_SMILEY, insert_smiley_cb, &toolbar->smiley, _("Insert Smiley")},
+#ifdef USE_FARSIGHT
+		{"", NULL, NULL, NULL},
+		{PIDGIN_STOCK_TOOLBAR_CALL, init_voice_call_cb, &toolbar->call, _("Call")},
+#endif
 		{NULL, NULL, NULL, NULL}
 	};
 	int iter;
@@ -1169,6 +1186,11 @@
 	GtkWidget *insert_button;
 	GtkWidget *font_button;
 	GtkWidget *smiley_button;
+	
+#ifdef USE_FARSIGHT
+	GtkWidget *call_button;
+#endif /* USE_FARSIGHT */
+	
 	GtkWidget *font_menu;
 	GtkWidget *insert_menu;
 	GtkWidget *menuitem;
@@ -1310,6 +1332,28 @@
 	g_signal_connect_swapped(G_OBJECT(smiley_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->smiley);
 	gtk_widget_show_all(smiley_button);
 
+#ifdef USE_FARSIGHT
+	/* Sep */
+	sep = gtk_vseparator_new();
+	gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
+	gtk_widget_show_all(sep);
+
+	/* Call */
+	call_button = gtk_button_new();
+	gtk_button_set_relief(GTK_BUTTON(call_button), GTK_RELIEF_NONE);
+	bbox = gtk_hbox_new(FALSE, 3);
+	gtk_container_add(GTK_CONTAINER(call_button), bbox);
+	image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_CALL,
+			gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
+	gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
+	label = gtk_label_new_with_mnemonic(_("Call"));
+	gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(box), call_button, FALSE, FALSE, 0);
+	g_signal_connect_swapped(G_OBJECT(call_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->call);
+	gtk_widget_show_all(call_button);
+
+#endif /* USE_FARSIGHT */
+
 	gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0);
 	g_object_set_data(G_OBJECT(hbox), "lean-view", box);
 	gtk_widget_show(box);
--- a/pidgin/gtkimhtmltoolbar.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/gtkimhtmltoolbar.h	Sat Mar 22 04:45:46 2008 +0000
@@ -76,6 +76,7 @@
 	char *sml;
 	GtkWidget *strikethrough;
 	GtkWidget *insert_hr;
+	GtkWidget *call;
 };
 
 struct _GtkIMHtmlToolbarClass {
--- a/pidgin/gtkmedia.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/gtkmedia.c	Sat Mar 22 04:45:46 2008 +0000
@@ -40,12 +40,15 @@
 	GstElement *send_level;
 	GstElement *recv_level;
 
+	GtkWidget *calling;
 	GtkWidget *accept;
 	GtkWidget *reject;
 	GtkWidget *hangup;
-	
+
 	GtkWidget *send_progress;
 	GtkWidget *recv_progress;
+
+	PidginMediaState state;
 };
 
 #define PIDGIN_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_MEDIA, PidginMediaPrivate))
@@ -103,7 +106,7 @@
 	GObjectClass *gobject_class = (GObjectClass*)klass;
 	GtkContainerClass *container_class = (GtkContainerClass*)klass;
 	parent_class = g_type_class_peek_parent(klass);
-	
+
 	gobject_class->finalize = pidgin_media_finalize;
 	gobject_class->set_property = pidgin_media_set_property;
 	gobject_class->get_property = pidgin_media_get_property;
@@ -119,7 +122,7 @@
 			"Send level",
 			"The GstElement of this media's send 'level'",
 			GST_TYPE_ELEMENT,
-			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));	
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
 	g_object_class_install_property(gobject_class, PROP_RECV_LEVEL,
 			g_param_spec_object("recv-level",
 			"Receive level",
@@ -140,15 +143,17 @@
 pidgin_media_init (PidginMedia *media)
 {
 	media->priv = PIDGIN_MEDIA_GET_PRIVATE(media);
+	media->priv->calling = gtk_label_new_with_mnemonic("Calling...");
 	media->priv->hangup = gtk_button_new_with_label("Hangup");
 	media->priv->accept = gtk_button_new_with_label("Accept");
 	media->priv->reject = gtk_button_new_with_label("Reject");
-	media->priv->send_progress = gtk_progress_bar_new();	
+	media->priv->send_progress = gtk_progress_bar_new();
 	media->priv->recv_progress = gtk_progress_bar_new();
 
 	gtk_widget_set_size_request(media->priv->send_progress, 70, 5);
 	gtk_widget_set_size_request(media->priv->recv_progress, 70, 5);
-	
+
+	gtk_box_pack_start(GTK_BOX(media), media->priv->calling, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(media), media->priv->hangup, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(media), media->priv->accept, FALSE, FALSE, 0);
 	gtk_box_pack_start(GTK_BOX(media), media->priv->reject, FALSE, FALSE, 0);
@@ -158,8 +163,11 @@
 
 	gtk_widget_show(media->priv->send_progress);
 	gtk_widget_show(media->priv->recv_progress);
-	gtk_widget_show_all(media->priv->accept);
-	gtk_widget_show_all(media->priv->reject);
+
+	/*
+	   gtk_widget_show_all(media->priv->accept);
+	   gtk_widget_show_all(media->priv->reject);
+	   */
 }
 
 static void
@@ -219,13 +227,13 @@
 	printf("\n\nbus: %p\n", gst_pipeline_get_bus(GST_PIPELINE(element)));
 }
 
+/* maybe we should have different callbacks for when we received the accept
+    and we accepted ourselves */
 static void
 pidgin_media_accept_cb(PurpleMedia *media, PidginMedia *gtkmedia)
 {
 	pidgin_media_emit_message(gtkmedia, _("Call in progress."));
-	gtk_widget_show(gtkmedia->priv->hangup);
-	gtk_widget_hide(gtkmedia->priv->accept);
-	gtk_widget_hide(gtkmedia->priv->reject);
+	pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_ACCEPTED);
 }
 
 static void
@@ -279,6 +287,8 @@
 				G_CALLBACK(pidgin_media_reject_cb), media);
 			g_signal_connect(G_OBJECT(media->priv->media), "got-hangup",
 				G_CALLBACK(pidgin_media_got_hangup_cb), media);
+			g_signal_connect(G_OBJECT(media->priv->media), "got-accept",
+				G_CALLBACK(pidgin_media_accept_cb), media);
 			break;
 		case PROP_SEND_LEVEL:
 			if (media->priv->send_level)
@@ -292,7 +302,7 @@
 			media->priv->recv_level = g_value_get_object(value);
 			g_object_ref(media->priv->recv_level);
 			break;
-		default:	
+		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
 	}
@@ -303,9 +313,9 @@
 {
 	PidginMedia *media;
 	g_return_if_fail(PIDGIN_IS_MEDIA(object));
-	
+
 	media = PIDGIN_MEDIA(object);
-	
+
 	switch (prop_id) {
 		case PROP_MEDIA:
 			g_value_set_object(value, media->priv->media);
@@ -316,16 +326,55 @@
 		case PROP_RECV_LEVEL:
 			g_value_set_object(value, media->priv->recv_level);
 			break;
-		default:	
-			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);	
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
 	}
 }
 
 GtkWidget *
-pidgin_media_new(PurpleMedia *media, GstElement *sendlevel, GstElement *recvlevel)
+pidgin_media_new(PurpleMedia *media, PidginMediaState state,
+                 GstElement *sendlevel, GstElement *recvlevel)
+{
+	PidginMedia *gtkmedia = g_object_new(pidgin_media_get_type(), "media", media,
+			"send-level", sendlevel,
+			"recv-level", recvlevel, NULL);
+	pidgin_media_set_state(gtkmedia, state);
+	return GTK_WIDGET(gtkmedia);
+}
+
+void
+pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state)
 {
-	return GTK_WIDGET(g_object_new(pidgin_media_get_type(), "media", media, "send-level", sendlevel, "recv-level", recvlevel, NULL));
+	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_hide(gtkmedia->priv->hangup);
+			break;
+		case PIDGIN_MEDIA_REQUESTED:
+			gtk_widget_show(gtkmedia->priv->accept);
+			gtk_widget_show(gtkmedia->priv->reject);
+			gtk_widget_hide(gtkmedia->priv->calling);
+			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;
+		case PIDGIN_MEDIA_REJECTED:
+			gtk_widget_hide(gtkmedia->priv->calling);
+			gtk_widget_hide(gtkmedia->priv->hangup);
+			gtk_widget_hide(gtkmedia->priv->accept);
+			gtk_widget_hide(gtkmedia->priv->reject);
+			break;
+		default:
+			break;
+	}
 }
 
 #endif  /* USE_FARSIGHT */
--- a/pidgin/gtkmedia.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/gtkmedia.h	Sat Mar 22 04:45:46 2008 +0000
@@ -26,10 +26,6 @@
 #ifndef __GTKMEDIA_H_
 #define __GTKMEDIA_H_
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
 #ifdef USE_FARSIGHT
 
 #include <farsight/farsight.h>
@@ -50,6 +46,7 @@
 typedef struct _PidginMedia PidginMedia;
 typedef struct _PidginMediaClass PidginMediaClass;
 typedef struct _PidginMediaPrivate PidginMediaPrivate;
+typedef enum _PidginMediaState PidginMediaState;
 
 struct _PidginMediaClass
 {
@@ -62,9 +59,25 @@
 	PidginMediaPrivate *priv;
 };
 
+enum _PidginMediaState
+{
+	/* Waiting for response */
+	PIDGIN_MEDIA_WAITING = 1,
+	/* Got request */
+	PIDGIN_MEDIA_REQUESTED,
+	/* Accepted call */
+	PIDGIN_MEDIA_ACCEPTED,
+	/* Rejected call */
+	PIDGIN_MEDIA_REJECTED,
+};
+
 GType pidgin_media_get_type(void);
 
-GtkWidget *pidgin_media_new(PurpleMedia *media, GstElement *send_level, GstElement *recv_level);
+GtkWidget *pidgin_media_new(PurpleMedia *media, PidginMediaState state,
+                            GstElement *send_level, GstElement *recv_level);
+PidginMediaState pidgin_media_get_state(PidginMedia *gtkmedia);
+void pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state);
+
 
 G_END_DECLS
 
--- a/pidgin/gtkprefs.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/gtkprefs.c	Sat Mar 22 04:45:46 2008 +0000
@@ -145,12 +145,10 @@
 
 	if (type == PURPLE_PREF_INT) {
 		int_value = GPOINTER_TO_INT(g_object_get_data(w, "value"));
-
 		purple_prefs_set_int(key, int_value);
 	}
 	else if (type == PURPLE_PREF_STRING) {
 		str_value = (const char *)g_object_get_data(w, "value");
-
 		purple_prefs_set_string(key, str_value);
 	}
 	else if (type == PURPLE_PREF_BOOLEAN) {
@@ -848,7 +846,7 @@
 					_("Never"), "never",
 					NULL);
 	gtk_size_group_add_widget(sg, label);
-        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
 
 	vbox = pidgin_make_frame(ret, _("Conversation Window Hiding"));
 	label = pidgin_prefs_dropdown(vbox, _("_Hide new IM conversations:"),
@@ -858,7 +856,7 @@
 					_("Always"), "always",
 					NULL);
 	gtk_size_group_add_widget(sg, label);
-        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
 
 
 	/* All the tab options! */
@@ -893,7 +891,7 @@
 #endif
 					NULL);
 	gtk_size_group_add_widget(sg, label);
-        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
 
 	names = pidgin_conv_placement_get_options();
 	label = pidgin_prefs_dropdown_from_list(vbox2, _("N_ew conversations:"),
@@ -1912,6 +1910,105 @@
 	return ret;
 }
 
+#ifdef USE_FARSIGHT
+
+/* get a GList of pairs name / device */
+static GList *
+get_device_items(const GstElement *element, 
+		 const GList *devices)
+{
+	GList *ret = NULL;
+
+	for(; devices ; devices = devices->next) {
+		gchar *name = purple_media_get_device_name(element, devices->data);
+		ret = g_list_append(ret, name);
+		ret = g_list_append(ret, name);
+	}
+
+	return ret;
+}
+
+/*
+ * Test functions to run video preview
+ * (this is not really functional right now...)
+ *
+ */
+static void
+preview_button_clicked(GtkWidget *widget, gpointer *data)
+{
+	GstElement *video = (GstElement *) data;
+
+	/* create a preview window... */
+	GstElement *output = gst_element_factory_make("autovideosink", NULL);
+	GstElement *pipeline = NULL;
+	GError *p_err;
+
+	gchar test_pipeline_str[50] = "v4lsrc ! ffmpegcolorspace ! autovideosink";
+	pipeline = gst_parse_launch (test_pipeline_str, &p_err);
+
+	gst_element_set_state(pipeline, GST_STATE_PLAYING);
+}
+
+static GtkWidget *
+media_page()
+{
+	GtkWidget *ret;
+	GtkWidget *sg;
+	GtkWidget *vbox;
+	GtkWidget *hbox;
+	GtkWidget *dd;
+	GtkWidget *preview_button;
+
+	GstElement *video = purple_media_get_element("v4lsrc");
+	GstElement *audio = purple_media_get_element("alsasrc");
+
+	GList *video_devices = purple_media_get_devices(video);
+	GList *audio_devices = purple_media_get_devices(audio);
+
+	GList *video_items = get_device_items(video, video_devices);
+	GList *audio_items = get_device_items(audio, audio_devices);
+
+	g_list_free(video_devices);
+	g_list_free(audio_devices);
+
+	ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+	gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
+
+	sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+
+	vbox = pidgin_make_frame (ret, _("Video Input"));
+	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	dd = pidgin_prefs_dropdown_from_list(hbox, _("_Device:"), PURPLE_PREF_STRING,
+			"/purple/media/video/device",
+			video_items);
+
+	gtk_size_group_add_widget(sg, dd);
+	gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5);
+
+	preview_button = gtk_button_new_with_mnemonic(_("_Preview"));
+	g_signal_connect(G_OBJECT(preview_button), "clicked",
+			G_CALLBACK(preview_button_clicked), video);
+
+	gtk_container_add(hbox, preview_button);
+	gtk_container_add(vbox, hbox);
+
+	vbox = pidgin_make_frame (ret, _("Audio Input"));
+	dd = pidgin_prefs_dropdown_from_list(vbox, _("_Device:"), PURPLE_PREF_STRING,
+			"/purple/media/audio/device",
+			audio_items);
+
+	gtk_size_group_add_widget(sg, dd);
+	gtk_misc_set_alignment(GTK_MISC(dd), 0, 0.5);
+
+	hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+	gtk_widget_show_all(ret);
+
+	return ret;
+}
+
+#endif
 
 static void
 set_idle_away(PurpleSavedStatus *status)
@@ -2037,6 +2134,10 @@
 	prefs_notebook_add_page(_("Conversations"), conv_page(), notebook_page++);
 	prefs_notebook_add_page(_("Smiley Themes"), theme_page(), notebook_page++);
 	prefs_notebook_add_page(_("Sounds"), sound_page(), notebook_page++);
+
+#if USE_FARSIGHT
+	prefs_notebook_add_page(_("Media"), media_page(), notebook_page++);
+#endif	
 	prefs_notebook_add_page(_("Network"), network_page(), notebook_page++);
 #ifndef _WIN32
 	/* We use the registered default browser in windows */
@@ -2162,6 +2263,14 @@
 	purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/smileys/theme",
 								smiley_theme_pref_cb, NULL);
 
+#ifdef USE_FARSIGHT
+	purple_prefs_add_none("/purple/media");
+	purple_prefs_add_none("/purple/media/video");
+	purple_prefs_add_string("/purple/media/video/device", "");
+	purple_prefs_add_none("/purple/media/audio");
+	purple_prefs_add_string("/purple/media/audio/device", "");
+#endif /* USE_FARSIGHT */
+
 	pidgin_prefs_update_old();
 }
 
--- a/pidgin/gtkprefs.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/gtkprefs.h	Sat Mar 22 04:45:46 2008 +0000
@@ -29,6 +29,7 @@
 
 #include "prefs.h"
 
+
 /**
  * Initializes all UI-specific preferences.
  */
--- a/pidgin/pidginstock.c	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/pidginstock.c	Sat Mar 22 04:45:46 2008 +0000
@@ -169,6 +169,10 @@
 	{ PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, "toolbar", "select-avatar.png", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TOOLBAR_SEND_FILE, "toolbar", "send-file.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
 
+#ifdef USE_FARSIGHT
+	{ PIDGIN_STOCK_TOOLBAR_CALL, "toolbar", "call.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL  },
+#endif
+
 	{ PIDGIN_STOCK_TRAY_AVAILABLE, "tray", "tray-online.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TRAY_INVISIBLE, "tray", "tray-invisible.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
 	{ PIDGIN_STOCK_TRAY_AWAY, "tray", "tray-away.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL  },
@@ -208,39 +212,39 @@
 
 	filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, file, NULL);
 	source = gtk_icon_source_new();
-        gtk_icon_source_set_filename(source, filename);
+	gtk_icon_source_set_filename(source, filename);
 	gtk_icon_source_set_direction(source, GTK_TEXT_DIR_LTR);
-        gtk_icon_source_set_direction_wildcarded(source, !rtl);
+	gtk_icon_source_set_direction_wildcarded(source, !rtl);
 	gtk_icon_source_set_size(source, sizeid);
-        gtk_icon_source_set_size_wildcarded(source, FALSE);
-        gtk_icon_source_set_state_wildcarded(source, TRUE);
-        gtk_icon_set_add_source(iconset, source);
+	gtk_icon_source_set_size_wildcarded(source, FALSE);
+	gtk_icon_source_set_state_wildcarded(source, TRUE);
+	gtk_icon_set_add_source(iconset, source);
 	gtk_icon_source_free(source);
 
 	if (sizeid == gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)) {
 		source = gtk_icon_source_new();
-	        gtk_icon_source_set_filename(source, filename);
-        	gtk_icon_source_set_direction_wildcarded(source, TRUE);
-	        gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU);
-	        gtk_icon_source_set_size_wildcarded(source, FALSE);
-        	gtk_icon_source_set_state_wildcarded(source, TRUE);
-	        gtk_icon_set_add_source(iconset, source);
-	        gtk_icon_source_free(source);
+		gtk_icon_source_set_filename(source, filename);
+		gtk_icon_source_set_direction_wildcarded(source, TRUE);
+		gtk_icon_source_set_size(source, GTK_ICON_SIZE_MENU);
+		gtk_icon_source_set_size_wildcarded(source, FALSE);
+		gtk_icon_source_set_state_wildcarded(source, TRUE);
+		gtk_icon_set_add_source(iconset, source);
+		gtk_icon_source_free(source);
 	}
-        g_free(filename);
+	g_free(filename);
 
-       if (rtl) {
+	if (rtl) {
 		filename = g_build_filename(DATADIR, "pixmaps", "pidgin", dir, size, "rtl", file, NULL);
-                source = gtk_icon_source_new();
-                gtk_icon_source_set_filename(source, filename);
-                gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL);
-                gtk_icon_source_set_size(source, sizeid);
-                gtk_icon_source_set_size_wildcarded(source, FALSE);
-                gtk_icon_source_set_state_wildcarded(source, TRUE);
-                gtk_icon_set_add_source(iconset, source);
+		source = gtk_icon_source_new();
+		gtk_icon_source_set_filename(source, filename);
+		gtk_icon_source_set_direction(source, GTK_TEXT_DIR_RTL);
+		gtk_icon_source_set_size(source, sizeid);
+		gtk_icon_source_set_size_wildcarded(source, FALSE);
+		gtk_icon_source_set_state_wildcarded(source, TRUE);
+		gtk_icon_set_add_source(iconset, source);
 		g_free(filename);
 		gtk_icon_source_free(source);
-        }
+	}
 
 
 }
--- a/pidgin/pidginstock.h	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/pidginstock.h	Sat Mar 22 04:45:46 2008 +0000
@@ -129,6 +129,9 @@
 #define PIDGIN_STOCK_TOOLBAR_UNBLOCK      "pidgin-unblock"
 #define PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR "pidgin-select-avatar"
 #define PIDGIN_STOCK_TOOLBAR_SEND_FILE    "pidgin-send-file"
+#ifdef USE_FARSIGHT
+#define PIDGIN_STOCK_TOOLBAR_CALL			"pidgin-call"
+#endif
 
 /* Tray icons */
 #define PIDGIN_STOCK_TRAY_AVAILABLE       "pidgin-tray-available"
--- a/pidgin/plugins/cap/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/plugins/cap/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -15,7 +15,7 @@
 
 endif
 
-cap_la_LIBADD = $(GTK_LIBS) $(SQLITE3_LIBS)
+cap_la_LIBADD = $(GTK_LIBS) $(SQLITE3_LIBS) $(FARSIGHT_LIBS) $(GSTPROPS_LIBS)
 
 AM_CPPFLAGS = \
 	-DDATADIR=\"$(datadir)\" \
@@ -24,6 +24,8 @@
 	-I$(top_srcdir)/pidgin \
 	$(DEBUG_CFLAGS) \
 	$(GTK_CFLAGS) \
+	$(FARSIGHT_CFLAGS) \
+	$(GSTPROPS_CFLAGS) \
 	$(SQLITE3_CFLAGS)
 
 EXTRA_DIST = Makefile.mingw
--- a/pidgin/plugins/gevolution/Makefile.am	Tue Mar 18 05:40:29 2008 +0000
+++ b/pidgin/plugins/gevolution/Makefile.am	Sat Mar 22 04:45:46 2008 +0000
@@ -26,4 +26,5 @@
 	-I$(top_srcdir)/pidgin \
 	$(EVOLUTION_ADDRESSBOOK_CFLAGS) \
 	$(DEBUG_CFLAGS) \
-	$(GTK_CFLAGS)
+	$(GTK_CFLAGS) \
+	$(FARSIGHT_CFLAGS)