pidgin.vv.ticket34: afe2ae57: Patch from Marcus Lundblad ('mlundblad')...

sadrul at pidgin.im sadrul at pidgin.im
Sat Mar 22 00:50:52 EDT 2008


-----------------------------------------------------------------
Revision: afe2ae57083c4e8da50615930fcb9adca74cb908
Ancestor: fe86c6a90c315dcfb846cb23f33116179bd47285
Author: sadrul at pidgin.im
Date: 2008-03-22T04:45:46
Branch: im.pidgin.pidgin.vv.ticket34
URL: http://d.pidgin.im/viewmtn/revision/info/afe2ae57083c4e8da50615930fcb9adca74cb908

Modified 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

ChangeLog: 

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.


ChangeLog: 

References #34.
-------------- next part --------------
============================================================
--- configure.ac	96d66a940e2592c486fe1f14198036039c4199b2
+++ configure.ac	c65bfa5e1b9b90e7eff1b53e9b40889762148be2
@@ -47,7 +47,7 @@ m4_define([purple_micro_version], [1])
 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,7 +696,30 @@ fi
 	])
 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 #######################################################################
 PKG_CHECK_MODULES(MEANWHILE, [meanwhile >= 1.0.0 meanwhile < 2.0.0], [
============================================================
--- libpurple/Makefile.am	bf9311c15a6aa49ada6f8f0c4f7fe750cc1bd1e3
+++ libpurple/Makefile.am	dc287c22222942e1a6d45a187a32e870856477a6
@@ -251,6 +251,8 @@ libpurple_la_LIBADD = \
 	$(LIBNM_LIBS) \
 	$(INTLLIBS) \
 	$(FARSIGHT_LIBS) \
+	$(GSTREAMER_LIBS) \
+	$(GSTPROPS_LIBS) \
 	-lm
 
 AM_CPPFLAGS = \
@@ -264,4 +266,6 @@ AM_CPPFLAGS = \
 	$(DBUS_CFLAGS) \
 	$(LIBXML_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(GSTPROPS_CFLAGS) \
 	$(LIBNM_CFLAGS)
============================================================
--- libpurple/media.c	571d7577bb68237c18d5f3ea8ce1b20d65ec1f6f
+++ libpurple/media.c	1d1da79dd93657152fe1552477c84d328cc938dd
@@ -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 @@ enum {
 	HANGUP,
 	REJECT,
 	GOT_HANGUP,
+	GOT_ACCEPT,
 	LAST_SIGNAL
 };
 static guint purple_media_signals[LAST_SIGNAL] = {0};
@@ -200,6 +205,10 @@ purple_media_class_init (PurpleMediaClas
 					 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)
 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 @@ purple_media_set_property (GObject *obje
 {
 	PurpleMedia *media;
 	g_return_if_fail(PURPLE_IS_MEDIA(object));
-	
+
 	media = PURPLE_MEDIA(object);
 
 	switch (prop_id) {
@@ -455,4 +464,161 @@ purple_media_got_hangup(PurpleMedia *med
 	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 */
============================================================
--- libpurple/media.h	963cdfd65958a5a868ea2e8d9f2ec324d193f2c5
+++ libpurple/media.h	f122be5e7842ecc46526ff7118feeb3ad6f6b610
@@ -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_got_hangup(PurpleMedia
 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 */
 
 
============================================================
--- libpurple/plugins/Makefile.am	71467dc08e458b207882910bfcd1b9b145511606
+++ libpurple/plugins/Makefile.am	6a089965e167f036ab7129b4982e9c05efaca70b
@@ -141,6 +141,8 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/libpurple \
 	$(DEBUG_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(PLUGIN_CFLAGS) \
 	$(DBUS_CFLAGS)
============================================================
--- libpurple/plugins/ssl/Makefile.am	ee65d38a207f7798f08404c448efde97ee4aba65
+++ libpurple/plugins/ssl/Makefile.am	7eb7a00005dc3a57cf02c86462d32fa3d62380ac
@@ -32,6 +32,8 @@ AM_CPPFLAGS = \
 	$(DEBUG_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS) \
 	$(PLUGIN_CFLAGS)
 
 ssl_gnutls_la_CFLAGS = $(AM_CPPFLAGS) $(GNUTLS_CFLAGS)
============================================================
--- libpurple/protocols/bonjour/Makefile.am	ea2050c82d974d92cfe9a89c862f870d75af1858
+++ libpurple/protocols/bonjour/Makefile.am	93f0895e687a0314dc011f82e43bce8e456f71f6
@@ -51,10 +51,12 @@ AM_CPPFLAGS = \
 	$(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
 
============================================================
--- libpurple/protocols/bonjour/bonjour.c	aa974a2180850546b3b57840cdb40318c6806302
+++ libpurple/protocols/bonjour/bonjour.c	71b2c9aa9ca5a81177e11e6fb17517611f843666
@@ -486,7 +486,9 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,                                                    /* send_attention */
 	NULL,                                                    /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),                        /* struct_size */
-	NULL                                                     /* initiate_media */
+	NULL,                                                     /* initiate_media */
+	NULL                                                     /* can_do_media */
+
 };
 
 static PurplePluginInfo info =
============================================================
--- libpurple/protocols/gg/Makefile.am	2b4a015581bf5cf6b524538a2aabfba9013425a6
+++ libpurple/protocols/gg/Makefile.am	6941dd9779cbb4e0d1b9e80ff8b3ec7ed43ea646
@@ -76,5 +76,7 @@ AM_CPPFLAGS = \
 	$(INTGG_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS) \
 	$(DEBUG_CFLAGS)
 
============================================================
--- libpurple/protocols/gg/gg.c	14d6777ca0154d54810524df0b713c6d383114ef
+++ libpurple/protocols/gg/gg.c	fa305dd2d15831a5fbac4a2343ff32261ae6065a
@@ -2151,7 +2151,8 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,				/* send_attention */
 	NULL,				/* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL				/* initiate_media */
+	NULL,				/* initiate_media */
+	NULL                /* can_do_media */
 };
 /* }}} */
 
============================================================
--- libpurple/protocols/irc/Makefile.am	f553b66d9cde1ca6dc3b94d3a0a6f9c54e49542f
+++ libpurple/protocols/irc/Makefile.am	b02546ce946f6bbaca2e78bfec550d04db9cfd8a
@@ -33,4 +33,5 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
 	$(DEBUG_CFLAGS)
============================================================
--- libpurple/protocols/irc/irc.c	b673ddac3236b77623cf6ba24bae593e1e1a34a9
+++ libpurple/protocols/irc/irc.c	40bf15d39056a0f517068880ab4a5b3daa2862cf
@@ -904,7 +904,8 @@ static PurplePluginProtocolInfo prpl_inf
 	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) {
============================================================
--- libpurple/protocols/jabber/Makefile.am	febb7a33d1c2027f8495cefd4880871b19e2a6a6
+++ libpurple/protocols/jabber/Makefile.am	694f33c99e46111ffcbbf5d1212119a86d16e66f
@@ -19,6 +19,8 @@ JABBERSOURCES = auth.c \
 			  iq.h \
 			  jabber.c \
 			  jabber.h \
+			  jingle.c \
+			  jingle.h \
 			  jutil.c \
 			  jutil.h \
 			  message.c \
@@ -69,7 +71,7 @@ noinst_LIBRARIES =
 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 @@ AM_CPPFLAGS = \
 	$(DEBUG_CFLAGS) \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
 	$(LIBXML_CFLAGS)
============================================================
--- libpurple/protocols/jabber/caps.c	ce2b1c9933e7c617f55ff2da3801ada27fcb0c36
+++ libpurple/protocols/jabber/caps.c	fea407bed717c7db1ce7a9c3d1aa4208444b49ca
@@ -27,6 +27,7 @@
 #include "util.h"
 #include "iq.h"
 
+
 #define JABBER_CAPS_FILENAME "xmpp-caps.xml"
 
 static GHashTable *capstable = NULL; /* JabberCapsKey -> JabberCapsValue */
============================================================
--- libpurple/protocols/jabber/disco.c	616a54e0de1eaae7b5890a5e55cce105bb97cb4a
+++ libpurple/protocols/jabber/disco.c	ecd502cc546493ca78133dec6984e269c4ef4497
@@ -79,7 +79,7 @@ void jabber_disco_info_parse(JabberStrea
 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 @@ void jabber_disco_info_parse(JabberStrea
 
 		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");
============================================================
--- libpurple/protocols/jabber/google.c	1154111c0f0befc427181497a37fddf05ff48fbc
+++ libpurple/protocols/jabber/google.c	54db4f757cb3e95ab92f7a957e38a0a72a9e232c
@@ -214,8 +214,7 @@ google_session_handle_initiate(JabberStr
 	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 @@ google_session_handle_initiate(JabberStr
 		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 @@ google_session_handle_candidates(JabberS
 	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++);
============================================================
--- libpurple/protocols/jabber/iq.c	97ef3bbcdcec47b45b1d18e6b42d37180332ceb0
+++ libpurple/protocols/jabber/iq.c	4059e5a51fa4935d0724d12c526a52d3e71592b7
@@ -313,7 +313,7 @@ void jabber_iq_parse(JabberStream *js, x
 	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,13 +353,38 @@ void jabber_iq_parse(JabberStream *js, x
 		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"))) {
 		JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
============================================================
--- libpurple/protocols/jabber/jabber.c	4e2a4ca9372d78397a19b819dd0de28ef5e7a2f3
+++ libpurple/protocols/jabber/jabber.c	10a29a007f2ffc19c1f97f94ea2cee98b10e0843
@@ -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 @@ gboolean jabber_offline_message(const Pu
 }
 
 #ifdef USE_FARSIGHT
-PurpleMedia *jabber_media_initiate(PurpleConnection *gc, const char *who, PurpleMediaStreamType type)
+
+static void
+jabber_session_send_accept(JingleSession *session)
 {
-	return NULL;
+	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)
+{
+	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 @@ jabber_init_plugin(PurplePlugin *plugin)
 void
 jabber_init_plugin(PurplePlugin *plugin)
 {
-        my_protocol = plugin;
+	my_protocol = plugin;
 }
============================================================
--- libpurple/protocols/jabber/jabber.h	1c369a555c71ed5ae5c4f6ff18dca1ab5f60894f
+++ libpurple/protocols/jabber/jabber.h	ef7cdb8b0be153ca1ab7c64adb5779d736a0c452
@@ -56,6 +56,7 @@ typedef struct _JabberStream JabberStrea
 #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 *pl
 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_ */
============================================================
--- libpurple/protocols/jabber/libxmpp.c	b85bc457c82c72c2939951327b41db363ada9e88
+++ libpurple/protocols/jabber/libxmpp.c	eff2ce9c67ea6a259f51981f1023d772e89625bd
@@ -116,7 +116,8 @@ static PurplePluginProtocolInfo prpl_inf
 	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)
============================================================
--- libpurple/protocols/msn/Makefile.am	d0150c39267fd432caddf066460a1aa1fb39337a
+++ libpurple/protocols/msn/Makefile.am	0f5950196d45210acf620addec30da90fc481ab9
@@ -97,3 +97,5 @@ AM_CPPFLAGS = \
 	$(GLIB_CFLAGS) \
 	$(DEBUG_CFLAGS) \
 	$(FARSIGHT_CFLAGS)
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
============================================================
--- libpurple/protocols/msn/msn.c	d5e87529bc62a0a823b528b7e2a8d5b74d4643b3
+++ libpurple/protocols/msn/msn.c	a4f81d5a7ab5e199e516926d0b9b34ea023a1e81
@@ -2293,7 +2293,8 @@ static PurplePluginProtocolInfo prpl_inf
 	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 =
============================================================
--- libpurple/protocols/msnp9/Makefile.am	ead6d50993bae445b879740fcbf03116a71a9524
+++ libpurple/protocols/msnp9/Makefile.am	7cc100a4b124fe3ad532a65289a592326952f7aa
@@ -88,5 +88,7 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(DEBUG_CFLAGS) \
-	$(FARSIGHT_CFLAGS)
+	$(FARSIGHT_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
 
============================================================
--- libpurple/protocols/msnp9/msn.c	927ee9fc39677412c60c2ae3c441638a148fb580
+++ libpurple/protocols/msnp9/msn.c	bbc236f1bfb12d7fc1fe5cecf52eced97c135062
@@ -2148,7 +2148,8 @@ static PurplePluginProtocolInfo prpl_inf
 	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 =
============================================================
--- libpurple/protocols/myspace/Makefile.am	a61272305f1bfb2f6c10cbf61dd3a1587febddb9
+++ libpurple/protocols/myspace/Makefile.am	cdc445d72892a2382c4f499d67e6753b49d1051f
@@ -41,4 +41,6 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
-	$(DEBUG_CFLAGS)
============================================================
--- libpurple/protocols/myspace/myspace.c	2ac7328aca326ed8663d589a33731dc37ad0816f
+++ libpurple/protocols/myspace/myspace.c	2cecbb75ac260517f519d7fdedab7407ce4b3162
@@ -3125,7 +3125,8 @@ static PurplePluginProtocolInfo prpl_inf
 	msim_send_attention,   /* send_attention */
 	msim_attention_types,  /* attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL                   /* initiate_media */
+	NULL,                   /* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 
============================================================
--- libpurple/protocols/novell/Makefile.am	52275ef0693a378e00f3da85d21ce2568fb7f3ae
+++ libpurple/protocols/novell/Makefile.am	a4863b4941b2c91fd0c2ab695824bdd22e3da0dd
@@ -55,4 +55,7 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/libpurple \
 	$(DEBUG_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
-	$(GLIB_CFLAGS)
============================================================
--- libpurple/protocols/novell/novell.c	f5330d848673adca6bfd5e8b43534a287c433204
+++ libpurple/protocols/novell/novell.c	439b5070645b20cec9193a625cce824217da0391
@@ -3516,7 +3516,8 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,						/* send_attention */
 	NULL,						/* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL						/* initiate_media */
+	NULL,						/* initiate_media */
+	NULL                        /* can_do_media */
 };
 
 static PurplePluginInfo info = {
============================================================
--- libpurple/protocols/null/nullprpl.c	06ee6959fa4dc0f8cd953f221264bdd1114035fb
+++ libpurple/protocols/null/nullprpl.c	9fdde013da5500797c28f36140cdd229e26005ae
@@ -1126,7 +1126,8 @@ static PurplePluginProtocolInfo prpl_inf
   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)
============================================================
--- libpurple/protocols/oscar/Makefile.am	3dc744e59284b9c0d753237c89799db02a452015
+++ libpurple/protocols/oscar/Makefile.am	27f0808081ab3449cc7b26ad437a0abdf0e4c9b9
@@ -77,4 +77,7 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
-	$(DEBUG_CFLAGS)
============================================================
--- libpurple/protocols/oscar/libaim.c	9d6425fbbe83324b519e76946f7d9164fb938517
+++ libpurple/protocols/oscar/libaim.c	a7ecd19def028eb08efb11e0fc02a116c546c237
@@ -96,7 +96,8 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,                   /* send_attention */
 	NULL,                   /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),    /* struct_size */
-	NULL                    /* initiate_media */
+	NULL,                    /* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 static PurplePluginInfo info =
============================================================
--- libpurple/protocols/oscar/libicq.c	0cbf976bd5d2aed5e40ad88d262fbf07c05eb300
+++ libpurple/protocols/oscar/libicq.c	a8d1609d29ace6672ad2300d380d652d8f29ba17
@@ -96,7 +96,8 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,                   /* send_attention */
 	NULL,                   /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),    /* struct_size */
-	NULL                    /* initiate_media */
+	NULL,                    /* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 static PurplePluginInfo info =
============================================================
--- libpurple/protocols/qq/Makefile.am	3790f0734ef7a073208d572fb5f61fc5112b0cf8
+++ libpurple/protocols/qq/Makefile.am	f6d2504d108b2ba1a2957dd96a1a46165c4a41bc
@@ -95,4 +95,7 @@ AM_CPPFLAGS = \
 	-DQQ_BUDDY_ICON_DIR=\"$(datadir)/pixmaps/purple/buddy_icons/qq\" \
 	$(DEBUG_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(GLIB_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
-	$(GLIB_CFLAGS)
============================================================
--- libpurple/protocols/qq/qq.c	3ce874dc91a567cbdb20973d4e00350bc5305d89
+++ libpurple/protocols/qq/qq.c	d263f6400459ae465ba5237f658c75173a34a41a
@@ -705,7 +705,8 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,							/* send_attention */
 	NULL,							/* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL							/* initiate_media */
+	NULL,							/* initiate_media */
+	NULL                            /* can_do_media */
 };
 
 static PurplePluginInfo info = {
============================================================
--- libpurple/protocols/sametime/sametime.c	fd60a89a040b4180c723fded79cde8843c839014
+++ libpurple/protocols/sametime/sametime.c	45ab1f57d5e8035afcc83476e6fe9a64f6c245c7
@@ -5188,7 +5188,7 @@ static PurplePluginProtocolInfo mw_prpl_
   .offline_message           = NULL,
   .whiteboard_prpl_ops       = NULL,
   .send_raw                  = NULL,
-  .struct_size               = sizeof(PurplePluginProtocolInfo)
+  .struct_size               = sizeof(PurplePluginProtocolInfo)		
 };
 
 
============================================================
--- libpurple/protocols/silc/Makefile.am	89211bf5147a1f69481276ec795a89058de7143c
+++ libpurple/protocols/silc/Makefile.am	9951c582806329979b5200c5434c3edb96a5d245
@@ -34,4 +34,7 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
-	$(DEBUG_CFLAGS)
============================================================
--- libpurple/protocols/silc/silc.c	86f482122d0192483567bc52cab01cb37c99e9ce
+++ libpurple/protocols/silc/silc.c	60e56074600edce98f631b5de88968e4c23f8983
@@ -1997,7 +1997,8 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,				        /* send_attention */
 	NULL,				        /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo),       /* struct_size */
-	NULL				        /* initiate_media */
+	NULL,				        /* initiate_media */
+	NULL                        /* can_do_media */
 };
 
 static PurplePluginInfo info =
============================================================
--- libpurple/protocols/silc10/silc.c	76c41333a83c116e5c7ac053288ac340ac51c10f
+++ libpurple/protocols/silc10/silc.c	3c2c68e3974170d0515efd12e79ab9723b48faee
@@ -1803,7 +1803,8 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,                   /* send_attention */
 	NULL,                   /* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL                    /* initiate_media */
+	NULL,                    /* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 static PurplePluginInfo info =
============================================================
--- libpurple/protocols/simple/Makefile.am	8c943cc987b3c61b1ec4aa966c1eac0eba852851
+++ libpurple/protocols/simple/Makefile.am	7bd7a48c811a8c086fe32b51d1d4e4ea63ff23bb
@@ -34,4 +34,7 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
-	$(DEBUG_CFLAGS)
============================================================
--- libpurple/protocols/simple/simple.c	aedb85a6037958c8f5df061d94169820ada0a20e
+++ libpurple/protocols/simple/simple.c	de89ca4a92d0c5ae06220dac31d6549ad101bf11
@@ -2051,7 +2051,8 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,					/* send_attention */
 	NULL,					/* get_attention_types */
 	sizeof(PurplePluginProtocolInfo), /* struct_size */
-	NULL					/* initiate_media */
+	NULL,					/* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 
============================================================
--- libpurple/protocols/yahoo/Makefile.am	bcfc90fd3dc215e7df8f5279c192bf18a7604e43
+++ libpurple/protocols/yahoo/Makefile.am	aa64cac5f3e0dbcceb1f7288a0220c6ce515575d
@@ -54,4 +54,7 @@ AM_CPPFLAGS = \
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
-	$(DEBUG_CFLAGS)
============================================================
--- libpurple/protocols/yahoo/yahoo.c	f0d148718990560f46f77b6eac116ea4367c4898
+++ libpurple/protocols/yahoo/yahoo.c	071e23c54150fe1319ceb9347ad00ae01a3717ce
@@ -4378,7 +4378,8 @@ static PurplePluginProtocolInfo prpl_inf
 	yahoo_send_attention,
 	yahoo_attention_types,
 	sizeof(PurplePluginProtocolInfo),
-	NULL
+	NULL,					/* initiate_media */
+	NULL                    /* can_do_media */
 };
 
 static PurplePluginInfo info =
============================================================
--- libpurple/protocols/zephyr/Makefile.am	85f6052793717093f3b2881a618db19882f36ce3
+++ libpurple/protocols/zephyr/Makefile.am	360cfab1176b75613c8f84904b9382b762e76afb
@@ -108,4 +108,7 @@ AM_CPPFLAGS = \
 	$(GLIB_CFLAGS) \
 	$(FARSIGHT_CFLAGS) \
 	$(KRB4_CFLAGS) \
+	$(DEBUG_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
+	$(LIBXML_CFLAGS)
+
-	$(DEBUG_CFLAGS)
============================================================
--- libpurple/protocols/zephyr/zephyr.c	0ea2ba288e5e587b0ec0a2c23023292a3e5375bb
+++ libpurple/protocols/zephyr/zephyr.c	92be9c983619c32042b4fa2604f3fec853b5a5fb
@@ -2911,7 +2911,9 @@ static PurplePluginProtocolInfo prpl_inf
 	NULL,
 	NULL,
 	NULL,
-        sizeof(PurplePluginProtocolInfo)
+        sizeof(PurplePluginProtocolInfo),
+	NULL,					/* initate_media */
+	NULL					/* can_do_media */
 };
 
 static PurplePluginInfo info = {
============================================================
--- libpurple/prpl.h	2bace58a9cd6efd2737ad006e4a8a93ef471cac2
+++ libpurple/prpl.h	58f91651af5680998e0bf906095f973c8293d149
@@ -403,9 +403,21 @@ struct _PurplePluginProtocolInfo
 	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
 };
 
============================================================
--- libpurple/server.c	2f84d197fbaa0bd1e623a3abb426260c3246df4c
+++ libpurple/server.c	1c73e257f3e79c482f9b1b9a1d9f12329c789e16
@@ -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 @@ void serv_send_file(PurpleConnection *gc
 		}
 	}
 }
+
+#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
============================================================
--- libpurple/server.h	28c75b20775231ef3c25d445bcf7a748bde04ae5
+++ libpurple/server.h	c317bb0acf066116d94d5a03b4fc4a6e252f7262
@@ -29,6 +29,7 @@
 #include "account.h"
 #include "conversation.h"
 #include "prpl.h"
+#include "media.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -171,6 +172,17 @@ void serv_send_file(PurpleConnection *gc
 					  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
============================================================
--- pidgin/Makefile.am	5948d1a1edf11ad92364bdd52ff078009ed0c5f8
+++ pidgin/Makefile.am	3aa85015abc971439bb2e1aede779993ec03b2f6
@@ -197,6 +197,7 @@ pidgin_LDADD = \
 	$(LIBXML_LIBS) \
 	$(GTK_LIBS) \
 	$(FARSIGHT_LIBS) \
+	$(GSTPROPS_LIBS) \
 	$(top_builddir)/libpurple/libpurple.la
 
 if USE_INTERNAL_LIBGADU
@@ -222,5 +223,6 @@ AM_CPPFLAGS = \
 	$(STARTUP_NOTIFICATION_CFLAGS) \
 	$(LIBXML_CFLAGS) \
 	$(INTGG_CFLAGS) \
-	$(FARSIGHT_CFLAGS) 
+	$(FARSIGHT_CFLAGS) \
+	$(GSTPROPS_CFLAGS)
 endif  # ENABLE_GTK
============================================================
--- pidgin/gtkconv.c	8eff8814ad576b1b7c1e60f7395d1ee59a3d4351
+++ pidgin/gtkconv.c	2aee2f05ec5f6bd773ea8b07ff79c598206ea23d
@@ -1172,6 +1172,14 @@ menu_find_cb(gpointer data, guint action
 	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 @@ static GtkItemFactoryEntry menu_items[] 
 
 	{ "/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 @@ setup_menubar(PidginWindow *win)
 		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 @@ gray_stuff_out(PidginConversation *gtkco
 		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 @@ pidgin_conv_update_buddy_icon(PurpleConv
 	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 @@ gboolean pidgin_conv_attach_to_conversat
 	return TRUE;
 }
 
+
+#ifdef USE_FARSIGHT
+
 static void
 pidgin_gtkmedia_message_cb(PidginMedia *media, const char *msg, PurpleConversation *conv)
 {
@@ -7582,8 +7626,43 @@ static void
 }
 
 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 @@ pidgin_conv_new_media_cb(PurpleMediaMana
 	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);
+	purple_media_audio_init_src(&sendbin, &sendlevel);
+	purple_media_audio_init_recv(&recvbin, &recvlevel);
 
-	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_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)
 {
============================================================
--- pidgin/gtkconvwin.h	250c2499fcc142e466d0695dc27e203f56ff9156
+++ pidgin/gtkconvwin.h	114f5a7abe58724574a5f55fcb8da4b6e129c9cc
@@ -50,6 +50,7 @@ struct _PidginWindow
 
 		GtkWidget *view_log;
 
+		GtkWidget *call;
 		GtkWidget *send_file;
 		GtkWidget *add_pounce;
 		GtkWidget *get_info;
============================================================
--- pidgin/gtkimhtml.h	92ad0cd035ed124fdc45d3d9b6a6043048c3dd25
+++ pidgin/gtkimhtml.h	e42b16570dec986fa8d12e5a00e05b979116fea2
@@ -76,6 +76,7 @@ typedef enum {
 	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;
 
============================================================
--- pidgin/gtkimhtmltoolbar.c	e21be01eb28ab2c96012516d71ad3558dab32185
+++ pidgin/gtkimhtmltoolbar.c	dde0d5fc06547f24b8a3380a90520c60ba411111
@@ -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 *widg
 
 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 @@ insert_smiley_cb(GtkWidget *smiley, GtkI
 	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 @@ static void update_buttons_cb(GtkIMHtml 
 	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 @@ static void gtk_imhtmltoolbar_create_old
 		{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 @@ static void gtk_imhtmltoolbar_init (GtkI
 	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 @@ static void gtk_imhtmltoolbar_init (GtkI
 	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);
============================================================
--- pidgin/gtkimhtmltoolbar.h	9bb8b93a7f7eacb6a58b6f4c1c255a2b2cf2f215
+++ pidgin/gtkimhtmltoolbar.h	9914dcf4c5a863e8baa4e8087cda638abd3ecf4b
@@ -76,6 +76,7 @@ struct _GtkIMHtmlToolbar {
 	char *sml;
 	GtkWidget *strikethrough;
 	GtkWidget *insert_hr;
+	GtkWidget *call;
 };
 
 struct _GtkIMHtmlToolbarClass {
============================================================
--- pidgin/gtkmedia.c	8eafb4d22e4ef00e77e7f4959734e37910d1129d
+++ pidgin/gtkmedia.c	ea8e54991d85383ff6c34fd65dd66f5147a5ddd4
@@ -40,12 +40,15 @@ struct _PidginMediaPrivate
 	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 @@ pidgin_media_class_init (PidginMediaClas
 	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 @@ pidgin_media_class_init (PidginMediaClas
 			"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)
 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 @@ pidgin_media_init (PidginMedia *media)
 
 	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 @@ pidgin_media_ready_cb(PurpleMedia *media
 	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 @@ pidgin_media_set_property (GObject *obje
 				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 @@ pidgin_media_set_property (GObject *obje
 			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 @@ pidgin_media_get_property (GObject *obje
 {
 	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 @@ pidgin_media_get_property (GObject *obje
 		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)
 {
-	return GTK_WIDGET(g_object_new(pidgin_media_get_type(), "media", media, "send-level", sendlevel, "recv-level", recvlevel, NULL));
+	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)
+{
+	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 */
============================================================
--- pidgin/gtkmedia.h	24dd2de18b5da816f5423b8326b9391c8be7f352
+++ pidgin/gtkmedia.h	6d53aae98f518afe254988b00345abc23adea610
@@ -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 _PidginMediaPrivate Pidgi
 typedef struct _PidginMedia PidginMedia;
 typedef struct _PidginMediaClass PidginMediaClass;
 typedef struct _PidginMediaPrivate PidginMediaPrivate;
+typedef enum _PidginMediaState PidginMediaState;
 
 struct _PidginMediaClass
 {
@@ -62,10 +59,26 @@ struct _PidginMedia
 	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
 
 #endif  /* USE_FARSIGHT */
============================================================
--- pidgin/gtkprefs.c	9e5b577d41296d852cc46d3cc26232a4d09b9e1d
+++ pidgin/gtkprefs.c	9a3650562cf429fb3814c4a9f3006d939026f1cb
@@ -145,12 +145,10 @@ dropdown_set(GObject *w, const char *key
 
 	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 @@ interface_page(void)
 					_("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 @@ interface_page(void)
 					_("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 @@ interface_page(void)
 #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,8 +1910,107 @@ sound_page(void)
 	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)
 {
 	purple_prefs_set_int("/purple/savedstatus/idleaway", purple_savedstatus_get_creation_time(status));
@@ -2037,6 +2134,10 @@ static void prefs_notebook_init(void) {
 	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 @@ pidgin_prefs_init(void)
 	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();
 }
 
============================================================
--- pidgin/gtkprefs.h	a94d912724df09de10791e143098d4d01591bd85
+++ pidgin/gtkprefs.h	8c8cb77cb9834b0f2457d0133fd9b9a4f1043916
@@ -29,6 +29,7 @@
 
 #include "prefs.h"
 
+
 /**
  * Initializes all UI-specific preferences.
  */
============================================================
--- pidgin/pidginstock.c	284a74fc79e3afe410fc8f73e459eeba6e6a7dda
+++ pidgin/pidginstock.c	463dbde8771d215a9e4565702290b448e4f3a4ed
@@ -169,6 +169,10 @@ static struct SizedStockIcon {
 	{ 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 @@ add_sized_icon(GtkIconSet *iconset, GtkI
 
 	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);
-        }
+	}
 
 
 }
============================================================
--- pidgin/pidginstock.h	cebcad7166aead1bf5a44b5ad7d2f40ebda98618
+++ pidgin/pidginstock.h	079c5d0f0ae0319e51280f52436ee1f05df404b2
@@ -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"
============================================================
--- pidgin/plugins/cap/Makefile.am	8004973d47c7666743fceb77c21c2e72c127479d
+++ pidgin/plugins/cap/Makefile.am	1871913f31f1270e3012ea36699cf445b1b9f2af
@@ -15,7 +15,7 @@ endif
 
 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 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/pidgin \
 	$(DEBUG_CFLAGS) \
 	$(GTK_CFLAGS) \
+	$(FARSIGHT_CFLAGS) \
+	$(GSTPROPS_CFLAGS) \
 	$(SQLITE3_CFLAGS)
 
 EXTRA_DIST = Makefile.mingw
============================================================
--- pidgin/plugins/gevolution/Makefile.am	3f94db301e306cb70e7e3caafb3a9ba261061ad0
+++ pidgin/plugins/gevolution/Makefile.am	e71be3502ef9d109d88e77b59eb958a23b8ac7ab
@@ -26,4 +26,5 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/pidgin \
 	$(EVOLUTION_ADDRESSBOOK_CFLAGS) \
 	$(DEBUG_CFLAGS) \
+	$(GTK_CFLAGS) \
+	$(FARSIGHT_CFLAGS)
-	$(GTK_CFLAGS)


More information about the Commits mailing list